diff options
Diffstat (limited to 'drivers/usb/gadget/u_serial.c')
-rw-r--r-- | drivers/usb/gadget/u_serial.c | 254 |
1 files changed, 151 insertions, 103 deletions
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 2f9353edca..b6a4afd7f4 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -22,6 +22,8 @@ #include <usb/cdc.h> #include <kfifo.h> #include <clock.h> +#include <linux/err.h> +#include <dma.h> #include "u_serial.h" @@ -31,11 +33,12 @@ * "serial port" functionality through the USB gadget stack. Each such * port is exposed through a /dev/ttyGS* node. * - * After initialization (gserial_setup), these TTY port devices stay - * available until they are removed (gserial_cleanup). Each one may be - * connected to a USB function (gserial_connect), or disconnected (with - * gserial_disconnect) when the USB host issues a config change event. - * Data can only flow when the port is connected to the host. + * After this module has been loaded, the individual TTY port can be requested + * (gserial_alloc_line()) and it will stay available until they are removed + * (gserial_free_line()). Each one may be connected to a USB function + * (gserial_connect), or disconnected (with gserial_disconnect) when the USB + * host issues a config change event. Data can only flow when the port is + * connected to the host. * * A given TTY port can be made available in multiple configurations. * For example, each one might expose a ttyGS0 node which provides a @@ -74,21 +77,27 @@ * next layer of buffering. For TX that's a circular buffer; for RX * consider it a NOP. A third layer is provided by the TTY code. */ -#define QUEUE_SIZE 128 +#define QUEUE_SIZE 16 #define WRITE_BUF_SIZE 8192 /* TX only */ #define RECV_FIFO_SIZE (1024 * 8) + +/* circular buffer */ +struct gs_buf { + unsigned buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + /* * The port structure holds info for each port, one for each minor number * (and thus for each /dev/ node). */ struct gs_port { - struct gserial *port_usb; struct console_device cdev; struct kfifo *recv_fifo; - unsigned open_count; - int openclose; /* open/close in progress */ u8 port_num; struct list_head read_pool; @@ -100,12 +109,9 @@ struct gs_port { struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ }; -/* increase N_PORTS if you need more */ -#define N_PORTS 4 static struct portmaster { struct gs_port *port; -} ports[N_PORTS]; -static unsigned n_ports; +} ports[MAX_U_SERIAL_PORTS]; #define GS_CLOSE_TIMEOUT 15 /* seconds */ @@ -162,6 +168,10 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) gs_start_rx(port); } +/*-------------------------------------------------------------------------*/ + +/* I/O glue between TTY (upper) and USB function (lower) driver layers */ + static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) { struct gs_port *port = ep->driver_data; @@ -176,7 +186,6 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) /* FALL THROUGH */ case 0: /* normal completion */ -// gs_start_tx(port); break; case -ESHUTDOWN: @@ -201,11 +210,24 @@ gs_alloc_req(struct usb_ep *ep, unsigned len) if (req != NULL) { req->length = len; - req->buf = xmemalign(32, len); + req->buf = dma_alloc(len); } return req; } +EXPORT_SYMBOL_GPL(gs_alloc_req); + +/* + * gs_free_req + * + * Free a usb_request and its buffer. + */ +void gs_free_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} +EXPORT_SYMBOL_GPL(gs_free_req); static void gs_free_requests(struct usb_ep *ep, struct list_head *head) { @@ -276,9 +298,7 @@ static int gs_start_io(struct gs_port *port) started = gs_start_rx(port); /* unblock any pending writes into our circular buffer */ - if (started) { -// tty_wakeup(port->port_tty); - } else { + if (!started) { gs_free_requests(ep, head); gs_free_requests(port->port_usb->in, &port->write_pool); status = -EIO; @@ -287,76 +307,84 @@ static int gs_start_io(struct gs_port *port) return status; } -/* - * gs_free_req - * - * Free a usb_request and its buffer. - */ -void gs_free_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} +/*-------------------------------------------------------------------------*/ -static int __init +static int gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) { struct gs_port *port; + int ret = 0; - port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); - if (port == NULL) - return -ENOMEM; + if (ports[port_num].port) { + ret = -EBUSY; + goto out; + } - port->port_num = port_num; - port->port_line_coding = *coding; + port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); + if (port == NULL) { + ret = -ENOMEM; + goto out; + } INIT_LIST_HEAD(&port->read_pool); INIT_LIST_HEAD(&port->write_pool); + port->port_num = port_num; + port->port_line_coding = *coding; + ports[port_num].port = port; +out: + return ret; +} - return 0; +static void gserial_free_port(struct gs_port *port) +{ + kfree(port); } -/** - * gserial_setup - initialize TTY driver for one or more ports - * @g: gadget to associate with these ports - * @count: how many ports to support - * Context: may sleep - * - * The TTY stack needs to know in advance how many devices it should - * plan to manage. Use this call to set up the ports you will be - * exporting through USB. Later, connect them to functions based - * on what configuration is activated by the USB host; and disconnect - * them as appropriate. - * - * An example would be a two-configuration device in which both - * configurations expose port 0, but through different functions. - * One configuration could even expose port 1 while the other - * one doesn't. - * - * Returns negative errno or zero. - */ -int __init gserial_setup(struct usb_gadget *g, unsigned count) +void gserial_free_line(unsigned char port_num) { - struct usb_cdc_line_coding coding; - int i, status; + struct gs_port *port; - /* make devices be openable */ - for (i = 0; i < count; i++) { - status = gs_port_alloc(i, &coding); - if (status) { - count = i; - goto fail; - } + if (WARN_ON(!ports[port_num].port)) + return; + + port = ports[port_num].port; + ports[port_num].port = NULL; + + gserial_free_port(port); +} +EXPORT_SYMBOL_GPL(gserial_free_line); + +int gserial_alloc_line(unsigned char *line_num) +{ + struct usb_cdc_line_coding coding; + int ret; + int port_num; + + coding.dwDTERate = cpu_to_le32(9600); + coding.bCharFormat = 8; + coding.bParityType = USB_CDC_NO_PARITY; + coding.bDataBits = USB_CDC_1_STOP_BITS; + + for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) { + ret = gs_port_alloc(port_num, &coding); + if (ret == -EBUSY) + continue; + if (ret) + return ret; + break; } - n_ports = count; - return 0; -fail: - while (count--) - kfree(ports[count].port); - return status; + if (ret) + return ret; + + /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ + + *line_num = port_num; + + return ret; } +EXPORT_SYMBOL_GPL(gserial_alloc_line); static void serial_putc(struct console_device *cdev, char c) { @@ -422,33 +450,59 @@ timeout: static void serial_flush(struct console_device *cdev) { } + static int serial_setbaudrate(struct console_device *cdev, int baudrate) { return 0; } -static struct console_device *mycdev; - +/** + * gserial_connect - notify TTY I/O glue that USB link is active + * @gser: the function, set up with endpoints and descriptors + * @port_num: which port is active + * Context: any (usually from irq) + * + * This is called activate endpoints and let the TTY layer know that + * the connection is active ... not unlike "carrier detect". It won't + * necessarily start I/O queues; unless the TTY is held open by any + * task, there would be no point. However, the endpoints will be + * activated so the USB host can perform I/O, subject to basic USB + * hardware flow control. + * + * Caller needs to have set up the endpoints and USB function in @dev + * before calling this, as well as the appropriate (speed-specific) + * endpoint descriptors, and also have allocate @port_num by calling + * @gserial_alloc_line(). + * + * Returns negative errno or zero. + * On success, ep->driver_data will be overwritten. + */ int gserial_connect(struct gserial *gser, u8 port_num) { struct gs_port *port; int status; struct console_device *cdev; - /* we "know" gserial_cleanup() hasn't been called */ - port = ports[port_num].port; + if (port_num >= MAX_U_SERIAL_PORTS) + return -ENXIO; - /* In case of multiple activation (ie. multiple SET_INTERFACE) */ - if (port->port_usb) - return 0; + port = ports[port_num].port; + if (!port) { + pr_err("serial line %d not allocated.\n", port_num); + return -EINVAL; + } + if (port->port_usb) { + pr_err("serial line %d is in use.\n", port_num); + return -EBUSY; + } /* activate the endpoints */ - status = usb_ep_enable(gser->in, gser->in_desc); + status = usb_ep_enable(gser->in); if (status < 0) return status; gser->in->driver_data = port; - status = usb_ep_enable(gser->out, gser->out_desc); + status = usb_ep_enable(gser->out); if (status < 0) goto fail_out; gser->out->driver_data = port; @@ -481,7 +535,19 @@ int gserial_connect(struct gserial *gser, u8 port_num) if (status) goto fail_out; - mycdev = cdev; + /* REVISIT if waiting on "carrier detect", signal. */ + + /* if it's already open, start I/O ... and notify the serial + * protocol about open/close status (connect/disconnect). + */ + if (1) { + pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); + if (gser->connect) + gser->connect(gser); + } else { + if (gser->disconnect) + gser->disconnect(gser); + } return status; @@ -490,27 +556,7 @@ fail_out: gser->in->driver_data = NULL; return status; } -#include <command.h> - -static int do_mycdev(int argc, char *argv[]) -{ - - int i,j; - for (i = 'a'; i < 'z'; i++) { - mycdev->putc(mycdev, i); - printf("%c", i); - mdelay(500); - for (j = 0; j < 100; j++) - usb_gadget_poll(); - } - return 0; -} - -BAREBOX_CMD_START(mycdev) - .cmd = do_mycdev, - BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) - BAREBOX_CMD_COMPLETE(empty_complete) -BAREBOX_CMD_END +EXPORT_SYMBOL_GPL(gserial_connect); /** * gserial_disconnect - notify TTY I/O glue that USB link is inactive @@ -531,7 +577,10 @@ void gserial_disconnect(struct gserial *gser) if (!port) return; + cdev = &port->cdev; + /* tell the TTY glue not to do I/O here any more */ + console_unregister(cdev); /* REVISIT as above: how best to track this? */ port->port_line_coding = gser->port_line_coding; @@ -550,6 +599,5 @@ void gserial_disconnect(struct gserial *gser) gs_free_requests(gser->out, &port->read_pool); gs_free_requests(gser->in, &port->write_pool); - cdev = &port->cdev; - console_unregister(cdev); + kfifo_free(port->recv_fifo); } |