diff --git a/us144mkii.h b/us144mkii.h index 1c8f1fb..151b65e 100644 --- a/us144mkii.h +++ b/us144mkii.h @@ -128,6 +128,7 @@ enum tascam_register { * @freq_q16: current frequency for the playback PLL in Q16.16 format * @feedback_synced: flag indicating if feedback is synced * @feedback_urb_skip_count: number of feedback URBs to skip at startup + * @running_ghost_playback: flag indicating if implicit playback is running * @stop_work: work struct for stopping all streams * @stop_pcm_work: work struct for stopping PCM streams */ @@ -183,6 +184,7 @@ struct tascam_card { u32 freq_q16; bool feedback_synced; unsigned int feedback_urb_skip_count; + bool running_ghost_playback; struct work_struct stop_work; struct work_struct stop_pcm_work; diff --git a/us144mkii_capture.c b/us144mkii_capture.c index aa28fae..09fd626 100644 --- a/us144mkii_capture.c +++ b/us144mkii_capture.c @@ -73,7 +73,7 @@ static snd_pcm_uframes_t tascam_capture_pointer(struct snd_pcm_substream *substr 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; + int i, u, ret = 0; bool start = false; bool stop = false; unsigned long flags; @@ -105,6 +105,20 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) if (tascam->capture_urbs[i]) usb_unlink_urb(tascam->capture_urbs[i]); } + + if (tascam->running_ghost_playback) { + atomic_set(&tascam->playback_active, 0); + tascam->running_ghost_playback = false; + + for (i = 0; i < NUM_PLAYBACK_URBS; i++) { + if (tascam->playback_urbs[i]) + usb_unlink_urb(tascam->playback_urbs[i]); + } + for (i = 0; i < NUM_FEEDBACK_URBS; i++) { + if (tascam->feedback_urbs[i]) + usb_unlink_urb(tascam->feedback_urbs[i]); + } + } } if (start) { @@ -121,6 +135,54 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) } atomic_inc(&tascam->active_urbs); } + + if (!atomic_read(&tascam->playback_active)) { + atomic_set(&tascam->playback_active, 1); + tascam->running_ghost_playback = true; + tascam->phase_accum = 0; + tascam->freq_q16 = div_u64(((u64)tascam->current_rate << 16), 8000); + tascam->feedback_urb_skip_count = 4; + tascam->feedback_synced = false; + + for (i = 0; i < NUM_FEEDBACK_URBS; i++) { + struct urb *f_urb = tascam->feedback_urbs[i]; + f_urb->number_of_packets = FEEDBACK_URB_PACKETS; + f_urb->transfer_buffer_length = FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE; + for (u = 0; u < FEEDBACK_URB_PACKETS; u++) { + f_urb->iso_frame_desc[u].offset = u * FEEDBACK_PACKET_SIZE; + f_urb->iso_frame_desc[u].length = FEEDBACK_PACKET_SIZE; + } + usb_anchor_urb(f_urb, &tascam->feedback_anchor); + if (usb_submit_urb(f_urb, GFP_ATOMIC) < 0) { + usb_unanchor_urb(f_urb); + atomic_dec(&tascam->active_urbs); + } else { + atomic_inc(&tascam->active_urbs); + } + } + + size_t nominal_bytes = (tascam->current_rate / 8000) * PLAYBACK_FRAME_SIZE; + for (u = 0; u < NUM_PLAYBACK_URBS; u++) { + struct urb *urb = tascam->playback_urbs[u]; + size_t total_bytes = 0; + urb->number_of_packets = PLAYBACK_URB_PACKETS; + for (i = 0; i < PLAYBACK_URB_PACKETS; i++) { + urb->iso_frame_desc[i].offset = i * nominal_bytes; + urb->iso_frame_desc[i].length = nominal_bytes; + total_bytes += nominal_bytes; + } + urb->transfer_buffer_length = total_bytes; + memset(urb->transfer_buffer, 0, total_bytes); + + usb_anchor_urb(urb, &tascam->playback_anchor); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { + usb_unanchor_urb(urb); + atomic_dec(&tascam->active_urbs); + } else { + atomic_inc(&tascam->active_urbs); + } + } + } } return ret; } diff --git a/us144mkii_playback.c b/us144mkii_playback.c index 70c0c8f..16860d7 100644 --- a/us144mkii_playback.c +++ b/us144mkii_playback.c @@ -59,6 +59,7 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream) tascam->playback_frames_consumed = 0; tascam->last_pb_period_pos = 0; tascam->feedback_synced = false; + tascam->running_ghost_playback = false; tascam->feedback_urb_skip_count = 4; @@ -204,7 +205,6 @@ void playback_urb_complete(struct urb *urb) goto exit_clear; substream = tascam->playback_substream; - runtime = substream->runtime; spin_lock_irqsave(&tascam->lock, flags); @@ -225,34 +225,39 @@ void playback_urb_complete(struct urb *urb) urb->transfer_buffer_length = total_bytes_for_urb; if (total_bytes_for_urb > 0) { - u8 *dst_buf = urb->transfer_buffer; - size_t ptr_bytes = frames_to_bytes(runtime, tascam->driver_playback_pos); - frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb); + if (substream) { + runtime = substream->runtime; + u8 *dst_buf = urb->transfer_buffer; + size_t ptr_bytes = frames_to_bytes(runtime, tascam->driver_playback_pos); + frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb); - if (tascam->driver_playback_pos + frames_to_copy > runtime->buffer_size) { - size_t part1 = runtime->buffer_size - tascam->driver_playback_pos; - size_t part1_bytes = frames_to_bytes(runtime, part1); + if (tascam->driver_playback_pos + frames_to_copy > runtime->buffer_size) { + size_t part1 = runtime->buffer_size - tascam->driver_playback_pos; + size_t part1_bytes = frames_to_bytes(runtime, part1); - memcpy(dst_buf, runtime->dma_area + ptr_bytes, part1_bytes); - memcpy(dst_buf + part1_bytes, runtime->dma_area, total_bytes_for_urb - part1_bytes); + memcpy(dst_buf, runtime->dma_area + ptr_bytes, part1_bytes); + memcpy(dst_buf + part1_bytes, runtime->dma_area, total_bytes_for_urb - part1_bytes); + } else { + memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb); + } + + tascam->driver_playback_pos += frames_to_copy; + if (tascam->driver_playback_pos >= runtime->buffer_size) + tascam->driver_playback_pos -= runtime->buffer_size; + + tascam->playback_frames_consumed += frames_to_copy; + + if (div_u64(tascam->playback_frames_consumed, runtime->period_size) > tascam->last_pb_period_pos) { + tascam->last_pb_period_pos = div_u64(tascam->playback_frames_consumed, runtime->period_size); + need_period_elapsed = true; + } } else { - memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb); - } - - tascam->driver_playback_pos += frames_to_copy; - if (tascam->driver_playback_pos >= runtime->buffer_size) - tascam->driver_playback_pos -= runtime->buffer_size; - - tascam->playback_frames_consumed += frames_to_copy; - - if (div_u64(tascam->playback_frames_consumed, runtime->period_size) > tascam->last_pb_period_pos) { - tascam->last_pb_period_pos = div_u64(tascam->playback_frames_consumed, runtime->period_size); - need_period_elapsed = true; + memset(urb->transfer_buffer, 0, total_bytes_for_urb); } } spin_unlock_irqrestore(&tascam->lock, flags); - if (need_period_elapsed) + if (need_period_elapsed && substream) snd_pcm_period_elapsed(substream); usb_anchor_urb(urb, &tascam->playback_anchor);