|  | // 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 = { | 
|  | .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"); |