diff --git a/us144mkii.c b/us144mkii.c index 35a1b62..5da5354 100644 --- a/us144mkii.c +++ b/us144mkii.c @@ -10,7 +10,7 @@ MODULE_LICENSE("GPL"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static bool enable[SNDRV_CARDS] = { 1, [1 ...(SNDRV_CARDS - 1)] = 0 }; -static int dev_idx; +static atomic_t dev_idx = ATOMIC_INIT(0); static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *usb_id); @@ -195,16 +195,27 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * struct snd_card *card; struct tascam_card *tascam; int err; + int idx; char *handshake_buf __free(kfree) = NULL; if (intf->cur_altsetting->desc.bInterfaceNumber == 1) return 0; - if (dev_idx >= SNDRV_CARDS) return -ENODEV; - if (!enable[dev_idx]) return -ENOENT; + idx = atomic_fetch_inc(&dev_idx); + if (idx >= SNDRV_CARDS) { + atomic_dec(&dev_idx); + return -ENODEV; + } + if (!enable[idx]) { + atomic_dec(&dev_idx); + return -ENOENT; + } handshake_buf = kmalloc(1, GFP_KERNEL); - if (!handshake_buf) return -ENOMEM; + if (!handshake_buf) { + atomic_dec(&dev_idx); + return -ENOMEM; + } err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV, @@ -212,15 +223,19 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * USB_CTRL_TIMEOUT_MS); if (err < 0) { dev_err(&dev->dev, "Handshake failed: %d\n", err); + atomic_dec(&dev_idx); return err; } usb_set_interface(dev, 0, 1); usb_set_interface(dev, 1, 1); - err = snd_card_new(&dev->dev, index[dev_idx], id[dev_idx], THIS_MODULE, + err = snd_card_new(&dev->dev, index[idx], id[idx], THIS_MODULE, sizeof(struct tascam_card), &card); - if (err < 0) return err; + if (err < 0) { + atomic_dec(&dev_idx); + return err; + } tascam = card->private_data; card->private_free = tascam_card_private_free; @@ -272,11 +287,11 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * if (err < 0) goto free_card; usb_set_intfdata(intf, tascam); - dev_idx++; return 0; free_card: snd_card_free(card); + atomic_dec(&dev_idx); return err; } @@ -286,11 +301,21 @@ static void tascam_disconnect(struct usb_interface *intf) if (!tascam) return; if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + atomic_set(&tascam->playback_active, 0); + atomic_set(&tascam->capture_active, 0); + + usb_kill_anchored_urbs(&tascam->playback_anchor); + usb_kill_anchored_urbs(&tascam->feedback_anchor); + usb_kill_anchored_urbs(&tascam->capture_anchor); + usb_kill_anchored_urbs(&tascam->midi_anchor); + snd_card_disconnect(tascam->card); cancel_work_sync(&tascam->stop_work); cancel_work_sync(&tascam->stop_pcm_work); + + usb_set_intfdata(intf, NULL); snd_card_free(tascam->card); - dev_idx--; + atomic_dec(&dev_idx); } } diff --git a/us144mkii.h b/us144mkii.h index 52d3644..05573ca 100644 --- a/us144mkii.h +++ b/us144mkii.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,19 +15,16 @@ #define DRIVER_NAME "us144mkii" -/* --- USB Device Identification --- */ #define USB_VID_TASCAM 0x0644 #define USB_PID_TASCAM_US144 0x800f #define USB_PID_TASCAM_US144MKII 0x8020 -/* --- USB Endpoints --- */ #define EP_PLAYBACK_FEEDBACK 0x81 #define EP_AUDIO_OUT 0x02 #define EP_MIDI_IN 0x83 #define EP_MIDI_OUT 0x04 #define EP_AUDIO_IN 0x86 -/* --- USB Control Message Protocol --- */ #define RT_H2D_CLASS_EP (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT) #define RT_D2H_CLASS_EP (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT) #define RT_H2D_VENDOR_DEV (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) @@ -66,7 +64,6 @@ enum tascam_register { #define REG_VAL_ENABLE 0x0101 -/* --- URB Configuration --- */ #define NUM_PLAYBACK_URBS 8 #define PLAYBACK_URB_PACKETS 2 #define NUM_FEEDBACK_URBS 2 @@ -75,38 +72,29 @@ enum tascam_register { #define NUM_CAPTURE_URBS 4 #define CAPTURE_PACKET_SIZE 512 -/* --- MIDI Configuration --- */ #define MIDI_PACKET_SIZE 9 #define MIDI_PAYLOAD_SIZE 8 -/* --- Audio Format Configuration --- */ #define BYTES_PER_SAMPLE 3 #define NUM_CHANNELS 4 #define BYTES_PER_FRAME (NUM_CHANNELS * BYTES_PER_SAMPLE) -/* --- PLL Config --- */ #define PLL_FILTER_OLD_WEIGHT 3 #define PLL_FILTER_NEW_WEIGHT 1 #define PLL_FILTER_DIVISOR (PLL_FILTER_OLD_WEIGHT + PLL_FILTER_NEW_WEIGHT) #define USB_CTRL_TIMEOUT_MS 1000 -/** - * struct tascam_card - Main driver data structure for the TASCAM US-144MKII. - */ struct tascam_card { - /* --- Core device pointers --- */ struct usb_device *dev; struct usb_interface *iface0; struct snd_card *card; struct snd_pcm *pcm; struct snd_rawmidi *rmidi; - /* --- PCM Substreams --- */ struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; - /* --- Audio URBs and Anchors --- */ struct urb *playback_urbs[NUM_PLAYBACK_URBS]; size_t playback_urb_alloc_size; struct urb *feedback_urbs[NUM_FEEDBACK_URBS]; @@ -117,7 +105,6 @@ struct tascam_card { struct usb_anchor feedback_anchor; struct usb_anchor capture_anchor; - /* --- MIDI State --- */ struct snd_rawmidi_substream *midi_input; struct snd_rawmidi_substream *midi_output; struct urb *midi_in_urb; @@ -128,43 +115,35 @@ struct tascam_card { bool midi_out_active; spinlock_t midi_lock; - /* --- Stream State --- */ spinlock_t lock; atomic_t playback_active; atomic_t capture_active; atomic_t active_urbs; int current_rate; - /* --- Playback Positions --- */ u64 playback_frames_consumed; snd_pcm_uframes_t driver_playback_pos; u64 last_pb_period_pos; - /* --- Capture Positions --- */ u64 capture_frames_processed; snd_pcm_uframes_t driver_capture_pos; u64 last_cap_period_pos; - /* --- PLL / Feedback State --- */ u32 phase_accum; u32 freq_q16; bool feedback_synced; unsigned int feedback_urb_skip_count; - /* --- Workqueues --- */ struct work_struct stop_work; struct work_struct stop_pcm_work; }; -/* main.c */ void tascam_free_urbs(struct tascam_card *tascam); int tascam_alloc_urbs(struct tascam_card *tascam); void tascam_stop_work_handler(struct work_struct *work); -/* us144mkii_pcm.h */ #include "us144mkii_pcm.h" -/* us144mkii_midi.c */ int tascam_create_midi(struct tascam_card *tascam); #endif /* __US144MKII_H */ diff --git a/us144mkii_capture.c b/us144mkii_capture.c index 57fb301..a126bc7 100644 --- a/us144mkii_capture.c +++ b/us144mkii_capture.c @@ -15,7 +15,7 @@ const struct snd_pcm_hardware tascam_capture_hw = { .channels_min = NUM_CHANNELS, .channels_max = NUM_CHANNELS, .buffer_bytes_max = 1024 * 1024, - .period_bytes_min = 64 * BYTES_PER_FRAME, + .period_bytes_min = 32 * BYTES_PER_FRAME, .period_bytes_max = 1024 * BYTES_PER_FRAME, .periods_min = 2, .periods_max = 1024, @@ -54,11 +54,14 @@ static snd_pcm_uframes_t tascam_capture_pointer(struct snd_pcm_substream *substr struct tascam_card *tascam = snd_pcm_substream_chip(substream); unsigned long flags; u64 pos; + snd_pcm_uframes_t buffer_size = substream->runtime->buffer_size; + if (!atomic_read(&tascam->capture_active)) return 0; spin_lock_irqsave(&tascam->lock, flags); pos = tascam->capture_frames_processed; spin_unlock_irqrestore(&tascam->lock, flags); - return do_div(pos, substream->runtime->buffer_size); + + return (snd_pcm_uframes_t)(pos % buffer_size); } static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) @@ -66,6 +69,7 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) struct tascam_card *tascam = snd_pcm_substream_chip(substream); int i, ret = 0; bool start = false; + bool stop = false; unsigned long flags; spin_lock_irqsave(&tascam->lock, flags); @@ -80,13 +84,8 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - atomic_set(&tascam->capture_active, 0); - - for (i = 0; i < NUM_CAPTURE_URBS; i++) { - if (tascam->capture_urbs[i]) - usb_unlink_urb(tascam->capture_urbs[i]); - } + stop = true; break; default: ret = -EINVAL; @@ -94,13 +93,22 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) } spin_unlock_irqrestore(&tascam->lock, flags); + if (stop) { + smp_mb(); + for (i = 0; i < NUM_CAPTURE_URBS; i++) { + if (tascam->capture_urbs[i]) + usb_unlink_urb(tascam->capture_urbs[i]); + } + } + if (start) { for (i = 0; i < NUM_CAPTURE_URBS; i++) { usb_anchor_urb(tascam->capture_urbs[i], &tascam->capture_anchor); if (usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC) < 0) { + usb_unanchor_urb(tascam->capture_urbs[i]); atomic_set(&tascam->capture_active, 0); - - for (int j = 0; j <= i; j++) + smp_mb(); + for (int j = 0; j < i; j++) usb_unlink_urb(tascam->capture_urbs[j]); ret = -EIO; break; @@ -162,38 +170,48 @@ void capture_urb_complete(struct urb *urb) unsigned long flags; int frames_received; snd_pcm_uframes_t write_pos; + snd_pcm_uframes_t buffer_size, period_size; + bool need_period_elapsed = false; + + if (!tascam) + return; if (urb->status) { atomic_dec(&tascam->active_urbs); return; } - if (!tascam || !atomic_read(&tascam->capture_active)) { + if (!atomic_read(&tascam->capture_active)) { atomic_dec(&tascam->active_urbs); return; } substream = tascam->capture_substream; - if (!substream || !substream->runtime) { + if (!substream) { atomic_dec(&tascam->active_urbs); return; } runtime = substream->runtime; + if (!runtime) { + atomic_dec(&tascam->active_urbs); + return; + } + buffer_size = runtime->buffer_size; + period_size = runtime->period_size; frames_received = urb->actual_length / 64; if (frames_received > 0) { - spin_lock_irqsave(&tascam->lock, flags); write_pos = tascam->driver_capture_pos; spin_unlock_irqrestore(&tascam->lock, flags); u32 *dma_ptr = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, write_pos)); - if (write_pos + frames_received <= runtime->buffer_size) { + if (write_pos + frames_received <= buffer_size) { tascam_decode_capture_chunk(urb->transfer_buffer, dma_ptr, frames_received); } else { - int part1 = runtime->buffer_size - write_pos; + int part1 = buffer_size - write_pos; int part2 = frames_received - part1; tascam_decode_capture_chunk(urb->transfer_buffer, dma_ptr, part1); tascam_decode_capture_chunk(urb->transfer_buffer + (part1 * 64), (u32 *)runtime->dma_area, part2); @@ -201,21 +219,22 @@ void capture_urb_complete(struct urb *urb) spin_lock_irqsave(&tascam->lock, flags); tascam->driver_capture_pos += frames_received; - if (tascam->driver_capture_pos >= runtime->buffer_size) - tascam->driver_capture_pos -= runtime->buffer_size; + if (tascam->driver_capture_pos >= buffer_size) + tascam->driver_capture_pos -= buffer_size; tascam->capture_frames_processed += frames_received; - if (runtime->period_size > 0) { - u64 current_period = div_u64(tascam->capture_frames_processed, runtime->period_size); + if (period_size > 0) { + u64 current_period = div_u64(tascam->capture_frames_processed, period_size); if (current_period > tascam->last_cap_period_pos) { tascam->last_cap_period_pos = current_period; - spin_unlock_irqrestore(&tascam->lock, flags); - snd_pcm_period_elapsed(substream); - spin_lock_irqsave(&tascam->lock, flags); + need_period_elapsed = true; } } spin_unlock_irqrestore(&tascam->lock, flags); + + if (need_period_elapsed) + snd_pcm_period_elapsed(substream); } if (usb_submit_urb(urb, GFP_ATOMIC) < 0) diff --git a/us144mkii_midi.c b/us144mkii_midi.c index 4642967..3603e06 100644 --- a/us144mkii_midi.c +++ b/us144mkii_midi.c @@ -9,6 +9,7 @@ static void tascam_midi_out_complete(struct urb *urb) unsigned long flags; int count; bool submit = false; + bool active; spin_lock_irqsave(&tascam->midi_lock, flags); @@ -18,10 +19,14 @@ static void tascam_midi_out_complete(struct urb *urb) return; } + active = tascam->midi_out_active; + spin_unlock_irqrestore(&tascam->midi_lock, flags); + + if (!active) + return; + count = snd_rawmidi_transmit(tascam->midi_output, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE); if (count > 0) { - spin_unlock_irqrestore(&tascam->midi_lock, flags); - if (count < MIDI_PAYLOAD_SIZE) memset(tascam->midi_out_buf + count, 0xFD, MIDI_PAYLOAD_SIZE - count); @@ -29,6 +34,7 @@ static void tascam_midi_out_complete(struct urb *urb) urb->transfer_buffer_length = MIDI_PACKET_SIZE; submit = true; } else { + spin_lock_irqsave(&tascam->midi_lock, flags); tascam->midi_out_active = false; spin_unlock_irqrestore(&tascam->midi_lock, flags); } @@ -47,22 +53,24 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream, struct tascam_card *tascam = substream->rmidi->private_data; unsigned long flags; int count; + bool do_submit = false; - spin_lock_irqsave(&tascam->midi_lock, flags); if (up) { + spin_lock_irqsave(&tascam->midi_lock, flags); tascam->midi_output = substream; - if (!tascam->midi_out_active) { + tascam->midi_out_active = true; + do_submit = true; + } + spin_unlock_irqrestore(&tascam->midi_lock, flags); + + if (do_submit) { count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE); if (count > 0) { - tascam->midi_out_active = true; - spin_unlock_irqrestore(&tascam->midi_lock, flags); - if (count < MIDI_PAYLOAD_SIZE) memset(tascam->midi_out_buf + count, 0xFD, MIDI_PAYLOAD_SIZE - count); tascam->midi_out_buf[8] = 0xE0; - tascam->midi_out_urb->transfer_buffer_length = MIDI_PACKET_SIZE; usb_anchor_urb(tascam->midi_out_urb, &tascam->midi_anchor); @@ -73,12 +81,13 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&tascam->midi_lock, flags); } } else { + spin_lock_irqsave(&tascam->midi_lock, flags); + tascam->midi_out_active = false; spin_unlock_irqrestore(&tascam->midi_lock, flags); } - } else { - spin_unlock_irqrestore(&tascam->midi_lock, flags); } } else { + spin_lock_irqsave(&tascam->midi_lock, flags); tascam->midi_output = NULL; spin_unlock_irqrestore(&tascam->midi_lock, flags); } @@ -175,14 +184,30 @@ int tascam_create_midi(struct tascam_card *tascam) tascam->rmidi = rmidi; tascam->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tascam->midi_out_urb) { + err = -ENOMEM; + goto err_out_urb; + } + tascam->midi_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!tascam->midi_out_urb || !tascam->midi_in_urb) return -ENOMEM; + if (!tascam->midi_in_urb) { + err = -ENOMEM; + goto err_in_urb; + } tascam->midi_out_buf = usb_alloc_coherent(tascam->dev, MIDI_PACKET_SIZE, GFP_KERNEL, &tascam->midi_out_urb->transfer_dma); + if (!tascam->midi_out_buf) { + err = -ENOMEM; + goto err_out_buf; + } + tascam->midi_in_buf = usb_alloc_coherent(tascam->dev, MIDI_PACKET_SIZE, GFP_KERNEL, &tascam->midi_in_urb->transfer_dma); - if (!tascam->midi_out_buf || !tascam->midi_in_buf) return -ENOMEM; + if (!tascam->midi_in_buf) { + err = -ENOMEM; + goto err_in_buf; + } usb_fill_bulk_urb(tascam->midi_out_urb, tascam->dev, usb_sndbulkpipe(tascam->dev, EP_MIDI_OUT), @@ -200,4 +225,16 @@ int tascam_create_midi(struct tascam_card *tascam) init_usb_anchor(&tascam->midi_anchor); return 0; + + err_in_buf: + usb_free_coherent(tascam->dev, MIDI_PACKET_SIZE, + tascam->midi_out_buf, tascam->midi_out_urb->transfer_dma); + err_out_buf: + usb_free_urb(tascam->midi_in_urb); + tascam->midi_in_urb = NULL; + err_in_urb: + usb_free_urb(tascam->midi_out_urb); + tascam->midi_out_urb = NULL; + err_out_urb: + return err; } diff --git a/us144mkii_playback.c b/us144mkii_playback.c index 4c1ad20..3bf184c 100644 --- a/us144mkii_playback.c +++ b/us144mkii_playback.c @@ -15,7 +15,7 @@ const struct snd_pcm_hardware tascam_playback_hw = { .channels_min = NUM_CHANNELS, .channels_max = NUM_CHANNELS, .buffer_bytes_max = 1024 * 1024, - .period_bytes_min = 64 * BYTES_PER_FRAME, + .period_bytes_min = 32 * BYTES_PER_FRAME, .period_bytes_max = 1024 * BYTES_PER_FRAME, .periods_min = 2, .periods_max = 1024, @@ -86,12 +86,14 @@ static snd_pcm_uframes_t tascam_playback_pointer(struct snd_pcm_substream *subst struct tascam_card *tascam = snd_pcm_substream_chip(substream); unsigned long flags; u64 pos; + snd_pcm_uframes_t buffer_size = substream->runtime->buffer_size; if (!atomic_read(&tascam->playback_active)) return 0; spin_lock_irqsave(&tascam->lock, flags); pos = tascam->playback_frames_consumed; spin_unlock_irqrestore(&tascam->lock, flags); - return do_div(pos, substream->runtime->buffer_size); + + return (snd_pcm_uframes_t)(pos % buffer_size); } static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) @@ -99,6 +101,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) struct tascam_card *tascam = snd_pcm_substream_chip(substream); int i, ret = 0; bool start = false; + bool stop = false; unsigned long flags; spin_lock_irqsave(&tascam->lock, flags); @@ -114,15 +117,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: atomic_set(&tascam->playback_active, 0); - - for (i = 0; i < NUM_FEEDBACK_URBS; i++) { - if (tascam->feedback_urbs[i]) - usb_unlink_urb(tascam->feedback_urbs[i]); - } - for (i = 0; i < NUM_PLAYBACK_URBS; i++) { - if (tascam->playback_urbs[i]) - usb_unlink_urb(tascam->playback_urbs[i]); - } + stop = true; break; default: ret = -EINVAL; @@ -130,20 +125,54 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) } spin_unlock_irqrestore(&tascam->lock, flags); + if (stop) { + smp_mb(); + for (i = 0; i < NUM_FEEDBACK_URBS; i++) { + if (tascam->feedback_urbs[i]) + usb_unlink_urb(tascam->feedback_urbs[i]); + } + for (i = 0; i < NUM_PLAYBACK_URBS; i++) { + if (tascam->playback_urbs[i]) + usb_unlink_urb(tascam->playback_urbs[i]); + } + } + if (start) { for (i = 0; i < NUM_FEEDBACK_URBS; i++) { usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor); - usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC); + if (usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC) < 0) { + usb_unanchor_urb(tascam->feedback_urbs[i]); + dev_err(&tascam->dev->dev, "Failed to submit feedback URB %d\n", i); + atomic_set(&tascam->playback_active, 0); + smp_mb(); + for (int j = 0; j < i; j++) + usb_unlink_urb(tascam->feedback_urbs[j]); + ret = -EIO; + goto error; + } atomic_inc(&tascam->active_urbs); } for (i = 0; i < NUM_PLAYBACK_URBS; i++) { usb_anchor_urb(tascam->playback_urbs[i], &tascam->playback_anchor); - usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC); + if (usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC) < 0) { + usb_unanchor_urb(tascam->playback_urbs[i]); + dev_err(&tascam->dev->dev, "Failed to submit playback URB %d\n", i); + atomic_set(&tascam->playback_active, 0); + smp_mb(); + for (int j = 0; j < NUM_FEEDBACK_URBS; j++) + usb_unlink_urb(tascam->feedback_urbs[j]); + for (int j = 0; j < i; j++) + usb_unlink_urb(tascam->playback_urbs[j]); + ret = -EIO; + goto error; + } atomic_inc(&tascam->active_urbs); } - } else if (atomic_read(&tascam->playback_active) == 0) { + } else if (stop && ret == 0) { schedule_work(&tascam->stop_work); } + + error: return ret; } @@ -157,22 +186,34 @@ void playback_urb_complete(struct urb *urb) snd_pcm_uframes_t frames_to_copy; int i; unsigned long flags; + bool need_period_elapsed = false; + snd_pcm_uframes_t buffer_size, period_size; + + if (!tascam) + return; if (urb->status) { atomic_dec(&tascam->active_urbs); return; } - if (!tascam || !atomic_read(&tascam->playback_active)) { + if (!atomic_read(&tascam->playback_active)) { atomic_dec(&tascam->active_urbs); return; } substream = tascam->playback_substream; - if (!substream || !substream->runtime) { + if (!substream) { atomic_dec(&tascam->active_urbs); return; } runtime = substream->runtime; + if (!runtime) { + atomic_dec(&tascam->active_urbs); + return; + } + + buffer_size = runtime->buffer_size; + period_size = runtime->period_size; spin_lock_irqsave(&tascam->lock, flags); for (i = 0; i < urb->number_of_packets; i++) { @@ -188,14 +229,14 @@ void playback_urb_complete(struct urb *urb) offset_frames = tascam->driver_playback_pos; frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb); - tascam->driver_playback_pos = (offset_frames + frames_to_copy) % runtime->buffer_size; + tascam->driver_playback_pos = (offset_frames + frames_to_copy) % buffer_size; spin_unlock_irqrestore(&tascam->lock, flags); if (total_bytes_for_urb > 0) { u8 *dst_buf = urb->transfer_buffer; size_t ptr_bytes = frames_to_bytes(runtime, offset_frames); - if (offset_frames + frames_to_copy > runtime->buffer_size) { - size_t part1 = runtime->buffer_size - offset_frames; + if (offset_frames + frames_to_copy > buffer_size) { + size_t part1 = buffer_size - offset_frames; memcpy(dst_buf, runtime->dma_area + ptr_bytes, frames_to_bytes(runtime, part1)); memcpy(dst_buf + frames_to_bytes(runtime, part1), runtime->dma_area, total_bytes_for_urb - frames_to_bytes(runtime, part1)); } else { @@ -206,17 +247,18 @@ void playback_urb_complete(struct urb *urb) spin_lock_irqsave(&tascam->lock, flags); tascam->playback_frames_consumed += frames_to_copy; - if (runtime->period_size > 0) { - u64 current_period = div_u64(tascam->playback_frames_consumed, runtime->period_size); + if (period_size > 0) { + u64 current_period = div_u64(tascam->playback_frames_consumed, period_size); if (current_period > tascam->last_pb_period_pos) { tascam->last_pb_period_pos = current_period; - spin_unlock_irqrestore(&tascam->lock, flags); - snd_pcm_period_elapsed(substream); - spin_lock_irqsave(&tascam->lock, flags); + need_period_elapsed = true; } } spin_unlock_irqrestore(&tascam->lock, flags); + if (need_period_elapsed) + snd_pcm_period_elapsed(substream); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) atomic_dec(&tascam->active_urbs); } @@ -227,11 +269,14 @@ void feedback_urb_complete(struct urb *urb) int ret, p; unsigned long flags; + if (!tascam) + return; + if (urb->status) { atomic_dec(&tascam->active_urbs); return; } - if (!tascam || !atomic_read(&tascam->playback_active)) { + if (!atomic_read(&tascam->playback_active)) { atomic_dec(&tascam->active_urbs); return; }