usb: offload: Decouple interrupter lifecycle and refactor usage tracking
Subsystems (4)
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
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.
--- 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
@@ -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
@@ -93,8 +93,6 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *estatic 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
RFC PATCH v2 2/2 ALSA: usb: qcom: manage offload device usage
In reply to Guan-Yu LinThe 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.
--- sound/usb/qcom/qc_audio_offload.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-)
sound/usb/qcom/qc_audio_offload.c +20 -3
@@ -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
kernel test robot
build botRe: [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##bvim +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