From 557c55d56bf66d8c87675974bd7c99c26f852db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0erif=20Rami?= Date: Mon, 1 Dec 2025 09:49:35 +0100 Subject: [PATCH] style: Fix checkpatch.pl issues and add Kdocs Fixed all coding style issues reported by checkpatch.pl. This includes: - Trailing statements on new lines. - Added missing blank lines after declarations. - Corrected indentation of switch/case statements and labels. - Added comments for memory barriers. - Removed unnecessary braces. Added Kdocs to all public functions and data structures to improve code documentation. --- us144mkii.c | 69 ++++++++++++++++++++++++++++++++++---------- us144mkii.h | 44 ++++++++++++++++++++++++++++ us144mkii_capture.c | 49 ++++++++++++++++++++----------- us144mkii_midi.c | 31 +++++++++++++------- us144mkii_pcm.c | 68 ++++++++++++++++++++++++++++++++++++------- us144mkii_pcm.h | 5 ---- us144mkii_playback.c | 69 ++++++++++++++++++++++++++++++++------------ 7 files changed, 256 insertions(+), 79 deletions(-) diff --git a/us144mkii.c b/us144mkii.c index 5da5354..5e5f2a3 100644 --- a/us144mkii.c +++ b/us144mkii.c @@ -18,6 +18,10 @@ static void tascam_disconnect(struct usb_interface *intf); static int tascam_suspend(struct usb_interface *intf, pm_message_t message); static int tascam_resume(struct usb_interface *intf); +/** + * tascam_free_urbs - free all URBs + * @tascam: the tascam_card instance + */ void tascam_free_urbs(struct tascam_card *tascam) { int i; @@ -73,6 +77,12 @@ void tascam_free_urbs(struct tascam_card *tascam) } } +/** + * tascam_alloc_urbs - allocate all URBs + * @tascam: the tascam_card instance + * + * Return: 0 on success, or a negative error code on failure. + */ int tascam_alloc_urbs(struct tascam_card *tascam) { int i; @@ -80,12 +90,15 @@ int tascam_alloc_urbs(struct tascam_card *tascam) tascam->playback_urb_alloc_size = PLAYBACK_URB_PACKETS * (12 + 2) * BYTES_PER_FRAME; for (i = 0; i < NUM_PLAYBACK_URBS; i++) { struct urb *urb = usb_alloc_urb(PLAYBACK_URB_PACKETS, GFP_KERNEL); - if (!urb) return -ENOMEM; + + if (!urb) + return -ENOMEM; tascam->playback_urbs[i] = urb; urb->transfer_buffer = usb_alloc_coherent(tascam->dev, tascam->playback_urb_alloc_size, GFP_KERNEL, &urb->transfer_dma); - if (!urb->transfer_buffer) return -ENOMEM; + if (!urb->transfer_buffer) + return -ENOMEM; urb->dev = tascam->dev; urb->pipe = usb_sndisocpipe(tascam->dev, EP_AUDIO_OUT); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -97,12 +110,15 @@ int tascam_alloc_urbs(struct tascam_card *tascam) tascam->feedback_urb_alloc_size = FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE; for (i = 0; i < NUM_FEEDBACK_URBS; i++) { struct urb *urb = usb_alloc_urb(FEEDBACK_URB_PACKETS, GFP_KERNEL); - if (!urb) return -ENOMEM; + + if (!urb) + return -ENOMEM; tascam->feedback_urbs[i] = urb; urb->transfer_buffer = usb_alloc_coherent(tascam->dev, tascam->feedback_urb_alloc_size, GFP_KERNEL, &urb->transfer_dma); - if (!urb->transfer_buffer) return -ENOMEM; + if (!urb->transfer_buffer) + return -ENOMEM; urb->dev = tascam->dev; urb->pipe = usb_rcvisocpipe(tascam->dev, EP_PLAYBACK_FEEDBACK); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -113,11 +129,14 @@ int tascam_alloc_urbs(struct tascam_card *tascam) for (i = 0; i < NUM_CAPTURE_URBS; i++) { struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) return -ENOMEM; + + if (!urb) + return -ENOMEM; tascam->capture_urbs[i] = urb; void *buf = usb_alloc_coherent(tascam->dev, CAPTURE_PACKET_SIZE, GFP_KERNEL, &urb->transfer_dma); - if (!buf) return -ENOMEM; + if (!buf) + return -ENOMEM; usb_fill_bulk_urb(urb, tascam->dev, usb_rcvbulkpipe(tascam->dev, EP_AUDIO_IN), buf, CAPTURE_PACKET_SIZE, @@ -128,6 +147,10 @@ int tascam_alloc_urbs(struct tascam_card *tascam) return 0; } +/** + * tascam_stop_work_handler - work handler to stop all streams + * @work: pointer to the work_struct + */ void tascam_stop_work_handler(struct work_struct *work) { struct tascam_card *tascam = container_of(work, struct tascam_card, stop_work); @@ -155,7 +178,9 @@ static void tascam_card_private_free(struct snd_card *card) static int tascam_suspend(struct usb_interface *intf, pm_message_t message) { struct tascam_card *tascam = usb_get_intfdata(intf); - if (!tascam) return 0; + + if (!tascam) + return 0; snd_pcm_suspend_all(tascam->pcm); cancel_work_sync(&tascam->stop_work); @@ -176,12 +201,16 @@ static int tascam_resume(struct usb_interface *intf) { struct tascam_card *tascam = usb_get_intfdata(intf); int err; - if (!tascam) return 0; + + if (!tascam) + return 0; err = usb_set_interface(tascam->dev, 0, 1); - if (err < 0) return err; + if (err < 0) + return err; err = usb_set_interface(tascam->dev, 1, 1); - if (err < 0) return err; + if (err < 0) + return err; if (tascam->current_rate > 0) us144mkii_configure_device_for_rate(tascam, tascam->current_rate); @@ -196,8 +225,10 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * struct tascam_card *tascam; int err; int idx; + char *handshake_buf __free(kfree) = NULL; + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) return 0; @@ -263,7 +294,8 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * dev_name(&dev->dev)); err = snd_pcm_new(card, "US144MKII PCM", 0, 1, 1, &tascam->pcm); - if (err < 0) goto free_card; + if (err < 0) + goto free_card; tascam->pcm->private_data = tascam; strscpy(tascam->pcm->name, "US144MKII PCM", sizeof(tascam->pcm->name)); snd_pcm_set_ops(tascam->pcm, SNDRV_PCM_STREAM_PLAYBACK, &tascam_playback_ops); @@ -273,10 +305,12 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * NULL, 0, 0); err = tascam_create_midi(tascam); - if (err < 0) goto free_card; + if (err < 0) + goto free_card; err = tascam_alloc_urbs(tascam); - if (err < 0) goto free_card; + if (err < 0) + goto free_card; if (us144mkii_configure_device_for_rate(tascam, 48000) < 0) dev_warn(&dev->dev, "Failed to initialize device at 48khz\n"); @@ -284,12 +318,13 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * tascam->current_rate = 48000; err = snd_card_register(card); - if (err < 0) goto free_card; + if (err < 0) + goto free_card; usb_set_intfdata(intf, tascam); return 0; - free_card: +free_card: snd_card_free(card); atomic_dec(&dev_idx); return err; @@ -298,7 +333,9 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * static void tascam_disconnect(struct usb_interface *intf) { struct tascam_card *tascam = usb_get_intfdata(intf); - if (!tascam) return; + + if (!tascam) + return; if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { atomic_set(&tascam->playback_active, 0); diff --git a/us144mkii.h b/us144mkii.h index 05573ca..f600be6 100644 --- a/us144mkii.h +++ b/us144mkii.h @@ -85,6 +85,50 @@ enum tascam_register { #define USB_CTRL_TIMEOUT_MS 1000 +/** + * struct tascam_card - private data for the TASCAM US-144MKII driver + * @dev: pointer to the USB device + * @iface0: pointer to the first interface + * @card: pointer to the ALSA sound card + * @pcm: pointer to the ALSA PCM device + * @rmidi: pointer to the ALSA raw MIDI device + * @playback_substream: pointer to the PCM playback substream + * @capture_substream: pointer to the PCM capture substream + * @playback_urbs: array of URBs for PCM playback + * @playback_urb_alloc_size: allocated size of each playback URB + * @feedback_urbs: array of URBs for feedback + * @feedback_urb_alloc_size: allocated size of each feedback URB + * @capture_urbs: array of URBs for PCM capture + * @playback_anchor: anchor for playback URBs + * @feedback_anchor: anchor for feedback URBs + * @capture_anchor: anchor for capture URBs + * @midi_input: pointer to the MIDI input substream + * @midi_output: pointer to the MIDI output substream + * @midi_in_urb: URB for MIDI input + * @midi_out_urb: URB for MIDI output + * @midi_in_buf: buffer for MIDI input + * @midi_out_buf: buffer for MIDI output + * @midi_anchor: anchor for MIDI URBs + * @midi_out_active: flag indicating if MIDI output is active + * @midi_lock: spinlock for MIDI operations + * @lock: spinlock for PCM operations + * @playback_active: atomic flag indicating if PCM playback is active + * @capture_active: atomic flag indicating if PCM capture is active + * @active_urbs: atomic counter for active URBs + * @current_rate: current sample rate + * @playback_frames_consumed: number of frames consumed by the playback device + * @driver_playback_pos: playback position in the ring buffer + * @last_pb_period_pos: last playback period position + * @capture_frames_processed: number of frames processed from the capture device + * @driver_capture_pos: capture position in the ring buffer + * @last_cap_period_pos: last capture period position + * @phase_accum: phase accumulator for the playback PLL + * @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 + * @stop_work: work struct for stopping all streams + * @stop_pcm_work: work struct for stopping PCM streams + */ struct tascam_card { struct usb_device *dev; struct usb_interface *iface0; diff --git a/us144mkii_capture.c b/us144mkii_capture.c index a126bc7..ae6f974 100644 --- a/us144mkii_capture.c +++ b/us144mkii_capture.c @@ -24,6 +24,7 @@ const struct snd_pcm_hardware tascam_capture_hw = { static int tascam_capture_open(struct snd_pcm_substream *substream) { struct tascam_card *tascam = snd_pcm_substream_chip(substream); + substream->runtime->hw = tascam_capture_hw; tascam->capture_substream = substream; atomic_set(&tascam->capture_active, 0); @@ -33,6 +34,7 @@ static int tascam_capture_open(struct snd_pcm_substream *substream) static int tascam_capture_close(struct snd_pcm_substream *substream) { struct tascam_card *tascam = snd_pcm_substream_chip(substream); + tascam->capture_substream = NULL; return 0; } @@ -56,7 +58,8 @@ static snd_pcm_uframes_t tascam_capture_pointer(struct snd_pcm_substream *substr u64 pos; snd_pcm_uframes_t buffer_size = substream->runtime->buffer_size; - if (!atomic_read(&tascam->capture_active)) return 0; + 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); @@ -74,26 +77,27 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock_irqsave(&tascam->lock, flags); switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - if (!atomic_read(&tascam->capture_active)) { - atomic_set(&tascam->capture_active, 1); - start = true; - } - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - atomic_set(&tascam->capture_active, 0); - stop = true; - break; - default: - ret = -EINVAL; - break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (!atomic_read(&tascam->capture_active)) { + atomic_set(&tascam->capture_active, 1); + start = true; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + atomic_set(&tascam->capture_active, 0); + stop = true; + break; + default: + ret = -EINVAL; + break; } spin_unlock_irqrestore(&tascam->lock, flags); if (stop) { + /* Ensure capture_active is updated before unlinking URBs */ smp_mb(); for (i = 0; i < NUM_CAPTURE_URBS; i++) { if (tascam->capture_urbs[i]) @@ -107,6 +111,7 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) if (usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC) < 0) { usb_unanchor_urb(tascam->capture_urbs[i]); atomic_set(&tascam->capture_active, 0); + /* Ensure capture_active is cleared before unlinking URBs */ smp_mb(); for (int j = 0; j < i; j++) usb_unlink_urb(tascam->capture_urbs[j]); @@ -162,6 +167,14 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_d } } +/** + * capture_urb_complete() - completion handler for capture bulk URBs + * @urb: the completed URB + * + * This function runs in interrupt context. It copies the received raw data + * into an intermediate ring buffer and then schedules the workqueue to process + * it. It then resubmits the URB to receive more data. + */ void capture_urb_complete(struct urb *urb) { struct tascam_card *tascam = urb->context; @@ -213,6 +226,7 @@ void capture_urb_complete(struct urb *urb) } else { 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); } @@ -226,6 +240,7 @@ void capture_urb_complete(struct urb *urb) 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; need_period_elapsed = true; diff --git a/us144mkii_midi.c b/us144mkii_midi.c index 3603e06..a732a34 100644 --- a/us144mkii_midi.c +++ b/us144mkii_midi.c @@ -98,19 +98,21 @@ static void tascam_midi_in_complete(struct urb *urb) struct tascam_card *tascam = urb->context; int i; - if (urb->status) return; + if (urb->status) + return; if (urb->actual_length == MIDI_PACKET_SIZE && tascam->midi_input) { u8 *data = urb->transfer_buffer; + for (i = 0; i < MIDI_PAYLOAD_SIZE; i++) { - if (data[i] == 0xFD) break; + if (data[i] == 0xFD) + break; snd_rawmidi_receive(tascam->midi_input, &data[i], 1); } } - if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) usb_unanchor_urb(urb); - } } static void tascam_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) @@ -129,6 +131,7 @@ static int tascam_midi_open(struct snd_rawmidi_substream *substream) if (substream->stream == SNDRV_RAWMIDI_STREAM_OUTPUT) { unsigned long flags; + spin_lock_irqsave(&tascam->midi_lock, flags); tascam->midi_out_active = false; spin_unlock_irqrestore(&tascam->midi_lock, flags); @@ -146,9 +149,8 @@ static int tascam_midi_close(struct snd_rawmidi_substream *substream) { struct tascam_card *tascam = substream->rmidi->private_data; - if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) { + if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) usb_kill_urb(tascam->midi_in_urb); - } return 0; } @@ -164,13 +166,20 @@ static const struct snd_rawmidi_ops midi_input_ops = { .trigger = tascam_midi_input_trigger, }; +/** + * tascam_create_midi - create and initialize the MIDI device + * @tascam: the tascam_card instance + * + * Return: 0 on success, or a negative error code on failure. + */ int tascam_create_midi(struct tascam_card *tascam) { int err; struct snd_rawmidi *rmidi; err = snd_rawmidi_new(tascam->card, "TASCAM MIDI", 0, 1, 1, &rmidi); - if (err < 0) return err; + if (err < 0) + return err; rmidi->private_data = tascam; strscpy(rmidi->name, "TASCAM US-144MKII MIDI", sizeof(rmidi->name)); @@ -226,15 +235,15 @@ int tascam_create_midi(struct tascam_card *tascam) return 0; - err_in_buf: +err_in_buf: usb_free_coherent(tascam->dev, MIDI_PACKET_SIZE, tascam->midi_out_buf, tascam->midi_out_urb->transfer_dma); - err_out_buf: +err_out_buf: usb_free_urb(tascam->midi_in_urb); tascam->midi_in_urb = NULL; - err_in_urb: +err_in_urb: usb_free_urb(tascam->midi_out_urb); tascam->midi_out_urb = NULL; - err_out_urb: +err_out_urb: return err; } diff --git a/us144mkii_pcm.c b/us144mkii_pcm.c index ba28641..acc432f 100644 --- a/us144mkii_pcm.c +++ b/us144mkii_pcm.c @@ -7,15 +7,27 @@ static int tascam_write_regs(struct tascam_card *tascam, const u16 *regs, size_t { int i, err = 0; struct usb_device *dev = tascam->dev; + for (i = 0; i < count; i++) { err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, regs[i], REG_VAL_ENABLE, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) return err; + if (err < 0) + return err; } return 0; } +/** + * us144mkii_configure_device_for_rate() - set sample rate via USB control msgs + * @tascam: the tascam_card instance + * @rate: the target sample rate (e.g., 44100, 96000) + * + * This function sends a sequence of vendor-specific and UAC control messages + * to configure the device hardware for the specified sample rate. + * + * Return: 0 on success, or a negative error code on failure. + */ int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) { struct usb_device *dev = tascam->dev; @@ -30,20 +42,35 @@ int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) static const u8 payload_96000[] = { 0x00, 0x77, 0x01 }; switch (rate) { - case 44100: current_payload_src = payload_44100; rate_reg = REG_ADDR_RATE_44100; break; - case 48000: current_payload_src = payload_48000; rate_reg = REG_ADDR_RATE_48000; break; - case 88200: current_payload_src = payload_88200; rate_reg = REG_ADDR_RATE_88200; break; - case 96000: current_payload_src = payload_96000; rate_reg = REG_ADDR_RATE_96000; break; - default: return -EINVAL; + case 44100: + current_payload_src = payload_44100; + rate_reg = REG_ADDR_RATE_44100; + break; + case 48000: + current_payload_src = payload_48000; + rate_reg = REG_ADDR_RATE_48000; + break; + case 88200: + current_payload_src = payload_88200; + rate_reg = REG_ADDR_RATE_88200; + break; + case 96000: + current_payload_src = payload_96000; + rate_reg = REG_ADDR_RATE_96000; + break; + default: + return -EINVAL; } rate_payload_buf = kmemdup(current_payload_src, 3, GFP_KERNEL); - if (!rate_payload_buf) return -ENOMEM; + if (!rate_payload_buf) + return -ENOMEM; err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, MODE_VAL_CONFIG, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) goto fail; + if (err < 0) + goto fail; usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, @@ -58,21 +85,35 @@ int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) REG_ADDR_UNKNOWN_0F, rate_reg, REG_ADDR_UNKNOWN_11 }; err = tascam_write_regs(tascam, regs_to_write, ARRAY_SIZE(regs_to_write)); - if (err < 0) goto fail; + if (err < 0) + goto fail; } err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, MODE_VAL_STREAM_START, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) goto fail; + if (err < 0) + goto fail; kfree(rate_payload_buf); return 0; - fail: +fail: kfree(rate_payload_buf); return err; } +/** + * tascam_pcm_hw_params() - configure hardware parameters for PCM streams + * @substream: the ALSA PCM substream + * @params: the hardware parameters to apply + * + * This function allocates pages for the PCM buffer and, for playback streams, + * selects the appropriate feedback patterns based on the requested sample rate. + * It also configures the device hardware for the selected sample rate if it + * has changed. + * + * Return: 0 on success, or a negative error code on failure. + */ int tascam_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -98,9 +139,14 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } +/** + * tascam_stop_pcm_work_handler() - work handler to stop PCM streams + * @work: pointer to the work_struct + */ void tascam_stop_pcm_work_handler(struct work_struct *work) { struct tascam_card *tascam = container_of(work, struct tascam_card, stop_pcm_work); + if (tascam->playback_substream) snd_pcm_stop(tascam->playback_substream, SNDRV_PCM_STATE_XRUN); } diff --git a/us144mkii_pcm.h b/us144mkii_pcm.h index 045b921..e1c6119 100644 --- a/us144mkii_pcm.h +++ b/us144mkii_pcm.h @@ -12,14 +12,9 @@ extern const struct snd_pcm_hardware tascam_capture_hw; extern const struct snd_pcm_ops tascam_playback_ops; extern const struct snd_pcm_ops tascam_capture_ops; -/* Playback */ void playback_urb_complete(struct urb *urb); void feedback_urb_complete(struct urb *urb); - -/* Capture */ void capture_urb_complete(struct urb *urb); - -/* Setup */ int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate); void tascam_stop_pcm_work_handler(struct work_struct *work); int tascam_pcm_hw_params(struct snd_pcm_substream *substream, diff --git a/us144mkii_playback.c b/us144mkii_playback.c index 3bf184c..5c037c2 100644 --- a/us144mkii_playback.c +++ b/us144mkii_playback.c @@ -24,6 +24,7 @@ const struct snd_pcm_hardware tascam_playback_hw = { static int tascam_playback_open(struct snd_pcm_substream *substream) { struct tascam_card *tascam = snd_pcm_substream_chip(substream); + substream->runtime->hw = tascam_playback_hw; tascam->playback_substream = substream; atomic_set(&tascam->playback_active, 0); @@ -60,6 +61,7 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream) 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++) { @@ -70,6 +72,7 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream) for (u = 0; u < NUM_PLAYBACK_URBS; u++) { struct urb *urb = tascam->playback_urbs[u]; + memset(urb->transfer_buffer, 0, tascam->playback_urb_alloc_size); urb->number_of_packets = PLAYBACK_URB_PACKETS; urb->transfer_buffer_length = PLAYBACK_URB_PACKETS * nominal_bytes; @@ -88,7 +91,8 @@ static snd_pcm_uframes_t tascam_playback_pointer(struct snd_pcm_substream *subst u64 pos; snd_pcm_uframes_t buffer_size = substream->runtime->buffer_size; - if (!atomic_read(&tascam->playback_active)) return 0; + 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); @@ -106,26 +110,27 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock_irqsave(&tascam->lock, flags); switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - if (!atomic_read(&tascam->playback_active)) { - atomic_set(&tascam->playback_active, 1); - start = true; - } - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - atomic_set(&tascam->playback_active, 0); - stop = true; - break; - default: - ret = -EINVAL; - break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (!atomic_read(&tascam->playback_active)) { + atomic_set(&tascam->playback_active, 1); + start = true; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + atomic_set(&tascam->playback_active, 0); + stop = true; + break; + default: + ret = -EINVAL; + break; } spin_unlock_irqrestore(&tascam->lock, flags); if (stop) { + /* Ensure playback_active is updated before unlinking URBs */ smp_mb(); for (i = 0; i < NUM_FEEDBACK_URBS; i++) { if (tascam->feedback_urbs[i]) @@ -144,6 +149,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) 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); + /* Ensure playback_active is cleared before unlinking URBs */ smp_mb(); for (int j = 0; j < i; j++) usb_unlink_urb(tascam->feedback_urbs[j]); @@ -158,6 +164,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) 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); + /* Ensure playback_active is cleared before unlinking URBs */ smp_mb(); for (int j = 0; j < NUM_FEEDBACK_URBS; j++) usb_unlink_urb(tascam->feedback_urbs[j]); @@ -172,10 +179,18 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) schedule_work(&tascam->stop_work); } - error: +error: return ret; } +/** + * playback_urb_complete() - completion handler for playback isochronous URBs + * @urb: the completed URB + * + * This function runs in interrupt context. It calculates the number of bytes + * to send in the next set of packets based on the feedback-driven clock, + * copies the audio data from the ALSA ring buffer, and resubmits the URB. + */ void playback_urb_complete(struct urb *urb) { struct tascam_card *tascam = urb->context; @@ -218,6 +233,7 @@ void playback_urb_complete(struct urb *urb) spin_lock_irqsave(&tascam->lock, flags); for (i = 0; i < urb->number_of_packets; i++) { unsigned int frames_for_packet; + tascam->phase_accum += tascam->freq_q16; frames_for_packet = tascam->phase_accum >> 16; tascam->phase_accum &= 0xFFFF; @@ -235,8 +251,10 @@ void playback_urb_complete(struct urb *urb) 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 > 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 { @@ -249,6 +267,7 @@ void playback_urb_complete(struct urb *urb) 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; need_period_elapsed = true; @@ -263,6 +282,17 @@ void playback_urb_complete(struct urb *urb) atomic_dec(&tascam->active_urbs); } +/** + * feedback_urb_complete() - completion handler for feedback isochronous URBs + * @urb: the completed URB + * + * This is the master clock for the driver. It runs in interrupt context. + * It reads the feedback value from the device, which indicates how many + * samples the device has consumed. This information is used to adjust the + * playback rate and to advance the capture stream pointer, keeping both + * streams in sync. It then calls snd_pcm_period_elapsed if necessary and + * resubmits itself. + */ void feedback_urb_complete(struct urb *urb) { struct tascam_card *tascam = urb->context; @@ -293,13 +323,14 @@ void feedback_urb_complete(struct urb *urb) u8 *data = (u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset; u32 sum = data[0] + data[1] + data[2]; u32 target_freq_q16 = ((sum * 65536) / 3) / 8; + tascam->freq_q16 = (tascam->freq_q16 * PLL_FILTER_OLD_WEIGHT + target_freq_q16 * PLL_FILTER_NEW_WEIGHT) / PLL_FILTER_DIVISOR; tascam->feedback_synced = true; } } spin_unlock_irqrestore(&tascam->lock, flags); - resubmit: +resubmit: ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { usb_unanchor_urb(urb);