blob: d3baed74deac3194ab1637709f3ef560614f23c7 [file] [log] [blame]
/*
* u_serial.c - utilities for USB gadget "serial port"/TTY support
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This code also borrows from usbserial.c, which is
* Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2000 Peter Berger (pberger@brimson.com)
* Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
*
* 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.
*/
/* #define VERBOSE_DEBUG */
#include <common.h>
#include <usb/cdc.h>
#include <kfifo.h>
#include "u_serial.h"
/*
* This component encapsulates the TTY layer glue needed to provide basic
* "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.
*
* A given TTY port can be made available in multiple configurations.
* For example, each one might expose a ttyGS0 node which provides a
* login application. In one case that might use CDC ACM interface 0,
* while another configuration might use interface 3 for that. The
* work to handle that (including descriptor management) is not part
* of this component.
*
* Configurations may expose more than one TTY port. For example, if
* ttyGS0 provides login service, then ttyGS1 might provide dialer access
* for a telephone or fax link. And ttyGS2 might be something that just
* needs a simple byte stream interface for some messaging protocol that
* is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
*/
#define PREFIX "ttyGS"
/*
* gserial is the lifecycle interface, used by USB functions
* gs_port is the I/O nexus, used by the tty driver
* tty_struct links to the tty/filesystem framework
*
* gserial <---> gs_port ... links will be null when the USB link is
* inactive; managed by gserial_{connect,disconnect}(). each gserial
* instance can wrap its own USB control protocol.
* gserial->ioport == usb_ep->driver_data ... gs_port
* gs_port->port_usb ... gserial
*
* gs_port <---> tty_struct ... links will be null when the TTY file
* isn't opened; managed by gs_open()/gs_close()
* gserial->port_tty ... tty_struct
* tty_struct->driver_data ... gserial
*/
/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
* 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 16
#define WRITE_BUF_SIZE 8192 /* TX only */
/*
* 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;
unsigned n_read;
struct list_head write_pool;
/* REVISIT this state ... */
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;
#define GS_CLOSE_TIMEOUT 15 /* seconds */
#ifdef VERBOSE_DEBUG
#define pr_vdebug(fmt, arg...) \
pr_debug(fmt, ##arg)
#else
#define pr_vdebug(fmt, arg...) \
({ if (0) pr_debug(fmt, ##arg); })
#endif
static unsigned gs_start_rx(struct gs_port *port)
{
struct list_head *pool = &port->read_pool;
struct usb_ep *out = port->port_usb->out;
unsigned started = 0;
while (!list_empty(pool)) {
struct usb_request *req;
int status;
req = list_entry(pool->next, struct usb_request, list);
list_del(&req->list);
req->length = out->maxpacket;
/* drop lock while we call out; the controller driver
* may need to call us back (e.g. for disconnect)
*/
status = usb_ep_queue(out, req);
if (status) {
pr_debug("%s: %s %s err %d\n",
__func__, "queue", out->name, status);
list_add(&req->list, pool);
break;
}
started++;
/* abort immediately after disconnect */
if (!port->port_usb)
break;
}
return started;
}
/*-------------------------------------------------------------------------*/
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_port *port = ep->driver_data;
if (req->status == -ESHUTDOWN)
return;
kfifo_put(port->recv_fifo, req->buf, req->actual);
list_add_tail(&req->list, &port->read_pool);
gs_start_rx(port);
}
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_port *port = ep->driver_data;
list_add(&req->list, &port->write_pool);
switch (req->status) {
default:
/* presumably a transient fault */
pr_warning("%s: unexpected %s status %d\n",
__func__, ep->name, req->status);
/* FALL THROUGH */
case 0:
/* normal completion */
// gs_start_tx(port);
break;
case -ESHUTDOWN:
/* disconnect */
pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
break;
}
}
/*
* gs_alloc_req
*
* Allocate a usb_request and its buffer. Returns a pointer to the
* usb_request or NULL if there is an error.
*/
struct usb_request *
gs_alloc_req(struct usb_ep *ep, unsigned len)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep);
if (req != NULL) {
req->length = len;
req->buf = xmemalign(32, len);
}
return req;
}
static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
{
struct usb_request *req;
while (!list_empty(head)) {
req = list_entry(head->next, struct usb_request, list);
list_del(&req->list);
gs_free_req(ep, req);
}
}
static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
void (*fn)(struct usb_ep *, struct usb_request *))
{
int i;
struct usb_request *req;
/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
* do quite that many this time, don't fail ... we just won't
* be as speedy as we might otherwise be.
*/
for (i = 0; i < QUEUE_SIZE; i++) {
req = gs_alloc_req(ep, ep->maxpacket);
if (!req)
return list_empty(head) ? -ENOMEM : 0;
req->complete = fn;
list_add_tail(&req->list, head);
}
return 0;
}
/**
* gs_start_io - start USB I/O streams
* @dev: encapsulates endpoints to use
* Context: holding port_lock; port_tty and port_usb are non-null
*
* We only start I/O when something is connected to both sides of
* this port. If nothing is listening on the host side, we may
* be pointlessly filling up our TX buffers and FIFO.
*/
static int gs_start_io(struct gs_port *port)
{
struct list_head *head = &port->read_pool;
struct usb_ep *ep = port->port_usb->out;
int status;
unsigned started;
/* Allocate RX and TX I/O buffers. We can't easily do this much
* earlier (with GFP_KERNEL) because the requests are coupled to
* endpoints, as are the packet sizes we'll be using. Different
* configurations may use different endpoints with a given port;
* and high speed vs full speed changes packet sizes too.
*/
status = gs_alloc_requests(ep, head, gs_read_complete);
if (status)
return status;
status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
gs_write_complete);
if (status) {
gs_free_requests(ep, head);
return status;
}
/* queue read requests */
port->n_read = 0;
started = gs_start_rx(port);
/* unblock any pending writes into our circular buffer */
if (started) {
// tty_wakeup(port->port_tty);
} else {
gs_free_requests(ep, head);
gs_free_requests(port->port_usb->in, &port->write_pool);
status = -EIO;
}
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
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
{
struct gs_port *port;
port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
if (port == NULL)
return -ENOMEM;
port->port_num = port_num;
port->port_line_coding = *coding;
INIT_LIST_HEAD(&port->read_pool);
INIT_LIST_HEAD(&port->write_pool);
ports[port_num].port = port;
return 0;
}
/**
* 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)
{
struct usb_cdc_line_coding coding;
int i, status;
/* make devices be openable */
for (i = 0; i < count; i++) {
status = gs_port_alloc(i, &coding);
if (status) {
count = i;
goto fail;
}
}
n_ports = count;
return 0;
fail:
while (count--)
kfree(ports[count].port);
return status;
}
static void serial_putc(struct console_device *cdev, char c)
{
struct gs_port *port = container_of(cdev,
struct gs_port, cdev);
struct list_head *pool = &port->write_pool;
struct usb_ep *in = port->port_usb->in;
struct usb_request *req;
int status;
if (list_empty(pool))
return;
req = list_entry(pool->next, struct usb_request, list);
req->length = 1;
list_del(&req->list);
*(unsigned char *)req->buf = c;
status = usb_ep_queue(in, req);
while (list_empty(pool))
fsl_udc_irq();
}
static int serial_tstc(struct console_device *cdev)
{
struct gs_port *port = container_of(cdev,
struct gs_port, cdev);
return (kfifo_len(port->recv_fifo) == 0) ? 0 : 1;
}
static int serial_getc(struct console_device *cdev)
{
struct gs_port *port = container_of(cdev,
struct gs_port, cdev);
unsigned char ch;
while (kfifo_getc(port->recv_fifo, &ch));
return ch;
}
static void serial_flush(struct console_device *cdev)
{
}
static struct console_device *mycdev;
int gserial_connect(struct gserial *gser, u8 port_num)
{
struct gs_port *port;
int status;
struct console_device *cdev;
printf("%s %p %d\n", __func__, gser, port_num);
/* we "know" gserial_cleanup() hasn't been called */
port = ports[port_num].port;
/* activate the endpoints */
status = usb_ep_enable(gser->in, gser->in_desc);
if (status < 0)
return status;
gser->in->driver_data = port;
status = usb_ep_enable(gser->out, gser->out_desc);
if (status < 0)
goto fail_out;
gser->out->driver_data = port;
/* then tell the tty glue that I/O can work */
gser->ioport = port;
port->port_usb = gser;
/* REVISIT unclear how best to handle this state...
* we don't really couple it with the Linux TTY.
*/
gser->port_line_coding = port->port_line_coding;
port->recv_fifo = kfifo_alloc(1024);
printf("gserial_connect: start ttyGS%d\n", port->port_num);
gs_start_io(port);
if (gser->connect)
gser->connect(gser);
cdev = &port->cdev;
cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR;
cdev->tstc = serial_tstc;
cdev->putc = serial_putc;
cdev->getc = serial_getc;
cdev->flush = serial_flush;
console_register(cdev);
mycdev = cdev;
return status;
fail_out:
usb_ep_disable(gser->in);
gser->in->driver_data = NULL;
return status;
}
#include <command.h>
static int do_mycdev(struct command *cmdtp, 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++)
fsl_udc_irq();
}
return 0;
}
BAREBOX_CMD_START(mycdev)
.cmd = do_mycdev,
BAREBOX_CMD_END
/**
* gserial_disconnect - notify TTY I/O glue that USB link is inactive
* @gser: the function, on which gserial_connect() was called
* Context: any (usually from irq)
*
* This is called to deactivate endpoints and let the TTY layer know
* that the connection went inactive ... not unlike "hangup".
*
* On return, the state is as if gserial_connect() had never been called;
* there is no active USB I/O on these endpoints.
*/
void gserial_disconnect(struct gserial *gser)
{
struct gs_port *port = gser->ioport;
printf("%s\n", __func__);
if (!port)
return;
/* tell the TTY glue not to do I/O here any more */
/* REVISIT as above: how best to track this? */
port->port_line_coding = gser->port_line_coding;
port->port_usb = NULL;
gser->ioport = NULL;
/* disable endpoints, aborting down any active I/O */
usb_ep_disable(gser->out);
gser->out->driver_data = NULL;
usb_ep_disable(gser->in);
gser->in->driver_data = NULL;
/* finally, free any unused/unusable I/O buffers */
gs_free_requests(gser->out, &port->read_pool);
gs_free_requests(gser->in, &port->write_pool);
}