]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usb: xhci: Add timeout argument in address_device USB HCD callback
authorHardik Gajjar <hgajjar@de.adit-jv.com>
Fri, 27 Oct 2023 15:20:28 +0000 (17:20 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 11:47:03 +0000 (12:47 +0100)
[ Upstream commit a769154c7cac037914ba375ae88aae55b2c853e0 ]

- The HCD address_device callback now accepts a user-defined timeout value
  in milliseconds, providing better control over command execution times.
- The default timeout value for the address_device command has been set
  to 5000 ms, aligning with the USB 3.2 specification. However, this
  timeout can be adjusted as needed.
- The xhci_setup_device function has been updated to accept the timeout
  value, allowing it to specify the maximum wait time for the command
  operation to complete.
- The hub driver has also been updated to accommodate the newly added
  timeout parameter during the SET_ADDRESS request.

Signed-off-by: Hardik Gajjar <hgajjar@de.adit-jv.com>
Reviewed-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20231027152029.104363-1-hgajjar@de.adit-jv.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Stable-dep-of: 1e0a19912adb ("usb: xhci: Fix NULL pointer dereference on certain command aborts")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/core/hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
include/linux/usb/hcd.h

index edf61091f202b80f529b4f38f65524c9206ac1d1..6e1e2493507d9e526b42180a1d1c8f5144796b08 100644 (file)
@@ -4662,7 +4662,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
        if (udev->state != USB_STATE_DEFAULT)
                return -EINVAL;
        if (hcd->driver->address_device)
-               retval = hcd->driver->address_device(hcd, udev);
+               retval = hcd->driver->address_device(hcd, udev, USB_CTRL_SET_TIMEOUT);
        else
                retval = usb_control_msg(udev, usb_sndaddr0pipe(),
                                USB_REQ_SET_ADDRESS, 0, devnum, 0,
index 006e1b15fbda9ee408a8061a8f869856ddbc9304..8740bd443f1387fe843628875a6494d2e4f6ac1d 100644 (file)
@@ -1779,6 +1779,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
        }
 
        command->status = 0;
+       /* set default timeout to 5000 ms */
+       command->timeout_ms = XHCI_CMD_DEFAULT_TIMEOUT;
        INIT_LIST_HEAD(&command->cmd_list);
        return command;
 }
index 32c039027d7fe436411218a78b3425f82c8d8135..100f392cd1dfc1fdd28a3b8476b30a5d237ff27c 100644 (file)
@@ -288,9 +288,10 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
        readl(&xhci->dba->doorbell[0]);
 }
 
-static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
+static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci)
 {
-       return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
+       return mod_delayed_work(system_wq, &xhci->cmd_timer,
+                       msecs_to_jiffies(xhci->current_cmd->timeout_ms));
 }
 
 static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci)
@@ -334,7 +335,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
        if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
            !(xhci->xhc_state & XHCI_STATE_DYING)) {
                xhci->current_cmd = cur_cmd;
-               xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+               xhci_mod_cmd_timer(xhci);
                xhci_ring_cmd_db(xhci);
        }
 }
@@ -1687,7 +1688,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
        if (!list_is_singular(&xhci->cmd_list)) {
                xhci->current_cmd = list_first_entry(&cmd->cmd_list,
                                                struct xhci_command, cmd_list);
-               xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+               xhci_mod_cmd_timer(xhci);
        } else if (xhci->current_cmd == cmd) {
                xhci->current_cmd = NULL;
        }
@@ -4203,7 +4204,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
        /* if there are no other commands queued we start the timeout timer */
        if (list_empty(&xhci->cmd_list)) {
                xhci->current_cmd = cmd;
-               xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+               xhci_mod_cmd_timer(xhci);
        }
 
        list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
index 19914d08fc0dd49324e98c6b9db87a7b64b8d87a..908445cff24f807c1111424178e05df0ce5fbf0a 100644 (file)
@@ -4116,12 +4116,18 @@ disable_slot:
        return 0;
 }
 
-/*
- * Issue an Address Device command and optionally send a corresponding
- * SetAddress request to the device.
+/**
+ * xhci_setup_device - issues an Address Device command to assign a unique
+ *                     USB bus address.
+ * @hcd: USB host controller data structure.
+ * @udev: USB dev structure representing the connected device.
+ * @setup: Enum specifying setup mode: address only or with context.
+ * @timeout_ms: Max wait time (ms) for the command operation to complete.
+ *
+ * Return: 0 if successful; otherwise, negative error code.
  */
 static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
-                            enum xhci_setup_dev setup)
+                            enum xhci_setup_dev setup, unsigned int timeout_ms)
 {
        const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address";
        unsigned long flags;
@@ -4178,6 +4184,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
        }
 
        command->in_ctx = virt_dev->in_ctx;
+       command->timeout_ms = timeout_ms;
 
        slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
        ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
@@ -4306,14 +4313,16 @@ out:
        return ret;
 }
 
-static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
+static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev,
+                              unsigned int timeout_ms)
 {
-       return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS);
+       return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS, timeout_ms);
 }
 
 static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
-       return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY);
+       return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY,
+                                XHCI_CMD_DEFAULT_TIMEOUT);
 }
 
 /*
index 6304e9b00ecc66b02ca8287b576eeaf2a65e1813..efce7f40dd949a62e2c117fceef444e9fcb3a033 100644 (file)
@@ -815,6 +815,8 @@ struct xhci_command {
        struct completion               *completion;
        union xhci_trb                  *command_trb;
        struct list_head                cmd_list;
+       /* xHCI command response timeout in milliseconds */
+       unsigned int                    timeout_ms;
 };
 
 /* drop context bitmasks */
@@ -1558,8 +1560,11 @@ struct xhci_td {
        unsigned int            num_trbs;
 };
 
-/* xHCI command default timeout value */
-#define XHCI_CMD_DEFAULT_TIMEOUT       (5 * HZ)
+/*
+ * xHCI command default timeout value in milliseconds.
+ * USB 3.2 spec, section 9.2.6.1
+ */
+#define XHCI_CMD_DEFAULT_TIMEOUT       5000
 
 /* command descriptor */
 struct xhci_cd {
index 4cd545402a6345e96738dc0a2b51427d304f31e9..4ff23d3ad3d98f52bbd728f2eef80223ab46a0c2 100644 (file)
@@ -385,8 +385,9 @@ struct hc_driver {
                 * or bandwidth constraints.
                 */
        void    (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
-               /* Returns the hardware-chosen device address */
-       int     (*address_device)(struct usb_hcd *, struct usb_device *udev);
+               /* Set the hardware-chosen device address */
+       int     (*address_device)(struct usb_hcd *, struct usb_device *udev,
+                                 unsigned int timeout_ms);
                /* prepares the hardware to send commands to the device */
        int     (*enable_device)(struct usb_hcd *, struct usb_device *udev);
                /* Notifies the HCD after a hub descriptor is fetched.