Re: [PATCH v2 1/1] usb: gadget: add raw-gadget interface

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

 



On Fri, Nov 15, 2019 at 2:37 PM Andrey Konovalov <andreyknvl@xxxxxxxxxx> wrote:
>
> USB Raw Gadget is a kernel module that provides a userspace interface for
> the USB Gadget subsystem. Essentially it allows to emulate USB devices
> from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
> currently a strictly debugging feature and shouldn't be used in
> production.
>
> Raw Gadget is similar to GadgetFS, but provides a more low-level and
> direct access to the USB Gadget layer for the userspace. The key
> differences are:
>
> 1. Every USB request is passed to the userspace to get a response, while
>    GadgetFS responds to some USB requests internally based on the provided
>    descriptors. However note, that the UDC driver might respond to some
>    requests on its own and never forward them to the Gadget layer.
>
> 2. GadgetFS performs some sanity checks on the provided USB descriptors,
>    while Raw Gadget allows you to provide arbitrary data as responses to
>    USB requests.
>
> 3. Raw Gadget provides a way to select a UDC device/driver to bind to,
>    while GadgetFS currently binds to the first available UDC.
>
> 4. Raw Gadget uses predictable endpoint names (handles) across different
>    UDCs (as long as UDCs have enough endpoints of each required transfer
>    type).
>
> 5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
>
> Signed-off-by: Andrey Konovalov <andreyknvl@xxxxxxxxxx>
> Change-Id: If35d66c52119c76ba27787ada23b3c00c7208975

Forgot to strip the Change-Id tag here :(

> ---
>  Documentation/usb/index.rst         |    1 +
>  Documentation/usb/raw-gadget.rst    |   60 ++
>  drivers/usb/gadget/legacy/Kconfig   |    8 +
>  drivers/usb/gadget/legacy/Makefile  |    1 +
>  drivers/usb/gadget/legacy/raw.c     | 1057 +++++++++++++++++++++++++++
>  include/uapi/linux/usb/raw_gadget.h |  167 +++++
>  6 files changed, 1294 insertions(+)
>  create mode 100644 Documentation/usb/raw-gadget.rst
>  create mode 100644 drivers/usb/gadget/legacy/raw.c
>  create mode 100644 include/uapi/linux/usb/raw_gadget.h
>
> diff --git a/Documentation/usb/index.rst b/Documentation/usb/index.rst
> index e55386a4abfb..90310e2a0c1f 100644
> --- a/Documentation/usb/index.rst
> +++ b/Documentation/usb/index.rst
> @@ -22,6 +22,7 @@ USB support
>      misc_usbsevseg
>      mtouchusb
>      ohci
> +    raw-gadget
>      rio
>      usbip_protocol
>      usbmon
> diff --git a/Documentation/usb/raw-gadget.rst b/Documentation/usb/raw-gadget.rst
> new file mode 100644
> index 000000000000..8595cd7278df
> --- /dev/null
> +++ b/Documentation/usb/raw-gadget.rst
> @@ -0,0 +1,60 @@
> +==============
> +USB Raw Gadget
> +==============
> +
> +USB Raw Gadget is a kernel module that provides a userspace interface for
> +the USB Gadget subsystem. Essentially it allows to emulate USB devices
> +from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
> +currently a strictly debugging feature and shouldn't be used in
> +production, use GadgetFS instead.
> +
> +Comparison to GadgetFS
> +~~~~~~~~~~~~~~~~~~~~~~
> +
> +Raw Gadget is similar to GadgetFS, but provides a more low-level and
> +direct access to the USB Gadget layer for the userspace. The key
> +differences are:
> +
> +1. Every USB request is passed to the userspace to get a response, while
> +   GadgetFS responds to some USB requests internally based on the provided
> +   descriptors. However note, that the UDC driver might respond to some
> +   requests on its own and never forward them to the Gadget layer.
> +
> +2. GadgetFS performs some sanity checks on the provided USB descriptors,
> +   while Raw Gadget allows you to provide arbitrary data as responses to
> +   USB requests.
> +
> +3. Raw Gadget provides a way to select a UDC device/driver to bind to,
> +   while GadgetFS currently binds to the first available UDC.
> +
> +4. Raw Gadget uses predictable endpoint names (handles) across different
> +   UDCs (as long as UDCs have enough endpoints of each required transfer
> +   type).
> +
> +5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
> +
> +Userspace interface
> +~~~~~~~~~~~~~~~~~~~
> +
> +To create a Raw Gadget instance open /sys/kernel/debug/usb/raw-gadget
> +(debugfs should be enabled and mounted). Multiple raw-gadget instances
> +(bound to different UDCs) can be used at the same time. The interaction
> +with the opened file happens through the ioctl() calls, see comments in
> +include/uapi/linux/usb/raw_gadget.h for details.
> +
> +The typical usage of Raw Gadget looks like:
> +
> +1. Open Raw Gadget instance via /sys/kernel/debug/usb/raw-gadget.
> +2. Initialize the instance via USB_RAW_IOCTL_INIT.
> +3. Launch the instance with USB_RAW_IOCTL_RUN.
> +4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from
> +   Raw Gadget and react to those depending on what kind of USB device
> +   needs to be emulated.
> +
> +Potential future improvements
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +- Implement ioctl's for setting/clearing halt status on endpoints.
> +
> +- Reporting more events (suspend, resume, etc.) through
> +  USB_RAW_IOCTL_EVENT_FETCH.
> diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
> index 69ff7f8c86f5..b04b1471534a 100644
> --- a/drivers/usb/gadget/legacy/Kconfig
> +++ b/drivers/usb/gadget/legacy/Kconfig
> @@ -489,3 +489,11 @@ config USB_G_WEBCAM
>
>           Say "y" to link the driver statically, or "m" to build a
>           dynamically linked module called "g_webcam".
> +
> +config USB_RAW_GADGET
> +       tristate "USB Raw Gadget"
> +       help
> +         USB Raw Gadget is a kernel module that provides a userspace interface
> +         for the USB Gadget subsystem. Essentially it allows to emulate USB
> +         devices from userspace. See Documentation/usb/raw-gadget.rst for
> +         details.
> diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
> index abd0c3e66a05..799feefeee58 100644
> --- a/drivers/usb/gadget/legacy/Makefile
> +++ b/drivers/usb/gadget/legacy/Makefile
> @@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM)    += g_webcam.o
>  obj-$(CONFIG_USB_G_NCM)                += g_ncm.o
>  obj-$(CONFIG_USB_G_ACM_MS)     += g_acm_ms.o
>  obj-$(CONFIG_USB_GADGET_TARGET)        += tcm_usb_gadget.o
> +obj-$(CONFIG_USB_RAW_GADGET)   += raw.o
> diff --git a/drivers/usb/gadget/legacy/raw.c b/drivers/usb/gadget/legacy/raw.c
> new file mode 100644
> index 000000000000..209fb67e5354
> --- /dev/null
> +++ b/drivers/usb/gadget/legacy/raw.c
> @@ -0,0 +1,1057 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USB Raw Gadget driver.
> + * See Documentation/usb/raw-gadget.rst for more details.
> + *
> + * Andrey Konovalov <andreyknvl@xxxxxxxxx>
> + */
> +
> +#define pr_fmt(fmt) "raw: %s: " fmt, __func__
> +
> +#include <linux/compiler.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/kref.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/semaphore.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/wait.h>
> +
> +#include <linux/usb.h>
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/ch11.h>
> +#include <linux/usb/gadget.h>
> +
> +#include <uapi/linux/usb/raw_gadget.h>
> +
> +#define        DRIVER_DESC "USB Raw Gadget"
> +#define DRIVER_NAME "raw-gadget"
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("Andrey Konovalov");
> +MODULE_LICENSE("GPL");
> +
> +/*----------------------------------------------------------------------*/
> +
> +#define RAW_EVENT_QUEUE_SIZE   128
> +
> +struct raw_event_queue {
> +       spinlock_t              lock;
> +       struct semaphore        sema;
> +       struct usb_raw_event    *events[RAW_EVENT_QUEUE_SIZE];
> +       int                     size;
> +};
> +
> +static void raw_event_queue_init(struct raw_event_queue *queue)
> +{
> +       spin_lock_init(&queue->lock);
> +       sema_init(&queue->sema, 0);
> +       queue->size = 0;
> +}
> +
> +static int raw_event_queue_add(struct raw_event_queue *queue,
> +       enum usb_raw_event_type type, size_t length, const void *data)
> +{
> +       unsigned long flags;
> +       struct usb_raw_event *event;
> +
> +       spin_lock_irqsave(&queue->lock, flags);
> +       if (queue->size >= RAW_EVENT_QUEUE_SIZE) {
> +               pr_err("too many events\n");
> +               spin_unlock_irqrestore(&queue->lock, flags);
> +               return -ENOMEM;
> +       }
> +       event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
> +       if (!event) {
> +               spin_unlock_irqrestore(&queue->lock, flags);
> +               return -ENOMEM;
> +       }
> +       event->type = type;
> +       event->length = length;
> +       if (event->length)
> +               memcpy(&event->data[0], data, length);
> +       queue->events[queue->size] = event;
> +       queue->size++;
> +       up(&queue->sema);
> +       spin_unlock_irqrestore(&queue->lock, flags);
> +       return 0;
> +}
> +
> +static struct usb_raw_event *raw_event_queue_fetch(
> +                               struct raw_event_queue *queue)
> +{
> +       unsigned long flags;
> +       struct usb_raw_event *event;
> +
> +       if (down_interruptible(&queue->sema))
> +               return NULL;
> +       spin_lock_irqsave(&queue->lock, flags);
> +       if (WARN_ON(!queue->size))
> +               return NULL;
> +       event = queue->events[0];
> +       queue->size--;
> +       memmove(&queue->events[0], &queue->events[1],
> +                       queue->size * sizeof(queue->events[0]));
> +       spin_unlock_irqrestore(&queue->lock, flags);
> +       return event;
> +}
> +
> +static void raw_event_queue_destroy(struct raw_event_queue *queue)
> +{
> +       int i;
> +
> +       for (i = 0; i < queue->size; i++)
> +               kfree(queue->events[i]);
> +       queue->size = 0;
> +}
> +
> +/*----------------------------------------------------------------------*/
> +
> +struct raw_dev;
> +
> +#define USB_RAW_MAX_ENDPOINTS 32
> +
> +enum ep_state {
> +       STATE_EP_DISABLED,
> +       STATE_EP_ENABLED,
> +};
> +
> +struct raw_ep {
> +       struct raw_dev          *dev;
> +       enum ep_state           state;
> +       struct usb_ep           *ep;
> +       struct usb_request      *req;
> +       bool                    urb_queued;
> +       bool                    disabling;
> +       ssize_t                 status;
> +};
> +
> +enum dev_state {
> +       STATE_DEV_INVALID = 0,
> +       STATE_DEV_OPENED,
> +       STATE_DEV_INITIALIZED,
> +       STATE_DEV_RUNNING,
> +       STATE_DEV_CLOSED,
> +       STATE_DEV_FAILED
> +};
> +
> +struct raw_dev {
> +       struct kref                     count;
> +       spinlock_t                      lock;
> +
> +       const char                      *udc_name;
> +       struct usb_gadget_driver        driver;
> +
> +       /* Protected by lock: */
> +       enum dev_state                  state;
> +       bool                            gadget_registered;
> +       struct usb_gadget               *gadget;
> +       struct usb_request              *req;
> +       bool                            ep0_in_pending;
> +       bool                            ep0_out_pending;
> +       bool                            ep0_urb_queued;
> +       ssize_t                         ep0_status;
> +       struct raw_ep                   eps[USB_RAW_MAX_ENDPOINTS];
> +
> +       struct completion               ep0_done;
> +       struct raw_event_queue          queue;
> +};
> +
> +static struct raw_dev *dev_new(void)
> +{
> +       struct raw_dev *dev;
> +
> +       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> +       if (!dev)
> +               return NULL;
> +       /* Matches kref_put() in raw_release(). */
> +       kref_init(&dev->count);
> +       spin_lock_init(&dev->lock);
> +       init_completion(&dev->ep0_done);
> +       raw_event_queue_init(&dev->queue);
> +       return dev;
> +}
> +
> +static void dev_free(struct kref *kref)
> +{
> +       struct raw_dev *dev = container_of(kref, struct raw_dev, count);
> +       int i;
> +
> +       kfree(dev->udc_name);
> +       kfree(dev->driver.udc_name);
> +       if (dev->req) {
> +               if (dev->ep0_urb_queued)
> +                       usb_ep_dequeue(dev->gadget->ep0, dev->req);
> +               usb_ep_free_request(dev->gadget->ep0, dev->req);
> +       }
> +       raw_event_queue_destroy(&dev->queue);
> +       for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
> +               if (dev->eps[i].state != STATE_EP_ENABLED)
> +                       continue;
> +               usb_ep_disable(dev->eps[i].ep);
> +               usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
> +               kfree(dev->eps[i].ep->desc);
> +               dev->eps[i].state = STATE_EP_DISABLED;
> +       }
> +       kfree(dev);
> +}
> +
> +/*----------------------------------------------------------------------*/
> +
> +static int raw_queue_event(struct raw_dev *dev,
> +       enum usb_raw_event_type type, size_t length, const void *data)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       ret = raw_event_queue_add(&dev->queue, type, length, data);
> +       if (ret < 0) {
> +               spin_lock_irqsave(&dev->lock, flags);
> +               dev->state = STATE_DEV_FAILED;
> +               spin_unlock_irqrestore(&dev->lock, flags);
> +       }
> +       return ret;
> +}
> +
> +static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
> +{
> +       struct raw_dev *dev = req->context;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (req->status)
> +               dev->ep0_status = req->status;
> +       else
> +               dev->ep0_status = req->actual;
> +       if (dev->ep0_in_pending)
> +               dev->ep0_in_pending = false;
> +       else
> +               dev->ep0_out_pending = false;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       complete(&dev->ep0_done);
> +}
> +
> +static int gadget_bind(struct usb_gadget *gadget,
> +                       struct usb_gadget_driver *driver)
> +{
> +       int ret = 0;
> +       struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
> +       struct usb_request *req;
> +       unsigned long flags;
> +
> +       if (strcmp(gadget->name, dev->udc_name) != 0)
> +               return -ENODEV;
> +
> +       set_gadget_data(gadget, dev);
> +       req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
> +       if (!req) {
> +               pr_err("usb_ep_alloc_request failed\n");
> +               set_gadget_data(gadget, NULL);
> +               return -ENOMEM;
> +       }
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       dev->req = req;
> +       dev->req->context = dev;
> +       dev->req->complete = gadget_ep0_complete;
> +       dev->gadget = gadget;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       /* Matches kref_put() in gadget_unbind(). */
> +       kref_get(&dev->count);
> +
> +       ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
> +       if (ret < 0)
> +               pr_err("failed to queue event\n");
> +
> +       return ret;
> +}
> +
> +static void gadget_unbind(struct usb_gadget *gadget)
> +{
> +       struct raw_dev *dev = get_gadget_data(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       set_gadget_data(gadget, NULL);
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       /* Matches kref_get() in gadget_bind(). */
> +       kref_put(&dev->count, dev_free);
> +}
> +
> +static int gadget_setup(struct usb_gadget *gadget,
> +                       const struct usb_ctrlrequest *ctrl)
> +{
> +       int ret = 0;
> +       struct raw_dev *dev = get_gadget_data(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_err("ignoring, device is not running\n");
> +               ret = -ENODEV;
> +               goto out_unlock;
> +       }
> +       if (dev->ep0_in_pending || dev->ep0_out_pending) {
> +               pr_debug("stalling, already have pending request\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
> +               dev->ep0_in_pending = true;
> +       else
> +               dev->ep0_out_pending = true;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
> +       if (ret < 0)
> +               pr_err("failed to queue event\n");
> +       goto out;
> +
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +out:
> +       return ret;
> +}
> +
> +/* These are currently ununsed but present in case UDC driver requires them. */
> +static void gadget_disconnect(struct usb_gadget *gadget) { }
> +static void gadget_suspend(struct usb_gadget *gadget) { }
> +static void gadget_resume(struct usb_gadget *gadget) { }
> +static void gadget_reset(struct usb_gadget *gadget) { }
> +
> +/*----------------------------------------------------------------------*/
> +
> +static int raw_open(struct inode *inode, struct file *fd)
> +{
> +       struct raw_dev *dev;
> +
> +       dev = dev_new();
> +       if (!dev) {
> +               pr_err("failed to created device");
> +               return -ENOMEM;
> +       }
> +       fd->private_data = dev;
> +       dev->state = STATE_DEV_OPENED;
> +       return 0;
> +}
> +
> +static int raw_release(struct inode *inode, struct file *fd)
> +{
> +       int ret = 0;
> +       struct raw_dev *dev = fd->private_data;
> +       unsigned long flags;
> +       bool unregister = false;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       dev->state = STATE_DEV_CLOSED;
> +       if (!dev->gadget) {
> +               spin_unlock_irqrestore(&dev->lock, flags);
> +               goto out_put;
> +       }
> +       if (dev->gadget_registered)
> +               unregister = true;
> +       dev->gadget_registered = false;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       if (unregister) {
> +               ret = usb_gadget_unregister_driver(&dev->driver);
> +               WARN_ON(ret != 0);
> +               /* Matches kref_get() in raw_ioctl_run(). */
> +               kref_put(&dev->count, dev_free);
> +       }
> +
> +out_put:
> +       /* Matches dev_new() in raw_open(). */
> +       kref_put(&dev->count, dev_free);
> +       return ret;
> +}
> +
> +/*----------------------------------------------------------------------*/
> +
> +static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       struct usb_raw_init arg;
> +       char *udc_driver_name;
> +       char *udc_device_name;
> +       unsigned long flags;
> +
> +       ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
> +       if (ret)
> +               return ret;
> +
> +       switch (arg.speed) {
> +       case USB_SPEED_LOW:
> +       case USB_SPEED_FULL:
> +       case USB_SPEED_HIGH:
> +       case USB_SPEED_SUPER:
> +               break;
> +       default:
> +               arg.speed = USB_SPEED_HIGH;
> +       }
> +
> +       udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
> +       if (!udc_driver_name)
> +               return -ENOMEM;
> +       ret = strncpy_from_user(udc_driver_name, arg.driver_name,
> +                                       UDC_NAME_LENGTH_MAX);
> +       if (ret < 0) {
> +               kfree(udc_driver_name);
> +               return ret;
> +       }
> +       ret = 0;
> +
> +       udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
> +       if (!udc_device_name) {
> +               kfree(udc_driver_name);
> +               return -ENOMEM;
> +       }
> +       ret = strncpy_from_user(udc_device_name, arg.device_name,
> +                                       UDC_NAME_LENGTH_MAX);
> +       if (ret < 0) {
> +               kfree(udc_driver_name);
> +               kfree(udc_device_name);
> +               return ret;
> +       }
> +       ret = 0;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_OPENED) {
> +               pr_debug("fail, device is not opened\n");
> +               kfree(udc_driver_name);
> +               kfree(udc_device_name);
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       dev->udc_name = udc_driver_name;
> +
> +       dev->driver.function = DRIVER_DESC;
> +       dev->driver.max_speed = arg.speed;
> +       dev->driver.setup = gadget_setup;
> +       dev->driver.disconnect = gadget_disconnect;
> +       dev->driver.bind = gadget_bind;
> +       dev->driver.unbind = gadget_unbind;
> +       dev->driver.suspend = gadget_suspend;
> +       dev->driver.resume = gadget_resume;
> +       dev->driver.reset = gadget_reset;
> +       dev->driver.driver.name = DRIVER_NAME;
> +       dev->driver.udc_name = udc_device_name;
> +       dev->driver.match_existing_only = 1;
> +
> +       dev->state = STATE_DEV_INITIALIZED;
> +
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       if (value)
> +               return -EINVAL;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_INITIALIZED) {
> +               pr_debug("fail, device is not initialized\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       ret = usb_gadget_probe_driver(&dev->driver);
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (ret) {
> +               pr_err("fail, usb_gadget_probe_driver returned %d\n", ret);
> +               dev->state = STATE_DEV_FAILED;
> +               goto out_unlock;
> +       }
> +       dev->gadget_registered = true;
> +       dev->state = STATE_DEV_RUNNING;
> +       /* Matches kref_put() in raw_release(). */
> +       kref_get(&dev->count);
> +
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       struct usb_raw_event arg;
> +       unsigned long flags;
> +       struct usb_raw_event *event;
> +       uint32_t length;
> +
> +       ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
> +       if (ret)
> +               return ret;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               spin_unlock_irqrestore(&dev->lock, flags);
> +               return -EINVAL;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               spin_unlock_irqrestore(&dev->lock, flags);
> +               return -EBUSY;
> +       }
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       event = raw_event_queue_fetch(&dev->queue);
> +       if (!event) {
> +               pr_debug("event fetching interrupted\n");
> +               return -EINTR;
> +       }
> +       length = min(arg.length, event->length);
> +       ret = copy_to_user((void __user *)value, event,
> +                               sizeof(*event) + length);
> +       return ret;
> +}
> +
> +static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
> +                               bool get_from_user)
> +{
> +       int ret;
> +       void *data;
> +
> +       ret = copy_from_user(io, ptr, sizeof(*io));
> +       if (ret)
> +               return ERR_PTR(ret);
> +       if (io->ep >= USB_RAW_MAX_ENDPOINTS)
> +               return ERR_PTR(-EINVAL);
> +       if (!usb_raw_io_flags_valid(io->flags))
> +               return ERR_PTR(-EINVAL);
> +       if (io->length > PAGE_SIZE)
> +               return ERR_PTR(-EINVAL);
> +       if (get_from_user)
> +               data = memdup_user(ptr + sizeof(*io), io->length);
> +       else {
> +               data = kmalloc(io->length, GFP_KERNEL);
> +               if (!data)
> +                       data = ERR_PTR(-ENOMEM);
> +       }
> +       return data;
> +}
> +
> +static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
> +                               void *data, bool in)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if (dev->ep0_urb_queued) {
> +               pr_debug("fail, urb already queued\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if ((in && !dev->ep0_in_pending) ||
> +                       (!in && !dev->ep0_out_pending)) {
> +               pr_debug("fail, wrong direction\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if (WARN_ON(in && dev->ep0_out_pending)) {
> +               ret = -ENODEV;
> +               dev->state = STATE_DEV_FAILED;
> +               goto out_done;
> +       }
> +       if (WARN_ON(!in && dev->ep0_in_pending)) {
> +               ret = -ENODEV;
> +               dev->state = STATE_DEV_FAILED;
> +               goto out_done;
> +       }
> +
> +       dev->req->buf = data;
> +       dev->req->length = io->length;
> +       dev->req->zero = usb_raw_io_flags_zero(io->flags);
> +       dev->ep0_urb_queued = true;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_ATOMIC);
> +       if (ret) {
> +               pr_err("fail, usb_ep_queue returned %d\n", ret);
> +               spin_lock_irqsave(&dev->lock, flags);
> +               dev->state = STATE_DEV_FAILED;
> +               goto out_done;
> +       }
> +
> +       ret = wait_for_completion_interruptible(&dev->ep0_done);
> +       if (ret) {
> +               pr_debug("wait interrupted\n");
> +               usb_ep_dequeue(dev->gadget->ep0, dev->req);
> +               wait_for_completion(&dev->ep0_done);
> +               spin_lock_irqsave(&dev->lock, flags);
> +               goto out_done;
> +       }
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       ret = dev->ep0_status;
> +
> +out_done:
> +       dev->ep0_urb_queued = false;
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       void *data;
> +       struct usb_raw_ep_io io;
> +
> +       data = raw_alloc_io_data(&io, (void __user *)value, true);
> +       if (IS_ERR(data))
> +               return PTR_ERR(data);
> +       ret = raw_process_ep0_io(dev, &io, data, true);
> +       kfree(data);
> +       return ret;
> +}
> +
> +static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       void *data;
> +       struct usb_raw_ep_io io;
> +       unsigned int length;
> +
> +       data = raw_alloc_io_data(&io, (void __user *)value, false);
> +       if (IS_ERR(data))
> +               return PTR_ERR(data);
> +       ret = raw_process_ep0_io(dev, &io, data, false);
> +       if (ret < 0) {
> +               kfree(data);
> +               return ret;
> +       }
> +       length = min(io.length, (unsigned int)ret);
> +       ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
> +       kfree(data);
> +       return ret;
> +}
> +
> +static bool check_ep_caps(struct usb_ep *ep,
> +                               struct usb_endpoint_descriptor *desc)
> +{
> +       switch (usb_endpoint_type(desc)) {
> +       case USB_ENDPOINT_XFER_ISOC:
> +               if (!ep->caps.type_iso)
> +                       return false;
> +               break;
> +       case USB_ENDPOINT_XFER_BULK:
> +               if (!ep->caps.type_bulk)
> +                       return false;
> +               break;
> +       case USB_ENDPOINT_XFER_INT:
> +               if (!ep->caps.type_int)
> +                       return false;
> +               break;
> +       default:
> +               return false;
> +       }
> +
> +       if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
> +               return false;
> +       if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
> +               return false;
> +
> +       return true;
> +}
> +
> +static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0, i;
> +       unsigned long flags;
> +       struct usb_endpoint_descriptor *desc;
> +       struct usb_ep *ep = NULL;
> +
> +       desc = memdup_user((void __user *)value, sizeof(*desc));
> +       if (IS_ERR(desc))
> +               return PTR_ERR(desc);
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               ret = -EINVAL;
> +               goto out_free;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               ret = -EBUSY;
> +               goto out_free;
> +       }
> +
> +       for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
> +               if (dev->eps[i].state == STATE_EP_ENABLED)
> +                       continue;
> +               break;
> +       }
> +       if (i == USB_RAW_MAX_ENDPOINTS) {
> +               pr_debug("fail, no device endpoints available\n");
> +               ret = -EBUSY;
> +               goto out_free;
> +       }
> +
> +       gadget_for_each_ep(ep, dev->gadget) {
> +               if (ep->enabled)
> +                       continue;
> +               if (!check_ep_caps(ep, desc))
> +                       continue;
> +               ep->desc = desc;
> +               ret = usb_ep_enable(ep);
> +               if (ret < 0) {
> +                       pr_err("fail, usb_ep_enable returned %d\n", ret);
> +                       goto out_free;
> +               }
> +               dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
> +               if (!dev->eps[i].req) {
> +                       pr_err("fail, usb_ep_alloc_request failed\n");
> +                       usb_ep_disable(ep);
> +                       ret = -ENOMEM;
> +                       goto out_free;
> +               }
> +               dev->eps[i].ep = ep;
> +               dev->eps[i].state = STATE_EP_ENABLED;
> +               ep->driver_data = &dev->eps[i];
> +               ret = i;
> +               goto out_unlock;
> +       }
> +
> +       pr_debug("fail, no gadget endpoints available\n");
> +       ret = -EBUSY;
> +
> +out_free:
> +       kfree(desc);
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0, i = value;
> +       unsigned long flags;
> +       const void *desc;
> +
> +       if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS)
> +               return -EINVAL;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if (dev->eps[i].state != STATE_EP_ENABLED) {
> +               pr_debug("fail, endpoint is not enabled\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       if (dev->eps[i].disabling) {
> +               pr_debug("fail, disable already in progress\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       dev->eps[i].disabling = true;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       usb_ep_disable(dev->eps[i].ep);
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
> +       desc = dev->eps[i].ep->desc;
> +       dev->eps[i].ep = NULL;
> +       dev->eps[i].state = STATE_EP_DISABLED;
> +       kfree(desc);
> +       dev->eps[i].disabling = false;
> +
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
> +{
> +       struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
> +       struct raw_dev *dev = r_ep->dev;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (req->status)
> +               r_ep->status = req->status;
> +       else
> +               r_ep->status = req->actual;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       complete((struct completion *)req->context);
> +}
> +
> +static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
> +                               void *data, bool in)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +       struct raw_ep *ep = &dev->eps[io->ep];
> +       DECLARE_COMPLETION_ONSTACK(done);
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if (ep->state != STATE_EP_ENABLED) {
> +               pr_debug("fail, endpoint is not enabled\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if (ep->disabling) {
> +               pr_debug("fail, endpoint is being disabled\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if (ep->urb_queued) {
> +               pr_debug("fail, urb already queued\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       if ((in && !ep->ep->caps.dir_in) || (!in && ep->ep->caps.dir_in)) {
> +               pr_debug("fail, wrong direction\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +
> +       ep->dev = dev;
> +       ep->req->context = &done;
> +       ep->req->complete = gadget_ep_complete;
> +       ep->req->buf = data;
> +       ep->req->length = io->length;
> +       ep->req->zero = usb_raw_io_flags_zero(io->flags);
> +       ep->urb_queued = true;
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +
> +       ret = usb_ep_queue(ep->ep, ep->req, GFP_ATOMIC);
> +       if (ret) {
> +               pr_err("fail, usb_ep_queue returned %d\n", ret);
> +               spin_lock_irqsave(&dev->lock, flags);
> +               dev->state = STATE_DEV_FAILED;
> +               goto out_done;
> +       }
> +
> +       ret = wait_for_completion_interruptible(&done);
> +       if (ret) {
> +               pr_debug("wait interrupted\n");
> +               usb_ep_dequeue(ep->ep, ep->req);
> +               wait_for_completion(&done);
> +               spin_lock_irqsave(&dev->lock, flags);
> +               goto out_done;
> +       }
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       ret = ep->status;
> +
> +out_done:
> +       ep->urb_queued = false;
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       char *data;
> +       struct usb_raw_ep_io io;
> +
> +       data = raw_alloc_io_data(&io, (void __user *)value, true);
> +       if (IS_ERR(data))
> +               return PTR_ERR(data);
> +       ret = raw_process_ep_io(dev, &io, data, true);
> +       kfree(data);
> +       return ret;
> +}
> +
> +static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       char *data;
> +       struct usb_raw_ep_io io;
> +       unsigned int length;
> +
> +       data = raw_alloc_io_data(&io, (void __user *)value, false);
> +       if (IS_ERR(data))
> +               return PTR_ERR(data);
> +       ret = raw_process_ep_io(dev, &io, data, false);
> +       if (ret < 0) {
> +               kfree(data);
> +               return ret;
> +       }
> +       length = min(io.length, (unsigned int)ret);
> +       ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
> +       kfree(data);
> +       return ret;
> +}
> +
> +static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       if (value)
> +               return -EINVAL;
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
> +
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->lock, flags);
> +       if (dev->state != STATE_DEV_RUNNING) {
> +               pr_debug("fail, device is not running\n");
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +       if (!dev->gadget) {
> +               pr_debug("fail, gadget is not bound\n");
> +               ret = -EBUSY;
> +               goto out_unlock;
> +       }
> +       usb_gadget_vbus_draw(dev->gadget, 2 * value);
> +
> +out_unlock:
> +       spin_unlock_irqrestore(&dev->lock, flags);
> +       return ret;
> +}
> +
> +static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
> +{
> +       struct raw_dev *dev = fd->private_data;
> +       int ret = 0;
> +
> +       if (!dev)
> +               return -EBUSY;
> +
> +       switch (cmd) {
> +       case USB_RAW_IOCTL_INIT:
> +               ret = raw_ioctl_init(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_RUN:
> +               ret = raw_ioctl_run(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EVENT_FETCH:
> +               ret = raw_ioctl_event_fetch(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EP0_WRITE:
> +               ret = raw_ioctl_ep0_write(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EP0_READ:
> +               ret = raw_ioctl_ep0_read(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EP_ENABLE:
> +               ret = raw_ioctl_ep_enable(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EP_DISABLE:
> +               ret = raw_ioctl_ep_disable(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EP_WRITE:
> +               ret = raw_ioctl_ep_write(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_EP_READ:
> +               ret = raw_ioctl_ep_read(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_CONFIGURE:
> +               ret = raw_ioctl_configure(dev, value);
> +               break;
> +       case USB_RAW_IOCTL_VBUS_DRAW:
> +               ret = raw_ioctl_vbus_draw(dev, value);
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/*----------------------------------------------------------------------*/
> +
> +static const struct file_operations raw_fops = {
> +       .open =                 raw_open,
> +       .unlocked_ioctl =       raw_ioctl,
> +       .release =              raw_release,
> +       .llseek =               no_llseek,
> +};
> +
> +static struct miscdevice raw_device = {
> +       .minor = MISC_DYNAMIC_MINOR,
> +       .name = "raw-gadget",
> +       .fops = &raw_fops,
> +};
> +
> +static int __init raw_init(void)
> +{
> +       int rv;
> +
> +       rv = misc_register(&raw_device);
> +       if (rv) {
> +               pr_err("failed to register raw-gadget device with %d\n", rv);
> +               return rv;
> +       }
> +       return 0;
> +}
> +
> +static void __exit raw_exit(void)
> +{
> +       misc_deregister(&raw_device);
> +}
> +
> +module_init(raw_init);
> +module_exit(raw_exit);
> diff --git a/include/uapi/linux/usb/raw_gadget.h b/include/uapi/linux/usb/raw_gadget.h
> new file mode 100644
> index 000000000000..33581c7a224d
> --- /dev/null
> +++ b/include/uapi/linux/usb/raw_gadget.h
> @@ -0,0 +1,167 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * USB Raw Gadget driver.
> + *
> + * See Documentation/usb/raw-gadget.rst for more details.
> + */
> +
> +#ifndef _UAPI__LINUX_USB_RAW_GADGET_H
> +#define _UAPI__LINUX_USB_RAW_GADGET_H
> +
> +#include <asm/ioctl.h>
> +#include <linux/types.h>
> +#include <linux/usb/ch9.h>
> +
> +/*
> + * struct usb_raw_init - argument for USB_RAW_IOCTL_INIT ioctl.
> + * @speed: The speed of the emulated USB device, takes the same values as
> + *     the usb_device_speed enum: USB_SPEED_FULL, USB_SPEED_HIGH, etc.
> + * @driver_name: The name of the UDC driver.
> + * @device_name: The name of a UDC instance.
> + *
> + * The last two fields identify a UDC the gadget driver should bind to.
> + * For example, Dummy UDC has "dummy_udc" as its driver_name and "dummy_udc.N"
> + * as its device_name, where N in the index of the Dummy UDC instance.
> + * At the same time the dwc2 driver that is used on Raspberry Pi Zero, has
> + * "20980000.usb" as both driver_name and device_name.
> + */
> +struct usb_raw_init {
> +       __u64           speed;
> +       const __u8      *driver_name;
> +       const __u8      *device_name;
> +};
> +
> +/* Maximum length of driver_name/device_name in the usb_raw_init struct. */
> +#define UDC_NAME_LENGTH_MAX 128
> +
> +/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */
> +enum usb_raw_event_type {
> +       USB_RAW_EVENT_INVALID,
> +
> +       /* This event is queued when the driver has bound to a UDC. */
> +       USB_RAW_EVENT_CONNECT,
> +
> +       /* This event is queued when a new control request arrived to ep0. */
> +       USB_RAW_EVENT_CONTROL,
> +
> +       /* The list might grow in the future. */
> +};
> +
> +/*
> + * struct usb_raw_event - argument for USB_RAW_IOCTL_EVENT_FETCH ioctl.
> + * @type: The type of the fetched event.
> + * @length: Length of the data buffer. Updated by the driver and set to the
> + *     actual length of the fetched event data.
> + * @data: A buffer to store the fetched event data.
> + *
> + * Currently the fetched data buffer is empty for USB_RAW_EVENT_CONNECT,
> + * and contains struct usb_ctrlrequest for USB_RAW_EVENT_CONTROL.
> + */
> +struct usb_raw_event {
> +       __u32           type;
> +       __u32           length;
> +       __u8            data[0];
> +};
> +
> +#define USB_RAW_IO_FLAGS_ZERO  0x0001
> +#define USB_RAW_IO_FLAGS_MASK  0x0001
> +
> +static int usb_raw_io_flags_valid(__u16 flags)
> +{
> +       return (flags & ~USB_RAW_IO_FLAGS_MASK) == 0;
> +}
> +
> +static int usb_raw_io_flags_zero(__u16 flags)
> +{
> +       return (flags & USB_RAW_IO_FLAGS_ZERO);
> +}
> +
> +/*
> + * struct usb_raw_ep_io - argument for USB_RAW_IOCTL_EP0/EP_WRITE/READ ioctls.
> + * @ep: Endpoint handle as returned by USB_RAW_IOCTL_EP_ENABLE for
> + *     USB_RAW_IOCTL_EP_WRITE/READ. Ignored for USB_RAW_IOCTL_EP0_WRITE/READ.
> + * @flags: When USB_RAW_IO_FLAGS_ZERO is specified, the zero flag is set on
> + *     the submitted USB request, see include/linux/usb/gadget.h for details.
> + * @length: Length of data.
> + * @data: Data to send for USB_RAW_IOCTL_EP0/EP_WRITE. Buffer to store received
> + *     data for USB_RAW_IOCTL_EP0/EP_READ.
> + */
> +struct usb_raw_ep_io {
> +       __u16           ep;
> +       __u16           flags;
> +       __u32           length;
> +       __u8            data[0];
> +};
> +
> +/*
> + * Initializes a Raw Gadget instance.
> + * Accepts a pointer to the usb_raw_init struct as an argument.
> + * Returns 0 on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_INIT             _IOW('U', 0, struct usb_raw_init)
> +
> +/*
> + * Instructs Raw Gadget to bind to a UDC and start emulating a USB device.
> + * Returns 0 on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_RUN              _IO('U', 1)
> +
> +/*
> + * A blocking ioctl that waits for an event and returns fetched event data to
> + * the user.
> + * Accepts a pointer to the usb_raw_event struct.
> + * Returns 0 on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_EVENT_FETCH      _IOR('U', 2, struct usb_raw_event)
> +
> +/*
> + * Queues an IN (OUT for READ) urb as a response to the last control request
> + * received on endpoint 0, provided that was an IN (OUT for READ) request and
> + * waits until the urb is completed. Copies received data to user for READ.
> + * Accepts a pointer to the usb_raw_ep_io struct as an argument.
> + * Returns length of trasferred data on success or negative error code on
> + * failure.
> + */
> +#define USB_RAW_IOCTL_EP0_WRITE                _IOW('U', 3, struct usb_raw_ep_io)
> +#define USB_RAW_IOCTL_EP0_READ         _IOWR('U', 4, struct usb_raw_ep_io)
> +
> +/*
> + * Finds an endpoint that supports the transfer type specified in the
> + * descriptor and enables it.
> + * Accepts a pointer to the usb_endpoint_descriptor struct as an argument.
> + * Returns enabled endpoint handle on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_EP_ENABLE                _IOW('U', 5, struct usb_endpoint_descriptor)
> +
> +/* Disables specified endpoint.
> + * Accepts endpoint handle as an argument.
> + * Returns 0 on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_EP_DISABLE       _IOW('U', 6, __u32)
> +
> +/*
> + * Queues an IN (OUT for READ) urb as a response to the last control request
> + * received on endpoint usb_raw_ep_io.ep, provided that was an IN (OUT for READ)
> + * request and waits until the urb is completed. Copies received data to user
> + * for READ.
> + * Accepts a pointer to the usb_raw_ep_io struct as an argument.
> + * Returns length of trasferred data on success or negative error code on
> + * failure.
> + */
> +#define USB_RAW_IOCTL_EP_WRITE         _IOW('U', 7, struct usb_raw_ep_io)
> +#define USB_RAW_IOCTL_EP_READ          _IOWR('U', 8, struct usb_raw_ep_io)
> +
> +/*
> + * Switches the gadget into the configured state.
> + * Returns 0 on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_CONFIGURE                _IO('U', 9)
> +
> +/*
> + * Constrains UDC VBUS power usage.
> + * Accepts current limit in 2 mA units as an argument.
> + * Returns 0 on success or negative error code on failure.
> + */
> +#define USB_RAW_IOCTL_VBUS_DRAW                _IOW('U', 10, __u32)
> +
> +#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */
> --
> 2.24.0.432.g9d3f5f5b63-goog
>



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

  Powered by Linux