diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 |
commit | a8931ef380c92d121ae74ecfb03b2d63f72eea6f (patch) | |
tree | 980fb6b019e11e6cb1ece55b7faff184721a8053 /drivers/usb/gadget/serial.c | |
parent | 90574d0a4d4b73308ae54a2a57a4f3f1fa98e984 (diff) | |
parent | e5a5816f7875207cb0a0a7032e39a4686c5e10a4 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/usb/gadget/serial.c')
-rw-r--r-- | drivers/usb/gadget/serial.c | 792 |
1 files changed, 364 insertions, 428 deletions
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 8d158e5640e..fa019fa7333 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -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> @@ -33,7 +32,7 @@ /* Defines */ #define GS_VERSION_STR "v2.2" -#define GS_VERSION_NUM 0x0202 +#define GS_VERSION_NUM 0x2200 #define GS_LONG_NAME "Gadget Serial" #define GS_SHORT_NAME "g_serial" @@ -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; @@ -119,12 +121,6 @@ struct gs_buf { char *buf_put; }; -/* list of requests */ -struct gs_req_entry { - struct list_head re_entry; - struct usb_request *re_req; -}; - /* the port structure holds info for each port, one for each minor number */ struct gs_port { struct gs_dev *port_dev; /* pointer to device struct */ @@ -135,7 +131,10 @@ struct gs_port { int port_in_use; /* open/close in progress */ wait_queue_head_t port_write_wait;/* waiting to write */ struct gs_buf *port_write_buf; - struct usb_cdc_line_coding port_line_coding; + struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ + u16 port_handshake_bits; +#define RS232_RTS (1 << 1) +#define RS232_DTE (1 << 0) }; /* the device structure holds info for the USB device */ @@ -161,26 +160,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); @@ -189,17 +169,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_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, @@ -209,10 +179,6 @@ static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags); static void gs_free_req(struct usb_ep *ep, struct usb_request *req); -static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len, - gfp_t kmalloc_flags); -static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req); - static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags); static void gs_free_ports(struct gs_dev *dev); @@ -227,62 +193,15 @@ 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 */ static struct gs_dev *gs_device; -static const char *EP_IN_NAME; -static const char *EP_OUT_NAME; -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 */ @@ -299,7 +218,6 @@ static char manufacturer[50]; static struct usb_string gs_strings[] = { { GS_MANUFACTURER_STR_ID, manufacturer }, { GS_PRODUCT_STR_ID, GS_LONG_NAME }, - { GS_SERIAL_STR_ID, "0" }, { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, { GS_CONTROL_STR_ID, "Gadget Serial Control" }, @@ -322,7 +240,6 @@ static struct usb_device_descriptor gs_device_desc = { .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), .iManufacturer = GS_MANUFACTURER_STR_ID, .iProduct = GS_PRODUCT_STR_ID, - .iSerialNumber = GS_SERIAL_STR_ID, .bNumConfigurations = GS_NUM_CONFIGS, }; @@ -359,7 +276,7 @@ static const struct usb_interface_descriptor gs_bulk_interface_desc = { .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_BULK_INTERFACE_ID, .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = GS_DATA_STR_ID, @@ -406,7 +323,7 @@ static struct usb_cdc_acm_descriptor gs_acm_descriptor = { .bLength = sizeof(gs_acm_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, - .bmCapabilities = 0, + .bmCapabilities = (1 << 1), }; static const struct usb_cdc_union_desc gs_union_desc = { @@ -516,6 +433,8 @@ static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { }; +/*-------------------------------------------------------------------------*/ + /* Module */ MODULE_DESCRIPTION(GS_LONG_NAME); MODULE_AUTHOR("Al Borchers"); @@ -526,84 +445,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 */ @@ -748,15 +606,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) { @@ -802,7 +660,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); } @@ -1060,6 +918,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 * @@ -1075,7 +950,6 @@ static int gs_send(struct gs_dev *dev) unsigned long flags; struct usb_ep *ep; struct usb_request *req; - struct gs_req_entry *req_entry; if (dev == NULL) { pr_err("gs_send: NULL device pointer\n"); @@ -1088,10 +962,8 @@ static int gs_send(struct gs_dev *dev) while(!list_empty(&dev->dev_req_list)) { - req_entry = list_entry(dev->dev_req_list.next, - struct gs_req_entry, re_entry); - - req = req_entry->re_req; + req = list_entry(dev->dev_req_list.next, + struct usb_request, list); len = gs_send_packet(dev, req->buf, ep->maxpacket); @@ -1101,7 +973,7 @@ static int gs_send(struct gs_dev *dev) *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2)); - list_del(&req_entry->re_entry); + list_del(&req->list); req->length = len; spin_unlock_irqrestore(&dev->dev_lock, flags); if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { @@ -1284,7 +1156,6 @@ requeue: static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) { struct gs_dev *dev = ep->driver_data; - struct gs_req_entry *gs_req = req->context; if (dev == NULL) { pr_err("gs_write_complete: NULL device pointer\n"); @@ -1295,13 +1166,8 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) case 0: /* normal completion */ requeue: - if (gs_req == NULL) { - pr_err("gs_write_complete: NULL request pointer\n"); - return; - } - spin_lock(&dev->dev_lock); - list_add(&gs_req->re_entry, &dev->dev_req_list); + list_add(&req->list, &dev->dev_req_list); spin_unlock(&dev->dev_lock); gs_send(dev); @@ -1323,9 +1189,39 @@ 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_reset_config(dev); + gs_free_ports(dev); + kfree(dev); + set_gadget_data(gadget, NULL); + } + + pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME, + GS_VERSION_STR); +} + +/* * gs_bind * * Called on module load. Allocates and initializes the device @@ -1357,19 +1253,23 @@ static int __init gs_bind(struct usb_gadget *gadget) __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); } + dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); if (!ep) goto autoconf_fail; - EP_IN_NAME = ep->name; - ep->driver_data = ep; /* claim the endpoint */ + dev->dev_in_ep = ep; + ep->driver_data = dev; /* claim the endpoint */ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); if (!ep) goto autoconf_fail; - EP_OUT_NAME = ep->name; - ep->driver_data = ep; /* claim the endpoint */ + dev->dev_out_ep = ep; + ep->driver_data = dev; /* claim the endpoint */ if (use_acm) { ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); @@ -1379,8 +1279,8 @@ static int __init gs_bind(struct usb_gadget *gadget) } gs_device_desc.idProduct = __constant_cpu_to_le16( GS_CDC_PRODUCT_ID), - EP_NOTIFY_NAME = ep->name; - ep->driver_data = ep; /* claim the endpoint */ + dev->dev_notify_ep = ep; + ep->driver_data = dev; /* claim the endpoint */ } gs_device_desc.bDeviceClass = use_acm @@ -1410,9 +1310,7 @@ static int __init gs_bind(struct usb_gadget *gadget) gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - gs_device = dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; + gs_device = dev; snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", init_utsname()->sysname, init_utsname()->release, @@ -1436,8 +1334,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", @@ -1446,97 +1342,11 @@ static int __init gs_bind(struct usb_gadget *gadget) return 0; autoconf_fail: + kfree(dev); pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name); 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); - - 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) { @@ -1666,6 +1476,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) { @@ -1679,18 +1525,14 @@ static int gs_setup_class(struct usb_gadget *gadget, switch (ctrl->bRequest) { case USB_CDC_REQ_SET_LINE_CODING: - /* FIXME Submit req to read the data; have its completion - * handler copy that data to port->port_line_coding (iff - * it's valid) and maybe pass it on. Until then, fail. - */ - pr_warning("gs_setup: set_line_coding " - "unuspported\n"); + if (wLength != sizeof(struct usb_cdc_line_coding)) + break; + ret = wLength; + req->complete = gs_setup_complete_set_line_coding; break; case USB_CDC_REQ_GET_LINE_CODING: - port = dev->dev_port[0]; /* ACM only has one port */ - ret = min(wLength, - (u16)sizeof(struct usb_cdc_line_coding)); + ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding)); if (port) { spin_lock(&port->port_lock); memcpy(req->buf, &port->port_line_coding, ret); @@ -1699,15 +1541,27 @@ static int gs_setup_class(struct usb_gadget *gadget, break; case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - /* FIXME Submit req to read the data; have its completion - * handler use that to set the state (iff it's valid) and - * maybe pass it on. Until then, fail. - */ - pr_warning("gs_setup: set_control_line_state " - "unuspported\n"); + if (wLength != 0) + break; + ret = 0; + if (port) { + /* REVISIT: we currently just remember this data. + * If we change that, update whatever hardware needs + * updating. + */ + spin_lock(&port->port_lock); + port->port_handshake_bits = wValue; + spin_unlock(&port->port_lock); + } break; default: + /* NOTE: strictly speaking, we should accept AT-commands + * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE. + * But our call management descriptor says we don't handle + * call management, so we should be able to get by without + * handling those "required" commands (except by stalling). + */ pr_err("gs_setup: unknown class request, " "type=%02x, request=%02x, value=%04x, " "index=%04x, length=%d\n", @@ -1732,6 +1586,62 @@ static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req) } /* + * 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; +} + +/* * gs_disconnect * * Called when the device is disconnected. Frees the closed @@ -1760,6 +1670,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 * @@ -1775,9 +1702,8 @@ static int gs_set_config(struct gs_dev *dev, unsigned config) int ret = 0; struct usb_gadget *gadget = dev->dev_gadget; struct usb_ep *ep; - struct usb_endpoint_descriptor *ep_desc; + struct usb_endpoint_descriptor *out, *in, *notify; struct usb_request *req; - struct gs_req_entry *req_entry; if (dev == NULL) { pr_err("gs_set_config: NULL device pointer\n"); @@ -1795,86 +1721,62 @@ 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; } - dev->dev_config = config; - - gadget_for_each_ep(ep, gadget) { - - if (EP_NOTIFY_NAME - && strcmp(ep->name, EP_NOTIFY_NAME) == 0) { - ep_desc = choose_ep_desc(gadget, + in = choose_ep_desc(gadget, + &gs_highspeed_in_desc, + &gs_fullspeed_in_desc); + out = choose_ep_desc(gadget, + &gs_highspeed_out_desc, + &gs_fullspeed_out_desc); + notify = dev->dev_notify_ep + ? choose_ep_desc(gadget, &gs_highspeed_notify_desc, - &gs_fullspeed_notify_desc); - ret = usb_ep_enable(ep,ep_desc); - if (ret == 0) { - ep->driver_data = dev; - dev->dev_notify_ep = ep; - dev->dev_notify_ep_desc = ep_desc; - } else { - pr_err("gs_set_config: cannot enable NOTIFY " - "endpoint %s, ret=%d\n", - ep->name, ret); - goto exit_reset_config; - } - } + &gs_fullspeed_notify_desc) + : NULL; - else if (strcmp(ep->name, EP_IN_NAME) == 0) { - ep_desc = choose_ep_desc(gadget, - &gs_highspeed_in_desc, - &gs_fullspeed_in_desc); - ret = usb_ep_enable(ep,ep_desc); - if (ret == 0) { - ep->driver_data = dev; - dev->dev_in_ep = ep; - dev->dev_in_ep_desc = ep_desc; - } else { - pr_err("gs_set_config: cannot enable IN " - "endpoint %s, ret=%d\n", - ep->name, ret); - goto exit_reset_config; - } - } - - else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - ep_desc = choose_ep_desc(gadget, - &gs_highspeed_out_desc, - &gs_fullspeed_out_desc); - ret = usb_ep_enable(ep,ep_desc); - if (ret == 0) { - ep->driver_data = dev; - dev->dev_out_ep = ep; - dev->dev_out_ep_desc = ep_desc; - } else { - pr_err("gs_set_config: cannot enable OUT " - "endpoint %s, ret=%d\n", - ep->name, ret); - goto exit_reset_config; - } - } + ret = usb_ep_enable(dev->dev_in_ep, in); + if (ret == 0) { + dev->dev_in_ep_desc = in; + } else { + pr_debug("%s: cannot enable %s %s, ret=%d\n", + __func__, "IN", dev->dev_in_ep->name, ret); + return ret; + } + ret = usb_ep_enable(dev->dev_out_ep, out); + if (ret == 0) { + dev->dev_out_ep_desc = out; + } else { + pr_debug("%s: cannot enable %s %s, ret=%d\n", + __func__, "OUT", dev->dev_out_ep->name, ret); +fail0: + usb_ep_disable(dev->dev_in_ep); + return ret; } - if (dev->dev_in_ep == NULL || dev->dev_out_ep == NULL - || (config != GS_BULK_CONFIG_ID && dev->dev_notify_ep == NULL)) { - pr_err("gs_set_config: cannot find endpoints\n"); - ret = -ENODEV; - goto exit_reset_config; + if (notify) { + ret = usb_ep_enable(dev->dev_notify_ep, notify); + if (ret == 0) { + dev->dev_notify_ep_desc = notify; + } else { + pr_debug("%s: cannot enable %s %s, ret=%d\n", + __func__, "NOTIFY", + dev->dev_notify_ep->name, ret); + usb_ep_disable(dev->dev_out_ep); + goto fail0; + } } + dev->dev_config = config; + /* allocate and queue read requests */ ep = dev->dev_out_ep; for (i=0; i<read_q_size && ret == 0; i++) { @@ -1895,9 +1797,10 @@ static int gs_set_config(struct gs_dev *dev, unsigned config) /* allocate write requests, and put on free list */ ep = dev->dev_in_ep; for (i=0; i<write_q_size; i++) { - if ((req_entry=gs_alloc_req_entry(ep, ep->maxpacket, GFP_ATOMIC))) { - req_entry->re_req->complete = gs_write_complete; - list_add(&req_entry->re_entry, &dev->dev_req_list); + req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); + if (req) { + req->complete = gs_write_complete; + list_add(&req->list, &dev->dev_req_list); } else { pr_err("gs_set_config: cannot allocate " "write requests\n"); @@ -1906,6 +1809,11 @@ static int gs_set_config(struct gs_dev *dev, unsigned config) } } + /* REVISIT the ACM mode should be able to actually *issue* some + * notifications, for at least serial state change events if + * not also for network connection; say so in bmCapabilities. + */ + pr_info("gs_set_config: %s configured, %s speed %s config\n", GS_LONG_NAME, gadget->speed == USB_SPEED_HIGH ? "high" : "full", @@ -1930,7 +1838,7 @@ exit_reset_config: */ static void gs_reset_config(struct gs_dev *dev) { - struct gs_req_entry *req_entry; + struct usb_request *req; if (dev == NULL) { pr_err("gs_reset_config: NULL device pointer\n"); @@ -1944,26 +1852,18 @@ static void gs_reset_config(struct gs_dev *dev) /* free write requests on the free list */ while(!list_empty(&dev->dev_req_list)) { - req_entry = list_entry(dev->dev_req_list.next, - struct gs_req_entry, re_entry); - list_del(&req_entry->re_entry); - gs_free_req_entry(dev->dev_in_ep, req_entry); + req = list_entry(dev->dev_req_list.next, + struct usb_request, list); + list_del(&req->list); + gs_free_req(dev->dev_in_ep, req); } /* disable endpoints, forcing completion of pending i/o; */ /* completion handlers free their requests in this case */ - if (dev->dev_notify_ep) { + if (dev->dev_notify_ep) usb_ep_disable(dev->dev_notify_ep); - dev->dev_notify_ep = NULL; - } - if (dev->dev_in_ep) { - usb_ep_disable(dev->dev_in_ep); - dev->dev_in_ep = NULL; - } - if (dev->dev_out_ep) { - usb_ep_disable(dev->dev_out_ep); - dev->dev_out_ep = NULL; - } + usb_ep_disable(dev->dev_in_ep); + usb_ep_disable(dev->dev_out_ep); } /* @@ -2057,46 +1957,6 @@ static void gs_free_req(struct usb_ep *ep, struct usb_request *req) } /* - * gs_alloc_req_entry - * - * Allocates a request and its buffer, using the given - * endpoint, buffer len, and kmalloc flags. - */ -static struct gs_req_entry * -gs_alloc_req_entry(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) -{ - struct gs_req_entry *req; - - req = kmalloc(sizeof(struct gs_req_entry), kmalloc_flags); - if (req == NULL) - return NULL; - - req->re_req = gs_alloc_req(ep, len, kmalloc_flags); - if (req->re_req == NULL) { - kfree(req); - return NULL; - } - - req->re_req->context = req; - - return req; -} - -/* - * gs_free_req_entry - * - * Frees a request and its buffer. - */ -static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req) -{ - if (ep != NULL && req != NULL) { - if (req->re_req != NULL) - gs_free_req(ep, req->re_req); - kfree(req); - } -} - -/* * gs_alloc_ports * * Allocate all ports and set the gs_dev struct to point to them. @@ -2177,6 +2037,8 @@ static void gs_free_ports(struct gs_dev *dev) } } +/*-------------------------------------------------------------------------*/ + /* Circular Buffer */ /* @@ -2337,3 +2199,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); |