xref: /linux/sound/usb/usx2y/us144mkii.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
1dee1bcf2SŠerif Rami // SPDX-License-Identifier: GPL-2.0-only
2dee1bcf2SŠerif Rami // Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
3dee1bcf2SŠerif Rami /*
4dee1bcf2SŠerif Rami  * ALSA Driver for TASCAM US-144MKII Audio Interface
5dee1bcf2SŠerif Rami  */
6dee1bcf2SŠerif Rami 
7dee1bcf2SŠerif Rami #include "us144mkii.h"
8dee1bcf2SŠerif Rami 
9dee1bcf2SŠerif Rami MODULE_AUTHOR("Šerif Rami <ramiserifpersia@gmail.com>");
10dee1bcf2SŠerif Rami MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII");
11dee1bcf2SŠerif Rami MODULE_LICENSE("GPL");
12dee1bcf2SŠerif Rami 
13dee1bcf2SŠerif Rami /**
14dee1bcf2SŠerif Rami  * @brief Module parameters for ALSA card instantiation.
15dee1bcf2SŠerif Rami  *
16dee1bcf2SŠerif Rami  * These parameters allow users to configure how the ALSA sound card
17dee1bcf2SŠerif Rami  * for the TASCAM US-144MKII is instantiated.
18dee1bcf2SŠerif Rami  *
19dee1bcf2SŠerif Rami  * @param index: Array of integers specifying the ALSA card index for each
20dee1bcf2SŠerif Rami  * device. Defaults to -1 (automatic).
21dee1bcf2SŠerif Rami  * @param id: Array of strings specifying the ALSA card ID for each device.
22dee1bcf2SŠerif Rami  *            Defaults to "US144MKII".
23dee1bcf2SŠerif Rami  * @param enable: Array of booleans to enable or disable each device.
24dee1bcf2SŠerif Rami  *                Defaults to {1, 0, ..., 0} (first device enabled).
255c8c1079SŠerif Rami  * @param dev_idx: Internal counter for the number of TASCAM devices probed.
26dee1bcf2SŠerif Rami  */
27dee1bcf2SŠerif Rami static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
28dee1bcf2SŠerif Rami static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
29dee1bcf2SŠerif Rami static bool enable[SNDRV_CARDS] = { 1, [1 ...(SNDRV_CARDS - 1)] = 0 };
30dee1bcf2SŠerif Rami static int dev_idx;
31dee1bcf2SŠerif Rami 
32dee1bcf2SŠerif Rami static int tascam_probe(struct usb_interface *intf,
33dee1bcf2SŠerif Rami 			const struct usb_device_id *usb_id);
34dee1bcf2SŠerif Rami static void tascam_disconnect(struct usb_interface *intf);
35dee1bcf2SŠerif Rami static int tascam_suspend(struct usb_interface *intf, pm_message_t message);
36dee1bcf2SŠerif Rami static int tascam_resume(struct usb_interface *intf);
37dee1bcf2SŠerif Rami 
38a2a2210fSŠerif Rami void tascam_free_urbs(struct tascam_card *tascam)
39a2a2210fSŠerif Rami {
40a2a2210fSŠerif Rami 	int i;
41a2a2210fSŠerif Rami 
42a2a2210fSŠerif Rami 	usb_kill_anchored_urbs(&tascam->playback_anchor);
43a2a2210fSŠerif Rami 	for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
44a2a2210fSŠerif Rami 		if (tascam->playback_urbs[i]) {
45a2a2210fSŠerif Rami 			usb_free_coherent(
46a2a2210fSŠerif Rami 				tascam->dev, tascam->playback_urb_alloc_size,
47a2a2210fSŠerif Rami 				tascam->playback_urbs[i]->transfer_buffer,
48a2a2210fSŠerif Rami 				tascam->playback_urbs[i]->transfer_dma);
49a2a2210fSŠerif Rami 			usb_free_urb(tascam->playback_urbs[i]);
50a2a2210fSŠerif Rami 			tascam->playback_urbs[i] = NULL;
51a2a2210fSŠerif Rami 		}
52a2a2210fSŠerif Rami 	}
53a2a2210fSŠerif Rami 
54a2a2210fSŠerif Rami 	usb_kill_anchored_urbs(&tascam->feedback_anchor);
55a2a2210fSŠerif Rami 	for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
56a2a2210fSŠerif Rami 		if (tascam->feedback_urbs[i]) {
57a2a2210fSŠerif Rami 			usb_free_coherent(
58a2a2210fSŠerif Rami 				tascam->dev, tascam->feedback_urb_alloc_size,
59a2a2210fSŠerif Rami 				tascam->feedback_urbs[i]->transfer_buffer,
60a2a2210fSŠerif Rami 				tascam->feedback_urbs[i]->transfer_dma);
61a2a2210fSŠerif Rami 			usb_free_urb(tascam->feedback_urbs[i]);
62a2a2210fSŠerif Rami 			tascam->feedback_urbs[i] = NULL;
63a2a2210fSŠerif Rami 		}
64a2a2210fSŠerif Rami 	}
65c1bb0c13SŠerif Rami 
66c1bb0c13SŠerif Rami 	usb_kill_anchored_urbs(&tascam->capture_anchor);
67c1bb0c13SŠerif Rami 	for (i = 0; i < NUM_CAPTURE_URBS; i++) {
68c1bb0c13SŠerif Rami 		if (tascam->capture_urbs[i]) {
69c1bb0c13SŠerif Rami 			usb_free_coherent(
70c1bb0c13SŠerif Rami 				tascam->dev, tascam->capture_urb_alloc_size,
71c1bb0c13SŠerif Rami 				tascam->capture_urbs[i]->transfer_buffer,
72c1bb0c13SŠerif Rami 				tascam->capture_urbs[i]->transfer_dma);
73c1bb0c13SŠerif Rami 			usb_free_urb(tascam->capture_urbs[i]);
74c1bb0c13SŠerif Rami 			tascam->capture_urbs[i] = NULL;
75c1bb0c13SŠerif Rami 		}
76c1bb0c13SŠerif Rami 	}
77c1bb0c13SŠerif Rami 
7867afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->midi_in_anchor);
7967afec15SŠerif Rami 	for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
8067afec15SŠerif Rami 		if (tascam->midi_in_urbs[i]) {
8167afec15SŠerif Rami 			usb_free_coherent(
8267afec15SŠerif Rami 				tascam->dev, MIDI_IN_BUF_SIZE,
8367afec15SŠerif Rami 				tascam->midi_in_urbs[i]->transfer_buffer,
8467afec15SŠerif Rami 				tascam->midi_in_urbs[i]->transfer_dma);
8567afec15SŠerif Rami 			usb_free_urb(tascam->midi_in_urbs[i]);
8667afec15SŠerif Rami 			tascam->midi_in_urbs[i] = NULL;
8767afec15SŠerif Rami 		}
8867afec15SŠerif Rami 	}
8967afec15SŠerif Rami 
9067afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->midi_out_anchor);
9167afec15SŠerif Rami 	for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
9267afec15SŠerif Rami 		if (tascam->midi_out_urbs[i]) {
9367afec15SŠerif Rami 			usb_free_coherent(
9467afec15SŠerif Rami 				tascam->dev, MIDI_OUT_BUF_SIZE,
9567afec15SŠerif Rami 				tascam->midi_out_urbs[i]->transfer_buffer,
9667afec15SŠerif Rami 				tascam->midi_out_urbs[i]->transfer_dma);
9767afec15SŠerif Rami 			usb_free_urb(tascam->midi_out_urbs[i]);
9867afec15SŠerif Rami 			tascam->midi_out_urbs[i] = NULL;
9967afec15SŠerif Rami 		}
10067afec15SŠerif Rami 	}
10167afec15SŠerif Rami 
102c1bb0c13SŠerif Rami 	kfree(tascam->capture_routing_buffer);
103c1bb0c13SŠerif Rami 	tascam->capture_routing_buffer = NULL;
104c1bb0c13SŠerif Rami 	kfree(tascam->capture_decode_dst_block);
105c1bb0c13SŠerif Rami 	tascam->capture_decode_dst_block = NULL;
106c1bb0c13SŠerif Rami 	kfree(tascam->capture_decode_raw_block);
107c1bb0c13SŠerif Rami 	tascam->capture_decode_raw_block = NULL;
108c1bb0c13SŠerif Rami 	kfree(tascam->capture_ring_buffer);
109c1bb0c13SŠerif Rami 	tascam->capture_ring_buffer = NULL;
110a2a2210fSŠerif Rami }
111a2a2210fSŠerif Rami 
112a2a2210fSŠerif Rami int tascam_alloc_urbs(struct tascam_card *tascam)
113a2a2210fSŠerif Rami {
114a2a2210fSŠerif Rami 	int i;
115a2a2210fSŠerif Rami 	size_t max_packet_size;
116a2a2210fSŠerif Rami 
117a2a2210fSŠerif Rami 	max_packet_size = ((96000 / 8000) + 2) * BYTES_PER_FRAME;
118a2a2210fSŠerif Rami 	tascam->playback_urb_alloc_size =
119a2a2210fSŠerif Rami 		max_packet_size * PLAYBACK_URB_PACKETS;
120a2a2210fSŠerif Rami 
121a2a2210fSŠerif Rami 	for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
122a2a2210fSŠerif Rami 		struct urb *urb =
123a2a2210fSŠerif Rami 			usb_alloc_urb(PLAYBACK_URB_PACKETS, GFP_KERNEL);
124a2a2210fSŠerif Rami 
125a2a2210fSŠerif Rami 		if (!urb)
126a2a2210fSŠerif Rami 			goto error;
127a2a2210fSŠerif Rami 		tascam->playback_urbs[i] = urb;
128a2a2210fSŠerif Rami 
129a2a2210fSŠerif Rami 		urb->transfer_buffer = usb_alloc_coherent(
130a2a2210fSŠerif Rami 			tascam->dev, tascam->playback_urb_alloc_size,
131a2a2210fSŠerif Rami 			GFP_KERNEL, &urb->transfer_dma);
132a2a2210fSŠerif Rami 		if (!urb->transfer_buffer)
133a2a2210fSŠerif Rami 			goto error;
134a2a2210fSŠerif Rami 
135a2a2210fSŠerif Rami 		urb->dev = tascam->dev;
136a2a2210fSŠerif Rami 		urb->pipe = usb_sndisocpipe(tascam->dev, EP_AUDIO_OUT);
137a2a2210fSŠerif Rami 		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
138a2a2210fSŠerif Rami 		urb->interval = 1;
139a2a2210fSŠerif Rami 		urb->context = tascam;
140a2a2210fSŠerif Rami 		urb->complete = playback_urb_complete;
141a2a2210fSŠerif Rami 	}
142a2a2210fSŠerif Rami 
143a2a2210fSŠerif Rami 	tascam->feedback_urb_alloc_size =
144a2a2210fSŠerif Rami 		FEEDBACK_PACKET_SIZE * FEEDBACK_URB_PACKETS;
145a2a2210fSŠerif Rami 
146a2a2210fSŠerif Rami 	for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
147a2a2210fSŠerif Rami 		struct urb *f_urb =
148a2a2210fSŠerif Rami 			usb_alloc_urb(FEEDBACK_URB_PACKETS, GFP_KERNEL);
149a2a2210fSŠerif Rami 
150a2a2210fSŠerif Rami 		if (!f_urb)
151a2a2210fSŠerif Rami 			goto error;
152a2a2210fSŠerif Rami 		tascam->feedback_urbs[i] = f_urb;
153a2a2210fSŠerif Rami 
154a2a2210fSŠerif Rami 		f_urb->transfer_buffer = usb_alloc_coherent(
155a2a2210fSŠerif Rami 			tascam->dev, tascam->feedback_urb_alloc_size,
156a2a2210fSŠerif Rami 			GFP_KERNEL, &f_urb->transfer_dma);
157a2a2210fSŠerif Rami 		if (!f_urb->transfer_buffer)
158a2a2210fSŠerif Rami 			goto error;
159a2a2210fSŠerif Rami 
160a2a2210fSŠerif Rami 		f_urb->dev = tascam->dev;
161a2a2210fSŠerif Rami 		f_urb->pipe =
162a2a2210fSŠerif Rami 			usb_rcvisocpipe(tascam->dev, EP_PLAYBACK_FEEDBACK);
163a2a2210fSŠerif Rami 		f_urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
164a2a2210fSŠerif Rami 		f_urb->interval = 4;
165a2a2210fSŠerif Rami 		f_urb->context = tascam;
166a2a2210fSŠerif Rami 		f_urb->complete = feedback_urb_complete;
167a2a2210fSŠerif Rami 	}
168a2a2210fSŠerif Rami 
169c1bb0c13SŠerif Rami 	tascam->capture_urb_alloc_size = CAPTURE_URB_SIZE;
170c1bb0c13SŠerif Rami 	for (i = 0; i < NUM_CAPTURE_URBS; i++) {
171c1bb0c13SŠerif Rami 		struct urb *c_urb = usb_alloc_urb(0, GFP_KERNEL);
172c1bb0c13SŠerif Rami 
173c1bb0c13SŠerif Rami 		if (!c_urb)
174c1bb0c13SŠerif Rami 			goto error;
175c1bb0c13SŠerif Rami 		tascam->capture_urbs[i] = c_urb;
176c1bb0c13SŠerif Rami 
177c1bb0c13SŠerif Rami 		c_urb->transfer_buffer = usb_alloc_coherent(
178c1bb0c13SŠerif Rami 			tascam->dev, tascam->capture_urb_alloc_size, GFP_KERNEL,
179c1bb0c13SŠerif Rami 			&c_urb->transfer_dma);
180c1bb0c13SŠerif Rami 		if (!c_urb->transfer_buffer)
181c1bb0c13SŠerif Rami 			goto error;
182c1bb0c13SŠerif Rami 
183c1bb0c13SŠerif Rami 		usb_fill_bulk_urb(c_urb, tascam->dev,
184c1bb0c13SŠerif Rami 				  usb_rcvbulkpipe(tascam->dev, EP_AUDIO_IN),
185c1bb0c13SŠerif Rami 				  c_urb->transfer_buffer,
186c1bb0c13SŠerif Rami 				  tascam->capture_urb_alloc_size,
187c1bb0c13SŠerif Rami 				  capture_urb_complete, tascam);
188c1bb0c13SŠerif Rami 		c_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
189c1bb0c13SŠerif Rami 	}
190c1bb0c13SŠerif Rami 
19167afec15SŠerif Rami 	/* MIDI URB and buffer allocation */
19267afec15SŠerif Rami 	for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
19367afec15SŠerif Rami 		struct urb *m_urb = usb_alloc_urb(0, GFP_KERNEL);
19467afec15SŠerif Rami 
19567afec15SŠerif Rami 		if (!m_urb)
19667afec15SŠerif Rami 			goto error;
19767afec15SŠerif Rami 		tascam->midi_in_urbs[i] = m_urb;
19867afec15SŠerif Rami 		m_urb->transfer_buffer =
19967afec15SŠerif Rami 			usb_alloc_coherent(tascam->dev, MIDI_IN_BUF_SIZE,
20067afec15SŠerif Rami 					   GFP_KERNEL, &m_urb->transfer_dma);
20167afec15SŠerif Rami 		if (!m_urb->transfer_buffer)
20267afec15SŠerif Rami 			goto error;
20367afec15SŠerif Rami 		usb_fill_bulk_urb(m_urb, tascam->dev,
20467afec15SŠerif Rami 				  usb_rcvbulkpipe(tascam->dev, EP_MIDI_IN),
20567afec15SŠerif Rami 				  m_urb->transfer_buffer, MIDI_IN_BUF_SIZE,
20667afec15SŠerif Rami 				  tascam_midi_in_urb_complete, tascam);
20767afec15SŠerif Rami 		m_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
20867afec15SŠerif Rami 	}
20967afec15SŠerif Rami 
21067afec15SŠerif Rami 	for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
21167afec15SŠerif Rami 		struct urb *m_urb = usb_alloc_urb(0, GFP_KERNEL);
21267afec15SŠerif Rami 
21367afec15SŠerif Rami 		if (!m_urb)
21467afec15SŠerif Rami 			goto error;
21567afec15SŠerif Rami 		tascam->midi_out_urbs[i] = m_urb;
21667afec15SŠerif Rami 		m_urb->transfer_buffer =
21767afec15SŠerif Rami 			usb_alloc_coherent(tascam->dev, MIDI_OUT_BUF_SIZE,
21867afec15SŠerif Rami 					   GFP_KERNEL, &m_urb->transfer_dma);
21967afec15SŠerif Rami 		if (!m_urb->transfer_buffer)
22067afec15SŠerif Rami 			goto error;
22167afec15SŠerif Rami 		usb_fill_bulk_urb(m_urb, tascam->dev,
22267afec15SŠerif Rami 				  usb_sndbulkpipe(tascam->dev, EP_MIDI_OUT),
22367afec15SŠerif Rami 				  m_urb->transfer_buffer,
22467afec15SŠerif Rami 				  0, /* length set later */
22567afec15SŠerif Rami 				  tascam_midi_out_urb_complete, tascam);
22667afec15SŠerif Rami 		m_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
22767afec15SŠerif Rami 	}
22867afec15SŠerif Rami 
229c1bb0c13SŠerif Rami 	tascam->capture_ring_buffer =
230c1bb0c13SŠerif Rami 		kmalloc(CAPTURE_RING_BUFFER_SIZE, GFP_KERNEL);
231c1bb0c13SŠerif Rami 	if (!tascam->capture_ring_buffer)
232c1bb0c13SŠerif Rami 		goto error;
233c1bb0c13SŠerif Rami 
234c1bb0c13SŠerif Rami 	tascam->capture_decode_raw_block =
235c1bb0c13SŠerif Rami 		kmalloc(RAW_BYTES_PER_DECODE_BLOCK, GFP_KERNEL);
236c1bb0c13SŠerif Rami 	if (!tascam->capture_decode_raw_block)
237c1bb0c13SŠerif Rami 		goto error;
238c1bb0c13SŠerif Rami 
239c1bb0c13SŠerif Rami 	tascam->capture_decode_dst_block =
240c1bb0c13SŠerif Rami 		kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
241c1bb0c13SŠerif Rami 				DECODED_SAMPLE_SIZE,
242c1bb0c13SŠerif Rami 			GFP_KERNEL);
243c1bb0c13SŠerif Rami 	if (!tascam->capture_decode_dst_block)
244c1bb0c13SŠerif Rami 		goto error;
245c1bb0c13SŠerif Rami 
246c1bb0c13SŠerif Rami 	tascam->capture_routing_buffer =
247c1bb0c13SŠerif Rami 		kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
248c1bb0c13SŠerif Rami 				DECODED_SAMPLE_SIZE,
249c1bb0c13SŠerif Rami 			GFP_KERNEL);
250c1bb0c13SŠerif Rami 	if (!tascam->capture_routing_buffer)
251c1bb0c13SŠerif Rami 		goto error;
252c1bb0c13SŠerif Rami 
253a2a2210fSŠerif Rami 	return 0;
254a2a2210fSŠerif Rami 
255a2a2210fSŠerif Rami error:
256a2a2210fSŠerif Rami 	dev_err(tascam->card->dev, "Failed to allocate URBs\n");
257a2a2210fSŠerif Rami 	tascam_free_urbs(tascam);
258a2a2210fSŠerif Rami 	return -ENOMEM;
259a2a2210fSŠerif Rami }
260a2a2210fSŠerif Rami 
261a2a2210fSŠerif Rami void tascam_stop_work_handler(struct work_struct *work)
262a2a2210fSŠerif Rami {
263a2a2210fSŠerif Rami 	struct tascam_card *tascam =
264a2a2210fSŠerif Rami 		container_of(work, struct tascam_card, stop_work);
265a2a2210fSŠerif Rami 
266a2a2210fSŠerif Rami 	usb_kill_anchored_urbs(&tascam->playback_anchor);
267a2a2210fSŠerif Rami 	usb_kill_anchored_urbs(&tascam->feedback_anchor);
268c1bb0c13SŠerif Rami 	usb_kill_anchored_urbs(&tascam->capture_anchor);
269a2a2210fSŠerif Rami 	atomic_set(&tascam->active_urbs, 0);
270a2a2210fSŠerif Rami }
271a2a2210fSŠerif Rami 
272dee1bcf2SŠerif Rami /**
2735c8c1079SŠerif Rami  * tascam_card_private_free() - Frees private data associated with the sound
274dee1bcf2SŠerif Rami  * card.
275dee1bcf2SŠerif Rami  * @card: Pointer to the ALSA sound card instance.
276dee1bcf2SŠerif Rami  *
277dee1bcf2SŠerif Rami  * This function is called when the sound card is being freed. It releases
27867afec15SŠerif Rami  * resources allocated for the tascam_card structure, including the MIDI
27967afec15SŠerif Rami  * input FIFO and decrements the USB device reference count.
280dee1bcf2SŠerif Rami  */
281dee1bcf2SŠerif Rami static void tascam_card_private_free(struct snd_card *card)
282dee1bcf2SŠerif Rami {
283dee1bcf2SŠerif Rami 	struct tascam_card *tascam = card->private_data;
284dee1bcf2SŠerif Rami 
28567afec15SŠerif Rami 	if (tascam) {
28667afec15SŠerif Rami 		kfifo_free(&tascam->midi_in_fifo);
28767afec15SŠerif Rami 		if (tascam->dev) {
288dee1bcf2SŠerif Rami 			usb_put_dev(tascam->dev);
289dee1bcf2SŠerif Rami 			tascam->dev = NULL;
290dee1bcf2SŠerif Rami 		}
291dee1bcf2SŠerif Rami 	}
29267afec15SŠerif Rami }
29367afec15SŠerif Rami 
29467afec15SŠerif Rami /**
29567afec15SŠerif Rami  * tascam_suspend() - Handles device suspension.
29667afec15SŠerif Rami  * @intf: The USB interface being suspended.
29767afec15SŠerif Rami  * @message: Power management message.
29867afec15SŠerif Rami  *
29967afec15SŠerif Rami  * This function is called when the device is suspended. It stops all active
30067afec15SŠerif Rami  * streams, kills all URBs, and sends a vendor-specific deep sleep command
30167afec15SŠerif Rami  * to the device to ensure a stable low-power state.
30267afec15SŠerif Rami  *
30367afec15SŠerif Rami  * Return: 0 on success.
30467afec15SŠerif Rami  */
30567afec15SŠerif Rami static int tascam_suspend(struct usb_interface *intf, pm_message_t message)
30667afec15SŠerif Rami {
30767afec15SŠerif Rami 	struct tascam_card *tascam = usb_get_intfdata(intf);
30867afec15SŠerif Rami 
30967afec15SŠerif Rami 	if (!tascam)
31067afec15SŠerif Rami 		return 0;
31167afec15SŠerif Rami 
31267afec15SŠerif Rami 	snd_pcm_suspend_all(tascam->pcm);
31367afec15SŠerif Rami 
31467afec15SŠerif Rami 	cancel_work_sync(&tascam->stop_work);
31567afec15SŠerif Rami 	cancel_work_sync(&tascam->capture_work);
31667afec15SŠerif Rami 	cancel_work_sync(&tascam->midi_in_work);
31767afec15SŠerif Rami 	cancel_work_sync(&tascam->midi_out_work);
31867afec15SŠerif Rami 	cancel_work_sync(&tascam->stop_pcm_work);
31967afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->playback_anchor);
32067afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->capture_anchor);
32167afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->feedback_anchor);
32267afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->midi_in_anchor);
32367afec15SŠerif Rami 	usb_kill_anchored_urbs(&tascam->midi_out_anchor);
32467afec15SŠerif Rami 
325fdd1a1aeSŠerif Rami 	dev_info(&intf->dev, "sending deep sleep command\n");
326fdd1a1aeSŠerif Rami 	int err = usb_control_msg(tascam->dev, usb_sndctrlpipe(tascam->dev, 0),
327fdd1a1aeSŠerif Rami 				  VENDOR_REQ_DEEP_SLEEP, RT_H2D_VENDOR_DEV,
328fdd1a1aeSŠerif Rami 				  0x0000, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
329fdd1a1aeSŠerif Rami 	if (err < 0)
330fdd1a1aeSŠerif Rami 		dev_err(&intf->dev, "deep sleep command failed: %d\n", err);
331fdd1a1aeSŠerif Rami 
33267afec15SŠerif Rami 	return 0;
33367afec15SŠerif Rami }
33467afec15SŠerif Rami 
33567afec15SŠerif Rami /**
33667afec15SŠerif Rami  * tascam_resume() - Handles device resumption from suspend.
33767afec15SŠerif Rami  * @intf: The USB interface being resumed.
33867afec15SŠerif Rami  *
33967afec15SŠerif Rami  * This function is called when the device resumes from suspend. It
34067afec15SŠerif Rami  * re-establishes the active USB interface settings and re-configures the sample
34167afec15SŠerif Rami  * rate if it was previously active.
34267afec15SŠerif Rami  *
34367afec15SŠerif Rami  * Return: 0 on success, or a negative error code on failure.
34467afec15SŠerif Rami  */
34567afec15SŠerif Rami static int tascam_resume(struct usb_interface *intf)
34667afec15SŠerif Rami {
34767afec15SŠerif Rami 	struct tascam_card *tascam = usb_get_intfdata(intf);
34867afec15SŠerif Rami 	int err;
34967afec15SŠerif Rami 
35067afec15SŠerif Rami 	if (!tascam)
35167afec15SŠerif Rami 		return 0;
35267afec15SŠerif Rami 
35367afec15SŠerif Rami 	dev_info(&intf->dev, "resuming TASCAM US-144MKII\n");
35467afec15SŠerif Rami 
35567afec15SŠerif Rami 	/*
35667afec15SŠerif Rami 	 * The device requires a full re-initialization sequence upon resume.
35767afec15SŠerif Rami 	 * First, re-establish the active USB interface settings.
35867afec15SŠerif Rami 	 */
35967afec15SŠerif Rami 	err = usb_set_interface(tascam->dev, 0, 1);
36067afec15SŠerif Rami 	if (err < 0) {
36167afec15SŠerif Rami 		dev_err(&intf->dev,
36267afec15SŠerif Rami 			"resume: failed to set alt setting on intf 0: %d\n",
36367afec15SŠerif Rami 			err);
36467afec15SŠerif Rami 		return err;
36567afec15SŠerif Rami 	}
36667afec15SŠerif Rami 	err = usb_set_interface(tascam->dev, 1, 1);
36767afec15SŠerif Rami 	if (err < 0) {
36867afec15SŠerif Rami 		dev_err(&intf->dev,
36967afec15SŠerif Rami 			"resume: failed to set alt setting on intf 1: %d\n",
37067afec15SŠerif Rami 			err);
37167afec15SŠerif Rami 		return err;
37267afec15SŠerif Rami 	}
37367afec15SŠerif Rami 
37467afec15SŠerif Rami 	/* Re-configure the sample rate if one was previously active */
37567afec15SŠerif Rami 	if (tascam->current_rate > 0)
37667afec15SŠerif Rami 		us144mkii_configure_device_for_rate(tascam,
37767afec15SŠerif Rami 						    tascam->current_rate);
37867afec15SŠerif Rami 
37967afec15SŠerif Rami 	return 0;
38067afec15SŠerif Rami }
38167afec15SŠerif Rami 
38267afec15SŠerif Rami static void tascam_error_timer(struct timer_list *t)
38367afec15SŠerif Rami {
38467afec15SŠerif Rami 	struct tascam_card *tascam =
38567afec15SŠerif Rami 		container_of(t, struct tascam_card, error_timer);
38667afec15SŠerif Rami 
38767afec15SŠerif Rami 	if (atomic_read(&tascam->midi_in_active))
38867afec15SŠerif Rami 		schedule_work(&tascam->midi_in_work);
38967afec15SŠerif Rami 	if (atomic_read(&tascam->midi_out_active))
39067afec15SŠerif Rami 		schedule_work(&tascam->midi_out_work);
39167afec15SŠerif Rami }
392dee1bcf2SŠerif Rami 
393dee1bcf2SŠerif Rami /**
394dee1bcf2SŠerif Rami  * tascam_probe() - Probes for the TASCAM US-144MKII device.
395dee1bcf2SŠerif Rami  * @intf: The USB interface being probed.
396dee1bcf2SŠerif Rami  * @usb_id: The USB device ID.
397dee1bcf2SŠerif Rami  *
3985c8c1079SŠerif Rami  * This function is the entry point for the USB driver when a matching device
399dee1bcf2SŠerif Rami  * is found. It performs initial device setup, including:
400dee1bcf2SŠerif Rami  * - Checking for the second interface (MIDI) and associating it.
401dee1bcf2SŠerif Rami  * - Performing a vendor-specific handshake with the device.
402dee1bcf2SŠerif Rami  * - Setting alternate settings for USB interfaces.
40367afec15SŠerif Rami  * - Creating and registering the ALSA sound card, PCM device, and MIDI device.
40467afec15SŠerif Rami  * - Allocating and initializing URBs for audio and MIDI transfers.
405dee1bcf2SŠerif Rami  *
406dee1bcf2SŠerif Rami  * Return: 0 on success, or a negative error code on failure.
407dee1bcf2SŠerif Rami  */
408dee1bcf2SŠerif Rami static int tascam_probe(struct usb_interface *intf,
409dee1bcf2SŠerif Rami 			const struct usb_device_id *usb_id)
410dee1bcf2SŠerif Rami {
411dee1bcf2SŠerif Rami 	struct usb_device *dev = interface_to_usbdev(intf);
412dee1bcf2SŠerif Rami 	struct snd_card *card;
413dee1bcf2SŠerif Rami 	struct tascam_card *tascam;
414dee1bcf2SŠerif Rami 	int err;
415dee1bcf2SŠerif Rami 	char *handshake_buf __free(kfree) = NULL;
416dee1bcf2SŠerif Rami 
417dee1bcf2SŠerif Rami 	if (dev->speed != USB_SPEED_HIGH)
418dee1bcf2SŠerif Rami 		dev_info(
419dee1bcf2SŠerif Rami 			&dev->dev,
420dee1bcf2SŠerif Rami 			"Device is connected to a USB 1.1 port, this is not supported.\n");
421dee1bcf2SŠerif Rami 
422dee1bcf2SŠerif Rami 	/* The device has two interfaces; we drive both from this driver. */
423dee1bcf2SŠerif Rami 	if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
424dee1bcf2SŠerif Rami 		tascam = usb_get_intfdata(usb_ifnum_to_if(dev, 0));
425dee1bcf2SŠerif Rami 		if (tascam) {
426dee1bcf2SŠerif Rami 			usb_set_intfdata(intf, tascam);
427dee1bcf2SŠerif Rami 			tascam->iface1 = intf;
428dee1bcf2SŠerif Rami 		}
429dee1bcf2SŠerif Rami 		return 0; /* Let the core handle this interface */
430dee1bcf2SŠerif Rami 	}
431dee1bcf2SŠerif Rami 
432dee1bcf2SŠerif Rami 	if (dev_idx >= SNDRV_CARDS) {
433dee1bcf2SŠerif Rami 		dev_err(&dev->dev, "Too many TASCAM devices present");
434dee1bcf2SŠerif Rami 		return -ENODEV;
435dee1bcf2SŠerif Rami 	}
436dee1bcf2SŠerif Rami 
437dee1bcf2SŠerif Rami 	if (!enable[dev_idx]) {
438dee1bcf2SŠerif Rami 		dev_info(&dev->dev, "TASCAM US-144MKII device disabled");
439dee1bcf2SŠerif Rami 		return -ENOENT;
440dee1bcf2SŠerif Rami 	}
441dee1bcf2SŠerif Rami 
442dee1bcf2SŠerif Rami 	handshake_buf = kmalloc(1, GFP_KERNEL);
443dee1bcf2SŠerif Rami 	if (!handshake_buf)
444dee1bcf2SŠerif Rami 		return -ENOMEM;
445dee1bcf2SŠerif Rami 
446dee1bcf2SŠerif Rami 	/* Perform vendor-specific handshake */
447dee1bcf2SŠerif Rami 	err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
448dee1bcf2SŠerif Rami 			      VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV,
449dee1bcf2SŠerif Rami 			      MODE_VAL_HANDSHAKE_READ, 0x0000, handshake_buf, 1,
450dee1bcf2SŠerif Rami 			      USB_CTRL_TIMEOUT_MS);
451dee1bcf2SŠerif Rami 	if (err < 0) {
452dee1bcf2SŠerif Rami 		dev_err(&dev->dev, "Handshake read failed with %d\n", err);
453dee1bcf2SŠerif Rami 		return err;
454dee1bcf2SŠerif Rami 	}
455dee1bcf2SŠerif Rami 
456dee1bcf2SŠerif Rami 	if (handshake_buf[0] != 0x12 && handshake_buf[0] != 0x16 &&
457*cdbd2aceSŠerif Rami 	    handshake_buf[0] != 0x30 && handshake_buf[0] != 0x32) {
458dee1bcf2SŠerif Rami 		dev_err(&dev->dev, "Unexpected handshake value: 0x%x\n",
459dee1bcf2SŠerif Rami 			handshake_buf[0]);
460dee1bcf2SŠerif Rami 		return -ENODEV;
461dee1bcf2SŠerif Rami 	}
462dee1bcf2SŠerif Rami 
463dee1bcf2SŠerif Rami 	/* Set alternate settings to enable audio/MIDI endpoints */
464dee1bcf2SŠerif Rami 	err = usb_set_interface(dev, 0, 1);
465dee1bcf2SŠerif Rami 	if (err < 0) {
466dee1bcf2SŠerif Rami 		dev_err(&dev->dev,
467dee1bcf2SŠerif Rami 			"Failed to set alt setting 1 on interface 0: %d\n",
468dee1bcf2SŠerif Rami 			err);
469dee1bcf2SŠerif Rami 		return err;
470dee1bcf2SŠerif Rami 	}
471dee1bcf2SŠerif Rami 
472dee1bcf2SŠerif Rami 	err = usb_set_interface(dev, 1, 1);
473dee1bcf2SŠerif Rami 	if (err < 0) {
474dee1bcf2SŠerif Rami 		dev_err(&dev->dev,
475dee1bcf2SŠerif Rami 			"Failed to set alt setting 1 on interface 1: %d\n",
476dee1bcf2SŠerif Rami 			err);
477dee1bcf2SŠerif Rami 		return err;
478dee1bcf2SŠerif Rami 	}
479dee1bcf2SŠerif Rami 
480dee1bcf2SŠerif Rami 	err = snd_card_new(&dev->dev, index[dev_idx], id[dev_idx], THIS_MODULE,
481dee1bcf2SŠerif Rami 			   sizeof(struct tascam_card), &card);
482dee1bcf2SŠerif Rami 	if (err < 0) {
483dee1bcf2SŠerif Rami 		dev_err(&dev->dev, "Failed to create sound card instance\n");
484dee1bcf2SŠerif Rami 		return err;
485dee1bcf2SŠerif Rami 	}
486dee1bcf2SŠerif Rami 
487dee1bcf2SŠerif Rami 	tascam = card->private_data;
488dee1bcf2SŠerif Rami 	card->private_free = tascam_card_private_free;
489dee1bcf2SŠerif Rami 	tascam->dev = usb_get_dev(dev);
490dee1bcf2SŠerif Rami 	tascam->card = card;
491dee1bcf2SŠerif Rami 	tascam->iface0 = intf;
492dee1bcf2SŠerif Rami 	tascam->digital_out_source = 1;
493dee1bcf2SŠerif Rami 	tascam->capture_34_source = 1;
494dee1bcf2SŠerif Rami 
4955c8c1079SŠerif Rami 	spin_lock_init(&tascam->lock);
49667afec15SŠerif Rami 	spin_lock_init(&tascam->midi_in_lock);
49767afec15SŠerif Rami 	spin_lock_init(&tascam->midi_out_lock);
498a2a2210fSŠerif Rami 	init_usb_anchor(&tascam->playback_anchor);
499c1bb0c13SŠerif Rami 	init_usb_anchor(&tascam->capture_anchor);
500a2a2210fSŠerif Rami 	init_usb_anchor(&tascam->feedback_anchor);
50167afec15SŠerif Rami 	init_usb_anchor(&tascam->midi_in_anchor);
50267afec15SŠerif Rami 	init_usb_anchor(&tascam->midi_out_anchor);
50367afec15SŠerif Rami 
50467afec15SŠerif Rami 	timer_setup(&tascam->error_timer, tascam_error_timer, 0);
505a2a2210fSŠerif Rami 
506a2a2210fSŠerif Rami 	INIT_WORK(&tascam->stop_work, tascam_stop_work_handler);
507a2a2210fSŠerif Rami 	INIT_WORK(&tascam->stop_pcm_work, tascam_stop_pcm_work_handler);
508c1bb0c13SŠerif Rami 	INIT_WORK(&tascam->capture_work, tascam_capture_work_handler);
50967afec15SŠerif Rami 	init_completion(&tascam->midi_out_drain_completion);
5105c8c1079SŠerif Rami 
51167afec15SŠerif Rami 	if (kfifo_alloc(&tascam->midi_in_fifo, MIDI_IN_FIFO_SIZE, GFP_KERNEL)) {
51267afec15SŠerif Rami 		snd_card_free(card);
51367afec15SŠerif Rami 		return -ENOMEM;
51467afec15SŠerif Rami 	}
515a2a2210fSŠerif Rami 
516dee1bcf2SŠerif Rami 	strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
517d9f06338SŠerif Rami 	if (le16_to_cpu(dev->descriptor.idProduct) == USB_PID_TASCAM_US144) {
518dee1bcf2SŠerif Rami 		strscpy(card->shortname, "TASCAM US-144",
519dee1bcf2SŠerif Rami 			sizeof(card->shortname));
520d9f06338SŠerif Rami 	} else if (le16_to_cpu(dev->descriptor.idProduct) == USB_PID_TASCAM_US144MKII) {
521dee1bcf2SŠerif Rami 		strscpy(card->shortname, "TASCAM US-144MKII",
522dee1bcf2SŠerif Rami 			sizeof(card->shortname));
523dee1bcf2SŠerif Rami 	} else {
524dee1bcf2SŠerif Rami 		strscpy(card->shortname, "TASCAM Unknown",
525dee1bcf2SŠerif Rami 			sizeof(card->shortname));
526dee1bcf2SŠerif Rami 	}
527dee1bcf2SŠerif Rami 	snprintf(card->longname, sizeof(card->longname), "%s (%04x:%04x) at %s",
528dee1bcf2SŠerif Rami 		 card->shortname, USB_VID_TASCAM, dev->descriptor.idProduct,
529dee1bcf2SŠerif Rami 		 dev_name(&dev->dev));
530dee1bcf2SŠerif Rami 
53167afec15SŠerif Rami 	err = snd_pcm_new(card, "US144MKII PCM", 0, 1, 1, &tascam->pcm);
53267afec15SŠerif Rami 	if (err < 0)
53367afec15SŠerif Rami 		goto free_card;
53467afec15SŠerif Rami 	tascam->pcm->private_data = tascam;
53567afec15SŠerif Rami 	strscpy(tascam->pcm->name, "US144MKII PCM", sizeof(tascam->pcm->name));
53667afec15SŠerif Rami 
53767afec15SŠerif Rami 	err = tascam_init_pcm(tascam->pcm);
53867afec15SŠerif Rami 	if (err < 0)
53967afec15SŠerif Rami 		goto free_card;
54067afec15SŠerif Rami 
54167afec15SŠerif Rami 	err = tascam_create_midi(tascam);
54267afec15SŠerif Rami 	if (err < 0)
54367afec15SŠerif Rami 		goto free_card;
54467afec15SŠerif Rami 
54567afec15SŠerif Rami 	err = tascam_create_controls(tascam);
54667afec15SŠerif Rami 	if (err < 0)
54767afec15SŠerif Rami 		goto free_card;
54867afec15SŠerif Rami 
54967afec15SŠerif Rami 	err = tascam_alloc_urbs(tascam);
55067afec15SŠerif Rami 	if (err < 0)
55167afec15SŠerif Rami 		goto free_card;
55267afec15SŠerif Rami 
553dee1bcf2SŠerif Rami 	err = snd_card_register(card);
554dee1bcf2SŠerif Rami 	if (err < 0)
555dee1bcf2SŠerif Rami 		goto free_card;
556dee1bcf2SŠerif Rami 
557dee1bcf2SŠerif Rami 	usb_set_intfdata(intf, tascam);
558dee1bcf2SŠerif Rami 
559dee1bcf2SŠerif Rami 	dev_idx++;
560dee1bcf2SŠerif Rami 	return 0;
561dee1bcf2SŠerif Rami 
562dee1bcf2SŠerif Rami free_card:
563a2a2210fSŠerif Rami 	tascam_free_urbs(tascam);
564dee1bcf2SŠerif Rami 	snd_card_free(card);
565dee1bcf2SŠerif Rami 	return err;
566dee1bcf2SŠerif Rami }
567dee1bcf2SŠerif Rami 
568dee1bcf2SŠerif Rami /**
569dee1bcf2SŠerif Rami  * tascam_disconnect() - Disconnects the TASCAM US-144MKII device.
570dee1bcf2SŠerif Rami  * @intf: The USB interface being disconnected.
571dee1bcf2SŠerif Rami  *
572dee1bcf2SŠerif Rami  * This function is called when the device is disconnected from the system.
57367afec15SŠerif Rami  * It cleans up all allocated resources, including killing URBs, freeing
57467afec15SŠerif Rami  * the sound card, and releasing memory.
575dee1bcf2SŠerif Rami  */
576dee1bcf2SŠerif Rami static void tascam_disconnect(struct usb_interface *intf)
577dee1bcf2SŠerif Rami {
578dee1bcf2SŠerif Rami 	struct tascam_card *tascam = usb_get_intfdata(intf);
579dee1bcf2SŠerif Rami 
580dee1bcf2SŠerif Rami 	if (!tascam)
581dee1bcf2SŠerif Rami 		return;
582dee1bcf2SŠerif Rami 
583dee1bcf2SŠerif Rami 	if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
58467afec15SŠerif Rami 		/* Ensure all deferred work is complete before freeing resources */
585dee1bcf2SŠerif Rami 		snd_card_disconnect(tascam->card);
586a2a2210fSŠerif Rami 		cancel_work_sync(&tascam->stop_work);
587c1bb0c13SŠerif Rami 		cancel_work_sync(&tascam->capture_work);
58867afec15SŠerif Rami 		cancel_work_sync(&tascam->midi_in_work);
58967afec15SŠerif Rami 		cancel_work_sync(&tascam->midi_out_work);
590a2a2210fSŠerif Rami 		cancel_work_sync(&tascam->stop_pcm_work);
59167afec15SŠerif Rami 
59267afec15SŠerif Rami 		usb_kill_anchored_urbs(&tascam->playback_anchor);
59367afec15SŠerif Rami 		usb_kill_anchored_urbs(&tascam->capture_anchor);
59467afec15SŠerif Rami 		usb_kill_anchored_urbs(&tascam->feedback_anchor);
59567afec15SŠerif Rami 		usb_kill_anchored_urbs(&tascam->midi_in_anchor);
59667afec15SŠerif Rami 		usb_kill_anchored_urbs(&tascam->midi_out_anchor);
59767afec15SŠerif Rami 		timer_delete_sync(&tascam->error_timer);
598a2a2210fSŠerif Rami 		tascam_free_urbs(tascam);
599dee1bcf2SŠerif Rami 		snd_card_free(tascam->card);
600dee1bcf2SŠerif Rami 		dev_idx--;
601dee1bcf2SŠerif Rami 	}
602dee1bcf2SŠerif Rami }
603dee1bcf2SŠerif Rami 
604dee1bcf2SŠerif Rami static const struct usb_device_id tascam_usb_ids[] = {
605dee1bcf2SŠerif Rami 	{ USB_DEVICE(USB_VID_TASCAM, USB_PID_TASCAM_US144) },
606dee1bcf2SŠerif Rami 	{ USB_DEVICE(USB_VID_TASCAM, USB_PID_TASCAM_US144MKII) },
607dee1bcf2SŠerif Rami 	{ /* Terminating entry */ }
608dee1bcf2SŠerif Rami };
609dee1bcf2SŠerif Rami MODULE_DEVICE_TABLE(usb, tascam_usb_ids);
610dee1bcf2SŠerif Rami 
611dee1bcf2SŠerif Rami static struct usb_driver tascam_alsa_driver = {
612dee1bcf2SŠerif Rami 	.name = DRIVER_NAME,
613dee1bcf2SŠerif Rami 	.probe = tascam_probe,
614dee1bcf2SŠerif Rami 	.disconnect = tascam_disconnect,
615dee1bcf2SŠerif Rami 	.suspend = tascam_suspend,
616dee1bcf2SŠerif Rami 	.resume = tascam_resume,
617dee1bcf2SŠerif Rami 	.id_table = tascam_usb_ids,
618dee1bcf2SŠerif Rami };
619dee1bcf2SŠerif Rami 
620dee1bcf2SŠerif Rami module_usb_driver(tascam_alsa_driver);
621