usb: raw-gadget: report suspend, resume, reset, and disconnect events
authorAndrey Konovalov <andreyknvl@gmail.com>
Thu, 26 Oct 2023 20:01:14 +0000 (22:01 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Oct 2023 10:58:16 +0000 (12:58 +0200)
Update USB_RAW_IOCTL_EVENT_FETCH to also report suspend, resume, reset,
and disconnect events.

This allows the code that emulates a USB device via Raw Gadget to handle
these events. For example, the device can restart enumeration when it
gets reset.

Also do not print a WARNING when the event queue overflows. With these new
events being queued, the queue might overflow if the device emulation code
stops fetching events.

Also print debug messages when a non-control event is received.

Signed-off-by: Andrey Konovalov <andreyknvl@gmail.com>
Link: https://lore.kernel.org/r/d610b629a5f32fb76c24012180743f7f0f1872c0.1698350424.git.andreyknvl@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/legacy/raw_gadget.c
include/uapi/linux/usb/raw_gadget.h

index daac1f0..399fca3 100644 (file)
@@ -65,7 +65,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue,
        struct usb_raw_event *event;
 
        spin_lock_irqsave(&queue->lock, flags);
-       if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) {
+       if (queue->size >= RAW_EVENT_QUEUE_SIZE) {
                spin_unlock_irqrestore(&queue->lock, flags);
                return -ENOMEM;
        }
@@ -311,9 +311,10 @@ static int gadget_bind(struct usb_gadget *gadget,
        dev->eps_num = i;
        spin_unlock_irqrestore(&dev->lock, flags);
 
+       dev_dbg(&gadget->dev, "gadget connected\n");
        ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
        if (ret < 0) {
-               dev_err(&gadget->dev, "failed to queue event\n");
+               dev_err(&gadget->dev, "failed to queue connect event\n");
                set_gadget_data(gadget, NULL);
                return ret;
        }
@@ -358,7 +359,7 @@ static int gadget_setup(struct usb_gadget *gadget,
 
        ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
        if (ret < 0)
-               dev_err(&gadget->dev, "failed to queue event\n");
+               dev_err(&gadget->dev, "failed to queue control event\n");
        goto out;
 
 out_unlock:
@@ -377,11 +378,46 @@ out:
        return ret;
 }
 
-/* These are currently unused 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 void gadget_disconnect(struct usb_gadget *gadget)
+{
+       struct raw_dev *dev = get_gadget_data(gadget);
+       int ret;
+
+       dev_dbg(&gadget->dev, "gadget disconnected\n");
+       ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL);
+       if (ret < 0)
+               dev_err(&gadget->dev, "failed to queue disconnect event\n");
+}
+static void gadget_suspend(struct usb_gadget *gadget)
+{
+       struct raw_dev *dev = get_gadget_data(gadget);
+       int ret;
+
+       dev_dbg(&gadget->dev, "gadget suspended\n");
+       ret = raw_queue_event(dev, USB_RAW_EVENT_SUSPEND, 0, NULL);
+       if (ret < 0)
+               dev_err(&gadget->dev, "failed to queue suspend event\n");
+}
+static void gadget_resume(struct usb_gadget *gadget)
+{
+       struct raw_dev *dev = get_gadget_data(gadget);
+       int ret;
+
+       dev_dbg(&gadget->dev, "gadget resumed\n");
+       ret = raw_queue_event(dev, USB_RAW_EVENT_RESUME, 0, NULL);
+       if (ret < 0)
+               dev_err(&gadget->dev, "failed to queue resume event\n");
+}
+static void gadget_reset(struct usb_gadget *gadget)
+{
+       struct raw_dev *dev = get_gadget_data(gadget);
+       int ret;
+
+       dev_dbg(&gadget->dev, "gadget reset\n");
+       ret = raw_queue_event(dev, USB_RAW_EVENT_RESET, 0, NULL);
+       if (ret < 0)
+               dev_err(&gadget->dev, "failed to queue reset event\n");
+}
 
 /*----------------------------------------------------------------------*/
 
index c7d2199..f0224a8 100644 (file)
@@ -44,6 +44,16 @@ enum usb_raw_event_type {
        /* This event is queued when a new control request arrived to ep0. */
        USB_RAW_EVENT_CONTROL = 2,
 
+       /*
+        * These events are queued when the gadget driver is suspended,
+        * resumed, reset, or disconnected. Note that some UDCs (e.g. dwc2)
+        * report a disconnect event instead of a reset.
+        */
+       USB_RAW_EVENT_SUSPEND = 3,
+       USB_RAW_EVENT_RESUME = 4,
+       USB_RAW_EVENT_RESET = 5,
+       USB_RAW_EVENT_DISCONNECT = 6,
+
        /* The list might grow in the future. */
 };
 
@@ -54,8 +64,8 @@ enum usb_raw_event_type {
  *     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.
+ * The fetched event data buffer contains struct usb_ctrlrequest for
+ * USB_RAW_EVENT_CONTROL and is empty for other events.
  */
 struct usb_raw_event {
        __u32           type;