USB gadget driver: adb


1. init clue:


/* 1]create data structure: adb_dev
 * 2]调用misc_register(&adb_device);

 **/
struct adb_data {
    bool opened;
    bool enabled;
};

struct adb_dev {
    struct usb_function function;
    struct usb_composite_dev *cdev;
    spinlock_t lock;

    struct usb_ep *ep_in;
    struct usb_ep *ep_out;

    int online;
    int error;

    atomic_t read_excl;
    atomic_t write_excl;
    atomic_t open_excl;

    struct list_head tx_idle;

    wait_queue_head_t read_wq;
    wait_queue_head_t write_wq;
    struct usb_request *rx_req;
    int rx_done;
};

static int
adb_function_init(struct android_usb_function *f,
        struct usb_composite_dev *cdev)
{
    f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL);
    if (!f->config)
        return -ENOMEM;

    return adb_setup();
}


static int adb_setup(void)
{
    struct adb_dev *dev;
    int ret;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);


    spin_lock_init(&dev->lock);

    init_waitqueue_head(&dev->read_wq);
    init_waitqueue_head(&dev->write_wq);

    atomic_set(&dev->open_excl, 0);
    atomic_set(&dev->read_excl, 0);
    atomic_set(&dev->write_excl, 0);

    INIT_LIST_HEAD(&dev->tx_idle);

    _adb_dev = dev;

    ret = misc_register(&adb_device);

    return 0;
}

/* file operations for ADB device /dev/android_adb */
static const struct file_operations adb_fops = {
    .owner = THIS_MODULE,
    .read = adb_read,
    .write = adb_write,
    .open = adb_open,
    .release = adb_release,
};

static struct miscdevice adb_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = adb_shortname,
    .fops = &adb_fops,
};

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

2. enable clue:

adb_function_bind_config -> adb_bind_config(c);

static int adb_bind_config(struct usb_configuration *c)
{
    struct adb_dev *dev = _adb_dev;

    printk(KERN_INFO "adb_bind_config\n");

    dev->cdev = c->cdev;
    dev->function.name = "adb";
    dev->function.descriptors = fs_adb_descs;
    dev->function.hs_descriptors = hs_adb_descs;
    dev->function.bind = adb_function_bind;
    dev->function.unbind = adb_function_unbind;
    dev->function.set_alt = adb_function_set_alt;
    dev->function.disable = adb_function_disable;

    return usb_add_function(c, &dev->function);
}

adb_function_bind(struct usb_configuration *c, struct usb_function *f)
{
    struct usb_composite_dev *cdev = c->cdev;
    struct adb_dev    *dev = func_to_adb(f);
    int            id;
    int            ret;

    dev->cdev = cdev;
    adb_interface_desc.bInterfaceNumber = id;

    /* allocate endpoints */
    ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc,
            &adb_fullspeed_out_desc);
}

static int adb_create_bulk_endpoints(struct adb_dev *dev,
                struct usb_endpoint_descriptor *in_desc,
                struct usb_endpoint_descriptor *out_desc)
{
    struct usb_composite_dev *cdev = dev->cdev;
    struct usb_request *req;
    struct usb_ep *ep;

    ep = usb_ep_autoconfig(cdev->gadget, in_desc);
    ep->driver_data = dev;        /* claim the endpoint */
    dev->ep_in = ep

    ep = usb_ep_autoconfig(cdev->gadget, out_desc);
    ep->driver_data = dev;        /* claim the endpoint */
    dev->ep_out = ep;

    req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE);
    req->complete = adb_complete_out;
    dev->rx_req = req;

    for (i = 0; i < TX_REQ_MAX; i++) {
        req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE);
        if (!req)
            goto fail;
        req->complete = adb_complete_in;
        adb_req_put(dev, &dev->tx_idle, req);
    }
    return 0;
}

/* add a request to the tail of a list */
void adb_req_put(struct adb_dev *dev, struct list_head *head,
        struct usb_request *req)
{
    unsigned long flags;
    spin_lock_irqsave(&dev->lock, flags);
    list_add_tail(&req->list, head);
    spin_unlock_irqrestore(&dev->lock, flags);
}

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

3. set interface



static int adb_function_set_alt(struct usb_function *f,
        unsigned intf, unsigned alt)
{
    struct adb_dev    *dev = func_to_adb(f);
    struct usb_composite_dev *cdev = f->config->cdev;
    int ret;

    DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);

    ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
    ret = usb_ep_enable(dev->ep_in);

    ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
    ret = usb_ep_enable(dev->ep_out);

    dev->online = 1;

    /* readers may be blocked waiting for us to go online */
    wake_up(&dev->read_wq);
    return 0;
}

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

4. adb_open/read/write

static int adb_open(struct inode *ip, struct file *fp)
{

    fp->private_data = _adb_dev;
    return 0;
}

static ssize_t adb_write(struct file *fp, const char __user *buf,
                 size_t count, loff_t *pos)
{
    struct adb_dev *dev = fp->private_data;
    struct usb_request *req = 0;
    while (count > 0) {

        /* get an idle tx request to use */
        req = 0;
        /*什么时候睡?当后面的参数condition 不成立的时候?
                 *什么时候醒?当后面的参数condition 成立的时候?
         **
/
        ret = wait_event_interruptible(dev->write_wq,
            (req = adb_req_get(dev, &dev->tx_idle)) || dev->error);

        if (req != 0) {
            if (count > ADB_BULK_BUFFER_SIZE)
                xfer = ADB_BULK_BUFFER_SIZE;
            else
                xfer = count;
            if (copy_from_user(req->buf, buf, xfer)) {
                r = -EFAULT;
                break;
            }

            req->length = xfer;
            ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);

            buf += xfer;
            count -= xfer;

            /* zero this so we don't try to free it on error exit */
            req = 0;
        }
    }
    return;
}
刚开始的时候条件:req = adb_req_get(dev, &dev->tx_idle)) || dev->error成立,thread肯定不睡。当
dev->tx_idle没有时,系统睡眠。当endpoint发送数据完毕后,调用函数

static void adb_complete_in(struct usb_ep *ep, struct usb_request *req)
{
    struct adb_dev *dev = _adb_dev;
    adb_req_put(dev, &dev->tx_idle, req);

    wake_up(&dev->write_wq);
     /*注意和wake_up_thread的区别,两者有什么的不同的用途?*/
}


static ssize_t adb_read(struct file *fp, char __user *buf,
                size_t count, loff_t *pos)
{
    struct adb_dev *dev = fp->private_data;
    struct usb_request *req;
    /* we will block until we're online */
    while (!(dev->online || dev->error)) {
        pr_debug("adb_read: waiting for online state\n");
        ret = wait_event_interruptible(dev->read_wq,
                (dev->online || dev->error));
        if (ret < 0) {
            adb_unlock(&dev->read_excl);
            return ret;
        }
    }
    /* queue a request */
    req = dev->rx_req;
    req->length = count;
    dev->rx_done = 0;
    ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);

    /* wait for a request to complete */
    ret = wait_event_interruptible(dev->read_wq, dev->rx_done);

    if (!dev->error) {
        /* If we got a 0-len packet, throw it back and try again. */
        xfer = (req->actual < count) ? req->actual : count;
        if (copy_to_user(buf, req->buf, xfer))
            r = -EFAULT;

    }
}

static void adb_complete_out(struct usb_ep *ep, struct usb_request *req)
{
    struct adb_dev *dev = _adb_dev;

    dev->rx_done = 1;
    if (req->status != 0 && req->status != -ECONNRESET)
        dev->error = 1;

    wake_up(&dev->read_wq);

}

ADB driver 看上去比较简单:使用一对 bulk IN/OUT端点,向外导出file operation open/write/read函数,当需要写数据时使用 in endpoint, 从用户空间得到数据

通过in end point 输出; 当读数据时提交一个 usb request,  然后等待接收到数据,最后把接收到的数据copy 到用户空间。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值