File Index
Files and directories
Sign in
FILE

us144mkii_capture.c

File preview with syntax highlighting.

linux-6.19.14/sound/usb/usx2y/us144mkii_capture.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2// Copyright (c) 2025 Ĺ erif Rami <ramiserifpersia@gmail.com>
  3
  4#include "us144mkii.h"
  5
  6/**
  7 * tascam_capture_open() - Opens the PCM capture substream.
  8 * @substream: The ALSA PCM substream to open.
  9 *
 10 * This function sets the hardware parameters for the capture substream
 11 * and stores a reference to the substream in the driver's private data.
 12 *
 13 * Return: 0 on success.
 14 */
 15static int tascam_capture_open(struct snd_pcm_substream *substream)
 16{
 17	struct tascam_card *tascam = snd_pcm_substream_chip(substream);
 18
 19	substream->runtime->hw = tascam_pcm_hw;
 20	tascam->capture_substream = substream;
 21	atomic_set(&tascam->capture_active, 0);
 22
 23	return 0;
 24}
 25
 26/**
 27 * tascam_capture_close() - Closes the PCM capture substream.
 28 * @substream: The ALSA PCM substream to close.
 29 *
 30 * This function clears the reference to the capture substream in the
 31 * driver's private data.
 32 *
 33 * Return: 0 on success.
 34 */
 35static int tascam_capture_close(struct snd_pcm_substream *substream)
 36{
 37	struct tascam_card *tascam = snd_pcm_substream_chip(substream);
 38
 39	tascam->capture_substream = NULL;
 40
 41	return 0;
 42}
 43
 44/**
 45 * tascam_capture_prepare() - Prepares the PCM capture substream for use.
 46 * @substream: The ALSA PCM substream to prepare.
 47 *
 48 * This function initializes capture-related counters and ring buffer pointers.
 49 *
 50 * Return: 0 on success.
 51 */
 52static int tascam_capture_prepare(struct snd_pcm_substream *substream)
 53{
 54	struct tascam_card *tascam = snd_pcm_substream_chip(substream);
 55
 56	tascam->driver_capture_pos = 0;
 57	tascam->capture_frames_processed = 0;
 58	tascam->last_capture_period_pos = 0;
 59	tascam->capture_ring_buffer_read_ptr = 0;
 60	tascam->capture_ring_buffer_write_ptr = 0;
 61
 62	return 0;
 63}
 64
 65/**
 66 * tascam_capture_pointer() - Returns the current capture pointer position.
 67 * @substream: The ALSA PCM substream.
 68 *
 69 * This function returns the current position of the capture pointer within
 70 * the ALSA ring buffer, in frames.
 71 *
 72 * Return: The current capture pointer position in frames.
 73 */
 74static snd_pcm_uframes_t
 75tascam_capture_pointer(struct snd_pcm_substream *substream)
 76{
 77	struct tascam_card *tascam = snd_pcm_substream_chip(substream);
 78	struct snd_pcm_runtime *runtime = substream->runtime;
 79	u64 pos;
 80
 81	if (!atomic_read(&tascam->capture_active))
 82		return 0;
 83
 84	scoped_guard(spinlock_irqsave, &tascam->lock) {
 85		pos = tascam->capture_frames_processed;
 86	}
 87
 88	if (runtime->buffer_size == 0)
 89		return 0;
 90
 91	return do_div(pos, runtime->buffer_size);
 92}
 93
 94/**
 95 * tascam_capture_ops - ALSA PCM operations for capture.
 96 *
 97 * This structure defines the callback functions for capture stream operations,
 98 * including open, close, ioctl, hardware parameters, hardware free, prepare,
 99 * trigger, and pointer.
100 */
101const struct snd_pcm_ops tascam_capture_ops = {
102	.open = tascam_capture_open,
103	.close = tascam_capture_close,
104	.ioctl = snd_pcm_lib_ioctl,
105	.hw_params = tascam_pcm_hw_params,
106	.hw_free = tascam_pcm_hw_free,
107	.prepare = tascam_capture_prepare,
108	.trigger = tascam_pcm_trigger,
109	.pointer = tascam_capture_pointer,
110};
111
112/**
113 * decode_tascam_capture_block() - Decodes a raw 512-byte block from the device.
114 * @src_block: Pointer to the 512-byte raw source block.
115 * @dst_block: Pointer to the destination buffer for decoded audio frames.
116 *
117 * The device sends audio data in a complex, multiplexed format. This function
118 * demultiplexes the bits from the raw block into 8 frames of 4-channel,
119 * 24-bit audio (stored in 32-bit containers).
120 */
121static void decode_tascam_capture_block(const u8 *src_block, s32 *dst_block)
122{
123	int frame, bit;
124
125	memset(dst_block, 0,
126	       FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
127		       DECODED_SAMPLE_SIZE);
128
129	for (frame = 0; frame < FRAMES_PER_DECODE_BLOCK; ++frame) {
130		const u8 *p_src_frame_base = src_block + frame * 64;
131		s32 *p_dst_frame = dst_block + frame * 4;
132
133		s32 ch[4] = { 0 };
134
135		for (bit = 0; bit < 24; ++bit) {
136			u8 byte1 = p_src_frame_base[bit];
137			u8 byte2 = p_src_frame_base[bit + 32];
138
139			ch[0] = (ch[0] << 1) | (byte1 & 1);
140			ch[2] = (ch[2] << 1) | ((byte1 >> 1) & 1);
141
142			ch[1] = (ch[1] << 1) | (byte2 & 1);
143			ch[3] = (ch[3] << 1) | ((byte2 >> 1) & 1);
144		}
145
146		/*
147		 * The result is a 24-bit sample. Shift left by 8 to align it to
148		 * the most significant bits of a 32-bit integer (S32_LE format).
149		 */
150		p_dst_frame[0] = ch[0] << 8;
151		p_dst_frame[1] = ch[1] << 8;
152		p_dst_frame[2] = ch[2] << 8;
153		p_dst_frame[3] = ch[3] << 8;
154	}
155}
156
157void tascam_capture_work_handler(struct work_struct *work)
158{
159	struct tascam_card *tascam =
160		container_of(work, struct tascam_card, capture_work);
161	struct snd_pcm_substream *substream = tascam->capture_substream;
162	struct snd_pcm_runtime *runtime;
163	u8 *raw_block = tascam->capture_decode_raw_block;
164	s32 *decoded_block = tascam->capture_decode_dst_block;
165	s32 *routed_block = tascam->capture_routing_buffer;
166
167	if (!substream || !substream->runtime)
168		return;
169	runtime = substream->runtime;
170
171	if (!raw_block || !decoded_block || !routed_block) {
172		dev_err(tascam->card->dev,
173			"Capture decode/routing buffers not allocated!\n");
174		return;
175	}
176
177	while (atomic_read(&tascam->capture_active)) {
178		size_t write_ptr, read_ptr, available_data;
179		bool can_process;
180
181		scoped_guard(spinlock_irqsave, &tascam->lock) {
182			write_ptr = tascam->capture_ring_buffer_write_ptr;
183			read_ptr = tascam->capture_ring_buffer_read_ptr;
184			available_data = (write_ptr >= read_ptr) ?
185						 (write_ptr - read_ptr) :
186						 (CAPTURE_RING_BUFFER_SIZE -
187						  read_ptr + write_ptr);
188			can_process =
189				(available_data >= RAW_BYTES_PER_DECODE_BLOCK);
190
191			if (can_process) {
192				size_t bytes_to_end =
193					CAPTURE_RING_BUFFER_SIZE - read_ptr;
194				if (bytes_to_end >=
195				    RAW_BYTES_PER_DECODE_BLOCK) {
196					memcpy(raw_block,
197					       tascam->capture_ring_buffer +
198						       read_ptr,
199					       RAW_BYTES_PER_DECODE_BLOCK);
200				} else {
201					memcpy(raw_block,
202					       tascam->capture_ring_buffer +
203						       read_ptr,
204					       bytes_to_end);
205					memcpy(raw_block + bytes_to_end,
206					       tascam->capture_ring_buffer,
207					       RAW_BYTES_PER_DECODE_BLOCK -
208						       bytes_to_end);
209				}
210				tascam->capture_ring_buffer_read_ptr =
211					(read_ptr +
212					 RAW_BYTES_PER_DECODE_BLOCK) %
213					CAPTURE_RING_BUFFER_SIZE;
214			}
215		}
216
217		if (!can_process)
218			break;
219
220		decode_tascam_capture_block(raw_block, decoded_block);
221		process_capture_routing_us144mkii(tascam, decoded_block,
222						  routed_block);
223
224		scoped_guard(spinlock_irqsave, &tascam->lock) {
225			if (atomic_read(&tascam->capture_active)) {
226				int f;
227
228				for (f = 0; f < FRAMES_PER_DECODE_BLOCK; ++f) {
229					u8 *dst_frame_start =
230						runtime->dma_area +
231						frames_to_bytes(
232							runtime,
233							tascam->driver_capture_pos);
234					s32 *routed_frame_start =
235						routed_block +
236						(f * NUM_CHANNELS);
237					int c;
238
239					for (c = 0; c < NUM_CHANNELS; c++) {
240						u8 *dst_channel =
241							dst_frame_start +
242							(c * BYTES_PER_SAMPLE);
243						s32 *src_channel_s32 =
244							routed_frame_start + c;
245
246						memcpy(dst_channel,
247						       ((char *)src_channel_s32) +
248							       1,
249						       3);
250					}
251
252					tascam->driver_capture_pos =
253						(tascam->driver_capture_pos +
254						 1) %
255						runtime->buffer_size;
256				}
257			}
258		}
259	}
260}
261
262void capture_urb_complete(struct urb *urb)
263{
264	struct tascam_card *tascam = urb->context;
265	int ret;
266
267	if (urb->status) {
268		if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
269		    urb->status != -ESHUTDOWN && urb->status != -ENODEV &&
270		    urb->status != -EPROTO)
271			dev_err_ratelimited(tascam->card->dev,
272					    "Capture URB failed: %d\n",
273					    urb->status);
274		goto out;
275	}
276	if (!tascam || !atomic_read(&tascam->capture_active))
277		goto out;
278
279	if (urb->actual_length > 0) {
280		scoped_guard(spinlock_irqsave, &tascam->lock) {
281			size_t write_ptr = tascam->capture_ring_buffer_write_ptr;
282			size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - write_ptr;
283
284			if (urb->actual_length > bytes_to_end) {
285				memcpy(tascam->capture_ring_buffer + write_ptr,
286				       urb->transfer_buffer, bytes_to_end);
287				memcpy(tascam->capture_ring_buffer,
288				       urb->transfer_buffer + bytes_to_end,
289				       urb->actual_length - bytes_to_end);
290			} else {
291				memcpy(tascam->capture_ring_buffer + write_ptr,
292				       urb->transfer_buffer,
293				       urb->actual_length);
294			}
295
296			tascam->capture_ring_buffer_write_ptr =
297				(write_ptr + urb->actual_length) %
298				CAPTURE_RING_BUFFER_SIZE;
299		}
300
301		schedule_work(&tascam->capture_work);
302	}
303
304	usb_get_urb(urb);
305	usb_anchor_urb(urb, &tascam->capture_anchor);
306	ret = usb_submit_urb(urb, GFP_ATOMIC);
307	if (ret < 0) {
308		dev_err_ratelimited(tascam->card->dev,
309				    "Failed to resubmit capture URB: %d\n",
310				    ret);
311		usb_unanchor_urb(urb);
312		usb_put_urb(urb);
313		atomic_dec(
314			&tascam->active_urbs); /* Decrement on failed resubmission */
315	}
316out:
317	usb_put_urb(urb);
318}
319
~/ Read-only file preview.