summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/serial.c')
-rw-r--r--drivers/usb/gadget/serial.c778
1 files changed, 329 insertions, 449 deletions
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 54cdd6f..fa019fa 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 */
@@ -164,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);
@@ -192,19 +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_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,
@@ -214,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);
@@ -232,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 */
@@ -304,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" },
@@ -327,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,
};
@@ -364,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,
@@ -521,6 +433,8 @@ static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
};
+/*-------------------------------------------------------------------------*/
+
/* Module */
MODULE_DESCRIPTION(GS_LONG_NAME);
MODULE_AUTHOR("Al Borchers");
@@ -531,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 */
@@ -753,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)
{
@@ -807,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);
}
@@ -1065,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
*
@@ -1080,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");
@@ -1093,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);
@@ -1106,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))) {
@@ -1289,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");
@@ -1300,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);
@@ -1328,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
@@ -1362,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);
@@ -1384,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
@@ -1415,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,
@@ -1441,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",
@@ -1451,99 +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);
-
- 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 +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)
{
@@ -1734,52 +1573,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 +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
*
@@ -1826,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");
@@ -1846,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;
- }
- }
-
- 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;
- }
- }
+ &gs_fullspeed_notify_desc)
+ : NULL;
- 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++) {
@@ -1946,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");
@@ -1986,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");
@@ -2000,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);
}
/*
@@ -2113,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.
@@ -2233,6 +2037,8 @@ static void gs_free_ports(struct gs_dev *dev)
}
}
+/*-------------------------------------------------------------------------*/
+
/* Circular Buffer */
/*
@@ -2393,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);
OpenPOWER on IntegriCloud