Re: [PATCH 09/10] usb: gadget: adb: Add ADB function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Dec 22, 2011 at 9:21 AM, Benoit Goby <benoit@xxxxxxxxxxx> wrote:
> Android Debug Bridge (adb) is a command line tool that lets
> users communicate with a Android-powered device. It is used
> mainly to debug applications and tranfer files. f_adb implements
> the transport layer between the ADB Server (on the host) and the
> ADBD daemon (on the device).
>
> Signed-off-by: Mike Lockwood <lockwood@xxxxxxxxxxx>
> ---
>  drivers/usb/gadget/android.c |   29 ++
>  drivers/usb/gadget/f_adb.c   |  611 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 640 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/usb/gadget/f_adb.c
>
> diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
> index 1af2402..99fdd71 100644
> --- a/drivers/usb/gadget/android.c
> +++ b/drivers/usb/gadget/android.c
> @@ -45,6 +45,7 @@
>  #include "f_mass_storage.c"
>  #include "u_serial.c"
>  #include "f_acm.c"
> +#include "f_adb.c"
>  #include "f_mtp.c"
>  #define USB_ETH_RNDIS y
>  #include "f_rndis.c"
> @@ -185,6 +186,33 @@ static void android_work(struct work_struct *data)
>  /*-------------------------------------------------------------------------*/
>  /* Supported functions initialization */
>
> +static int
> +adb_function_init(struct android_usb_function *f,
> +               struct usb_composite_dev *cdev)
> +{
> +       return adb_setup();
> +}
> +
> +static void adb_function_cleanup(struct android_usb_function *f)
> +{
> +       adb_cleanup();
> +}
> +
> +static int
> +adb_function_bind_config(struct android_usb_function *f,
> +               struct usb_configuration *c)
> +{
> +       return adb_bind_config(c);
> +}
> +
> +static struct android_usb_function adb_function = {
> +       .name           = "adb",
> +       .init           = adb_function_init,
> +       .cleanup        = adb_function_cleanup,
> +       .bind_config    = adb_function_bind_config,
> +};
> +
> +
>  #define MAX_ACM_INSTANCES 4
>  struct acm_function_config {
>        int instances;
> @@ -606,6 +634,7 @@ static struct android_usb_function mass_storage_function = {
>
>
>  static struct android_usb_function *supported_functions[] = {
> +       &adb_function,
>        &acm_function,
>        &mtp_function,
>        &ptp_function,
> diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
> new file mode 100644
> index 0000000..5415353
> --- /dev/null
> +++ b/drivers/usb/gadget/f_adb.c
> @@ -0,0 +1,611 @@
> +/*
> + * Gadget Driver for Android ADB
> + *
> + * Copyright (C) 2008 Google, Inc.
> + * Author: Mike Lockwood <lockwood@xxxxxxxxxxx>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/poll.h>
> +#include <linux/delay.h>
> +#include <linux/wait.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/sched.h>
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/miscdevice.h>
> +
> +#define ADB_BULK_BUFFER_SIZE           4096
> +
> +/* number of tx requests to allocate */
> +#define TX_REQ_MAX 4
> +
> +static const char adb_shortname[] = "android_adb";
> +
> +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 struct usb_interface_descriptor adb_interface_desc = {
> +       .bLength                = USB_DT_INTERFACE_SIZE,
> +       .bDescriptorType        = USB_DT_INTERFACE,
> +       .bInterfaceNumber       = 0,
> +       .bNumEndpoints          = 2,
> +       .bInterfaceClass        = 0xFF,
> +       .bInterfaceSubClass     = 0x42,
> +       .bInterfaceProtocol     = 1,
> +};
> +
> +static struct usb_endpoint_descriptor adb_highspeed_in_desc = {
> +       .bLength                = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType        = USB_DT_ENDPOINT,
> +       .bEndpointAddress       = USB_DIR_IN,
> +       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
> +       .wMaxPacketSize         = __constant_cpu_to_le16(512),
> +};
> +
> +static struct usb_endpoint_descriptor adb_highspeed_out_desc = {
> +       .bLength                = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType        = USB_DT_ENDPOINT,
> +       .bEndpointAddress       = USB_DIR_OUT,
> +       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
> +       .wMaxPacketSize         = __constant_cpu_to_le16(512),
> +};
> +
> +static struct usb_endpoint_descriptor adb_fullspeed_in_desc = {
> +       .bLength                = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType        = USB_DT_ENDPOINT,
> +       .bEndpointAddress       = USB_DIR_IN,
> +       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
> +};
> +
> +static struct usb_endpoint_descriptor adb_fullspeed_out_desc = {
> +       .bLength                = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType        = USB_DT_ENDPOINT,
> +       .bEndpointAddress       = USB_DIR_OUT,
> +       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
> +};
> +
> +static struct usb_descriptor_header *fs_adb_descs[] = {
> +       (struct usb_descriptor_header *) &adb_interface_desc,
> +       (struct usb_descriptor_header *) &adb_fullspeed_in_desc,
> +       (struct usb_descriptor_header *) &adb_fullspeed_out_desc,
> +       NULL,
> +};
> +
> +static struct usb_descriptor_header *hs_adb_descs[] = {
> +       (struct usb_descriptor_header *) &adb_interface_desc,
> +       (struct usb_descriptor_header *) &adb_highspeed_in_desc,
> +       (struct usb_descriptor_header *) &adb_highspeed_out_desc,
> +       NULL,
> +};
> +
> +
> +/* temporary variable used between adb_open() and adb_gadget_bind() */
> +static struct adb_dev *_adb_dev;
> +
> +static inline struct adb_dev *func_to_adb(struct usb_function *f)
> +{
> +       return container_of(f, struct adb_dev, function);
> +}
> +
> +
> +static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size)
> +{
> +       struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
> +       if (!req)
> +               return NULL;
> +
> +       /* now allocate buffers for the requests */
> +       req->buf = kmalloc(buffer_size, GFP_KERNEL);
> +       if (!req->buf) {
> +               usb_ep_free_request(ep, req);
> +               return NULL;
> +       }
> +
> +       return req;
> +}
> +
> +static void adb_request_free(struct usb_request *req, struct usb_ep *ep)
> +{
> +       if (req) {
> +               kfree(req->buf);
> +               usb_ep_free_request(ep, req);
> +       }
> +}
> +
> +static inline int adb_lock(atomic_t *excl)
> +{
> +       if (atomic_inc_return(excl) == 1) {
> +               return 0;
> +       } else {
> +               atomic_dec(excl);
> +               return -1;
> +       }
> +}
> +
> +static inline void adb_unlock(atomic_t *excl)
> +{
> +       atomic_dec(excl);
> +}
> +
> +/* 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);
> +}
> +
> +/* remove a request from the head of a list */
> +struct usb_request *adb_req_get(struct adb_dev *dev, struct list_head *head)
> +{
> +       unsigned long flags;
> +       struct usb_request *req;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (list_empty(head)) {
> +               req = 0;
> +       } else {
> +               req = list_first_entry(head, struct usb_request, list);
> +               list_del(&req->list);
> +       }
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return req;
> +}
> +
> +static void adb_complete_in(struct usb_ep *ep, struct usb_request *req)
> +{
> +       struct adb_dev *dev = _adb_dev;
> +
> +       if (req->status != 0)
> +               dev->error = 1;
> +
> +       adb_req_put(dev, &dev->tx_idle, req);
> +
> +       wake_up(&dev->write_wq);
> +}
> +
> +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)
> +               dev->error = 1;
> +
> +       wake_up(&dev->read_wq);
> +}
> +
> +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;
> +       int i;
> +
> +       DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
> +
> +       ep = usb_ep_autoconfig(cdev->gadget, in_desc);
> +       if (!ep) {
> +               DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
> +               return -ENODEV;
> +       }
> +       DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
> +       ep->driver_data = dev;          /* claim the endpoint */
> +       dev->ep_in = ep;
> +
> +       ep = usb_ep_autoconfig(cdev->gadget, out_desc);
> +       if (!ep) {
> +               DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
> +               return -ENODEV;
> +       }
> +       DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name);
> +       ep->driver_data = dev;          /* claim the endpoint */
> +       dev->ep_out = ep;
> +
> +       /* now allocate requests for our endpoints */
> +       req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE);
> +       if (!req)
> +               goto fail;
> +       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;
> +
> +fail:
> +       printk(KERN_ERR "adb_bind() could not allocate requests\n");
> +       return -1;
> +}
> +
> +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;
> +       int r = count, xfer;
> +       int ret;
> +
> +       pr_debug("adb_read(%d)\n", count);
> +       if (!_adb_dev)
> +               return -ENODEV;
> +
> +       if (count > ADB_BULK_BUFFER_SIZE)
> +               return -EINVAL;
> +
> +       if (adb_lock(&dev->read_excl))
> +               return -EBUSY;
> +
> +       /* 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;
> +               }
> +       }
> +       if (dev->error) {
> +               r = -EIO;
> +               goto done;
> +       }
> +
> +requeue_req:
> +       /* queue a request */
> +       req = dev->rx_req;
> +       req->length = count;
> +       dev->rx_done = 0;
> +       ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
> +       if (ret < 0) {
> +               pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret);
> +               r = -EIO;
> +               dev->error = 1;
> +               goto done;
> +       } else {
> +               pr_debug("rx %p queue\n", req);
> +       }
> +
> +       /* wait for a request to complete */
> +       ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
> +       if (ret < 0) {
> +               dev->error = 1;
> +               r = ret;
> +               usb_ep_dequeue(dev->ep_out, req);
> +               goto done;
> +       }
> +       if (!dev->error) {
> +               /* If we got a 0-len packet, throw it back and try again. */
> +               if (req->actual == 0)
> +                       goto requeue_req;
> +
> +               pr_debug("rx %p %d\n", req, req->actual);
> +               xfer = (req->actual < count) ? req->actual : count;
> +               if (copy_to_user(buf, req->buf, xfer))
> +                       r = -EFAULT;
> +
> +       } else
> +               r = -EIO;
> +
> +done:
> +       adb_unlock(&dev->read_excl);
> +       pr_debug("adb_read returning %d\n", r);
> +       return r;
> +}
here maybe a little bug, as I ever pointed out in android kernel list,
if the transfer succeed, the
return result is always the length
set by read count, e.g. if you read with length 512, actually
read maybe 200, but the return length is always 512.
following is the patch:

--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -331,6 +331,7 @@ requeue_req:

               DBG(cdev, "rx %p %d\n", req, req->actual);
               xfer = (req->actual < count) ? req->actual : count;
+               r = xfer;
               if (copy_to_user(buf, req->buf, xfer))
                       r = -EFAULT;
       } else


> --
> 1.7.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux