diff --git a/build_and_install.sh b/build_and_install.sh old mode 100644 new mode 100755 diff --git a/tascam_controls/build_appimage.sh b/tascam_controls/build_appimage.sh old mode 100644 new mode 100755 diff --git a/us144mkii.c b/us144mkii.c index c8da90e..f6572a5 100644 --- a/us144mkii.c +++ b/us144mkii.c @@ -258,7 +258,16 @@ error: return -ENOMEM; } +void tascam_stop_work_handler(struct work_struct *work) +{ + struct tascam_card *tascam = + container_of(work, struct tascam_card, stop_work); + usb_kill_anchored_urbs(&tascam->playback_anchor); + usb_kill_anchored_urbs(&tascam->feedback_anchor); + usb_kill_anchored_urbs(&tascam->capture_anchor); + atomic_set(&tascam->active_urbs, 0); +} /** * tascam_card_private_free() - Frees private data associated with the sound @@ -302,6 +311,7 @@ static int tascam_suspend(struct usb_interface *intf, pm_message_t message) snd_pcm_suspend_all(tascam->pcm); + cancel_work_sync(&tascam->stop_work); cancel_work_sync(&tascam->capture_work); cancel_work_sync(&tascam->midi_in_work); cancel_work_sync(&tascam->midi_out_work); @@ -483,10 +493,8 @@ static int tascam_probe(struct usb_interface *intf, tascam->capture_34_source = 1; spin_lock_init(&tascam->lock); - spin_lock_init(&tascam->trigger_lock); spin_lock_init(&tascam->midi_in_lock); spin_lock_init(&tascam->midi_out_lock); - atomic_set(&tascam->stream_active, 0); init_usb_anchor(&tascam->playback_anchor); init_usb_anchor(&tascam->capture_anchor); init_usb_anchor(&tascam->feedback_anchor); @@ -495,6 +503,7 @@ static int tascam_probe(struct usb_interface *intf, timer_setup(&tascam->error_timer, tascam_error_timer, 0); + INIT_WORK(&tascam->stop_work, tascam_stop_work_handler); INIT_WORK(&tascam->stop_pcm_work, tascam_stop_pcm_work_handler); INIT_WORK(&tascam->capture_work, tascam_capture_work_handler); init_completion(&tascam->midi_out_drain_completion); @@ -574,6 +583,7 @@ static void tascam_disconnect(struct usb_interface *intf) if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { /* Ensure all deferred work is complete before freeing resources */ snd_card_disconnect(tascam->card); + cancel_work_sync(&tascam->stop_work); cancel_work_sync(&tascam->capture_work); cancel_work_sync(&tascam->midi_in_work); cancel_work_sync(&tascam->midi_out_work); diff --git a/us144mkii.h b/us144mkii.h index cb31364..95c4341 100644 --- a/us144mkii.h +++ b/us144mkii.h @@ -229,10 +229,8 @@ struct tascam_card { /* --- Stream State --- */ spinlock_t lock; - spinlock_t trigger_lock; atomic_t playback_active; atomic_t capture_active; - atomic_t stream_active; atomic_t active_urbs; int current_rate; @@ -275,6 +273,7 @@ struct tascam_card { struct us144mkii_frame_pattern_observer fpo; /* --- Workqueues --- */ + struct work_struct stop_work; struct work_struct stop_pcm_work; struct work_struct capture_work; struct work_struct midi_in_work; @@ -309,7 +308,14 @@ void tascam_free_urbs(struct tascam_card *tascam); */ int tascam_alloc_urbs(struct tascam_card *tascam); - +/** + * tascam_stop_work_handler() - Work handler to stop all active streams. + * @work: Pointer to the work_struct. + * + * This function is scheduled to stop all active URBs (playback, feedback, + * capture) and reset the active_urbs counter. + */ +void tascam_stop_work_handler(struct work_struct *work); /* us144mkii_pcm.h */ #include "us144mkii_pcm.h" diff --git a/us144mkii_pcm.c b/us144mkii_pcm.c index e30470d..886b0c5 100644 --- a/us144mkii_pcm.c +++ b/us144mkii_pcm.c @@ -282,103 +282,97 @@ int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct tascam_card *tascam = snd_pcm_substream_chip(substream); int err = 0; int i; - unsigned long flags; + bool do_start = false; + bool do_stop = false; - spin_lock_irqsave(&tascam->trigger_lock, flags); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (atomic_xchg(&tascam->playback_active, 1) == 0) { - if (atomic_fetch_inc(&tascam->stream_active) == 0) { - for (i = 0; i < NUM_FEEDBACK_URBS; i++) { - usb_get_urb(tascam->feedback_urbs[i]); - usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor); - err = usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC); - if (err < 0) { - usb_unanchor_urb(tascam->feedback_urbs[i]); - usb_put_urb(tascam->feedback_urbs[i]); - atomic_dec(&tascam->active_urbs); - break; - } - atomic_inc(&tascam->active_urbs); - } - } - - for (i = 0; i < NUM_PLAYBACK_URBS; i++) { - usb_get_urb(tascam->playback_urbs[i]); - usb_anchor_urb(tascam->playback_urbs[i], &tascam->playback_anchor); - err = usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC); - if (err < 0) { - usb_unanchor_urb(tascam->playback_urbs[i]); - usb_put_urb(tascam->playback_urbs[i]); - break; - } - atomic_inc(&tascam->active_urbs); - } + scoped_guard(spinlock_irqsave, &tascam->lock) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (!atomic_read(&tascam->playback_active)) { + atomic_set(&tascam->playback_active, 1); + atomic_set(&tascam->capture_active, 1); + do_start = true; } - } else { - if (atomic_xchg(&tascam->capture_active, 1) == 0) { - if (atomic_fetch_inc(&tascam->stream_active) == 0) { - for (i = 0; i < NUM_FEEDBACK_URBS; i++) { - usb_get_urb(tascam->feedback_urbs[i]); - usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor); - err = usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC); - if (err < 0) { - usb_unanchor_urb(tascam->feedback_urbs[i]); - usb_put_urb(tascam->feedback_urbs[i]); - atomic_dec(&tascam->active_urbs); - break; - } - atomic_inc(&tascam->active_urbs); - } - } - - for (i = 0; i < NUM_CAPTURE_URBS; i++) { - usb_get_urb(tascam->capture_urbs[i]); - usb_anchor_urb(tascam->capture_urbs[i], &tascam->capture_anchor); - err = usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC); - if (err < 0) { - usb_unanchor_urb(tascam->capture_urbs[i]); - usb_put_urb(tascam->capture_urbs[i]); - break; - } - atomic_inc(&tascam->active_urbs); - } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (atomic_read(&tascam->playback_active)) { + atomic_set(&tascam->playback_active, 0); + atomic_set(&tascam->capture_active, 0); + do_stop = true; } + break; + default: + err = -EINVAL; + break; } - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (atomic_xchg(&tascam->playback_active, 0) == 1) { - if (atomic_dec_and_test(&tascam->stream_active)) - usb_kill_anchored_urbs(&tascam->feedback_anchor); - - usb_kill_anchored_urbs(&tascam->playback_anchor); - - } - } else { - if (atomic_xchg(&tascam->capture_active, 0) == 1) { - if (atomic_dec_and_test(&tascam->stream_active)) - usb_kill_anchored_urbs(&tascam->feedback_anchor); - - usb_kill_anchored_urbs(&tascam->capture_anchor); - - } - } - break; - default: - err = -EINVAL; - break; } - spin_unlock_irqrestore(&tascam->trigger_lock, flags); + if (do_start) { + if (atomic_read(&tascam->active_urbs) > 0) { + dev_warn(tascam->card->dev, + "Cannot start, URBs still active.\n"); + return -EAGAIN; + } + + for (i = 0; i < NUM_FEEDBACK_URBS; i++) { + usb_get_urb(tascam->feedback_urbs[i]); + usb_anchor_urb(tascam->feedback_urbs[i], + &tascam->feedback_anchor); + err = usb_submit_urb(tascam->feedback_urbs[i], + GFP_ATOMIC); + if (err < 0) { + usb_unanchor_urb(tascam->feedback_urbs[i]); + usb_put_urb(tascam->feedback_urbs[i]); + atomic_dec(&tascam->active_urbs); + goto start_rollback; + } + atomic_inc(&tascam->active_urbs); + } + for (i = 0; i < NUM_PLAYBACK_URBS; i++) { + usb_get_urb(tascam->playback_urbs[i]); + usb_anchor_urb(tascam->playback_urbs[i], + &tascam->playback_anchor); + err = usb_submit_urb(tascam->playback_urbs[i], + GFP_ATOMIC); + if (err < 0) { + usb_unanchor_urb(tascam->playback_urbs[i]); + usb_put_urb(tascam->playback_urbs[i]); + atomic_dec(&tascam->active_urbs); + goto start_rollback; + } + atomic_inc(&tascam->active_urbs); + } + for (i = 0; i < NUM_CAPTURE_URBS; i++) { + usb_get_urb(tascam->capture_urbs[i]); + usb_anchor_urb(tascam->capture_urbs[i], + &tascam->capture_anchor); + err = usb_submit_urb(tascam->capture_urbs[i], + GFP_ATOMIC); + if (err < 0) { + usb_unanchor_urb(tascam->capture_urbs[i]); + usb_put_urb(tascam->capture_urbs[i]); + atomic_dec(&tascam->active_urbs); + goto start_rollback; + } + atomic_inc(&tascam->active_urbs); + } + + return 0; +start_rollback: + dev_err(tascam->card->dev, + "Failed to submit URBs to start stream: %d\n", err); + do_stop = true; + } + + if (do_stop) + schedule_work(&tascam->stop_work); return err; } + int tascam_init_pcm(struct snd_pcm *pcm) { struct tascam_card *tascam = pcm->private_data; diff --git a/us144mkii_playback.c b/us144mkii_playback.c index 392da96..0cb9699 100644 --- a/us144mkii_playback.c +++ b/us144mkii_playback.c @@ -276,7 +276,7 @@ void feedback_urb_complete(struct urb *urb) } goto out; } - if (!tascam || !atomic_read(&tascam->stream_active)) + if (!tascam || !atomic_read(&tascam->playback_active)) goto out; playback_ss = tascam->playback_substream;