]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ALSA: usx2y: Drain pending US-428 pipe-4 output commands
authorCássio Gabriel <cassiogabrielcontato@gmail.com>
Tue, 19 May 2026 03:20:41 +0000 (00:20 -0300)
committerTakashi Iwai <tiwai@suse.de>
Tue, 19 May 2026 06:09:36 +0000 (08:09 +0200)
The US-428 pipe-4 output path submits at most one pending p4out
entry from the shared-memory ring per input interrupt. If userspace
queues more than one command before the interrupt handler runs, later
commands remain pending until later input interrupts, even when async
pipe-4 URBs are available.

Drain pending entries while idle async URBs are available. Copy each
command into the existing per-URB async buffer before submission, so the
submitted transfer does not depend on a userspace-mapped ring slot
remaining unchanged after p4out_sent is advanced.

Also update p4out_sent only after usb_submit_urb() succeeds, so a
failed submission is not reported as sent.

This keeps the shared-memory ABI unchanged and fixes only the local
queue-draining behavior.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260519-alsa-usx2y-p4out-drain-v1-1-8f0a4550bae2@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/usx2y/usbusx2y.c

index f34e78910200aabe7f69dbc34b9a231935d92b3a..4190227c5a2a58d2d4311c391d15527ba7c69c06 100644 (file)
@@ -180,7 +180,7 @@ static void i_usx2y_in04_int(struct urb *urb)
        struct usx2ydev         *usx2y = urb->context;
        struct us428ctls_sharedmem      *us428ctls = usx2y->us428ctls_sharedmem;
        struct us428_p4out *p4out;
-       int i, j, n, diff, send;
+       int i, j, n, diff, send, len;
 
        usx2y->in04_int_calls++;
 
@@ -222,24 +222,31 @@ static void i_usx2y_in04_int(struct urb *urb)
                        } while (!err && usx2y->us04->submitted < usx2y->us04->len);
                }
        } else {
-               if (us428ctls && us428ctls->p4out_last >= 0 && us428ctls->p4out_last < N_US428_P4OUT_BUFS) {
-                       if (us428ctls->p4out_last != us428ctls->p4out_sent) {
-                               send = us428ctls->p4out_sent + 1;
-                               if (send >= N_US428_P4OUT_BUFS)
-                                       send = 0;
-                               for (j = 0; j < URBS_ASYNC_SEQ && !err; ++j) {
-                                       if (!usx2y->as04.urb[j]->status) {
-                                               p4out = us428ctls->p4out + send;        // FIXME if more than 1 p4out is new, 1 gets lost.
-                                               usb_fill_bulk_urb(usx2y->as04.urb[j], usx2y->dev,
-                                                                 usb_sndbulkpipe(usx2y->dev, 0x04), &p4out->val.vol,
-                                                                 p4out->type == ELT_LIGHT ? sizeof(struct us428_lights) : 5,
-                                                                 i_usx2y_out04_int, usx2y);
-                                               err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC);
+               while (us428ctls &&
+                      us428ctls->p4out_last >= 0 &&
+                      us428ctls->p4out_last < N_US428_P4OUT_BUFS &&
+                      us428ctls->p4out_last != us428ctls->p4out_sent) {
+                       for (j = 0; j < URBS_ASYNC_SEQ && !err; ++j) {
+                               if (!usx2y->as04.urb[j]->status) {
+                                       send = us428ctls->p4out_sent + 1;
+                                       if (send >= N_US428_P4OUT_BUFS)
+                                               send = 0;
+
+                                       p4out = us428ctls->p4out + send;
+                                       len = p4out->type == ELT_LIGHT ?
+                                               sizeof(struct us428_lights) : 5;
+                                       memcpy(usx2y->as04.urb[j]->transfer_buffer,
+                                              &p4out->val.vol, len);
+                                       usx2y->as04.urb[j]->transfer_buffer_length = len;
+                                       err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC);
+                                       if (!err)
                                                us428ctls->p4out_sent = send;
-                                               break;
-                                       }
+
+                                       break;
                                }
                        }
+                       if (j >= URBS_ASYNC_SEQ || err)
+                               break;
                }
        }