Skip to contentSkip to conversation

usb: offload: Decouple interrupter lifecycle and refactor usage tracking

RFC PATCH Superseded Guan-Yu Lin 2 participants 4 messages Feb 13 – Feb 15

Subsystems (4)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover usb: offload: Decouple interrupter lifecycle and refactor usage tracking

The current USB offload implementation couples the allocation of xHCI sideband interrupters with the device's offload usage counter. This coupling is conceptually incorrect, as hardware resource availability and power management state serve distinct purposes.

This series decouples these mechanisms by removing the usage counting logic from the xHCI sideband layer and shifting the responsibility to the consumer drivers. This allows interrupters to be managed independently of the device's active offload status.

Furthermore, this refactoring addresses a recursive locking issue. When a USB device is disconnected, the USB core invokes the driver's disconnect callback while holding the udev device lock. Previously, the xHCI sideband layer would call usb_offload_put(), which attempted to re-acquire the same udev lock, resulting in a deadlock.

By shifting lock acquisition responsibility to the upper-level USB driver, we ensure that offload usage updates can be safely performed from contexts where the lock is already held. This standardizes the lock hierarchy and prevents potential deadlocks during teardown.

Patch 1 performs the core refactoring by updating the offload APIs to require caller-held device locks and removing implicit usage counting from the interrupter paths.

Patch 2 updates the Qualcomm USB audio offload driver to explicitly manage the usage counter during its stream lifecycle, serving as a derivative consumer of the new API contract. This also ensures the device remains active during playback, preventing premature autosuspend.

---
Changes in v2:
- Move device locking to callers
- Decouple sideband from offload counting.
Link to v1: https://lore.kernel.org/all/[email protected]/
---
Guan-Yu Lin (2):
usb: offload: move device locking to callers in offload.c
ALSA: usb: qcom: manage offload device usage

 drivers/usb/core/offload.c        | 34 +++++++++++--------------------
 drivers/usb/host/xhci-sideband.c  | 14 +------------
 sound/usb/qcom/qc_audio_offload.c | 23 ++++++++++++++++++---
 3 files changed, 33 insertions(+), 38 deletions(-)
Signature · 1 line
2.53.0.273.g2a3d683680-goog
#1 lore raw

RFC PATCH v2 1/2 usb: offload: move device locking to callers in offload.c

Update usb_offload_get() and usb_offload_put() to require that the caller holds the USB device lock. Remove the internal call to usb_lock_device() and add device_lock_assert() to ensure synchronization is handled by the caller. These functions continue to manage the device's power state via autoresume/autosuspend and update the offload_usage counter.

Additionally, decouple the xHCI sideband interrupter lifecycle from the offload usage counter by removing the calls to usb_offload_get() and usb_offload_put() from the interrupter creation and removal paths. This allows interrupters to be managed independently of the device's offload activity status.

Fixes ef82a4803aab ("xhci: sideband: add api to trace sideband usage")
---
 drivers/usb/core/offload.c       | 34 +++++++++++---------------------
 drivers/usb/host/xhci-sideband.c | 14 +------------
 2 files changed, 13 insertions(+), 35 deletions(-)
drivers/usb/core/offload.c +12 -22
index 7c699f1b8d2b..e13a4c21d61b 100644
@@ -20,6 +20,7 @@
  * enabled on this usb_device; that is, another entity is actively handling USB
  * transfers. This information allows the USB driver to adjust its power
  * management policy based on offload activity.
+ * The caller must hold @udev's device lock.
  *
  * Return: 0 on success. A negative error code otherwise.
  */
@@ -27,31 +28,25 @@ int usb_offload_get(struct usb_device *udev)
 {
 	int ret;
 
-	usb_lock_device(udev);
-	if (udev->state == USB_STATE_NOTATTACHED) {
-		usb_unlock_device(udev);
+	device_lock_assert(&udev->dev);
+
+	if (udev->state == USB_STATE_NOTATTACHED)
 		return -ENODEV;
-	}
 
 	if (udev->state == USB_STATE_SUSPENDED ||
-		   udev->offload_at_suspend) {
-		usb_unlock_device(udev);
+	    udev->offload_at_suspend)
 		return -EBUSY;
-	}
 
 	/*
 	 * offload_usage could only be modified when the device is active, since
 	 * it will alter the suspend flow of the device.
 	 */
 	ret = usb_autoresume_device(udev);
-	if (ret < 0) {
-		usb_unlock_device(udev);
+	if (ret < 0)
 		return ret;
-	}
 
 	udev->offload_usage++;
 	usb_autosuspend_device(udev);
-	usb_unlock_device(udev);
 
 	return ret;
 }
@@ -64,6 +59,7 @@ EXPORT_SYMBOL_GPL(usb_offload_get);
  * The inverse operation of usb_offload_get, which drops the offload_usage of
  * a USB device. This information allows the USB driver to adjust its power
  * management policy based on offload activity.
+ * The caller must hold @udev's device lock.
  *
  * Return: 0 on success. A negative error code otherwise.
  */
@@ -71,33 +67,27 @@ int usb_offload_put(struct usb_device *udev)
 {
 	int ret;
 
-	usb_lock_device(udev);
-	if (udev->state == USB_STATE_NOTATTACHED) {
-		usb_unlock_device(udev);
+	device_lock_assert(&udev->dev);
+
+	if (udev->state == USB_STATE_NOTATTACHED)
 		return -ENODEV;
-	}
 
 	if (udev->state == USB_STATE_SUSPENDED ||
-		   udev->offload_at_suspend) {
-		usb_unlock_device(udev);
+	    udev->offload_at_suspend)
 		return -EBUSY;
-	}
 
 	/*
 	 * offload_usage could only be modified when the device is active, since
 	 * it will alter the suspend flow of the device.
 	 */
 	ret = usb_autoresume_device(udev);
-	if (ret < 0) {
-		usb_unlock_device(udev);
+	if (ret < 0)
 		return ret;
-	}
 
 	/* Drop the count when it wasn't 0, ignore the operation otherwise. */
 	if (udev->offload_usage)
 		udev->offload_usage--;
 	usb_autosuspend_device(udev);
-	usb_unlock_device(udev);
 
 	return ret;
 }
drivers/usb/host/xhci-sideband.c +1 -13
index 2bd77255032b..6fc0ad658d66 100644
@@ -93,8 +93,6 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
 static void
 __xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
 {
-	struct usb_device *udev;
-
 	lockdep_assert_held(&sb->mutex);
 
 	if (!sb->ir)
@@ -102,10 +100,6 @@ __xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
 
 	xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
 	sb->ir = NULL;
-	udev = sb->vdev->udev;
-
-	if (udev->state != USB_STATE_NOTATTACHED)
-		usb_offload_put(udev);
 }
 
 /* sideband api functions */
@@ -328,9 +322,6 @@ int
 xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
 				 bool ip_autoclear, u32 imod_interval, int intr_num)
 {
-	int ret = 0;
-	struct usb_device *udev;
-
 	if (!sb || !sb->xhci)
 		return -ENODEV;
 
@@ -348,12 +339,9 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
 	if (!sb->ir)
 		return -ENOMEM;
 
-	udev = sb->vdev->udev;
-	ret = usb_offload_get(udev);
-
 	sb->ir->ip_autoclear = ip_autoclear;
 
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
 
Signature · 1 line
2.53.0.273.g2a3d683680-goog
#2 lore raw

RFC PATCH v2 2/2 ALSA: usb: qcom: manage offload device usage

In reply to Guan-Yu Lin

The Qualcomm USB audio offload driver currently does not report its offload activity to the USB core. This prevents the USB core from properly tracking active offload sessions, which could allow the device to auto-suspend while audio offloading is in progress.

Integrate usb_offload_get() and usb_offload_put() calls into the offload stream setup and teardown paths. Specifically, call usb_offload_get() when initializing the event ring and usb_offload_put() when freeing it.

Since the updated usb_offload_get() and usb_offload_put() APIs require the caller to hold the USB device lock, add the necessary device locking in handle_uaudio_stream_req() and qmi_stop_session() to satisfy this requirement.

Fixes ef82a4803aab ("xhci: sideband: add api to trace sideband usage")
---
 sound/usb/qcom/qc_audio_offload.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)
sound/usb/qcom/qc_audio_offload.c +20 -3
index cfb30a195364..6bcb6033e688 100644
@@ -699,6 +699,7 @@ static void uaudio_event_ring_cleanup_free(struct uaudio_dev *dev)
 		uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
 				   PAGE_SIZE);
 		xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb);
+		usb_offload_put(dev->udev);
 	}
 }
 
@@ -750,6 +751,7 @@ static void qmi_stop_session(void)
 	struct snd_usb_substream *subs;
 	struct usb_host_endpoint *ep;
 	struct snd_usb_audio *chip;
+	struct usb_device *udev;
 	struct intf_info *info;
 	int pcm_card_num;
 	int if_idx;
@@ -791,8 +793,13 @@ static void qmi_stop_session(void)
 			disable_audio_stream(subs);
 		}
 		atomic_set(&uadev[idx].in_use, 0);
-		guard(mutex)(&chip->mutex);
-		uaudio_dev_cleanup(&uadev[idx]);
+
+		udev = uadev[idx].udev;
+		if (udev) {
+			guard(device)(&udev->dev);
+			guard(mutex)(&chip->mutex);
+			uaudio_dev_cleanup(&uadev[idx]);
+		}
 	}
 }
 
@@ -1183,11 +1190,15 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
 	er_pa = 0;
 
 	/* event ring */
+	ret = usb_offload_get(subs->dev);
+	if (ret < 0)
+		goto exit;
+
 	ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false,
 					       0, uaudio_qdev->data->intr_num);
 	if (ret < 0) {
 		dev_err(&subs->dev->dev, "failed to fetch interrupter\n");
-		goto exit;
+		goto put_offload;
 	}
 
 	sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb);
@@ -1219,6 +1230,8 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
 	mem_info->dma = 0;
 remove_interrupter:
 	xhci_sideband_remove_interrupter(uadev[card_num].sb);
+put_offload:
+	usb_offload_put(subs->dev);
 exit:
 	return ret;
 }
@@ -1483,6 +1496,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
 	uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE);
 free_sec_ring:
 	xhci_sideband_remove_interrupter(uadev[card_num].sb);
+	usb_offload_put(subs->dev);
 drop_sync_ep:
 	if (subs->sync_endpoint) {
 		uaudio_iommu_unmap(MEM_XFER_RING,
@@ -1528,6 +1542,7 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
 	u8 pcm_card_num;
 	u8 pcm_dev_num;
 	u8 direction;
+	struct usb_device *udev = NULL;
 	int ret = 0;
 
 	if (!svc->client_connected) {
@@ -1597,6 +1612,8 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
 
 	uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
 
+	udev = subs->dev;
+	guard(device)(&udev->dev);
 	if (req_msg->enable) {
 		ret = enable_audio_stream(subs,
 					  map_pcm_format(req_msg->audio_format),
Signature · 1 line
2.53.0.273.g2a3d683680-goog
#3 lore raw

Re: [RFC PATCH v2 2/2] ALSA: usb: qcom: manage offload device usage

kernel test robot noticed the following build errors:

Hi Guan-Yu,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build errors:

[auto build test ERROR on usb/usb-testing]
[also build test ERROR on usb/usb-next usb/usb-linus tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.19 next-20260213] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Guan-Yu-Lin/usb-offload-move-device-locking-to-callers-in-offload-c/20260213-181132
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20260213100736.2914690-3-guanyulin%40google.com
patch subject: [RFC PATCH v2 2/2] ALSA: usb: qcom: manage offload device usage
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20260215/[email protected]/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260215/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

>> sound/usb/qcom/qc_audio_offload.c:1608:4: error: cannot jump from this goto statement to its label
    1608 |                         goto response;
         |                         ^
   sound/usb/qcom/qc_audio_offload.c:1616:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    1616 |         guard(device)(&udev->dev);
         |         ^
   include/linux/cleanup.h:414:15: note: expanded from macro 'guard'
     414 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:168:2: note: expanded from macro '__UNIQUE_ID'
     168 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:78:1: note: expanded from here
      78 | __UNIQUE_ID_guard_609
         | ^
   sound/usb/qcom/qc_audio_offload.c:1598:5: error: cannot jump from this goto statement to its label
    1598 |                                 goto response;
         |                                 ^
   sound/usb/qcom/qc_audio_offload.c:1616:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    1616 |         guard(device)(&udev->dev);
         |         ^
   include/linux/cleanup.h:414:15: note: expanded from macro 'guard'
     414 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:168:2: note: expanded from macro '__UNIQUE_ID'
     168 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:78:1: note: expanded from here
      78 | __UNIQUE_ID_guard_609
         | ^
   sound/usb/qcom/qc_audio_offload.c:1591:3: error: cannot jump from this goto statement to its label
    1591 |                 goto response;
         |                 ^
   sound/usb/qcom/qc_audio_offload.c:1616:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    1616 |         guard(device)(&udev->dev);
         |         ^
   include/linux/cleanup.h:414:15: note: expanded from macro 'guard'
     414 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:168:2: note: expanded from macro '__UNIQUE_ID'
     168 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:78:1: note: expanded from here
      78 | __UNIQUE_ID_guard_609
         | ^
   sound/usb/qcom/qc_audio_offload.c:1583:3: error: cannot jump from this goto statement to its label
    1583 |                 goto response;
         |                 ^
   sound/usb/qcom/qc_audio_offload.c:1616:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    1616 |         guard(device)(&udev->dev);
         |         ^
   include/linux/cleanup.h:414:15: note: expanded from macro 'guard'
     414 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:168:2: note: expanded from macro '__UNIQUE_ID'
     168 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:78:1: note: expanded from here
      78 | __UNIQUE_ID_guard_609
         | ^
   sound/usb/qcom/qc_audio_offload.c:1576:3: error: cannot jump from this goto statement to its label
    1576 |                 goto response;
         |                 ^
   sound/usb/qcom/qc_audio_offload.c:1616:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    1616 |         guard(device)(&udev->dev);
         |         ^
   include/linux/cleanup.h:414:15: note: expanded from macro 'guard'
     414 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:168:2: note: expanded from macro '__UNIQUE_ID'
     168 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b

vim +1608 sound/usb/qcom/qc_audio_offload.c

326bbc348298ab Wesley Cheng 2025-04-09  1516  
326bbc348298ab Wesley Cheng 2025-04-09  1517  /**
326bbc348298ab Wesley Cheng 2025-04-09  1518   * handle_uaudio_stream_req() - handle stream enable/disable request
326bbc348298ab Wesley Cheng 2025-04-09  1519   * @handle: QMI client handle
326bbc348298ab Wesley Cheng 2025-04-09  1520   * @sq: qrtr socket
326bbc348298ab Wesley Cheng 2025-04-09  1521   * @txn: QMI transaction context
326bbc348298ab Wesley Cheng 2025-04-09  1522   * @decoded_msg: decoded QMI message
326bbc348298ab Wesley Cheng 2025-04-09  1523   *
326bbc348298ab Wesley Cheng 2025-04-09  1524   * Main handler for the QMI stream enable/disable requests.  This executes the
326bbc348298ab Wesley Cheng 2025-04-09  1525   * corresponding enable/disable stream apis, respectively.
326bbc348298ab Wesley Cheng 2025-04-09  1526   *
326bbc348298ab Wesley Cheng 2025-04-09  1527   */
326bbc348298ab Wesley Cheng 2025-04-09  1528  static void handle_uaudio_stream_req(struct qmi_handle *handle,
326bbc348298ab Wesley Cheng 2025-04-09  1529  				     struct sockaddr_qrtr *sq,
326bbc348298ab Wesley Cheng 2025-04-09  1530  				     struct qmi_txn *txn,
326bbc348298ab Wesley Cheng 2025-04-09  1531  				     const void *decoded_msg)
326bbc348298ab Wesley Cheng 2025-04-09  1532  {
326bbc348298ab Wesley Cheng 2025-04-09  1533  	struct qmi_uaudio_stream_req_msg_v01 *req_msg;
326bbc348298ab Wesley Cheng 2025-04-09  1534  	struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0};
326bbc348298ab Wesley Cheng 2025-04-09  1535  	struct uaudio_qmi_svc *svc = uaudio_svc;
326bbc348298ab Wesley Cheng 2025-04-09  1536  	struct snd_usb_audio *chip = NULL;
326bbc348298ab Wesley Cheng 2025-04-09  1537  	struct snd_usb_substream *subs;
326bbc348298ab Wesley Cheng 2025-04-09  1538  	struct usb_host_endpoint *ep;
326bbc348298ab Wesley Cheng 2025-04-09  1539  	int datainterval = -EINVAL;
326bbc348298ab Wesley Cheng 2025-04-09  1540  	int info_idx = -EINVAL;
326bbc348298ab Wesley Cheng 2025-04-09  1541  	struct intf_info *info;
326bbc348298ab Wesley Cheng 2025-04-09  1542  	u8 pcm_card_num;
326bbc348298ab Wesley Cheng 2025-04-09  1543  	u8 pcm_dev_num;
326bbc348298ab Wesley Cheng 2025-04-09  1544  	u8 direction;
0357ca53e65e90 Guan-Yu Lin  2026-02-13  1545  	struct usb_device *udev = NULL;
326bbc348298ab Wesley Cheng 2025-04-09  1546  	int ret = 0;
326bbc348298ab Wesley Cheng 2025-04-09  1547  
326bbc348298ab Wesley Cheng 2025-04-09  1548  	if (!svc->client_connected) {
326bbc348298ab Wesley Cheng 2025-04-09  1549  		svc->client_sq = *sq;
326bbc348298ab Wesley Cheng 2025-04-09  1550  		svc->client_connected = true;
326bbc348298ab Wesley Cheng 2025-04-09  1551  	}
326bbc348298ab Wesley Cheng 2025-04-09  1552  
326bbc348298ab Wesley Cheng 2025-04-09  1553  	mutex_lock(&qdev_mutex);
326bbc348298ab Wesley Cheng 2025-04-09  1554  	req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg;
326bbc348298ab Wesley Cheng 2025-04-09  1555  	if (!req_msg->audio_format_valid || !req_msg->bit_rate_valid ||
326bbc348298ab Wesley Cheng 2025-04-09  1556  	    !req_msg->number_of_ch_valid || !req_msg->xfer_buff_size_valid) {
326bbc348298ab Wesley Cheng 2025-04-09  1557  		ret = -EINVAL;
326bbc348298ab Wesley Cheng 2025-04-09  1558  		goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1559  	}
326bbc348298ab Wesley Cheng 2025-04-09  1560  
326bbc348298ab Wesley Cheng 2025-04-09  1561  	if (!uaudio_qdev) {
326bbc348298ab Wesley Cheng 2025-04-09  1562  		ret = -EINVAL;
326bbc348298ab Wesley Cheng 2025-04-09  1563  		goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1564  	}
326bbc348298ab Wesley Cheng 2025-04-09  1565  
326bbc348298ab Wesley Cheng 2025-04-09  1566  	direction = (req_msg->usb_token & QMI_STREAM_REQ_DIRECTION);
326bbc348298ab Wesley Cheng 2025-04-09  1567  	pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
326bbc348298ab Wesley Cheng 2025-04-09  1568  	pcm_card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
326bbc348298ab Wesley Cheng 2025-04-09  1569  	if (pcm_card_num >= SNDRV_CARDS) {
326bbc348298ab Wesley Cheng 2025-04-09  1570  		ret = -EINVAL;
326bbc348298ab Wesley Cheng 2025-04-09  1571  		goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1572  	}
326bbc348298ab Wesley Cheng 2025-04-09  1573  
326bbc348298ab Wesley Cheng 2025-04-09  1574  	if (req_msg->audio_format > USB_QMI_PCM_FORMAT_U32_BE) {
326bbc348298ab Wesley Cheng 2025-04-09  1575  		ret = -EINVAL;
326bbc348298ab Wesley Cheng 2025-04-09  1576  		goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1577  	}
326bbc348298ab Wesley Cheng 2025-04-09  1578  
326bbc348298ab Wesley Cheng 2025-04-09  1579  	subs = find_substream(pcm_card_num, pcm_dev_num, direction);
326bbc348298ab Wesley Cheng 2025-04-09  1580  	chip = uadev[pcm_card_num].chip;
326bbc348298ab Wesley Cheng 2025-04-09  1581  	if (!subs || !chip || atomic_read(&chip->shutdown)) {
326bbc348298ab Wesley Cheng 2025-04-09  1582  		ret = -ENODEV;
326bbc348298ab Wesley Cheng 2025-04-09  1583  		goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1584  	}
326bbc348298ab Wesley Cheng 2025-04-09  1585  
326bbc348298ab Wesley Cheng 2025-04-09  1586  	info_idx = info_idx_from_ifnum(pcm_card_num, subs->cur_audiofmt ?
326bbc348298ab Wesley Cheng 2025-04-09  1587  			subs->cur_audiofmt->iface : -1, req_msg->enable);
326bbc348298ab Wesley Cheng 2025-04-09  1588  	if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm ||
326bbc348298ab Wesley Cheng 2025-04-09  1589  	    !subs->stream->chip) {
326bbc348298ab Wesley Cheng 2025-04-09  1590  		ret = -ENODEV;
326bbc348298ab Wesley Cheng 2025-04-09  1591  		goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1592  	}
326bbc348298ab Wesley Cheng 2025-04-09  1593  
e5d3eeb4261a3a Takashi Iwai 2025-08-29  1594  	scoped_guard(mutex, &chip->mutex) {
326bbc348298ab Wesley Cheng 2025-04-09  1595  		if (req_msg->enable) {
6a348e9236c336 Wesley Cheng 2025-04-09  1596  			if (info_idx < 0 || chip->system_suspend || subs->opened) {
326bbc348298ab Wesley Cheng 2025-04-09  1597  				ret = -EBUSY;
326bbc348298ab Wesley Cheng 2025-04-09  1598  				goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1599  			}
6a348e9236c336 Wesley Cheng 2025-04-09  1600  			subs->opened = 1;
326bbc348298ab Wesley Cheng 2025-04-09  1601  		}
e5d3eeb4261a3a Takashi Iwai 2025-08-29  1602  	}
326bbc348298ab Wesley Cheng 2025-04-09  1603  
326bbc348298ab Wesley Cheng 2025-04-09  1604  	if (req_msg->service_interval_valid) {
326bbc348298ab Wesley Cheng 2025-04-09  1605  		ret = get_data_interval_from_si(subs,
326bbc348298ab Wesley Cheng 2025-04-09  1606  						req_msg->service_interval);
326bbc348298ab Wesley Cheng 2025-04-09  1607  		if (ret == -EINVAL)
326bbc348298ab Wesley Cheng 2025-04-09 @1608  			goto response;
326bbc348298ab Wesley Cheng 2025-04-09  1609  
326bbc348298ab Wesley Cheng 2025-04-09  1610  		datainterval = ret;
326bbc348298ab Wesley Cheng 2025-04-09  1611  	}
326bbc348298ab Wesley Cheng 2025-04-09  1612  
326bbc348298ab Wesley Cheng 2025-04-09  1613  	uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
326bbc348298ab Wesley Cheng 2025-04-09  1614  
0357ca53e65e90 Guan-Yu Lin  2026-02-13  1615  	udev = subs->dev;
0357ca53e65e90 Guan-Yu Lin  2026-02-13  1616  	guard(device)(&udev->dev);
326bbc348298ab Wesley Cheng 2025-04-09  1617  	if (req_msg->enable) {
326bbc348298ab Wesley Cheng 2025-04-09  1618  		ret = enable_audio_stream(subs,
326bbc348298ab Wesley Cheng 2025-04-09  1619  					  map_pcm_format(req_msg->audio_format),
326bbc348298ab Wesley Cheng 2025-04-09  1620  					  req_msg->number_of_ch, req_msg->bit_rate,
326bbc348298ab Wesley Cheng 2025-04-09  1621  					  datainterval);
326bbc348298ab Wesley Cheng 2025-04-09  1622  
326bbc348298ab Wesley Cheng 2025-04-09  1623  		if (!ret)
326bbc348298ab Wesley Cheng 2025-04-09  1624  			ret = prepare_qmi_response(subs, req_msg, &resp,
326bbc348298ab Wesley Cheng 2025-04-09  1625  						   info_idx);
6a348e9236c336 Wesley Cheng 2025-04-09  1626  		if (ret < 0) {
e5d3eeb4261a3a Takashi Iwai 2025-08-29  1627  			guard(mutex)(&chip->mutex);
6a348e9236c336 Wesley Cheng 2025-04-09  1628  			subs->opened = 0;
6a348e9236c336 Wesley Cheng 2025-04-09  1629  		}
326bbc348298ab Wesley Cheng 2025-04-09  1630  	} else {
326bbc348298ab Wesley Cheng 2025-04-09  1631  		info = &uadev[pcm_card_num].info[info_idx];
326bbc348298ab Wesley Cheng 2025-04-09  1632  		if (info->data_ep_pipe) {
326bbc348298ab Wesley Cheng 2025-04-09  1633  			ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
326bbc348298ab Wesley Cheng 2025-04-09  1634  					       info->data_ep_pipe);
326bbc348298ab Wesley Cheng 2025-04-09  1635  			if (ep) {
326bbc348298ab Wesley Cheng 2025-04-09  1636  				xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
326bbc348298ab Wesley Cheng 2025-04-09  1637  							    ep);
326bbc348298ab Wesley Cheng 2025-04-09  1638  				xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
326bbc348298ab Wesley Cheng 2025-04-09  1639  							      ep);
326bbc348298ab Wesley Cheng 2025-04-09  1640  			}
326bbc348298ab Wesley Cheng 2025-04-09  1641  
326bbc348298ab Wesley Cheng 2025-04-09  1642  			info->data_ep_pipe = 0;
326bbc348298ab Wesley Cheng 2025-04-09  1643  		}
326bbc348298ab Wesley Cheng 2025-04-09  1644  
326bbc348298ab Wesley Cheng 2025-04-09  1645  		if (info->sync_ep_pipe) {
326bbc348298ab Wesley Cheng 2025-04-09  1646  			ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
326bbc348298ab Wesley Cheng 2025-04-09  1647  					       info->sync_ep_pipe);
326bbc348298ab Wesley Cheng 2025-04-09  1648  			if (ep) {
326bbc348298ab Wesley Cheng 2025-04-09  1649  				xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
326bbc348298ab Wesley Cheng 2025-04-09  1650  							    ep);
326bbc348298ab Wesley Cheng 2025-04-09  1651  				xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
326bbc348298ab Wesley Cheng 2025-04-09  1652  							      ep);
326bbc348298ab Wesley Cheng 2025-04-09  1653  			}
326bbc348298ab Wesley Cheng 2025-04-09  1654  
326bbc348298ab Wesley Cheng 2025-04-09  1655  			info->sync_ep_pipe = 0;
326bbc348298ab Wesley Cheng 2025-04-09  1656  		}
326bbc348298ab Wesley Cheng 2025-04-09  1657  
326bbc348298ab Wesley Cheng 2025-04-09  1658  		disable_audio_stream(subs);
e5d3eeb4261a3a Takashi Iwai 2025-08-29  1659  		guard(mutex)(&chip->mutex);
6a348e9236c336 Wesley Cheng 2025-04-09  1660  		subs->opened = 0;
326bbc348298ab Wesley Cheng 2025-04-09  1661  	}
326bbc348298ab Wesley Cheng 2025-04-09  1662  
326bbc348298ab Wesley Cheng 2025-04-09  1663  response:
326bbc348298ab Wesley Cheng 2025-04-09  1664  	if (!req_msg->enable && ret != -EINVAL && ret != -ENODEV) {
e5d3eeb4261a3a Takashi Iwai 2025-08-29  1665  		guard(mutex)(&chip->mutex);
326bbc348298ab Wesley Cheng 2025-04-09  1666  		if (info_idx >= 0) {
326bbc348298ab Wesley Cheng 2025-04-09  1667  			info = &uadev[pcm_card_num].info[info_idx];
326bbc348298ab Wesley Cheng 2025-04-09  1668  			uaudio_dev_intf_cleanup(uadev[pcm_card_num].udev,
326bbc348298ab Wesley Cheng 2025-04-09  1669  						info);
326bbc348298ab Wesley Cheng 2025-04-09  1670  		}
326bbc348298ab Wesley Cheng 2025-04-09  1671  		if (atomic_read(&uadev[pcm_card_num].in_use))
326bbc348298ab Wesley Cheng 2025-04-09  1672  			kref_put(&uadev[pcm_card_num].kref,
326bbc348298ab Wesley Cheng 2025-04-09  1673  				 uaudio_dev_release);
326bbc348298ab Wesley Cheng 2025-04-09  1674  	}
326bbc348298ab Wesley Cheng 2025-04-09  1675  	mutex_unlock(&qdev_mutex);
326bbc348298ab Wesley Cheng 2025-04-09  1676  
326bbc348298ab Wesley Cheng 2025-04-09  1677  	resp.usb_token = req_msg->usb_token;
326bbc348298ab Wesley Cheng 2025-04-09  1678  	resp.usb_token_valid = 1;
326bbc348298ab Wesley Cheng 2025-04-09  1679  	resp.internal_status = ret;
326bbc348298ab Wesley Cheng 2025-04-09  1680  	resp.internal_status_valid = 1;
326bbc348298ab Wesley Cheng 2025-04-09  1681  	resp.status = ret ? USB_QMI_STREAM_REQ_FAILURE_V01 : ret;
326bbc348298ab Wesley Cheng 2025-04-09  1682  	resp.status_valid = 1;
326bbc348298ab Wesley Cheng 2025-04-09  1683  	ret = qmi_send_response(svc->uaudio_svc_hdl, sq, txn,
326bbc348298ab Wesley Cheng 2025-04-09  1684  				QMI_UAUDIO_STREAM_RESP_V01,
326bbc348298ab Wesley Cheng 2025-04-09  1685  				QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN,
326bbc348298ab Wesley Cheng 2025-04-09  1686  				qmi_uaudio_stream_resp_msg_v01_ei, &resp);
326bbc348298ab Wesley Cheng 2025-04-09  1687  }
326bbc348298ab Wesley Cheng 2025-04-09  1688  
Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki