USB gadget driver: ACM

来源:互联网 发布:九曲 数据 编辑:程序博客网 时间:2024/05/16 05:20

有关 acm的代码在文件: android.c/ f_acm.c and u_serial.c中。


有关的数据结构主要包括:tty_struct/ gs_port and gserial.

三者之间的关系及建立:

/*

*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 linkis

*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 TTYfile

*isn't opened; managed by gs_open()/gs_close()

* gserial->port_tty... tty_struct

* tty_struct->driver_data... gserial

*/

何时创建的gserial?

当创建f_acm是创建的gserial

structf_acm {

structgserial port;

---

}

intacm_bind_config(struct usb_configuration *c, u8 port_num)

{

structf_acm *acm;

int status;

/*allocate and initialize one new instance */

acm= kzalloc(sizeof *acm, GFP_KERNEL);

acm->port.connect= acm_connect;

acm->port.disconnect= acm_disconnect;

acm->port.send_break= acm_send_break;

acm->port.func.name= "acm";

acm->port.func.strings= acm_strings;

/*descriptors are per-instance copies */

acm->port.func.bind= acm_bind;

acm->port.func.unbind= acm_unbind;

acm->port.func.set_alt= acm_set_alt;

acm->port.func.setup= acm_setup;

acm->port.func.disable= acm_disable;

status= usb_add_function(c, &acm->port.func);

}

gs_port又是何时创建的?

gserial_setup->gs_port_alloc


tty_struct/gs_port/ gserial是何时关联上的?

intgserial_connect(struct gserial *gser, u8 port_num)

{

structgs_port *port;

port= ports[port_num].port;


/*activate the endpoints */

status= usb_ep_enable(gser->in);

gser->in->driver_data= port;


status= usb_ep_enable(gser->out);

gser->out->driver_data= port;


/*then tell the tty glue that I/O can work */

gser->ioport= port;

port->port_usb= gser;

}


staticint gs_open(struct tty_struct *tty, struct file *file)

{

int port_num= tty->index;

structgs_port *port;


port= ports[port_num].port;

tty->driver_data= port;

port->port_tty= tty;

}


以 init为线索:

acm_function_init -> gserial_setup(cdev->gadget, MAX_ACM_INSTANCES);

int gserial_setup(struct usb_gadget *g, unsigned count)
{
    struct usb_cdc_line_coding    coding;
    for (i = 0; i < count; i++)
        gs_port_alloc(i, &coding);

    gs_tty_driver = alloc_tty_driver(count);
    tty_set_operations(gs_tty_driver, &gs_tty_ops);
    tty_register_driver(gs_tty_driver);
    tty_register_device(gs_tty_driver, i, &g->dev);
}


/*分为两部分:
 *1] register tty_driver: gs_tty_driver;这是就可看到 ttyGS0-3
 *2] 创建 gs_port,关联usb_cdc_line_coding,并赋值给全局变量ports[N_PORTS]

 **/

tty_operations gs_tty_ops 使用gs_port这个结构体

struct gs_port {
    spinlock_t        port_lock;    /* guard port_* access */

    struct gserial        *port_usb;
    struct tty_struct    *port_tty;

    unsigned        open_count;
    bool            openclose;    /* open/close in progress */
    u8            port_num;

    wait_queue_head_t    close_wait;    /* wait for last close */

    struct list_head    read_pool;
    int read_started;
    int read_allocated;
    struct list_head    read_queue;
    unsigned        n_read;
    struct tasklet_struct    push;

    struct list_head    write_pool;
    int write_started;
    int write_allocated;
    struct gs_buf        port_write_buf;
    wait_queue_head_t    drain_wait;    /* wait while writes drain */

    /* REVISIT this state ... */
    struct usb_cdc_line_coding port_line_coding;    /* 8-N-1 etc */

};

以enable function为线索:

acm_function_bind_config -> acm_bind_config

int acm_bind_config(struct usb_configuration *c, u8 port_num)
{
    struct f_acm    *acm;
    /* allocate and initialize one new instance */
    acm = kzalloc(sizeof *acm, GFP_KERNEL);

    acm->port_num = port_num;

    acm->port.connect = acm_connect;
    acm->port.disconnect = acm_disconnect;
    acm->port.send_break = acm_send_break;

    acm->port.func.name = "acm";
    acm->port.func.strings = acm_strings;
    /* descriptors are per-instance copies */
    acm->port.func.bind = acm_bind;
    acm->port.func.unbind = acm_unbind;
    acm->port.func.set_alt = acm_set_alt;
    acm->port.func.setup = acm_setup;
    acm->port.func.disable = acm_disable;

    status = usb_add_function(c, &acm->port.func);
    return status;
}

/*
 *这里创建了f_acm:主要成员有gserial/通知端点
 *gserial 和 gs_port有什么不同?
 **/

struct f_acm {
    struct gserial            port;
    u8                ctrl_id, data_id;
    u8                port_num;
    u8                pending;
    spinlock_t            lock;
    struct usb_ep            *notify;
    struct usb_request        *notify_req;
    struct usb_cdc_line_coding    port_line_coding;    /* 8-N-1 etc */
    /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
    u16                port_handshake_bits;
    /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
    u16                serial_state;
};

/*gserial主要包含:in/out endpoit; notification callbacks; 和gs_port关联;
 *composite需要的usb_function
 **/

struct gserial {
    struct usb_function        func;

    /* port is managed by gserial_{connect,disconnect} */
    struct gs_port            *ioport;

    struct usb_ep            *in;
    struct usb_ep            *out;

    /* REVISIT avoid this CDC-ACM support harder ... */
    struct usb_cdc_line_coding port_line_coding;    /* 9600-8-N-1 etc */

    /* notification callbacks */
    void (*connect)(struct gserial *p);
    void (*disconnect)(struct gserial *p);
    int (*send_break)(struct gserial *p, int duration);
};

usb_add_function -> function->bind();
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
    struct usb_composite_dev *cdev = c->cdev;
    struct f_acm        *acm = func_to_acm(f);
    struct usb_ep        *ep;

    /* allocate instance-specific endpoints */
    ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
    acm->port.in = ep;
    ep->driver_data = cdev;    /* claim */

    ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
    acm->port.out = ep;
    ep->driver_data = cdev;    /* claim */

    ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
    acm->notify = ep;
    ep->driver_data = cdev;    /* claim */

    /* allocate notification */
    acm->notify_req = gs_alloc_req(ep,
            sizeof(struct usb_cdc_notification) + 2,
            GFP_KERNEL);
    acm->notify_req->complete = acm_cdc_notify_complete;
    acm->notify_req->context = acm;
}


以gserail_connect为线索

/* 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.
 **/
composite_setup{
    case USB_REQ_SET_INTERFACE:
        value = f->set_alt(f, w_index, w_value);
        break;

}
acm->port.func.set_alt = acm_set_alt;
acm_set_alt-> gserial_connect(&acm->port, acm->port_num);


tty 使用的数据结构是 gs_port

USB 相关使用的结构体为 f_acm and gserial

在枚举过程中set interface时,这些数据结构关联,tty 层和USB 关联。

int gserial_connect(struct gserial *gser, u8 port_num)
{
    struct gs_port    *port;
    unsigned long    flags;
    int        status;

    /* we "know" gserial_cleanup() hasn't been called */
    port = ports[port_num].port;

    /* activate the endpoints */
    status = usb_ep_enable(gser->in);
    gser->in->driver_data = port;

    status = usb_ep_enable(gser->out);
    gser->out->driver_data = port;

    /* then tell the tty glue that I/O can work */
    spin_lock_irqsave(&port->port_lock, flags);
    gser->ioport = port;
    port->port_usb = gser;

    gser->port_line_coding = port->port_line_coding;

    /* if it's already open, start I/O ... and notify the serial
     * protocol about open/close status (connect/disconnect).
     */
    /*哪里改变这个数?gs_open*/
    if (port->open_count) {
        pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
        gs_start_io(port);
        if (gser->connect)
            gser->connect(gser);
    } else {
        if (gser->disconnect)
            gser->disconnect(gser);
    }

    spin_unlock_irqrestore(&port->port_lock, flags);

    return status;
}

/* connect == the TTY link is open */
static void acm_connect(struct gserial *port)
{
    struct f_acm        *acm = port_to_acm(port);
    acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
    acm_notify_serial_state(acm);
}
acm_notify_serial_state -> acm_cdc_notify -> usb_ep_queue;


以gs_open为线索

static int gs_open(struct tty_struct *tty, struct file *file)
{
    int        port_num = tty->index;
    struct gs_port    *port;
    int        status;

    /* allocate circular buffer on first open */
    if (port->port_write_buf.buf_buf == NULL) {
        spin_unlock_irq(&port->port_lock);
        status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
        spin_lock_irq(&port->port_lock);
    }

    tty->driver_data = port;
    port->port_tty = tty;

    port->open_count = 1;
    port->openclose = false;
    /* if connected, start the I/O stream */
    if (port->port_usb) {
        struct gserial    *gser = port->port_usb;
        gs_start_io(port);

        if (gser->connect)
            gser->connect(gser);
    }
}

static int gs_start_io(struct gs_port *port)
{
    struct list_head    *head = &port->read_pool;
    struct usb_ep        *ep = port->port_usb->out;

    /*read pool and out endpoit*/
    gs_alloc_requests(ep, head, gs_read_complete,&port->read_allocated);

    gs_alloc_requests(port->port_usb->in, &port->write_pool,
                gs_write_complete, &port->write_allocated);

    /* queue read requests */
    port->n_read = 0;
    started = gs_start_rx(port);
}


有关gs_write/read

/******************************************************************************/

static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
    struct gs_port    *port = tty->driver_data;
    unsigned long    flags;
    int        status;

    spin_lock_irqsave(&port->port_lock, flags);
    if (count)
        count = gs_buf_put(&port->port_write_buf, buf, count);

    if (port->port_usb)
        status = gs_start_tx(port);
    spin_unlock_irqrestore(&port->port_lock, flags);

    return count;
}

/* circular buffer
 * 分配了一个大小为8K的write_buffer
 */

struct gs_buf {
    unsigned        buf_size;
    char            *buf_buf;
    char            *buf_get;
    char            *buf_put;
};
#define WRITE_BUF_SIZE        8192
gs_open -> gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE)

gs_write -> gs_buf_put(&port->port_write_buf, buf, count);
/*调用函数gs_buf_put(&port->port_write_buf, buf, count);
 *把来自用户空间的数据copy到port_write_buf,并更新buf_put指针
 **/


gs_start_io ->
    gs_alloc_requests(port->port_usb->in, &port->write_pool,
            gs_write_complete, &port->write_allocated);

/*
 *分配usb_request, 并把usb_request add到port->write_pool中.
 * 形成了一个write_pool指向的usb_requset双向链表,元素的个数为
 * #define QUEUE_SIZE    16
 **/

static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
        void (*fn)(struct usb_ep *, struct usb_request *),
        int *allocated)
{
    int            i;
    struct usb_request    *req;
    int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;

    for (i = 0; i < n; i++) {
        req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
        req->complete = fn;
        list_add_tail(&req->list, head);
        if (allocated)
            (*allocated)++;
    }
    return 0;
}

/*调用端点具体的方法得到usb_requset,把并分配memory for usb_request*/
struct usb_request *
gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
{
    struct usb_request *req;

    req = usb_ep_alloc_request(ep, kmalloc_flags);

    if (req != NULL) {
        req->length = len;
        req->buf = kmalloc(len, kmalloc_flags);
        if (req->buf == NULL) {
            usb_ep_free_request(ep, req);
            return NULL;
        }
    }

    return req;
}

static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
                               gfp_t gfp_flags)
{
    return ep->ops->alloc_request(ep, gfp_flags);
}


/*
 * gs_start_tx
 *
 * This function finds available write requests, calls
 * gs_send_packet to fill these packets with data, and
 * continues until either there are no more write requests
 * available or no more data to send.  This function is
 * run whenever data arrives or write requests are available.
 *
 * Context: caller owns port_lock; port_usb is non-null.
 */

static int gs_start_tx(struct gs_port *port)
{
    struct list_head    *pool = &port->write_pool;
    struct usb_ep        *in = port->port_usb->in;

    while (!list_empty(pool)) {
        struct usb_request    *req;
        int            len;
        /*判断是否还有没有使用的 usb_request*/
        if (port->write_started >= QUEUE_SIZE)
            break;
        /*从链表中得到一个usb_request*/
        req = list_entry(pool->next, struct usb_request, list);
        /*把数据从circular buffer中copy到usb_request的buffer中 */
        len = gs_send_packet(port, req->buf, in->maxpacket);
        /*如果从circular buffer中copy到usb_request的buffer中数据大小是0,
             *则退出发送过程
         **/

        if (len == 0) {/*this variable used to check write finished ?*/
            wake_up_interruptible(&port->drain_wait);
            break;
        }
        do_tty_wake = true;
        /*根据实际的大小,赋值usb_request.length*/
        req->length = len;
        /*把usb_request从list中删除*/
        list_del(&req->list);
        /*根据剩余的数据设置zero标志位*/
        req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
        /*为什么使用port_lock?
             *把usb_request提交到usb_endpoit queue上
         **/
        /* Drop lock while we call out of driver; completions
         * could be issued while we do so.  Disconnection may
         * happen too; maybe immediately before we queue this!
         *
         * NOTE that we may keep sending data for a while after
         * the TTY closed (dev->ioport->port_tty is NULL).
         */

        spin_unlock(&port->port_lock);
        status = usb_ep_queue(in, req, GFP_ATOMIC);
        spin_lock(&port->port_lock);
        /*更新使用的usb_request个数*/
        port->write_started++;

        /* abort immediately after disconnect */
        if (!port->port_usb)
            break;
    }
    
    return status;
}


/*
 *write usb_reqeust处理完成后,调用注册的gs_write_complete
 **/

static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{
    struct gs_port    *port = ep->driver_data;

    spin_lock(&port->port_lock);
    /*把使用完的usb_request放入pool中,减少使用的个数*/
    list_add(&req->list, &port->write_pool);
    port->write_started--;
    /*根据使用后的 usb_request的状态,分别处理*/
    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;
    }

    spin_unlock(&port->port_lock);
}


最后总是调到定义在 gadget.h文件中的gadget_API:usb_ep_queue(in, req, GFP_ATOMIC);

当通过Out endpoint收到数据后,调用注册的回调函数gs_read_complete
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{
    struct gs_port    *port = ep->driver_data;

    /* Queue all received data until the tty layer is ready for it. */
    spin_lock(&port->port_lock);
    list_add_tail(&req->list, &port->read_queue);
    tasklet_schedule(&port->push);
    spin_unlock(&port->port_lock);
}
tasklet_init(&port->push, gs_rx_push, (unsigned long) port);

gs_rx_push(unsigned long _port) -> tty_insert_flip_string(tty, packet, size);

下面分析几种情况:

1. 在插入USB之前,向ttyGSx write data:


static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
    struct gs_port    *port = tty->driver_data;
    unsigned long    flags;
    int        status;

    pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
            port->port_num, tty, count);

    spin_lock_irqsave(&port->port_lock, flags);
    if (count)
        count = gs_buf_put(&port->port_write_buf, buf, count);
    /* treat count == 0 as flush_chars() */
    if (port->port_usb)
        status = gs_start_tx(port);
    spin_unlock_irqrestore(&port->port_lock, flags);

    return count;
}
/*何时分配的port_write_buf?*/
gs_open -> gs_buf_alloc(&port->port_write_buf, 8K);

/*write buffer共8K,如果没有及时传输 buffer满的话,avail space == 0, return 0
 *不会有什么严重的后果
 **/

gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
{
    unsigned len;

    len  = gs_buf_space_avail(gb);
    if (count > len)
        count = len;
    if (count == 0)
        return 0;
    ----
}

2. write failed?

  --