diff options
Diffstat (limited to 'drivers/usb/serial/wishbone-serial.c')
-rw-r--r-- | drivers/usb/serial/wishbone-serial.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c new file mode 100644 index 0000000000..ff4092f9b3 --- /dev/null +++ b/drivers/usb/serial/wishbone-serial.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * USB Wishbone-Serial adapter driver + * + * Copyright (C) 2013 Wesley W. Terpstra <w.terpstra@gsi.de> + * Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH + */ + +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <linux/uaccess.h> + +#define GSI_VENDOR_OPENCLOSE 0xB0 + +static const struct usb_device_id id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x1D50, 0x6062, 0xFF, 0xFF, 0xFF) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Etherbone must be told that a new stream has begun before data arrives. + * This is necessary to restart the negotiation of Wishbone bus parameters. + * Similarly, when the stream ends, Etherbone must be told so that the cycle + * line can be driven low in the case that userspace failed to do so. + */ +static int usb_gsi_openclose(struct usb_serial_port *port, int value) +{ + struct usb_device *dev = port->serial->dev; + + return usb_control_msg( + dev, + usb_sndctrlpipe(dev, 0), /* Send to EP0OUT */ + GSI_VENDOR_OPENCLOSE, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + value, /* wValue = device is open(1) or closed(0) */ + port->serial->interface->cur_altsetting->desc.bInterfaceNumber, + NULL, 0, /* There is no data stage */ + 5000); /* Timeout till operation fails */ +} + +static int wishbone_serial_open(struct tty_struct *tty, + struct usb_serial_port *port) +{ + int retval; + + retval = usb_gsi_openclose(port, 1); + if (retval) { + dev_err(&port->serial->dev->dev, + "Could not mark device as open (%d)\n", + retval); + return retval; + } + + retval = usb_serial_generic_open(tty, port); + if (retval) + usb_gsi_openclose(port, 0); + + return retval; +} + +static void wishbone_serial_close(struct usb_serial_port *port) +{ + usb_serial_generic_close(port); + usb_gsi_openclose(port, 0); +} + +static struct usb_serial_driver wishbone_serial_device = { + .driver = { + .owner = THIS_MODULE, + .name = "wishbone_serial", + }, + .id_table = id_table, + .num_ports = 1, + .open = &wishbone_serial_open, + .close = &wishbone_serial_close, +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &wishbone_serial_device, NULL +}; + +module_usb_serial_driver(serial_drivers, id_table); + +MODULE_AUTHOR("Wesley W. Terpstra <w.terpstra@gsi.de>"); +MODULE_DESCRIPTION("USB Wishbone-Serial adapter"); +MODULE_LICENSE("GPL"); |