diff options
Diffstat (limited to 'drivers/usb/gadget/serial.c')
-rw-r--r-- | drivers/usb/gadget/serial.c | 225 |
1 files changed, 108 insertions, 117 deletions
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index dd33ff0ae4ce..f5738eb8e765 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -17,33 +17,15 @@ * */ -#include <linux/module.h> #include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> #include <linux/utsname.h> -#include <linux/wait.h> -#include <linux/proc_fs.h> #include <linux/device.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> -#include <asm/unaligned.h> -#include <asm/uaccess.h> - #include <linux/usb/ch9.h> #include <linux/usb/cdc.h> -#include <linux/usb_gadget.h> +#include <linux/usb/gadget.h> #include "gadget_chips.h" @@ -88,30 +70,29 @@ #define GS_DEFAULT_PARITY USB_CDC_NO_PARITY #define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS -/* select highspeed/fullspeed, hiding highspeed if not configured */ -#ifdef CONFIG_USB_GADGET_DUALSPEED -#define GS_SPEED_SELECT(is_hs,hs,fs) ((is_hs) ? (hs) : (fs)) -#else -#define GS_SPEED_SELECT(is_hs,hs,fs) (fs) -#endif /* CONFIG_USB_GADGET_DUALSPEED */ +/* maxpacket and other transfer characteristics vary by speed. */ +static inline struct usb_endpoint_descriptor * +choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + /* debug settings */ -#ifdef GS_DEBUG +#ifdef DEBUG static int debug = 1; +#else +#define debug 0 +#endif #define gs_debug(format, arg...) \ do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0) #define gs_debug_level(level, format, arg...) \ do { if (debug>=level) printk(KERN_DEBUG format, ## arg); } while(0) -#else - -#define gs_debug(format, arg...) \ - do { } while(0) -#define gs_debug_level(level, format, arg...) \ - do { } while(0) - -#endif /* GS_DEBUG */ /* Thanks to NetChip Technologies for donating this product ID. * @@ -146,10 +127,10 @@ struct gs_req_entry { /* 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 */ + struct gs_dev *port_dev; /* pointer to device struct */ struct tty_struct *port_tty; /* pointer to tty struct */ spinlock_t port_lock; - int port_num; + int port_num; int port_open_count; int port_in_use; /* open/close in progress */ wait_queue_head_t port_write_wait;/* waiting to write */ @@ -187,7 +168,7 @@ 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, +static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count); static void gs_put_char(struct tty_struct *tty, unsigned char ch); static void gs_flush_chars(struct tty_struct *tty); @@ -221,7 +202,7 @@ static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req); static void gs_disconnect(struct usb_gadget *gadget); 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, enum usb_device_speed speed, +static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, u8 type, unsigned int index, int is_otg); static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, @@ -258,7 +239,7 @@ static const char *EP_IN_NAME; static const char *EP_OUT_NAME; static const char *EP_NOTIFY_NAME; -static struct semaphore gs_open_close_sem[GS_NUM_PORTS]; +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; @@ -414,18 +395,18 @@ static const struct usb_cdc_header_desc gs_header_desc = { }; static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { - .bLength = sizeof(gs_call_mgmt_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, - .bmCapabilities = 0, - .bDataInterface = 1, /* index of data interface */ + .bLength = sizeof(gs_call_mgmt_descriptor), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + .bmCapabilities = 0, + .bDataInterface = 1, /* index of data interface */ }; 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, + .bLength = sizeof(gs_acm_descriptor), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + .bmCapabilities = 0, }; static const struct usb_cdc_union_desc gs_union_desc = { @@ -435,7 +416,7 @@ static const struct usb_cdc_union_desc gs_union_desc = { .bMasterInterface0 = 0, /* index of control interface */ .bSlaveInterface0 = 1, /* index of data interface */ }; - + static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -481,7 +462,6 @@ static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { NULL, }; -#ifdef CONFIG_USB_GADGET_DUALSPEED static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -535,15 +515,13 @@ static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { NULL, }; -#endif /* CONFIG_USB_GADGET_DUALSPEED */ - /* Module */ MODULE_DESCRIPTION(GS_LONG_NAME); MODULE_AUTHOR("Al Borchers"); MODULE_LICENSE("GPL"); -#ifdef GS_DEBUG +#ifdef DEBUG module_param(debug, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on"); #endif @@ -595,7 +573,7 @@ static int __init gs_module_init(void) tty_set_operations(gs_tty_driver, &gs_tty_ops); for (i=0; i < GS_NUM_PORTS; i++) - sema_init(&gs_open_close_sem[i], 1); + mutex_init(&gs_open_close_lock[i]); retval = tty_register_driver(gs_tty_driver); if (retval) { @@ -635,7 +613,7 @@ static int gs_open(struct tty_struct *tty, struct file *file) struct gs_port *port; struct gs_dev *dev; struct gs_buf *buf; - struct semaphore *sem; + struct mutex *mtx; int ret; port_num = tty->index; @@ -656,10 +634,10 @@ static int gs_open(struct tty_struct *tty, struct file *file) return -ENODEV; } - sem = &gs_open_close_sem[port_num]; - if (down_interruptible(sem)) { + mtx = &gs_open_close_lock[port_num]; + if (mutex_lock_interruptible(mtx)) { printk(KERN_ERR - "gs_open: (%d,%p,%p) interrupted waiting for semaphore\n", + "gs_open: (%d,%p,%p) interrupted waiting for mutex\n", port_num, tty, file); return -ERESTARTSYS; } @@ -754,12 +732,12 @@ static int gs_open(struct tty_struct *tty, struct file *file) exit_unlock_port: spin_unlock_irqrestore(&port->port_lock, flags); - up(sem); + mutex_unlock(mtx); return ret; exit_unlock_dev: spin_unlock_irqrestore(&dev->dev_lock, flags); - up(sem); + mutex_unlock(mtx); return ret; } @@ -781,7 +759,7 @@ exit_unlock_dev: static void gs_close(struct tty_struct *tty, struct file *file) { struct gs_port *port = tty->driver_data; - struct semaphore *sem; + struct mutex *mtx; if (port == NULL) { printk(KERN_ERR "gs_close: NULL port pointer\n"); @@ -790,8 +768,8 @@ static void gs_close(struct tty_struct *tty, struct file *file) gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file); - sem = &gs_open_close_sem[port->port_num]; - down(sem); + mtx = &gs_open_close_lock[port->port_num]; + mutex_lock(mtx); spin_lock_irq(&port->port_lock); @@ -846,7 +824,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) exit: spin_unlock_irq(&port->port_lock); - up(sem); + mutex_unlock(mtx); } /* @@ -914,7 +892,8 @@ static void gs_put_char(struct tty_struct *tty, unsigned char ch) return; } - gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2)); + gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", + port->port_num, tty, ch, __builtin_return_address(0)); spin_lock_irqsave(&port->port_lock, flags); @@ -1115,7 +1094,11 @@ static int gs_send(struct gs_dev *dev) len = gs_send_packet(dev, req->buf, ep->maxpacket); if (len > 0) { -gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2)); + gs_debug_level(3, "gs_send: len=%d, 0x%2.2x " + "0x%2.2x 0x%2.2x ...\n", len, + *((unsigned char *)req->buf), + *((unsigned char *)req->buf+1), + *((unsigned char *)req->buf+2)); list_del(&req_entry->re_entry); req->length = len; spin_unlock_irqrestore(&dev->dev_lock, flags); @@ -1268,7 +1251,7 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) switch(req->status) { case 0: - /* normal completion */ + /* normal completion */ gs_recv_packet(dev, req->buf, req->actual); requeue: req->length = ep->maxpacket; @@ -1405,29 +1388,30 @@ static int __init gs_bind(struct usb_gadget *gadget) ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; -#ifdef CONFIG_USB_GADGET_DUALSPEED - gs_qualifier_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; - /* assume ep0 uses the same packet size for both speeds */ - gs_qualifier_desc.bMaxPacketSize0 = gs_device_desc.bMaxPacketSize0; - /* assume endpoints are dual-speed */ - gs_highspeed_notify_desc.bEndpointAddress = - gs_fullspeed_notify_desc.bEndpointAddress; - gs_highspeed_in_desc.bEndpointAddress = - gs_fullspeed_in_desc.bEndpointAddress; - gs_highspeed_out_desc.bEndpointAddress = - gs_fullspeed_out_desc.bEndpointAddress; -#endif /* CONFIG_USB_GADGET_DUALSPEED */ + if (gadget_is_dualspeed(gadget)) { + gs_qualifier_desc.bDeviceClass = use_acm + ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; + /* assume ep0 uses the same packet size for both speeds */ + gs_qualifier_desc.bMaxPacketSize0 = + gs_device_desc.bMaxPacketSize0; + /* assume endpoints are dual-speed */ + gs_highspeed_notify_desc.bEndpointAddress = + gs_fullspeed_notify_desc.bEndpointAddress; + gs_highspeed_in_desc.bEndpointAddress = + gs_fullspeed_in_desc.bEndpointAddress; + gs_highspeed_out_desc.bEndpointAddress = + gs_fullspeed_out_desc.bEndpointAddress; + } usb_gadget_set_selfpowered(gadget); - if (gadget->is_otg) { + if (gadget_is_otg(gadget)) { gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - gs_device = dev = kmalloc(sizeof(struct gs_dev), GFP_KERNEL); + gs_device = dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; @@ -1435,7 +1419,6 @@ static int __init gs_bind(struct usb_gadget *gadget) init_utsname()->sysname, init_utsname()->release, gadget->name); - memset(dev, 0, sizeof(struct gs_dev)); dev->dev_gadget = gadget; spin_lock_init(&dev->dev_lock); INIT_LIST_HEAD(&dev->dev_req_list); @@ -1487,6 +1470,12 @@ static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget) 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); } @@ -1570,9 +1559,8 @@ static int gs_setup_standard(struct usb_gadget *gadget, memcpy(req->buf, &gs_device_desc, ret); break; -#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: - if (!gadget->is_dualspeed) + if (!gadget_is_dualspeed(gadget)) break; ret = min(wLength, (u16)sizeof(struct usb_qualifier_descriptor)); @@ -1580,14 +1568,13 @@ static int gs_setup_standard(struct usb_gadget *gadget, break; case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget->is_dualspeed) + if (!gadget_is_dualspeed(gadget)) break; /* fall through */ -#endif /* CONFIG_USB_GADGET_DUALSPEED */ case USB_DT_CONFIG: - ret = gs_build_config_buf(req->buf, gadget->speed, + ret = gs_build_config_buf(req->buf, gadget, wValue >> 8, wValue & 0xff, - gadget->is_otg); + gadget_is_otg(gadget)); if (ret >= 0) ret = min(wLength, (u16)ret); break; @@ -1691,14 +1678,12 @@ static int gs_setup_class(struct usb_gadget *gadget, switch (ctrl->bRequest) { case USB_CDC_REQ_SET_LINE_CODING: - ret = min(wLength, - (u16)sizeof(struct usb_cdc_line_coding)); - if (port) { - spin_lock(&port->port_lock); - memcpy(&port->port_line_coding, req->buf, ret); - spin_unlock(&port->port_lock); - } - ret = 0; + /* 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. + */ + printk(KERN_WARNING "gs_setup: set_line_coding " + "unuspported\n"); break; case USB_CDC_REQ_GET_LINE_CODING: @@ -1713,11 +1698,18 @@ static int gs_setup_class(struct usb_gadget *gadget, break; case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - ret = 0; + /* 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. + */ + printk(KERN_WARNING "gs_setup: set_control_line_state " + "unuspported\n"); break; default: - printk(KERN_ERR "gs_setup: unknown class request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", + printk(KERN_ERR "gs_setup: unknown class request, " + "type=%02x, request=%02x, value=%04x, " + "index=%04x, length=%d\n", ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); break; @@ -1822,8 +1814,7 @@ static int gs_set_config(struct gs_dev *dev, unsigned config) if (EP_NOTIFY_NAME && strcmp(ep->name, EP_NOTIFY_NAME) == 0) { - ep_desc = GS_SPEED_SELECT( - gadget->speed == USB_SPEED_HIGH, + ep_desc = choose_ep_desc(gadget, &gs_highspeed_notify_desc, &gs_fullspeed_notify_desc); ret = usb_ep_enable(ep,ep_desc); @@ -1839,9 +1830,8 @@ static int gs_set_config(struct gs_dev *dev, unsigned config) } else if (strcmp(ep->name, EP_IN_NAME) == 0) { - ep_desc = GS_SPEED_SELECT( - gadget->speed == USB_SPEED_HIGH, - &gs_highspeed_in_desc, + ep_desc = choose_ep_desc(gadget, + &gs_highspeed_in_desc, &gs_fullspeed_in_desc); ret = usb_ep_enable(ep,ep_desc); if (ret == 0) { @@ -1856,8 +1846,7 @@ static int gs_set_config(struct gs_dev *dev, unsigned config) } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - ep_desc = GS_SPEED_SELECT( - gadget->speed == USB_SPEED_HIGH, + ep_desc = choose_ep_desc(gadget, &gs_highspeed_out_desc, &gs_fullspeed_out_desc); ret = usb_ep_enable(ep,ep_desc); @@ -1976,11 +1965,11 @@ static void gs_reset_config(struct gs_dev *dev) * Builds the config descriptors in the given buffer and returns the * length, or a negative error number. */ -static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, +static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, u8 type, unsigned int index, int is_otg) { int len; - int high_speed; + int high_speed = 0; const struct usb_config_descriptor *config_desc; const struct usb_descriptor_header **function; @@ -1988,20 +1977,22 @@ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, return -EINVAL; /* other speed switches high and full speed */ - high_speed = (speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - high_speed = !high_speed; + if (gadget_is_dualspeed(g)) { + high_speed = (g->speed == USB_SPEED_HIGH); + if (type == USB_DT_OTHER_SPEED_CONFIG) + high_speed = !high_speed; + } if (use_acm) { config_desc = &gs_acm_config_desc; - function = GS_SPEED_SELECT(high_speed, - gs_acm_highspeed_function, - gs_acm_fullspeed_function); + function = high_speed + ? gs_acm_highspeed_function + : gs_acm_fullspeed_function; } else { config_desc = &gs_bulk_config_desc; - function = GS_SPEED_SELECT(high_speed, - gs_bulk_highspeed_function, - gs_bulk_fullspeed_function); + function = high_speed + ? gs_bulk_highspeed_function + : gs_bulk_fullspeed_function; } /* for now, don't advertise srp-only devices */ |