Fix critical bugs: MIDI byte order, trigger error handling, playback race condition

- BUG-01: Fix MIDI output packet format - header 0xE0 must be at index 0
  per spec, not index 8. Shift payload to indices 1-8.
- BUG-02: Skip header byte (index 0) when parsing incoming MIDI packets
  so ALSA does not receive spurious Active Sensing events.
- BUG-03: Check return values of submit_urbs in playback trigger;
  report -EIO to ALSA if URB submission fails.
- BUG-04: Cache runtime->dma_area and buffer size under spinlock before
  releasing it, eliminating race condition on concurrent close/hw_params.
This commit is contained in:
Marvin 2026-04-22 19:26:32 -03:00
parent 4c9ff01806
commit 57cbd3d53f
2 changed files with 20 additions and 15 deletions

View File

@ -18,12 +18,12 @@ static void tascam_midi_out_complete(struct urb *urb)
return;
}
count = snd_rawmidi_transmit(sub, tascam->midi_out_buf, 8);
count = snd_rawmidi_transmit(sub, tascam->midi_out_buf + 1, 8);
if (count > 0) {
tascam->midi_out_buf[0] = 0xE0;
if (count < 8)
memset(tascam->midi_out_buf + count, 0xFD, 8 - count);
memset(tascam->midi_out_buf + 1 + count, 0xFD, 8 - count);
tascam->midi_out_buf[8] = 0xE0;
urb->transfer_buffer_length = 9;
usb_anchor_urb(urb, &tascam->midi_anchor);
@ -49,12 +49,12 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *sub, int up
tascam->midi_output = sub;
if (!tascam->midi_out_active) {
tascam->midi_out_active = true;
count = snd_rawmidi_transmit(sub, tascam->midi_out_buf, 8);
count = snd_rawmidi_transmit(sub, tascam->midi_out_buf + 1, 8);
if (count > 0) {
tascam->midi_out_buf[0] = 0xE0;
if (count < 8)
memset(tascam->midi_out_buf + count, 0xFD, 8 - count);
memset(tascam->midi_out_buf + 1 + count, 0xFD, 8 - count);
tascam->midi_out_buf[8] = 0xE0;
tascam->midi_out_urb->transfer_buffer_length = 9;
usb_anchor_urb(tascam->midi_out_urb, &tascam->midi_anchor);
@ -87,10 +87,11 @@ static void tascam_midi_in_complete(struct urb *urb)
spin_unlock_irqrestore(&tascam->midi_lock, flags);
if (urb->actual_length == 9 && sub) {
while (len < 8 && ((u8 *)urb->transfer_buffer)[len] != 0xFD)
u8 *buf = (u8 *)urb->transfer_buffer + 1;
while (len < 8 && buf[len] != 0xFD)
len++;
if (len > 0)
snd_rawmidi_receive(sub, urb->transfer_buffer, len);
snd_rawmidi_receive(sub, buf, len);
}
usb_anchor_urb(urb, &tascam->midi_anchor);

View File

@ -179,8 +179,10 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
/* Re-prepare descriptors with current rate in case hw_params changed */
prepare_urb_descriptors(tascam);
} else {
submit_urbs(tascam, tascam->feedback_urbs, NUM_FEEDBACK_URBS, &tascam->feedback_anchor);
submit_urbs(tascam, tascam->playback_urbs, NUM_PLAYBACK_URBS, &tascam->playback_anchor);
if (submit_urbs(tascam, tascam->feedback_urbs, NUM_FEEDBACK_URBS, &tascam->feedback_anchor) < 0 ||
submit_urbs(tascam, tascam->playback_urbs, NUM_PLAYBACK_URBS, &tascam->playback_anchor) < 0) {
ret = -EIO;
}
}
}
break;
@ -285,15 +287,17 @@ void playback_urb_complete(struct urb *urb)
runtime = tascam->playback_substream->runtime;
ptr_bytes = frames_to_bytes(runtime, tascam->driver_playback_pos);
void *dma_area = runtime->dma_area;
size_t buf_size_bytes = frames_to_bytes(runtime, runtime->buffer_size);
spin_unlock_irqrestore(&tascam->lock, flags);
/* Copy from ALSA Buffer */
if (total_bytes + ptr_bytes > frames_to_bytes(runtime, runtime->buffer_size)) {
part1_bytes = frames_to_bytes(runtime, runtime->buffer_size) - ptr_bytes;
memcpy(urb->transfer_buffer, runtime->dma_area + ptr_bytes, part1_bytes);
memcpy(urb->transfer_buffer + part1_bytes, runtime->dma_area, total_bytes - part1_bytes);
if (total_bytes + ptr_bytes > buf_size_bytes) {
part1_bytes = buf_size_bytes - ptr_bytes;
memcpy(urb->transfer_buffer, dma_area + ptr_bytes, part1_bytes);
memcpy(urb->transfer_buffer + part1_bytes, dma_area, total_bytes - part1_bytes);
} else {
memcpy(urb->transfer_buffer, runtime->dma_area + ptr_bytes, total_bytes);
memcpy(urb->transfer_buffer, dma_area + ptr_bytes, total_bytes);
}
spin_lock_irqsave(&tascam->lock, flags);