|
@@ -14,7 +14,6 @@
|
|
|
* This software is distributed under the terms of the GNU General
|
|
|
* Public License ("GPL") as published by the Free Software Foundation,
|
|
|
* either version 2 of that License or (at your option) any later version.
|
|
|
- *
|
|
|
*/
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
@@ -41,7 +40,11 @@
|
|
|
#define GS_MAJOR 127
|
|
|
#define GS_MINOR_START 0
|
|
|
|
|
|
-#define GS_NUM_PORTS 16
|
|
|
+/* REVISIT only one port is supported for now;
|
|
|
+ * see gs_{send,recv}_packet() ... no multiplexing,
|
|
|
+ * and no support for multiple ACM devices.
|
|
|
+ */
|
|
|
+#define GS_NUM_PORTS 1
|
|
|
|
|
|
#define GS_NUM_CONFIGS 1
|
|
|
#define GS_NO_CONFIG_ID 0
|
|
@@ -65,6 +68,9 @@
|
|
|
|
|
|
#define GS_DEFAULT_USE_ACM 0
|
|
|
|
|
|
+/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults
|
|
|
+ * expected by "usbser.sys" on MS-Windows.
|
|
|
+ */
|
|
|
#define GS_DEFAULT_DTE_RATE 9600
|
|
|
#define GS_DEFAULT_DATA_BITS 8
|
|
|
#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY
|
|
@@ -107,10 +113,6 @@ static int debug = 1;
|
|
|
#define GS_NOTIFY_MAXPACKET 8
|
|
|
|
|
|
|
|
|
-/* Structures */
|
|
|
-
|
|
|
-struct gs_dev;
|
|
|
-
|
|
|
/* circular buffer */
|
|
|
struct gs_buf {
|
|
|
unsigned int buf_size;
|
|
@@ -164,26 +166,7 @@ struct gs_dev {
|
|
|
|
|
|
/* Functions */
|
|
|
|
|
|
-/* module */
|
|
|
-static int __init gs_module_init(void);
|
|
|
-static void __exit gs_module_exit(void);
|
|
|
-
|
|
|
-/* tty driver */
|
|
|
-static int gs_open(struct tty_struct *tty, struct file *file);
|
|
|
-static void gs_close(struct tty_struct *tty, struct file *file);
|
|
|
-static int gs_write(struct tty_struct *tty,
|
|
|
- const unsigned char *buf, int count);
|
|
|
-static int gs_put_char(struct tty_struct *tty, unsigned char ch);
|
|
|
-static void gs_flush_chars(struct tty_struct *tty);
|
|
|
-static int gs_write_room(struct tty_struct *tty);
|
|
|
-static int gs_chars_in_buffer(struct tty_struct *tty);
|
|
|
-static void gs_throttle(struct tty_struct * tty);
|
|
|
-static void gs_unthrottle(struct tty_struct * tty);
|
|
|
-static void gs_break(struct tty_struct *tty, int break_state);
|
|
|
-static int gs_ioctl(struct tty_struct *tty, struct file *file,
|
|
|
- unsigned int cmd, unsigned long arg);
|
|
|
-static void gs_set_termios(struct tty_struct *tty, struct ktermios *old);
|
|
|
-
|
|
|
+/* tty driver internals */
|
|
|
static int gs_send(struct gs_dev *dev);
|
|
|
static int gs_send_packet(struct gs_dev *dev, char *packet,
|
|
|
unsigned int size);
|
|
@@ -192,19 +175,7 @@ static int gs_recv_packet(struct gs_dev *dev, char *packet,
|
|
|
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req);
|
|
|
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req);
|
|
|
|
|
|
-/* gadget driver */
|
|
|
-static int gs_bind(struct usb_gadget *gadget);
|
|
|
-static void gs_unbind(struct usb_gadget *gadget);
|
|
|
-static int gs_setup(struct usb_gadget *gadget,
|
|
|
- const struct usb_ctrlrequest *ctrl);
|
|
|
-static int gs_setup_standard(struct usb_gadget *gadget,
|
|
|
- const struct usb_ctrlrequest *ctrl);
|
|
|
-static int gs_setup_class(struct usb_gadget *gadget,
|
|
|
- const struct usb_ctrlrequest *ctrl);
|
|
|
-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req);
|
|
|
-static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
|
|
|
- struct usb_request *req);
|
|
|
-static void gs_disconnect(struct usb_gadget *gadget);
|
|
|
+/* gadget driver internals */
|
|
|
static int gs_set_config(struct gs_dev *dev, unsigned config);
|
|
|
static void gs_reset_config(struct gs_dev *dev);
|
|
|
static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
|
|
@@ -232,9 +203,6 @@ static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf,
|
|
|
static unsigned int gs_buf_get(struct gs_buf *gb, char *buf,
|
|
|
unsigned int count);
|
|
|
|
|
|
-/* external functions */
|
|
|
-extern int net2280_set_fifo_mode(struct usb_gadget *gadget, int mode);
|
|
|
-
|
|
|
|
|
|
/* Globals */
|
|
|
|
|
@@ -246,48 +214,8 @@ static const char *EP_NOTIFY_NAME;
|
|
|
|
|
|
static struct mutex gs_open_close_lock[GS_NUM_PORTS];
|
|
|
|
|
|
-static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
|
|
|
-static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
|
|
|
-
|
|
|
-static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
|
|
|
-
|
|
|
-static unsigned int use_acm = GS_DEFAULT_USE_ACM;
|
|
|
-
|
|
|
-
|
|
|
-/* tty driver struct */
|
|
|
-static const struct tty_operations gs_tty_ops = {
|
|
|
- .open = gs_open,
|
|
|
- .close = gs_close,
|
|
|
- .write = gs_write,
|
|
|
- .put_char = gs_put_char,
|
|
|
- .flush_chars = gs_flush_chars,
|
|
|
- .write_room = gs_write_room,
|
|
|
- .ioctl = gs_ioctl,
|
|
|
- .set_termios = gs_set_termios,
|
|
|
- .throttle = gs_throttle,
|
|
|
- .unthrottle = gs_unthrottle,
|
|
|
- .break_ctl = gs_break,
|
|
|
- .chars_in_buffer = gs_chars_in_buffer,
|
|
|
-};
|
|
|
-static struct tty_driver *gs_tty_driver;
|
|
|
-
|
|
|
-/* gadget driver struct */
|
|
|
-static struct usb_gadget_driver gs_gadget_driver = {
|
|
|
-#ifdef CONFIG_USB_GADGET_DUALSPEED
|
|
|
- .speed = USB_SPEED_HIGH,
|
|
|
-#else
|
|
|
- .speed = USB_SPEED_FULL,
|
|
|
-#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
|
|
- .function = GS_LONG_NAME,
|
|
|
- .bind = gs_bind,
|
|
|
- .unbind = gs_unbind,
|
|
|
- .setup = gs_setup,
|
|
|
- .disconnect = gs_disconnect,
|
|
|
- .driver = {
|
|
|
- .name = GS_SHORT_NAME,
|
|
|
- },
|
|
|
-};
|
|
|
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
/* USB descriptors */
|
|
|
|
|
@@ -521,6 +449,8 @@ static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
|
|
|
};
|
|
|
|
|
|
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+
|
|
|
/* Module */
|
|
|
MODULE_DESCRIPTION(GS_LONG_NAME);
|
|
|
MODULE_AUTHOR("Al Borchers");
|
|
@@ -531,84 +461,23 @@ module_param(debug, int, S_IRUGO|S_IWUSR);
|
|
|
MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
|
|
|
#endif
|
|
|
|
|
|
+static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
|
|
|
module_param(read_q_size, uint, S_IRUGO);
|
|
|
MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32");
|
|
|
|
|
|
+static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
|
|
|
module_param(write_q_size, uint, S_IRUGO);
|
|
|
MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32");
|
|
|
|
|
|
+static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
|
|
|
module_param(write_buf_size, uint, S_IRUGO);
|
|
|
MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192");
|
|
|
|
|
|
+static unsigned int use_acm = GS_DEFAULT_USE_ACM;
|
|
|
module_param(use_acm, uint, S_IRUGO);
|
|
|
MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no");
|
|
|
|
|
|
-module_init(gs_module_init);
|
|
|
-module_exit(gs_module_exit);
|
|
|
-
|
|
|
-/*
|
|
|
-* gs_module_init
|
|
|
-*
|
|
|
-* Register as a USB gadget driver and a tty driver.
|
|
|
-*/
|
|
|
-static int __init gs_module_init(void)
|
|
|
-{
|
|
|
- int i;
|
|
|
- int retval;
|
|
|
-
|
|
|
- retval = usb_gadget_register_driver(&gs_gadget_driver);
|
|
|
- if (retval) {
|
|
|
- pr_err("gs_module_init: cannot register gadget driver, "
|
|
|
- "ret=%d\n", retval);
|
|
|
- return retval;
|
|
|
- }
|
|
|
-
|
|
|
- gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
|
|
|
- if (!gs_tty_driver)
|
|
|
- return -ENOMEM;
|
|
|
- gs_tty_driver->owner = THIS_MODULE;
|
|
|
- gs_tty_driver->driver_name = GS_SHORT_NAME;
|
|
|
- gs_tty_driver->name = "ttygs";
|
|
|
- gs_tty_driver->major = GS_MAJOR;
|
|
|
- gs_tty_driver->minor_start = GS_MINOR_START;
|
|
|
- gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
|
|
- gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
|
|
- gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
|
|
- gs_tty_driver->init_termios = tty_std_termios;
|
|
|
- gs_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
|
|
- tty_set_operations(gs_tty_driver, &gs_tty_ops);
|
|
|
-
|
|
|
- for (i=0; i < GS_NUM_PORTS; i++)
|
|
|
- mutex_init(&gs_open_close_lock[i]);
|
|
|
-
|
|
|
- retval = tty_register_driver(gs_tty_driver);
|
|
|
- if (retval) {
|
|
|
- usb_gadget_unregister_driver(&gs_gadget_driver);
|
|
|
- put_tty_driver(gs_tty_driver);
|
|
|
- pr_err("gs_module_init: cannot register tty driver, "
|
|
|
- "ret=%d\n", retval);
|
|
|
- return retval;
|
|
|
- }
|
|
|
-
|
|
|
- pr_info("gs_module_init: %s %s loaded\n",
|
|
|
- GS_LONG_NAME, GS_VERSION_STR);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-* gs_module_exit
|
|
|
-*
|
|
|
-* Unregister as a tty driver and a USB gadget driver.
|
|
|
-*/
|
|
|
-static void __exit gs_module_exit(void)
|
|
|
-{
|
|
|
- tty_unregister_driver(gs_tty_driver);
|
|
|
- put_tty_driver(gs_tty_driver);
|
|
|
- usb_gadget_unregister_driver(&gs_gadget_driver);
|
|
|
-
|
|
|
- pr_info("gs_module_exit: %s %s unloaded\n",
|
|
|
- GS_LONG_NAME, GS_VERSION_STR);
|
|
|
-}
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
/* TTY Driver */
|
|
|
|
|
@@ -753,15 +622,15 @@ exit_unlock_dev:
|
|
|
* gs_close
|
|
|
*/
|
|
|
|
|
|
-#define GS_WRITE_FINISHED_EVENT_SAFELY(p) \
|
|
|
-({ \
|
|
|
- int cond; \
|
|
|
- \
|
|
|
- spin_lock_irq(&(p)->port_lock); \
|
|
|
- cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); \
|
|
|
- spin_unlock_irq(&(p)->port_lock); \
|
|
|
- cond; \
|
|
|
-})
|
|
|
+static int gs_write_finished_event_safely(struct gs_port *p)
|
|
|
+{
|
|
|
+ int cond;
|
|
|
+
|
|
|
+ spin_lock_irq(&(p)->port_lock);
|
|
|
+ cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf);
|
|
|
+ spin_unlock_irq(&(p)->port_lock);
|
|
|
+ return cond;
|
|
|
+}
|
|
|
|
|
|
static void gs_close(struct tty_struct *tty, struct file *file)
|
|
|
{
|
|
@@ -807,7 +676,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|
|
if (gs_buf_data_avail(port->port_write_buf) > 0) {
|
|
|
spin_unlock_irq(&port->port_lock);
|
|
|
wait_event_interruptible_timeout(port->port_write_wait,
|
|
|
- GS_WRITE_FINISHED_EVENT_SAFELY(port),
|
|
|
+ gs_write_finished_event_safely(port),
|
|
|
GS_CLOSE_TIMEOUT * HZ);
|
|
|
spin_lock_irq(&port->port_lock);
|
|
|
}
|
|
@@ -1065,6 +934,23 @@ static void gs_set_termios(struct tty_struct *tty, struct ktermios *old)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
+static const struct tty_operations gs_tty_ops = {
|
|
|
+ .open = gs_open,
|
|
|
+ .close = gs_close,
|
|
|
+ .write = gs_write,
|
|
|
+ .put_char = gs_put_char,
|
|
|
+ .flush_chars = gs_flush_chars,
|
|
|
+ .write_room = gs_write_room,
|
|
|
+ .ioctl = gs_ioctl,
|
|
|
+ .set_termios = gs_set_termios,
|
|
|
+ .throttle = gs_throttle,
|
|
|
+ .unthrottle = gs_unthrottle,
|
|
|
+ .break_ctl = gs_break,
|
|
|
+ .chars_in_buffer = gs_chars_in_buffer,
|
|
|
+};
|
|
|
+
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+
|
|
|
/*
|
|
|
* gs_send
|
|
|
*
|
|
@@ -1328,8 +1214,43 @@ requeue:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+
|
|
|
/* Gadget Driver */
|
|
|
|
|
|
+/*
|
|
|
+ * gs_unbind
|
|
|
+ *
|
|
|
+ * Called on module unload. Frees the control request and device
|
|
|
+ * structure.
|
|
|
+ */
|
|
|
+static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget)
|
|
|
+{
|
|
|
+ struct gs_dev *dev = get_gadget_data(gadget);
|
|
|
+
|
|
|
+ gs_device = NULL;
|
|
|
+
|
|
|
+ /* read/write requests already freed, only control request remains */
|
|
|
+ if (dev != NULL) {
|
|
|
+ if (dev->dev_ctrl_req != NULL) {
|
|
|
+ gs_free_req(gadget->ep0, dev->dev_ctrl_req);
|
|
|
+ dev->dev_ctrl_req = NULL;
|
|
|
+ }
|
|
|
+ gs_free_ports(dev);
|
|
|
+ if (dev->dev_notify_ep)
|
|
|
+ usb_ep_disable(dev->dev_notify_ep);
|
|
|
+ if (dev->dev_in_ep)
|
|
|
+ usb_ep_disable(dev->dev_in_ep);
|
|
|
+ if (dev->dev_out_ep)
|
|
|
+ usb_ep_disable(dev->dev_out_ep);
|
|
|
+ kfree(dev);
|
|
|
+ set_gadget_data(gadget, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
|
|
|
+ GS_VERSION_STR);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* gs_bind
|
|
|
*
|
|
@@ -1441,8 +1362,6 @@ static int __init gs_bind(struct usb_gadget *gadget)
|
|
|
gs_unbind(gadget);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
- dev->dev_ctrl_req->complete = gs_setup_complete;
|
|
|
-
|
|
|
gadget->ep0->driver_data = dev;
|
|
|
|
|
|
pr_info("gs_bind: %s %s bound\n",
|
|
@@ -1455,95 +1374,6 @@ autoconf_fail:
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * gs_unbind
|
|
|
- *
|
|
|
- * Called on module unload. Frees the control request and device
|
|
|
- * structure.
|
|
|
- */
|
|
|
-static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget)
|
|
|
-{
|
|
|
- struct gs_dev *dev = get_gadget_data(gadget);
|
|
|
-
|
|
|
- gs_device = NULL;
|
|
|
-
|
|
|
- /* read/write requests already freed, only control request remains */
|
|
|
- if (dev != NULL) {
|
|
|
- if (dev->dev_ctrl_req != NULL) {
|
|
|
- gs_free_req(gadget->ep0, dev->dev_ctrl_req);
|
|
|
- dev->dev_ctrl_req = NULL;
|
|
|
- }
|
|
|
- gs_free_ports(dev);
|
|
|
- if (dev->dev_notify_ep)
|
|
|
- usb_ep_disable(dev->dev_notify_ep);
|
|
|
- if (dev->dev_in_ep)
|
|
|
- usb_ep_disable(dev->dev_in_ep);
|
|
|
- if (dev->dev_out_ep)
|
|
|
- usb_ep_disable(dev->dev_out_ep);
|
|
|
- kfree(dev);
|
|
|
- set_gadget_data(gadget, NULL);
|
|
|
- }
|
|
|
-
|
|
|
- pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
|
|
|
- GS_VERSION_STR);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * gs_setup
|
|
|
- *
|
|
|
- * Implements all the control endpoint functionality that's not
|
|
|
- * handled in hardware or the hardware driver.
|
|
|
- *
|
|
|
- * Returns the size of the data sent to the host, or a negative
|
|
|
- * error number.
|
|
|
- */
|
|
|
-static int gs_setup(struct usb_gadget *gadget,
|
|
|
- const struct usb_ctrlrequest *ctrl)
|
|
|
-{
|
|
|
- int ret = -EOPNOTSUPP;
|
|
|
- struct gs_dev *dev = get_gadget_data(gadget);
|
|
|
- struct usb_request *req = dev->dev_ctrl_req;
|
|
|
- u16 wIndex = le16_to_cpu(ctrl->wIndex);
|
|
|
- u16 wValue = le16_to_cpu(ctrl->wValue);
|
|
|
- u16 wLength = le16_to_cpu(ctrl->wLength);
|
|
|
-
|
|
|
- req->complete = gs_setup_complete;
|
|
|
-
|
|
|
- switch (ctrl->bRequestType & USB_TYPE_MASK) {
|
|
|
- case USB_TYPE_STANDARD:
|
|
|
- ret = gs_setup_standard(gadget,ctrl);
|
|
|
- break;
|
|
|
-
|
|
|
- case USB_TYPE_CLASS:
|
|
|
- ret = gs_setup_class(gadget,ctrl);
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- pr_err("gs_setup: unknown request, type=%02x, request=%02x, "
|
|
|
- "value=%04x, index=%04x, length=%d\n",
|
|
|
- ctrl->bRequestType, ctrl->bRequest,
|
|
|
- wValue, wIndex, wLength);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- /* respond with data transfer before status phase? */
|
|
|
- if (ret >= 0) {
|
|
|
- req->length = ret;
|
|
|
- req->zero = ret < wLength
|
|
|
- && (ret % gadget->ep0->maxpacket) == 0;
|
|
|
- ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
|
|
- if (ret < 0) {
|
|
|
- pr_err("gs_setup: cannot queue response, ret=%d\n",
|
|
|
- ret);
|
|
|
- req->status = 0;
|
|
|
- gs_setup_complete(gadget->ep0, req);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* device either stalls (ret < 0) or reports success */
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
static int gs_setup_standard(struct usb_gadget *gadget,
|
|
|
const struct usb_ctrlrequest *ctrl)
|
|
|
{
|
|
@@ -1673,6 +1503,42 @@ set_interface_done:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
|
|
|
+ struct usb_request *req)
|
|
|
+{
|
|
|
+ struct gs_dev *dev = ep->driver_data;
|
|
|
+ struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
|
|
|
+
|
|
|
+ switch (req->status) {
|
|
|
+ case 0:
|
|
|
+ /* normal completion */
|
|
|
+ if (req->actual != sizeof(port->port_line_coding))
|
|
|
+ usb_ep_set_halt(ep);
|
|
|
+ else if (port) {
|
|
|
+ struct usb_cdc_line_coding *value = req->buf;
|
|
|
+
|
|
|
+ /* REVISIT: we currently just remember this data.
|
|
|
+ * If we change that, (a) validate it first, then
|
|
|
+ * (b) update whatever hardware needs updating.
|
|
|
+ */
|
|
|
+ spin_lock(&port->port_lock);
|
|
|
+ port->port_line_coding = *value;
|
|
|
+ spin_unlock(&port->port_lock);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case -ESHUTDOWN:
|
|
|
+ /* disconnect */
|
|
|
+ gs_free_req(ep, req);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ /* unexpected */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
static int gs_setup_class(struct usb_gadget *gadget,
|
|
|
const struct usb_ctrlrequest *ctrl)
|
|
|
{
|
|
@@ -1734,52 +1600,72 @@ static int gs_setup_class(struct usb_gadget *gadget,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
|
|
|
- struct usb_request *req)
|
|
|
+/*
|
|
|
+ * gs_setup_complete
|
|
|
+ */
|
|
|
+static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
|
|
{
|
|
|
- struct gs_dev *dev = ep->driver_data;
|
|
|
- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
|
|
|
+ if (req->status || req->actual != req->length) {
|
|
|
+ pr_err("gs_setup_complete: status error, status=%d, "
|
|
|
+ "actual=%d, length=%d\n",
|
|
|
+ req->status, req->actual, req->length);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- switch (req->status) {
|
|
|
- case 0:
|
|
|
- /* normal completion */
|
|
|
- if (req->actual != sizeof(port->port_line_coding))
|
|
|
- usb_ep_set_halt(ep);
|
|
|
- else if (port) {
|
|
|
- struct usb_cdc_line_coding *value = req->buf;
|
|
|
+/*
|
|
|
+ * gs_setup
|
|
|
+ *
|
|
|
+ * Implements all the control endpoint functionality that's not
|
|
|
+ * handled in hardware or the hardware driver.
|
|
|
+ *
|
|
|
+ * Returns the size of the data sent to the host, or a negative
|
|
|
+ * error number.
|
|
|
+ */
|
|
|
+static int gs_setup(struct usb_gadget *gadget,
|
|
|
+ const struct usb_ctrlrequest *ctrl)
|
|
|
+{
|
|
|
+ int ret = -EOPNOTSUPP;
|
|
|
+ struct gs_dev *dev = get_gadget_data(gadget);
|
|
|
+ struct usb_request *req = dev->dev_ctrl_req;
|
|
|
+ u16 wIndex = le16_to_cpu(ctrl->wIndex);
|
|
|
+ u16 wValue = le16_to_cpu(ctrl->wValue);
|
|
|
+ u16 wLength = le16_to_cpu(ctrl->wLength);
|
|
|
|
|
|
- /* REVISIT: we currently just remember this data.
|
|
|
- * If we change that, (a) validate it first, then
|
|
|
- * (b) update whatever hardware needs updating.
|
|
|
- */
|
|
|
- spin_lock(&port->port_lock);
|
|
|
- port->port_line_coding = *value;
|
|
|
- spin_unlock(&port->port_lock);
|
|
|
- }
|
|
|
+ req->complete = gs_setup_complete;
|
|
|
+
|
|
|
+ switch (ctrl->bRequestType & USB_TYPE_MASK) {
|
|
|
+ case USB_TYPE_STANDARD:
|
|
|
+ ret = gs_setup_standard(gadget, ctrl);
|
|
|
break;
|
|
|
|
|
|
- case -ESHUTDOWN:
|
|
|
- /* disconnect */
|
|
|
- gs_free_req(ep, req);
|
|
|
+ case USB_TYPE_CLASS:
|
|
|
+ ret = gs_setup_class(gadget, ctrl);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
- /* unexpected */
|
|
|
+ pr_err("gs_setup: unknown request, type=%02x, request=%02x, "
|
|
|
+ "value=%04x, index=%04x, length=%d\n",
|
|
|
+ ctrl->bRequestType, ctrl->bRequest,
|
|
|
+ wValue, wIndex, wLength);
|
|
|
break;
|
|
|
}
|
|
|
- return;
|
|
|
-}
|
|
|
|
|
|
-/*
|
|
|
- * gs_setup_complete
|
|
|
- */
|
|
|
-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
|
|
-{
|
|
|
- if (req->status || req->actual != req->length) {
|
|
|
- pr_err("gs_setup_complete: status error, status=%d, "
|
|
|
- "actual=%d, length=%d\n",
|
|
|
- req->status, req->actual, req->length);
|
|
|
+ /* respond with data transfer before status phase? */
|
|
|
+ if (ret >= 0) {
|
|
|
+ req->length = ret;
|
|
|
+ req->zero = ret < wLength
|
|
|
+ && (ret % gadget->ep0->maxpacket) == 0;
|
|
|
+ ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("gs_setup: cannot queue response, ret=%d\n",
|
|
|
+ ret);
|
|
|
+ req->status = 0;
|
|
|
+ gs_setup_complete(gadget->ep0, req);
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ /* device either stalls (ret < 0) or reports success */
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1811,6 +1697,23 @@ static void gs_disconnect(struct usb_gadget *gadget)
|
|
|
pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME);
|
|
|
}
|
|
|
|
|
|
+static struct usb_gadget_driver gs_gadget_driver = {
|
|
|
+#ifdef CONFIG_USB_GADGET_DUALSPEED
|
|
|
+ .speed = USB_SPEED_HIGH,
|
|
|
+#else
|
|
|
+ .speed = USB_SPEED_FULL,
|
|
|
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
|
|
+ .function = GS_LONG_NAME,
|
|
|
+ .bind = gs_bind,
|
|
|
+ .unbind = gs_unbind,
|
|
|
+ .setup = gs_setup,
|
|
|
+ .disconnect = gs_disconnect,
|
|
|
+ .driver = {
|
|
|
+ .name = GS_SHORT_NAME,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* gs_set_config
|
|
|
*
|
|
@@ -1846,16 +1749,10 @@ static int gs_set_config(struct gs_dev *dev, unsigned config)
|
|
|
case GS_BULK_CONFIG_ID:
|
|
|
if (use_acm)
|
|
|
return -EINVAL;
|
|
|
- /* device specific optimizations */
|
|
|
- if (gadget_is_net2280(gadget))
|
|
|
- net2280_set_fifo_mode(gadget, 1);
|
|
|
break;
|
|
|
case GS_ACM_CONFIG_ID:
|
|
|
if (!use_acm)
|
|
|
return -EINVAL;
|
|
|
- /* device specific optimizations */
|
|
|
- if (gadget_is_net2280(gadget))
|
|
|
- net2280_set_fifo_mode(gadget, 1);
|
|
|
break;
|
|
|
default:
|
|
|
return -EINVAL;
|
|
@@ -2233,6 +2130,8 @@ static void gs_free_ports(struct gs_dev *dev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+
|
|
|
/* Circular Buffer */
|
|
|
|
|
|
/*
|
|
@@ -2393,3 +2292,77 @@ gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
|
|
|
|
|
|
return count;
|
|
|
}
|
|
|
+
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+
|
|
|
+static struct tty_driver *gs_tty_driver;
|
|
|
+
|
|
|
+/*
|
|
|
+ * gs_module_init
|
|
|
+ *
|
|
|
+ * Register as a USB gadget driver and a tty driver.
|
|
|
+ */
|
|
|
+static int __init gs_module_init(void)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ retval = usb_gadget_register_driver(&gs_gadget_driver);
|
|
|
+ if (retval) {
|
|
|
+ pr_err("gs_module_init: cannot register gadget driver, "
|
|
|
+ "ret=%d\n", retval);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
|
|
|
+ if (!gs_tty_driver)
|
|
|
+ return -ENOMEM;
|
|
|
+ gs_tty_driver->owner = THIS_MODULE;
|
|
|
+ gs_tty_driver->driver_name = GS_SHORT_NAME;
|
|
|
+ gs_tty_driver->name = "ttygs";
|
|
|
+ gs_tty_driver->major = GS_MAJOR;
|
|
|
+ gs_tty_driver->minor_start = GS_MINOR_START;
|
|
|
+ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
|
|
+ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
|
|
+ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
|
|
+ gs_tty_driver->init_termios = tty_std_termios;
|
|
|
+ /* must match GS_DEFAULT_DTE_RATE and friends */
|
|
|
+ gs_tty_driver->init_termios.c_cflag =
|
|
|
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
|
|
+ gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE;
|
|
|
+ gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE;
|
|
|
+ tty_set_operations(gs_tty_driver, &gs_tty_ops);
|
|
|
+
|
|
|
+ for (i = 0; i < GS_NUM_PORTS; i++)
|
|
|
+ mutex_init(&gs_open_close_lock[i]);
|
|
|
+
|
|
|
+ retval = tty_register_driver(gs_tty_driver);
|
|
|
+ if (retval) {
|
|
|
+ usb_gadget_unregister_driver(&gs_gadget_driver);
|
|
|
+ put_tty_driver(gs_tty_driver);
|
|
|
+ pr_err("gs_module_init: cannot register tty driver, "
|
|
|
+ "ret=%d\n", retval);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_info("gs_module_init: %s %s loaded\n",
|
|
|
+ GS_LONG_NAME, GS_VERSION_STR);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+module_init(gs_module_init);
|
|
|
+
|
|
|
+/*
|
|
|
+ * gs_module_exit
|
|
|
+ *
|
|
|
+ * Unregister as a tty driver and a USB gadget driver.
|
|
|
+ */
|
|
|
+static void __exit gs_module_exit(void)
|
|
|
+{
|
|
|
+ tty_unregister_driver(gs_tty_driver);
|
|
|
+ put_tty_driver(gs_tty_driver);
|
|
|
+ usb_gadget_unregister_driver(&gs_gadget_driver);
|
|
|
+
|
|
|
+ pr_info("gs_module_exit: %s %s unloaded\n",
|
|
|
+ GS_LONG_NAME, GS_VERSION_STR);
|
|
|
+}
|
|
|
+module_exit(gs_module_exit);
|