aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/usx2y
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/usb/usx2y
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/usb/usx2y')
-rw-r--r--sound/usb/usx2y/Makefile3
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c280
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.h6
-rw-r--r--sound/usb/usx2y/usbus428ctldefs.h108
-rw-r--r--sound/usb/usx2y/usbusx2y.c461
-rw-r--r--sound/usb/usx2y/usbusx2y.h84
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c1026
-rw-r--r--sound/usb/usx2y/usx2y.h51
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c807
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.h21
10 files changed, 2847 insertions, 0 deletions
diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile
new file mode 100644
index 000000000000..9ac22bce1124
--- /dev/null
+++ b/sound/usb/usx2y/Makefile
@@ -0,0 +1,3 @@
1snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
2
3obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
new file mode 100644
index 000000000000..bef9b0c142c4
--- /dev/null
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -0,0 +1,280 @@
1/*
2 * Driver for Tascam US-X2Y USB soundcards
3 *
4 * FPGA Loader + ALSA Startup
5 *
6 * Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <sound/driver.h>
24#include <linux/interrupt.h>
25#include <linux/usb.h>
26#include <sound/core.h>
27#include <sound/memalloc.h>
28#include <sound/pcm.h>
29#include <sound/hwdep.h>
30#include "usx2y.h"
31#include "usbusx2y.h"
32#include "usX2Yhwdep.h"
33
34int usX2Y_hwdep_pcm_new(snd_card_t* card);
35
36
37static struct page * snd_us428ctls_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
38{
39 unsigned long offset;
40 struct page * page;
41 void *vaddr;
42
43 snd_printdd("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n",
44 area->vm_start,
45 address - area->vm_start,
46 (address - area->vm_start) >> PAGE_SHIFT,
47 address);
48
49 offset = area->vm_pgoff << PAGE_SHIFT;
50 offset += address - area->vm_start;
51 snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
52 vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->us428ctls_sharedmem + offset;
53 page = virt_to_page(vaddr);
54 get_page(page);
55 snd_printdd( "vaddr=%p made us428ctls_vm_nopage() return %p; offset=%lX\n", vaddr, page, offset);
56
57 if (type)
58 *type = VM_FAULT_MINOR;
59
60 return page;
61}
62
63static struct vm_operations_struct us428ctls_vm_ops = {
64 .nopage = snd_us428ctls_vm_nopage,
65};
66
67static int snd_us428ctls_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
68{
69 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
70 usX2Ydev_t *us428 = (usX2Ydev_t*)hw->private_data;
71
72 // FIXME this hwdep interface is used twice: fpga download and mmap for controlling Lights etc. Maybe better using 2 hwdep devs?
73 // so as long as the device isn't fully initialised yet we return -EBUSY here.
74 if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
75 return -EBUSY;
76
77 /* if userspace tries to mmap beyond end of our buffer, fail */
78 if (size > ((PAGE_SIZE - 1 + sizeof(us428ctls_sharedmem_t)) / PAGE_SIZE) * PAGE_SIZE) {
79 snd_printd( "%lu > %lu\n", size, (unsigned long)sizeof(us428ctls_sharedmem_t));
80 return -EINVAL;
81 }
82
83 if (!us428->us428ctls_sharedmem) {
84 init_waitqueue_head(&us428->us428ctls_wait_queue_head);
85 if(!(us428->us428ctls_sharedmem = snd_malloc_pages(sizeof(us428ctls_sharedmem_t), GFP_KERNEL)))
86 return -ENOMEM;
87 memset(us428->us428ctls_sharedmem, -1, sizeof(us428ctls_sharedmem_t));
88 us428->us428ctls_sharedmem->CtlSnapShotLast = -2;
89 }
90 area->vm_ops = &us428ctls_vm_ops;
91 area->vm_flags |= VM_RESERVED;
92 area->vm_private_data = hw->private_data;
93 return 0;
94}
95
96static unsigned int snd_us428ctls_poll(snd_hwdep_t *hw, struct file *file, poll_table *wait)
97{
98 unsigned int mask = 0;
99 usX2Ydev_t *us428 = (usX2Ydev_t*)hw->private_data;
100 us428ctls_sharedmem_t *shm = us428->us428ctls_sharedmem;
101 if (us428->chip_status & USX2Y_STAT_CHIP_HUP)
102 return POLLHUP;
103
104 poll_wait(file, &us428->us428ctls_wait_queue_head, wait);
105
106 if (shm != NULL && shm->CtlSnapShotLast != shm->CtlSnapShotRed)
107 mask |= POLLIN;
108
109 return mask;
110}
111
112
113static int snd_usX2Y_hwdep_open(snd_hwdep_t *hw, struct file *file)
114{
115 return 0;
116}
117
118static int snd_usX2Y_hwdep_release(snd_hwdep_t *hw, struct file *file)
119{
120 return 0;
121}
122
123static int snd_usX2Y_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
124{
125 static char *type_ids[USX2Y_TYPE_NUMS] = {
126 [USX2Y_TYPE_122] = "us122",
127 [USX2Y_TYPE_224] = "us224",
128 [USX2Y_TYPE_428] = "us428",
129 };
130 int id = -1;
131
132 switch (le16_to_cpu(((usX2Ydev_t*)hw->private_data)->chip.dev->descriptor.idProduct)) {
133 case USB_ID_US122:
134 id = USX2Y_TYPE_122;
135 break;
136 case USB_ID_US224:
137 id = USX2Y_TYPE_224;
138 break;
139 case USB_ID_US428:
140 id = USX2Y_TYPE_428;
141 break;
142 }
143 if (0 > id)
144 return -ENODEV;
145 strcpy(info->id, type_ids[id]);
146 info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code
147 if (((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT)
148 info->chip_ready = 1;
149 info->version = USX2Y_DRIVER_VERSION;
150 return 0;
151}
152
153
154static int usX2Y_create_usbmidi(snd_card_t* card )
155{
156 static snd_usb_midi_endpoint_info_t quirk_data_1 = {
157 .out_ep =0x06,
158 .in_ep = 0x06,
159 .out_cables = 0x001,
160 .in_cables = 0x001
161 };
162 static snd_usb_audio_quirk_t quirk_1 = {
163 .vendor_name = "TASCAM",
164 .product_name = NAME_ALLCAPS,
165 .ifnum = 0,
166 .type = QUIRK_MIDI_FIXED_ENDPOINT,
167 .data = &quirk_data_1
168 };
169 static snd_usb_midi_endpoint_info_t quirk_data_2 = {
170 .out_ep =0x06,
171 .in_ep = 0x06,
172 .out_cables = 0x003,
173 .in_cables = 0x003
174 };
175 static snd_usb_audio_quirk_t quirk_2 = {
176 .vendor_name = "TASCAM",
177 .product_name = "US428",
178 .ifnum = 0,
179 .type = QUIRK_MIDI_FIXED_ENDPOINT,
180 .data = &quirk_data_2
181 };
182 struct usb_device *dev = usX2Y(card)->chip.dev;
183 struct usb_interface *iface = usb_ifnum_to_if(dev, 0);
184 snd_usb_audio_quirk_t *quirk = le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ? &quirk_2 : &quirk_1;
185
186 snd_printdd("usX2Y_create_usbmidi \n");
187 return snd_usb_create_midi_interface(&usX2Y(card)->chip, iface, quirk);
188}
189
190static int usX2Y_create_alsa_devices(snd_card_t* card)
191{
192 int err;
193
194 do {
195 if ((err = usX2Y_create_usbmidi(card)) < 0) {
196 snd_printk("usX2Y_create_alsa_devices: usX2Y_create_usbmidi error %i \n", err);
197 break;
198 }
199 if ((err = usX2Y_audio_create(card)) < 0)
200 break;
201 if ((err = usX2Y_hwdep_pcm_new(card)) < 0)
202 break;
203 if ((err = snd_card_register(card)) < 0)
204 break;
205 } while (0);
206
207 return err;
208}
209
210static int snd_usX2Y_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
211{
212 usX2Ydev_t *priv = hw->private_data;
213 int lret, err = -EINVAL;
214 snd_printdd( "dsp_load %s\n", dsp->name);
215
216 if (access_ok(VERIFY_READ, dsp->image, dsp->length)) {
217 struct usb_device* dev = priv->chip.dev;
218 char *buf = kmalloc(dsp->length, GFP_KERNEL);
219 if (!buf)
220 return -ENOMEM;
221 if (copy_from_user(buf, dsp->image, dsp->length)) {
222 kfree(buf);
223 return -EFAULT;
224 }
225 err = usb_set_interface(dev, 0, 1);
226 if (err)
227 snd_printk("usb_set_interface error \n");
228 else
229 err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6000);
230 kfree(buf);
231 }
232 if (err)
233 return err;
234 if (dsp->index == 1) {
235 set_current_state(TASK_UNINTERRUPTIBLE);
236 schedule_timeout(HZ/4); // give the device some time
237 err = usX2Y_AsyncSeq04_init(priv);
238 if (err) {
239 snd_printk("usX2Y_AsyncSeq04_init error \n");
240 return err;
241 }
242 err = usX2Y_In04_init(priv);
243 if (err) {
244 snd_printk("usX2Y_In04_init error \n");
245 return err;
246 }
247 err = usX2Y_create_alsa_devices(hw->card);
248 if (err) {
249 snd_printk("usX2Y_create_alsa_devices error %i \n", err);
250 snd_card_free(hw->card);
251 return err;
252 }
253 priv->chip_status |= USX2Y_STAT_CHIP_INIT;
254 snd_printdd("%s: alsa all started\n", hw->name);
255 }
256 return err;
257}
258
259
260int usX2Y_hwdep_new(snd_card_t* card, struct usb_device* device)
261{
262 int err;
263 snd_hwdep_t *hw;
264
265 if ((err = snd_hwdep_new(card, SND_USX2Y_LOADER_ID, 0, &hw)) < 0)
266 return err;
267
268 hw->iface = SNDRV_HWDEP_IFACE_USX2Y;
269 hw->private_data = usX2Y(card);
270 hw->ops.open = snd_usX2Y_hwdep_open;
271 hw->ops.release = snd_usX2Y_hwdep_release;
272 hw->ops.dsp_status = snd_usX2Y_hwdep_dsp_status;
273 hw->ops.dsp_load = snd_usX2Y_hwdep_dsp_load;
274 hw->ops.mmap = snd_us428ctls_mmap;
275 hw->ops.poll = snd_us428ctls_poll;
276 hw->exclusive = 1;
277 sprintf(hw->name, "/proc/bus/usb/%03d/%03d", device->bus->busnum, device->devnum);
278 return 0;
279}
280
diff --git a/sound/usb/usx2y/usX2Yhwdep.h b/sound/usb/usx2y/usX2Yhwdep.h
new file mode 100644
index 000000000000..d612a26eb77c
--- /dev/null
+++ b/sound/usb/usx2y/usX2Yhwdep.h
@@ -0,0 +1,6 @@
1#ifndef USX2YHWDEP_H
2#define USX2YHWDEP_H
3
4int usX2Y_hwdep_new(snd_card_t* card, struct usb_device* device);
5
6#endif
diff --git a/sound/usb/usx2y/usbus428ctldefs.h b/sound/usb/usx2y/usbus428ctldefs.h
new file mode 100644
index 000000000000..6af16438d2c7
--- /dev/null
+++ b/sound/usb/usx2y/usbus428ctldefs.h
@@ -0,0 +1,108 @@
1/*
2 *
3 * Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20enum E_In84{
21 eFader0 = 0,
22 eFader1,
23 eFader2,
24 eFader3,
25 eFader4,
26 eFader5,
27 eFader6,
28 eFader7,
29 eFaderM,
30 eTransport,
31 eModifier = 10,
32 eFilterSelect,
33 eSelect,
34 eMute,
35
36 eSwitch = 15,
37 eWheelGain,
38 eWheelFreq,
39 eWheelQ,
40 eWheelPan,
41 eWheel = 20
42};
43
44#define T_RECORD 1
45#define T_PLAY 2
46#define T_STOP 4
47#define T_F_FWD 8
48#define T_REW 0x10
49#define T_SOLO 0x20
50#define T_REC 0x40
51#define T_NULL 0x80
52
53
54struct us428_ctls{
55 unsigned char Fader[9];
56 unsigned char Transport;
57 unsigned char Modifier;
58 unsigned char FilterSelect;
59 unsigned char Select;
60 unsigned char Mute;
61 unsigned char UNKNOWN;
62 unsigned char Switch;
63 unsigned char Wheel[5];
64};
65
66typedef struct us428_ctls us428_ctls_t;
67
68typedef struct us428_setByte{
69 unsigned char Offset,
70 Value;
71}us428_setByte_t;
72
73enum {
74 eLT_Volume = 0,
75 eLT_Light
76};
77
78typedef struct usX2Y_volume {
79 unsigned char Channel,
80 LH,
81 LL,
82 RH,
83 RL;
84} usX2Y_volume_t;
85
86struct us428_lights{
87 us428_setByte_t Light[7];
88};
89typedef struct us428_lights us428_lights_t;
90
91typedef struct {
92 char type;
93 union {
94 usX2Y_volume_t vol;
95 us428_lights_t lights;
96 } val;
97} us428_p4out_t;
98
99#define N_us428_ctl_BUFS 16
100#define N_us428_p4out_BUFS 16
101struct us428ctls_sharedmem{
102 us428_ctls_t CtlSnapShot[N_us428_ctl_BUFS];
103 int CtlSnapShotDiffersAt[N_us428_ctl_BUFS];
104 int CtlSnapShotLast, CtlSnapShotRed;
105 us428_p4out_t p4out[N_us428_p4out_BUFS];
106 int p4outLast, p4outSent;
107};
108typedef struct us428ctls_sharedmem us428ctls_sharedmem_t;
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
new file mode 100644
index 000000000000..b06a267e5dac
--- /dev/null
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -0,0 +1,461 @@
1/*
2 * usbusy2y.c - ALSA USB US-428 Driver
3 *
42004-12-14 Karsten Wiese
5 Version 0.8.7.1:
6 snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open.
7
82004-12-02 Karsten Wiese
9 Version 0.8.7:
10 Use macro usb_maxpacket() for portability.
11
122004-10-26 Karsten Wiese
13 Version 0.8.6:
14 wake_up() process waiting in usX2Y_urbs_start() on error.
15
162004-10-21 Karsten Wiese
17 Version 0.8.5:
18 nrpacks is runtime or compiletime configurable now with tested values from 1 to 4.
19
202004-10-03 Karsten Wiese
21 Version 0.8.2:
22 Avoid any possible racing while in prepare callback.
23
242004-09-30 Karsten Wiese
25 Version 0.8.0:
26 Simplified things and made ohci work again.
27
282004-09-20 Karsten Wiese
29 Version 0.7.3:
30 Use usb_kill_urb() instead of deprecated (kernel 2.6.9) usb_unlink_urb().
31
322004-07-13 Karsten Wiese
33 Version 0.7.1:
34 Don't sleep in START/STOP callbacks anymore.
35 us428 channels C/D not handled just for this version, sorry.
36
372004-06-21 Karsten Wiese
38 Version 0.6.4:
39 Temporarely suspend midi input
40 to sanely call usb_set_interface() when setting format.
41
422004-06-12 Karsten Wiese
43 Version 0.6.3:
44 Made it thus the following rule is enforced:
45 "All pcm substreams of one usX2Y have to operate at the same rate & format."
46
472004-04-06 Karsten Wiese
48 Version 0.6.0:
49 Runs on 2.6.5 kernel without any "--with-debug=" things.
50 us224 reported running.
51
522004-01-14 Karsten Wiese
53 Version 0.5.1:
54 Runs with 2.6.1 kernel.
55
562003-12-30 Karsten Wiese
57 Version 0.4.1:
58 Fix 24Bit 4Channel capturing for the us428.
59
602003-11-27 Karsten Wiese, Martin Langer
61 Version 0.4:
62 us122 support.
63 us224 could be tested by uncommenting the sections containing USB_ID_US224
64
652003-11-03 Karsten Wiese
66 Version 0.3:
67 24Bit support.
68 "arecord -D hw:1 -c 2 -r 48000 -M -f S24_3LE|aplay -D hw:1 -c 2 -r 48000 -M -f S24_3LE" works.
69
702003-08-22 Karsten Wiese
71 Version 0.0.8:
72 Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader.
73 See:
74 http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz
75
762003-06-18 Karsten Wiese
77 Version 0.0.5:
78 changed to compile with kernel 2.4.21 and alsa 0.9.4
79
802002-10-16 Karsten Wiese
81 Version 0.0.4:
82 compiles again with alsa-current.
83 USB_ISO_ASAP not used anymore (most of the time), instead
84 urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore.
85
86 To get the best out of this:
87 Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds.
88 This helped me much on my slowish PII 400 & PIII 500.
89 ACPI yet untested but might cause the same bad behaviour.
90 Use a kernel with lowlatency and preemptiv patches applied.
91 To autoload snd-usb-midi append a line
92 post-install snd-usb-us428 modprobe snd-usb-midi
93 to /etc/modules.conf.
94
95 known problems:
96 sliders, knobs, lights not yet handled except MASTER Volume slider.
97 "pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does.
98 KDE3: "Enable full duplex operation" deadlocks.
99
100
1012002-08-31 Karsten Wiese
102 Version 0.0.3: audio also simplex;
103 simplifying: iso urbs only 1 packet, melted structs.
104 ASYNC_UNLINK not used anymore: no more crashes so far.....
105 for alsa 0.9 rc3.
106
1072002-08-09 Karsten Wiese
108 Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol.
109 The firmware has been sniffed from win2k us-428 driver 3.09.
110
111 * Copyright (c) 2002 - 2004 Karsten Wiese
112 *
113 * This program is free software; you can redistribute it and/or modify
114 * it under the terms of the GNU General Public License as published by
115 * the Free Software Foundation; either version 2 of the License, or
116 * (at your option) any later version.
117 *
118 * This program is distributed in the hope that it will be useful,
119 * but WITHOUT ANY WARRANTY; without even the implied warranty of
120 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121 * GNU General Public License for more details.
122 *
123 * You should have received a copy of the GNU General Public License
124 * along with this program; if not, write to the Free Software
125 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
126*/
127
128#include <sound/driver.h>
129#include <linux/init.h>
130#include <linux/module.h>
131#include <linux/moduleparam.h>
132#include <linux/interrupt.h>
133#include <linux/usb.h>
134#include <sound/core.h>
135#include <sound/initval.h>
136#include <sound/pcm.h>
137
138#include <sound/rawmidi.h>
139#include "usx2y.h"
140#include "usbusx2y.h"
141#include "usX2Yhwdep.h"
142
143
144
145MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
146MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.1");
147MODULE_LICENSE("GPL");
148MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}");
149
150static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
151static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
152static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
153
154module_param_array(index, int, NULL, 0444);
155MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
156module_param_array(id, charp, NULL, 0444);
157MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");
158module_param_array(enable, bool, NULL, 0444);
159MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");
160
161
162static int snd_usX2Y_card_used[SNDRV_CARDS];
163
164static void usX2Y_usb_disconnect(struct usb_device* usb_device, void* ptr);
165static void snd_usX2Y_card_private_free(snd_card_t *card);
166
167/*
168 * pipe 4 is used for switching the lamps, setting samplerate, volumes ....
169 */
170static void i_usX2Y_Out04Int(struct urb* urb, struct pt_regs *regs)
171{
172#ifdef CONFIG_SND_DEBUG
173 if (urb->status) {
174 int i;
175 usX2Ydev_t* usX2Y = urb->context;
176 for (i = 0; i < 10 && usX2Y->AS04.urb[i] != urb; i++);
177 snd_printdd("i_usX2Y_Out04Int() urb %i status=%i\n", i, urb->status);
178 }
179#endif
180}
181
182static void i_usX2Y_In04Int(struct urb* urb, struct pt_regs *regs)
183{
184 int err = 0;
185 usX2Ydev_t *usX2Y = urb->context;
186 us428ctls_sharedmem_t *us428ctls = usX2Y->us428ctls_sharedmem;
187
188 usX2Y->In04IntCalls++;
189
190 if (urb->status) {
191 snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status);
192 return;
193 }
194
195 // printk("%i:0x%02X ", 8, (int)((unsigned char*)usX2Y->In04Buf)[8]); Master volume shows 0 here if fader is at max during boot ?!?
196 if (us428ctls) {
197 int diff = -1;
198 if (-2 == us428ctls->CtlSnapShotLast) {
199 diff = 0;
200 memcpy(usX2Y->In04Last, usX2Y->In04Buf, sizeof(usX2Y->In04Last));
201 us428ctls->CtlSnapShotLast = -1;
202 } else {
203 int i;
204 for (i = 0; i < 21; i++) {
205 if (usX2Y->In04Last[i] != ((char*)usX2Y->In04Buf)[i]) {
206 if (diff < 0)
207 diff = i;
208 usX2Y->In04Last[i] = ((char*)usX2Y->In04Buf)[i];
209 }
210 }
211 }
212 if (0 <= diff) {
213 int n = us428ctls->CtlSnapShotLast + 1;
214 if (n >= N_us428_ctl_BUFS || n < 0)
215 n = 0;
216 memcpy(us428ctls->CtlSnapShot + n, usX2Y->In04Buf, sizeof(us428ctls->CtlSnapShot[0]));
217 us428ctls->CtlSnapShotDiffersAt[n] = diff;
218 us428ctls->CtlSnapShotLast = n;
219 wake_up(&usX2Y->us428ctls_wait_queue_head);
220 }
221 }
222
223
224 if (usX2Y->US04) {
225 if (0 == usX2Y->US04->submitted)
226 do
227 err = usb_submit_urb(usX2Y->US04->urb[usX2Y->US04->submitted++], GFP_ATOMIC);
228 while (!err && usX2Y->US04->submitted < usX2Y->US04->len);
229 } else
230 if (us428ctls && us428ctls->p4outLast >= 0 && us428ctls->p4outLast < N_us428_p4out_BUFS) {
231 if (us428ctls->p4outLast != us428ctls->p4outSent) {
232 int j, send = us428ctls->p4outSent + 1;
233 if (send >= N_us428_p4out_BUFS)
234 send = 0;
235 for (j = 0; j < URBS_AsyncSeq && !err; ++j)
236 if (0 == usX2Y->AS04.urb[j]->status) {
237 us428_p4out_t *p4out = us428ctls->p4out + send; // FIXME if more then 1 p4out is new, 1 gets lost.
238 usb_fill_bulk_urb(usX2Y->AS04.urb[j], usX2Y->chip.dev,
239 usb_sndbulkpipe(usX2Y->chip.dev, 0x04), &p4out->val.vol,
240 p4out->type == eLT_Light ? sizeof(us428_lights_t) : 5,
241 i_usX2Y_Out04Int, usX2Y);
242 err = usb_submit_urb(usX2Y->AS04.urb[j], GFP_ATOMIC);
243 us428ctls->p4outSent = send;
244 break;
245 }
246 }
247 }
248
249 if (err) {
250 snd_printk("In04Int() usb_submit_urb err=%i\n", err);
251 }
252
253 urb->dev = usX2Y->chip.dev;
254 usb_submit_urb(urb, GFP_ATOMIC);
255}
256
257/*
258 * Prepare some urbs
259 */
260int usX2Y_AsyncSeq04_init(usX2Ydev_t* usX2Y)
261{
262 int err = 0,
263 i;
264
265 if (NULL == (usX2Y->AS04.buffer = kmalloc(URB_DataLen_AsyncSeq*URBS_AsyncSeq, GFP_KERNEL))) {
266 err = -ENOMEM;
267 } else
268 for (i = 0; i < URBS_AsyncSeq; ++i) {
269 if (NULL == (usX2Y->AS04.urb[i] = usb_alloc_urb(0, GFP_KERNEL))) {
270 err = -ENOMEM;
271 break;
272 }
273 usb_fill_bulk_urb( usX2Y->AS04.urb[i], usX2Y->chip.dev,
274 usb_sndbulkpipe(usX2Y->chip.dev, 0x04),
275 usX2Y->AS04.buffer + URB_DataLen_AsyncSeq*i, 0,
276 i_usX2Y_Out04Int, usX2Y
277 );
278 }
279 return err;
280}
281
282int usX2Y_In04_init(usX2Ydev_t* usX2Y)
283{
284 int err = 0;
285 if (! (usX2Y->In04urb = usb_alloc_urb(0, GFP_KERNEL)))
286 return -ENOMEM;
287
288 if (! (usX2Y->In04Buf = kmalloc(21, GFP_KERNEL))) {
289 usb_free_urb(usX2Y->In04urb);
290 return -ENOMEM;
291 }
292
293 init_waitqueue_head(&usX2Y->In04WaitQueue);
294 usb_fill_int_urb(usX2Y->In04urb, usX2Y->chip.dev, usb_rcvintpipe(usX2Y->chip.dev, 0x4),
295 usX2Y->In04Buf, 21,
296 i_usX2Y_In04Int, usX2Y,
297 10);
298 err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL);
299 return err;
300}
301
302static void usX2Y_unlinkSeq(snd_usX2Y_AsyncSeq_t* S)
303{
304 int i;
305 for (i = 0; i < URBS_AsyncSeq; ++i) {
306 if (S[i].urb) {
307 usb_kill_urb(S->urb[i]);
308 usb_free_urb(S->urb[i]);
309 S->urb[i] = NULL;
310 }
311 }
312 kfree(S->buffer);
313}
314
315
316static struct usb_device_id snd_usX2Y_usb_id_table[] = {
317 {
318 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
319 .idVendor = 0x1604,
320 .idProduct = USB_ID_US428
321 },
322 {
323 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
324 .idVendor = 0x1604,
325 .idProduct = USB_ID_US122
326 },
327 {
328 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
329 .idVendor = 0x1604,
330 .idProduct = USB_ID_US224
331 },
332 { /* terminator */ }
333};
334
335static snd_card_t* usX2Y_create_card(struct usb_device* device)
336{
337 int dev;
338 snd_card_t* card;
339 for (dev = 0; dev < SNDRV_CARDS; ++dev)
340 if (enable[dev] && !snd_usX2Y_card_used[dev])
341 break;
342 if (dev >= SNDRV_CARDS)
343 return NULL;
344 card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(usX2Ydev_t));
345 if (!card)
346 return NULL;
347 snd_usX2Y_card_used[usX2Y(card)->chip.index = dev] = 1;
348 card->private_free = snd_usX2Y_card_private_free;
349 usX2Y(card)->chip.dev = device;
350 usX2Y(card)->chip.card = card;
351 init_waitqueue_head(&usX2Y(card)->prepare_wait_queue);
352 init_MUTEX (&usX2Y(card)->prepare_mutex);
353 INIT_LIST_HEAD(&usX2Y(card)->chip.midi_list);
354 strcpy(card->driver, "USB "NAME_ALLCAPS"");
355 sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
356 sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
357 card->shortname,
358 le16_to_cpu(device->descriptor.idVendor),
359 le16_to_cpu(device->descriptor.idProduct),
360 0,//us428(card)->usbmidi.ifnum,
361 usX2Y(card)->chip.dev->bus->busnum, usX2Y(card)->chip.dev->devnum
362 );
363 snd_card_set_dev(card, &device->dev);
364 return card;
365}
366
367
368static void* usX2Y_usb_probe(struct usb_device* device, struct usb_interface *intf, const struct usb_device_id* device_id)
369{
370 int err;
371 snd_card_t* card;
372 if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||
373 (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
374 le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
375 le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428) ||
376 !(card = usX2Y_create_card(device)))
377 return NULL;
378 if ((err = usX2Y_hwdep_new(card, device)) < 0 ||
379 (err = snd_card_register(card)) < 0) {
380 snd_card_free(card);
381 return NULL;
382 }
383 return card;
384}
385
386/*
387 * new 2.5 USB kernel API
388 */
389static int snd_usX2Y_probe(struct usb_interface *intf, const struct usb_device_id *id)
390{
391 void *chip;
392 chip = usX2Y_usb_probe(interface_to_usbdev(intf), intf, id);
393 if (chip) {
394 dev_set_drvdata(&intf->dev, chip);
395 return 0;
396 } else
397 return -EIO;
398}
399
400static void snd_usX2Y_disconnect(struct usb_interface *intf)
401{
402 usX2Y_usb_disconnect(interface_to_usbdev(intf),
403 dev_get_drvdata(&intf->dev));
404}
405
406MODULE_DEVICE_TABLE(usb, snd_usX2Y_usb_id_table);
407static struct usb_driver snd_usX2Y_usb_driver = {
408 .owner = THIS_MODULE,
409 .name = "snd-usb-usx2y",
410 .probe = snd_usX2Y_probe,
411 .disconnect = snd_usX2Y_disconnect,
412 .id_table = snd_usX2Y_usb_id_table,
413};
414
415static void snd_usX2Y_card_private_free(snd_card_t *card)
416{
417 kfree(usX2Y(card)->In04Buf);
418 usb_free_urb(usX2Y(card)->In04urb);
419 if (usX2Y(card)->us428ctls_sharedmem)
420 snd_free_pages(usX2Y(card)->us428ctls_sharedmem, sizeof(*usX2Y(card)->us428ctls_sharedmem));
421 if (usX2Y(card)->chip.index >= 0 && usX2Y(card)->chip.index < SNDRV_CARDS)
422 snd_usX2Y_card_used[usX2Y(card)->chip.index] = 0;
423}
424
425/*
426 * Frees the device.
427 */
428static void usX2Y_usb_disconnect(struct usb_device* device, void* ptr)
429{
430 if (ptr) {
431 usX2Ydev_t* usX2Y = usX2Y((snd_card_t*)ptr);
432 struct list_head* p;
433 if (usX2Y->chip_status == USX2Y_STAT_CHIP_HUP) // on 2.6.1 kernel snd_usbmidi_disconnect()
434 return; // calls us back. better leave :-) .
435 usX2Y->chip.shutdown = 1;
436 usX2Y->chip_status = USX2Y_STAT_CHIP_HUP;
437 usX2Y_unlinkSeq(&usX2Y->AS04);
438 usb_kill_urb(usX2Y->In04urb);
439 snd_card_disconnect((snd_card_t*)ptr);
440 /* release the midi resources */
441 list_for_each(p, &usX2Y->chip.midi_list) {
442 snd_usbmidi_disconnect(p, &snd_usX2Y_usb_driver);
443 }
444 if (usX2Y->us428ctls_sharedmem)
445 wake_up(&usX2Y->us428ctls_wait_queue_head);
446 snd_card_free_in_thread((snd_card_t*)ptr);
447 }
448}
449
450static int __init snd_usX2Y_module_init(void)
451{
452 return usb_register(&snd_usX2Y_usb_driver);
453}
454
455static void __exit snd_usX2Y_module_exit(void)
456{
457 usb_deregister(&snd_usX2Y_usb_driver);
458}
459
460module_init(snd_usX2Y_module_init)
461module_exit(snd_usX2Y_module_exit)
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
new file mode 100644
index 000000000000..f65f3a7194ca
--- /dev/null
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -0,0 +1,84 @@
1#ifndef USBUSX2Y_H
2#define USBUSX2Y_H
3#include "../usbaudio.h"
4#include "usbus428ctldefs.h"
5
6#define NRURBS 2
7
8
9#define URBS_AsyncSeq 10
10#define URB_DataLen_AsyncSeq 32
11typedef struct {
12 struct urb* urb[URBS_AsyncSeq];
13 char* buffer;
14} snd_usX2Y_AsyncSeq_t;
15
16typedef struct {
17 int submitted;
18 int len;
19 struct urb* urb[0];
20} snd_usX2Y_urbSeq_t;
21
22typedef struct snd_usX2Y_substream snd_usX2Y_substream_t;
23#include "usx2yhwdeppcm.h"
24
25typedef struct {
26 snd_usb_audio_t chip;
27 int stride;
28 struct urb *In04urb;
29 void *In04Buf;
30 char In04Last[24];
31 unsigned In04IntCalls;
32 snd_usX2Y_urbSeq_t *US04;
33 wait_queue_head_t In04WaitQueue;
34 snd_usX2Y_AsyncSeq_t AS04;
35 unsigned int rate,
36 format;
37 int chip_status;
38 struct semaphore prepare_mutex;
39 us428ctls_sharedmem_t *us428ctls_sharedmem;
40 int wait_iso_frame;
41 wait_queue_head_t us428ctls_wait_queue_head;
42 snd_usX2Y_hwdep_pcm_shm_t *hwdep_pcm_shm;
43 snd_usX2Y_substream_t *subs[4];
44 snd_usX2Y_substream_t * volatile prepare_subs;
45 wait_queue_head_t prepare_wait_queue;
46} usX2Ydev_t;
47
48
49struct snd_usX2Y_substream {
50 usX2Ydev_t *usX2Y;
51 snd_pcm_substream_t *pcm_substream;
52
53 int endpoint;
54 unsigned int maxpacksize; /* max packet size in bytes */
55
56 atomic_t state;
57#define state_STOPPED 0
58#define state_STARTING1 1
59#define state_STARTING2 2
60#define state_STARTING3 3
61#define state_PREPARED 4
62#define state_PRERUNNING 6
63#define state_RUNNING 8
64
65 int hwptr; /* free frame position in the buffer (only for playback) */
66 int hwptr_done; /* processed frame position in the buffer */
67 int transfer_done; /* processed frames since last period update */
68
69 struct urb *urb[NRURBS]; /* data urb table */
70 struct urb *completed_urb;
71 char *tmpbuf; /* temporary buffer for playback */
72};
73
74
75#define usX2Y(c) ((usX2Ydev_t*)(c)->private_data)
76
77int usX2Y_audio_create(snd_card_t* card);
78
79int usX2Y_AsyncSeq04_init(usX2Ydev_t* usX2Y);
80int usX2Y_In04_init(usX2Ydev_t* usX2Y);
81
82#define NAME_ALLCAPS "US-X2Y"
83
84#endif
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
new file mode 100644
index 000000000000..4c292e090069
--- /dev/null
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -0,0 +1,1026 @@
1/*
2 * US-X2Y AUDIO
3 * Copyright (c) 2002-2004 by Karsten Wiese
4 *
5 * based on
6 *
7 * (Tentative) USB Audio Driver for ALSA
8 *
9 * Main and PCM part
10 *
11 * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
12 *
13 * Many codes borrowed from audio.c by
14 * Alan Cox (alan@lxorguk.ukuu.org.uk)
15 * Thomas Sailer (sailer@ife.ee.ethz.ch)
16 *
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 */
32
33
34#include <sound/driver.h>
35#include <linux/interrupt.h>
36#include <linux/usb.h>
37#include <sound/core.h>
38#include <sound/info.h>
39#include <sound/pcm.h>
40#include <sound/pcm_params.h>
41#include "usx2y.h"
42#include "usbusx2y.h"
43
44#define USX2Y_NRPACKS 4 /* Default value used for nr of packs per urb.
45 1 to 4 have been tested ok on uhci.
46 To use 3 on ohci, you'd need a patch:
47 look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on
48 "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"
49 .
50 1, 2 and 4 work out of the box on ohci, if I recall correctly.
51 Bigger is safer operation,
52 smaller gives lower latencies.
53 */
54#define USX2Y_NRPACKS_VARIABLE y /* If your system works ok with this module's parameter
55 nrpacks set to 1, you might as well comment
56 this #define out, and thereby produce smaller, faster code.
57 You'd also set USX2Y_NRPACKS to 1 then.
58 */
59
60#ifdef USX2Y_NRPACKS_VARIABLE
61 static int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
62 #define nr_of_packs() nrpacks
63 module_param(nrpacks, int, 0444);
64 MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
65#else
66 #define nr_of_packs() USX2Y_NRPACKS
67#endif
68
69
70static int usX2Y_urb_capt_retire(snd_usX2Y_substream_t *subs)
71{
72 struct urb *urb = subs->completed_urb;
73 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
74 unsigned char *cp;
75 int i, len, lens = 0, hwptr_done = subs->hwptr_done;
76 usX2Ydev_t *usX2Y = subs->usX2Y;
77
78 for (i = 0; i < nr_of_packs(); i++) {
79 cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
80 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
81 snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
82 return urb->iso_frame_desc[i].status;
83 }
84 len = urb->iso_frame_desc[i].actual_length / usX2Y->stride;
85 if (! len) {
86 snd_printd("0 == len ERROR!\n");
87 continue;
88 }
89
90 /* copy a data chunk */
91 if ((hwptr_done + len) > runtime->buffer_size) {
92 int cnt = runtime->buffer_size - hwptr_done;
93 int blen = cnt * usX2Y->stride;
94 memcpy(runtime->dma_area + hwptr_done * usX2Y->stride, cp, blen);
95 memcpy(runtime->dma_area, cp + blen, len * usX2Y->stride - blen);
96 } else {
97 memcpy(runtime->dma_area + hwptr_done * usX2Y->stride, cp, len * usX2Y->stride);
98 }
99 lens += len;
100 if ((hwptr_done += len) >= runtime->buffer_size)
101 hwptr_done -= runtime->buffer_size;
102 }
103
104 subs->hwptr_done = hwptr_done;
105 subs->transfer_done += lens;
106 /* update the pointer, call callback if necessary */
107 if (subs->transfer_done >= runtime->period_size) {
108 subs->transfer_done -= runtime->period_size;
109 snd_pcm_period_elapsed(subs->pcm_substream);
110 }
111 return 0;
112}
113/*
114 * prepare urb for playback data pipe
115 *
116 * we copy the data directly from the pcm buffer.
117 * the current position to be copied is held in hwptr field.
118 * since a urb can handle only a single linear buffer, if the total
119 * transferred area overflows the buffer boundary, we cannot send
120 * it directly from the buffer. thus the data is once copied to
121 * a temporary buffer and urb points to that.
122 */
123static int usX2Y_urb_play_prepare(snd_usX2Y_substream_t *subs,
124 struct urb *cap_urb,
125 struct urb *urb)
126{
127 int count, counts, pack;
128 usX2Ydev_t* usX2Y = subs->usX2Y;
129 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
130
131 count = 0;
132 for (pack = 0; pack < nr_of_packs(); pack++) {
133 /* calculate the size of a packet */
134 counts = cap_urb->iso_frame_desc[pack].actual_length / usX2Y->stride;
135 count += counts;
136 if (counts < 43 || counts > 50) {
137 snd_printk("should not be here with counts=%i\n", counts);
138 return -EPIPE;
139 }
140 /* set up descriptor */
141 urb->iso_frame_desc[pack].offset = pack ?
142 urb->iso_frame_desc[pack - 1].offset + urb->iso_frame_desc[pack - 1].length :
143 0;
144 urb->iso_frame_desc[pack].length = cap_urb->iso_frame_desc[pack].actual_length;
145 }
146 if (atomic_read(&subs->state) >= state_PRERUNNING)
147 if (subs->hwptr + count > runtime->buffer_size) {
148 /* err, the transferred area goes over buffer boundary.
149 * copy the data to the temp buffer.
150 */
151 int len;
152 len = runtime->buffer_size - subs->hwptr;
153 urb->transfer_buffer = subs->tmpbuf;
154 memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * usX2Y->stride, len * usX2Y->stride);
155 memcpy(subs->tmpbuf + len * usX2Y->stride, runtime->dma_area, (count - len) * usX2Y->stride);
156 subs->hwptr += count;
157 subs->hwptr -= runtime->buffer_size;
158 } else {
159 /* set the buffer pointer */
160 urb->transfer_buffer = runtime->dma_area + subs->hwptr * usX2Y->stride;
161 if ((subs->hwptr += count) >= runtime->buffer_size)
162 subs->hwptr -= runtime->buffer_size;
163 }
164 else
165 urb->transfer_buffer = subs->tmpbuf;
166 urb->transfer_buffer_length = count * usX2Y->stride;
167 return 0;
168}
169
170/*
171 * process after playback data complete
172 *
173 * update the current position and call callback if a period is processed.
174 */
175static void usX2Y_urb_play_retire(snd_usX2Y_substream_t *subs, struct urb *urb)
176{
177 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
178 int len = urb->actual_length / subs->usX2Y->stride;
179
180 subs->transfer_done += len;
181 subs->hwptr_done += len;
182 if (subs->hwptr_done >= runtime->buffer_size)
183 subs->hwptr_done -= runtime->buffer_size;
184 if (subs->transfer_done >= runtime->period_size) {
185 subs->transfer_done -= runtime->period_size;
186 snd_pcm_period_elapsed(subs->pcm_substream);
187 }
188}
189
190static int usX2Y_urb_submit(snd_usX2Y_substream_t *subs, struct urb *urb, int frame)
191{
192 int err;
193 if (!urb)
194 return -ENODEV;
195 urb->start_frame = (frame + NRURBS * nr_of_packs()); // let hcd do rollover sanity checks
196 urb->hcpriv = NULL;
197 urb->dev = subs->usX2Y->chip.dev; /* we need to set this at each time */
198 if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
199 snd_printk("usb_submit_urb() returned %i\n", err);
200 return err;
201 }
202 return 0;
203}
204
205static inline int usX2Y_usbframe_complete(snd_usX2Y_substream_t *capsubs, snd_usX2Y_substream_t *playbacksubs, int frame)
206{
207 int err, state;
208 {
209 struct urb *urb = playbacksubs->completed_urb;
210
211 state = atomic_read(&playbacksubs->state);
212 if (NULL != urb) {
213 if (state == state_RUNNING)
214 usX2Y_urb_play_retire(playbacksubs, urb);
215 else
216 if (state >= state_PRERUNNING) {
217 atomic_inc(&playbacksubs->state);
218 }
219 } else {
220 switch (state) {
221 case state_STARTING1:
222 urb = playbacksubs->urb[0];
223 atomic_inc(&playbacksubs->state);
224 break;
225 case state_STARTING2:
226 urb = playbacksubs->urb[1];
227 atomic_inc(&playbacksubs->state);
228 break;
229 }
230 }
231 if (urb) {
232 if ((err = usX2Y_urb_play_prepare(playbacksubs, capsubs->completed_urb, urb)) ||
233 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
234 return err;
235 }
236 }
237
238 playbacksubs->completed_urb = NULL;
239 }
240 state = atomic_read(&capsubs->state);
241 if (state >= state_PREPARED) {
242 if (state == state_RUNNING) {
243 if ((err = usX2Y_urb_capt_retire(capsubs)))
244 return err;
245 } else
246 if (state >= state_PRERUNNING) {
247 atomic_inc(&capsubs->state);
248 }
249 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
250 return err;
251 }
252 capsubs->completed_urb = NULL;
253 return 0;
254}
255
256
257static void usX2Y_clients_stop(usX2Ydev_t *usX2Y)
258{
259 int s, u;
260 for (s = 0; s < 4; s++) {
261 snd_usX2Y_substream_t *subs = usX2Y->subs[s];
262 if (subs) {
263 snd_printdd("%i %p state=%i\n", s, subs, atomic_read(&subs->state));
264 atomic_set(&subs->state, state_STOPPED);
265 }
266 }
267 for (s = 0; s < 4; s++) {
268 snd_usX2Y_substream_t *subs = usX2Y->subs[s];
269 if (subs) {
270 if (atomic_read(&subs->state) >= state_PRERUNNING) {
271 snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
272 }
273 for (u = 0; u < NRURBS; u++) {
274 struct urb *urb = subs->urb[u];
275 if (NULL != urb)
276 snd_printdd("%i status=%i start_frame=%i\n", u, urb->status, urb->start_frame);
277 }
278 }
279 }
280 usX2Y->prepare_subs = NULL;
281 wake_up(&usX2Y->prepare_wait_queue);
282}
283
284static void usX2Y_error_urb_status(usX2Ydev_t *usX2Y, snd_usX2Y_substream_t *subs, struct urb *urb)
285{
286 snd_printk("ep=%i stalled with status=%i\n", subs->endpoint, urb->status);
287 urb->status = 0;
288 usX2Y_clients_stop(usX2Y);
289}
290
291static void usX2Y_error_sequence(usX2Ydev_t *usX2Y, snd_usX2Y_substream_t *subs, struct urb *urb)
292{
293 snd_printk("Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
294 "Most propably some urb of usb-frame %i is still missing.\n"
295 "Cause could be too long delays in usb-hcd interrupt handling.\n",
296 usb_get_current_frame_number(usX2Y->chip.dev),
297 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame);
298 usX2Y_clients_stop(usX2Y);
299}
300
301static void i_usX2Y_urb_complete(struct urb *urb, struct pt_regs *regs)
302{
303 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
304 usX2Ydev_t *usX2Y = subs->usX2Y;
305
306 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
307 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", usb_get_current_frame_number(usX2Y->chip.dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", urb->status, urb->start_frame);
308 return;
309 }
310 if (unlikely(urb->status)) {
311 usX2Y_error_urb_status(usX2Y, subs, urb);
312 return;
313 }
314 if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
315 subs->completed_urb = urb;
316 else {
317 usX2Y_error_sequence(usX2Y, subs, urb);
318 return;
319 }
320 {
321 snd_usX2Y_substream_t *capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE],
322 *playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
323 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
324 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
325 if (!usX2Y_usbframe_complete(capsubs, playbacksubs, urb->start_frame)) {
326 if (nr_of_packs() <= urb->start_frame &&
327 urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
328 usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
329 else
330 usX2Y->wait_iso_frame += nr_of_packs();
331 } else {
332 snd_printdd("\n");
333 usX2Y_clients_stop(usX2Y);
334 }
335 }
336 }
337}
338
339static void usX2Y_urbs_set_complete(usX2Ydev_t * usX2Y, void (*complete)(struct urb *, struct pt_regs *))
340{
341 int s, u;
342 for (s = 0; s < 4; s++) {
343 snd_usX2Y_substream_t *subs = usX2Y->subs[s];
344 if (NULL != subs)
345 for (u = 0; u < NRURBS; u++) {
346 struct urb * urb = subs->urb[u];
347 if (NULL != urb)
348 urb->complete = complete;
349 }
350 }
351}
352
353static void usX2Y_subs_startup_finish(usX2Ydev_t * usX2Y)
354{
355 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_urb_complete);
356 usX2Y->prepare_subs = NULL;
357}
358
359static void i_usX2Y_subs_startup(struct urb *urb, struct pt_regs *regs)
360{
361 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
362 usX2Ydev_t *usX2Y = subs->usX2Y;
363 snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
364 if (NULL != prepare_subs)
365 if (urb->start_frame == prepare_subs->urb[0]->start_frame) {
366 usX2Y_subs_startup_finish(usX2Y);
367 atomic_inc(&prepare_subs->state);
368 wake_up(&usX2Y->prepare_wait_queue);
369 }
370
371 i_usX2Y_urb_complete(urb, regs);
372}
373
374static void usX2Y_subs_prepare(snd_usX2Y_substream_t *subs)
375{
376 snd_printdd("usX2Y_substream_prepare(%p) ep=%i urb0=%p urb1=%p\n", subs, subs->endpoint, subs->urb[0], subs->urb[1]);
377 /* reset the pointer */
378 subs->hwptr = 0;
379 subs->hwptr_done = 0;
380 subs->transfer_done = 0;
381}
382
383
384static void usX2Y_urb_release(struct urb** urb, int free_tb)
385{
386 if (*urb) {
387 usb_kill_urb(*urb);
388 if (free_tb)
389 kfree((*urb)->transfer_buffer);
390 usb_free_urb(*urb);
391 *urb = NULL;
392 }
393}
394/*
395 * release a substreams urbs
396 */
397static void usX2Y_urbs_release(snd_usX2Y_substream_t *subs)
398{
399 int i;
400 snd_printdd("usX2Y_urbs_release() %i\n", subs->endpoint);
401 for (i = 0; i < NRURBS; i++)
402 usX2Y_urb_release(subs->urb + i, subs != subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK]);
403
404 if (subs->tmpbuf) {
405 kfree(subs->tmpbuf);
406 subs->tmpbuf = NULL;
407 }
408}
409/*
410 * initialize a substream's urbs
411 */
412static int usX2Y_urbs_allocate(snd_usX2Y_substream_t *subs)
413{
414 int i;
415 unsigned int pipe;
416 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
417 struct usb_device *dev = subs->usX2Y->chip.dev;
418
419 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
420 usb_rcvisocpipe(dev, subs->endpoint);
421 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
422 if (!subs->maxpacksize)
423 return -EINVAL;
424
425 if (is_playback && NULL == subs->tmpbuf) { /* allocate a temporary buffer for playback */
426 subs->tmpbuf = kcalloc(nr_of_packs(), subs->maxpacksize, GFP_KERNEL);
427 if (NULL == subs->tmpbuf) {
428 snd_printk(KERN_ERR "cannot malloc tmpbuf\n");
429 return -ENOMEM;
430 }
431 }
432 /* allocate and initialize data urbs */
433 for (i = 0; i < NRURBS; i++) {
434 struct urb** purb = subs->urb + i;
435 if (*purb) {
436 usb_kill_urb(*purb);
437 continue;
438 }
439 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
440 if (NULL == *purb) {
441 usX2Y_urbs_release(subs);
442 return -ENOMEM;
443 }
444 if (!is_playback && !(*purb)->transfer_buffer) {
445 /* allocate a capture buffer per urb */
446 (*purb)->transfer_buffer = kmalloc(subs->maxpacksize * nr_of_packs(), GFP_KERNEL);
447 if (NULL == (*purb)->transfer_buffer) {
448 usX2Y_urbs_release(subs);
449 return -ENOMEM;
450 }
451 }
452 (*purb)->dev = dev;
453 (*purb)->pipe = pipe;
454 (*purb)->number_of_packets = nr_of_packs();
455 (*purb)->context = subs;
456 (*purb)->interval = 1;
457 (*purb)->complete = i_usX2Y_subs_startup;
458 }
459 return 0;
460}
461
462static void usX2Y_subs_startup(snd_usX2Y_substream_t *subs)
463{
464 usX2Ydev_t *usX2Y = subs->usX2Y;
465 usX2Y->prepare_subs = subs;
466 subs->urb[0]->start_frame = -1;
467 wmb();
468 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_subs_startup);
469}
470
471static int usX2Y_urbs_start(snd_usX2Y_substream_t *subs)
472{
473 int i, err;
474 usX2Ydev_t *usX2Y = subs->usX2Y;
475
476 if ((err = usX2Y_urbs_allocate(subs)) < 0)
477 return err;
478 subs->completed_urb = NULL;
479 for (i = 0; i < 4; i++) {
480 snd_usX2Y_substream_t *subs = usX2Y->subs[i];
481 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
482 goto start;
483 }
484 usX2Y->wait_iso_frame = -1;
485 start:
486 {
487 usX2Y_subs_startup(subs);
488 for (i = 0; i < NRURBS; i++) {
489 struct urb *urb = subs->urb[i];
490 if (usb_pipein(urb->pipe)) {
491 unsigned long pack;
492 if (0 == i)
493 atomic_set(&subs->state, state_STARTING3);
494 urb->dev = usX2Y->chip.dev;
495 urb->transfer_flags = URB_ISO_ASAP;
496 for (pack = 0; pack < nr_of_packs(); pack++) {
497 urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack;
498 urb->iso_frame_desc[pack].length = subs->maxpacksize;
499 }
500 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
501 if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
502 snd_printk (KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);
503 err = -EPIPE;
504 goto cleanup;
505 } else {
506 if (0 > usX2Y->wait_iso_frame)
507 usX2Y->wait_iso_frame = urb->start_frame;
508 }
509 urb->transfer_flags = 0;
510 } else {
511 atomic_set(&subs->state, state_STARTING1);
512 break;
513 }
514 }
515 err = 0;
516 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
517 if (atomic_read(&subs->state) != state_PREPARED) {
518 err = -EPIPE;
519 }
520
521 cleanup:
522 if (err) {
523 usX2Y_subs_startup_finish(usX2Y);
524 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
525 }
526 }
527 return err;
528}
529
530/*
531 * return the current pcm pointer. just return the hwptr_done value.
532 */
533static snd_pcm_uframes_t snd_usX2Y_pcm_pointer(snd_pcm_substream_t *substream)
534{
535 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)substream->runtime->private_data;
536 return subs->hwptr_done;
537}
538/*
539 * start/stop substream
540 */
541static int snd_usX2Y_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
542{
543 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)substream->runtime->private_data;
544
545 switch (cmd) {
546 case SNDRV_PCM_TRIGGER_START:
547 snd_printdd("snd_usX2Y_pcm_trigger(START)\n");
548 if (atomic_read(&subs->state) == state_PREPARED &&
549 atomic_read(&subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]->state) >= state_PREPARED) {
550 atomic_set(&subs->state, state_PRERUNNING);
551 } else {
552 snd_printdd("\n");
553 return -EPIPE;
554 }
555 break;
556 case SNDRV_PCM_TRIGGER_STOP:
557 snd_printdd("snd_usX2Y_pcm_trigger(STOP)\n");
558 if (atomic_read(&subs->state) >= state_PRERUNNING)
559 atomic_set(&subs->state, state_PREPARED);
560 break;
561 default:
562 return -EINVAL;
563 }
564 return 0;
565}
566
567
568/*
569 * allocate a buffer, setup samplerate
570 *
571 * so far we use a physically linear buffer although packetize transfer
572 * doesn't need a continuous area.
573 * if sg buffer is supported on the later version of alsa, we'll follow
574 * that.
575 */
576static struct s_c2
577{
578 char c1, c2;
579}
580 SetRate44100[] =
581{
582 { 0x14, 0x08}, // this line sets 44100, well actually a little less
583 { 0x18, 0x40}, // only tascam / frontier design knows the further lines .......
584 { 0x18, 0x42},
585 { 0x18, 0x45},
586 { 0x18, 0x46},
587 { 0x18, 0x48},
588 { 0x18, 0x4A},
589 { 0x18, 0x4C},
590 { 0x18, 0x4E},
591 { 0x18, 0x50},
592 { 0x18, 0x52},
593 { 0x18, 0x54},
594 { 0x18, 0x56},
595 { 0x18, 0x58},
596 { 0x18, 0x5A},
597 { 0x18, 0x5C},
598 { 0x18, 0x5E},
599 { 0x18, 0x60},
600 { 0x18, 0x62},
601 { 0x18, 0x64},
602 { 0x18, 0x66},
603 { 0x18, 0x68},
604 { 0x18, 0x6A},
605 { 0x18, 0x6C},
606 { 0x18, 0x6E},
607 { 0x18, 0x70},
608 { 0x18, 0x72},
609 { 0x18, 0x74},
610 { 0x18, 0x76},
611 { 0x18, 0x78},
612 { 0x18, 0x7A},
613 { 0x18, 0x7C},
614 { 0x18, 0x7E}
615};
616static struct s_c2 SetRate48000[] =
617{
618 { 0x14, 0x09}, // this line sets 48000, well actually a little less
619 { 0x18, 0x40}, // only tascam / frontier design knows the further lines .......
620 { 0x18, 0x42},
621 { 0x18, 0x45},
622 { 0x18, 0x46},
623 { 0x18, 0x48},
624 { 0x18, 0x4A},
625 { 0x18, 0x4C},
626 { 0x18, 0x4E},
627 { 0x18, 0x50},
628 { 0x18, 0x52},
629 { 0x18, 0x54},
630 { 0x18, 0x56},
631 { 0x18, 0x58},
632 { 0x18, 0x5A},
633 { 0x18, 0x5C},
634 { 0x18, 0x5E},
635 { 0x18, 0x60},
636 { 0x18, 0x62},
637 { 0x18, 0x64},
638 { 0x18, 0x66},
639 { 0x18, 0x68},
640 { 0x18, 0x6A},
641 { 0x18, 0x6C},
642 { 0x18, 0x6E},
643 { 0x18, 0x70},
644 { 0x18, 0x73},
645 { 0x18, 0x74},
646 { 0x18, 0x76},
647 { 0x18, 0x78},
648 { 0x18, 0x7A},
649 { 0x18, 0x7C},
650 { 0x18, 0x7E}
651};
652#define NOOF_SETRATE_URBS ARRAY_SIZE(SetRate48000)
653
654static void i_usX2Y_04Int(struct urb* urb, struct pt_regs *regs)
655{
656 usX2Ydev_t* usX2Y = urb->context;
657
658 if (urb->status) {
659 snd_printk("snd_usX2Y_04Int() urb->status=%i\n", urb->status);
660 }
661 if (0 == --usX2Y->US04->len)
662 wake_up(&usX2Y->In04WaitQueue);
663}
664
665static int usX2Y_rate_set(usX2Ydev_t *usX2Y, int rate)
666{
667 int err = 0, i;
668 snd_usX2Y_urbSeq_t *us = NULL;
669 int *usbdata = NULL;
670 struct s_c2 *ra = rate == 48000 ? SetRate48000 : SetRate44100;
671
672 if (usX2Y->rate != rate) {
673 us = kmalloc(sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS, GFP_KERNEL);
674 if (NULL == us) {
675 err = -ENOMEM;
676 goto cleanup;
677 }
678 memset(us, 0, sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS);
679 usbdata = kmalloc(sizeof(int)*NOOF_SETRATE_URBS, GFP_KERNEL);
680 if (NULL == usbdata) {
681 err = -ENOMEM;
682 goto cleanup;
683 }
684 for (i = 0; i < NOOF_SETRATE_URBS; ++i) {
685 if (NULL == (us->urb[i] = usb_alloc_urb(0, GFP_KERNEL))) {
686 err = -ENOMEM;
687 goto cleanup;
688 }
689 ((char*)(usbdata + i))[0] = ra[i].c1;
690 ((char*)(usbdata + i))[1] = ra[i].c2;
691 usb_fill_bulk_urb(us->urb[i], usX2Y->chip.dev, usb_sndbulkpipe(usX2Y->chip.dev, 4),
692 usbdata + i, 2, i_usX2Y_04Int, usX2Y);
693#ifdef OLD_USB
694 us->urb[i]->transfer_flags = USB_QUEUE_BULK;
695#endif
696 }
697 us->submitted = 0;
698 us->len = NOOF_SETRATE_URBS;
699 usX2Y->US04 = us;
700 wait_event_timeout(usX2Y->In04WaitQueue, 0 == us->len, HZ);
701 usX2Y->US04 = NULL;
702 if (us->len)
703 err = -ENODEV;
704 cleanup:
705 if (us) {
706 us->submitted = 2*NOOF_SETRATE_URBS;
707 for (i = 0; i < NOOF_SETRATE_URBS; ++i) {
708 struct urb *urb = us->urb[i];
709 if (urb->status) {
710 if (!err)
711 err = -ENODEV;
712 usb_kill_urb(urb);
713 }
714 usb_free_urb(urb);
715 }
716 usX2Y->US04 = NULL;
717 kfree(usbdata);
718 kfree(us);
719 if (!err) {
720 usX2Y->rate = rate;
721 }
722 }
723 }
724
725 return err;
726}
727
728
729static int usX2Y_format_set(usX2Ydev_t *usX2Y, snd_pcm_format_t format)
730{
731 int alternate, err;
732 struct list_head* p;
733 if (format == SNDRV_PCM_FORMAT_S24_3LE) {
734 alternate = 2;
735 usX2Y->stride = 6;
736 } else {
737 alternate = 1;
738 usX2Y->stride = 4;
739 }
740 list_for_each(p, &usX2Y->chip.midi_list) {
741 snd_usbmidi_input_stop(p);
742 }
743 usb_kill_urb(usX2Y->In04urb);
744 if ((err = usb_set_interface(usX2Y->chip.dev, 0, alternate))) {
745 snd_printk("usb_set_interface error \n");
746 return err;
747 }
748 usX2Y->In04urb->dev = usX2Y->chip.dev;
749 err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL);
750 list_for_each(p, &usX2Y->chip.midi_list) {
751 snd_usbmidi_input_start(p);
752 }
753 usX2Y->format = format;
754 usX2Y->rate = 0;
755 return err;
756}
757
758
759static int snd_usX2Y_pcm_hw_params(snd_pcm_substream_t *substream,
760 snd_pcm_hw_params_t *hw_params)
761{
762 int err = 0;
763 unsigned int rate = params_rate(hw_params);
764 snd_pcm_format_t format = params_format(hw_params);
765 snd_printdd("snd_usX2Y_hw_params(%p, %p)\n", substream, hw_params);
766
767 { // all pcm substreams off one usX2Y have to operate at the same rate & format
768 snd_card_t *card = substream->pstr->pcm->card;
769 struct list_head *list;
770 list_for_each(list, &card->devices) {
771 snd_device_t *dev;
772 snd_pcm_t *pcm;
773 int s;
774 dev = snd_device(list);
775 if (dev->type != SNDRV_DEV_PCM)
776 continue;
777 pcm = dev->device_data;
778 for (s = 0; s < 2; ++s) {
779 snd_pcm_substream_t *test_substream;
780 test_substream = pcm->streams[s].substream;
781 if (test_substream && test_substream != substream &&
782 test_substream->runtime &&
783 ((test_substream->runtime->format &&
784 test_substream->runtime->format != format) ||
785 (test_substream->runtime->rate &&
786 test_substream->runtime->rate != rate)))
787 return -EINVAL;
788 }
789 }
790 }
791 if (0 > (err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)))) {
792 snd_printk("snd_pcm_lib_malloc_pages(%p, %i) returned %i\n", substream, params_buffer_bytes(hw_params), err);
793 return err;
794 }
795 return 0;
796}
797
798/*
799 * free the buffer
800 */
801static int snd_usX2Y_pcm_hw_free(snd_pcm_substream_t *substream)
802{
803 snd_pcm_runtime_t *runtime = substream->runtime;
804 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
805 down(&subs->usX2Y->prepare_mutex);
806 snd_printdd("snd_usX2Y_hw_free(%p)\n", substream);
807
808 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
809 snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
810 atomic_set(&subs->state, state_STOPPED);
811 usX2Y_urbs_release(subs);
812 if (!cap_subs->pcm_substream ||
813 !cap_subs->pcm_substream->runtime ||
814 !cap_subs->pcm_substream->runtime->status ||
815 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
816 atomic_set(&cap_subs->state, state_STOPPED);
817 usX2Y_urbs_release(cap_subs);
818 }
819 } else {
820 snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
821 if (atomic_read(&playback_subs->state) < state_PREPARED) {
822 atomic_set(&subs->state, state_STOPPED);
823 usX2Y_urbs_release(subs);
824 }
825 }
826 up(&subs->usX2Y->prepare_mutex);
827 return snd_pcm_lib_free_pages(substream);
828}
829/*
830 * prepare callback
831 *
832 * set format and initialize urbs
833 */
834static int snd_usX2Y_pcm_prepare(snd_pcm_substream_t *substream)
835{
836 snd_pcm_runtime_t *runtime = substream->runtime;
837 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
838 usX2Ydev_t *usX2Y = subs->usX2Y;
839 snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
840 int err = 0;
841 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
842
843 down(&usX2Y->prepare_mutex);
844 usX2Y_subs_prepare(subs);
845// Start hardware streams
846// SyncStream first....
847 if (atomic_read(&capsubs->state) < state_PREPARED) {
848 if (usX2Y->format != runtime->format)
849 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
850 goto up_prepare_mutex;
851 if (usX2Y->rate != runtime->rate)
852 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
853 goto up_prepare_mutex;
854 snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
855 if (0 > (err = usX2Y_urbs_start(capsubs)))
856 goto up_prepare_mutex;
857 }
858
859 if (subs != capsubs && atomic_read(&subs->state) < state_PREPARED)
860 err = usX2Y_urbs_start(subs);
861
862 up_prepare_mutex:
863 up(&usX2Y->prepare_mutex);
864 return err;
865}
866
867static snd_pcm_hardware_t snd_usX2Y_2c =
868{
869 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
870 SNDRV_PCM_INFO_BLOCK_TRANSFER |
871 SNDRV_PCM_INFO_MMAP_VALID),
872 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
873 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
874 .rate_min = 44100,
875 .rate_max = 48000,
876 .channels_min = 2,
877 .channels_max = 2,
878 .buffer_bytes_max = (2*128*1024),
879 .period_bytes_min = 64,
880 .period_bytes_max = (128*1024),
881 .periods_min = 2,
882 .periods_max = 1024,
883 .fifo_size = 0
884};
885
886
887
888static int snd_usX2Y_pcm_open(snd_pcm_substream_t *substream)
889{
890 snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **)
891 snd_pcm_substream_chip(substream))[substream->stream];
892 snd_pcm_runtime_t *runtime = substream->runtime;
893
894 if (subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS)
895 return -EBUSY;
896
897 runtime->hw = snd_usX2Y_2c;
898 runtime->private_data = subs;
899 subs->pcm_substream = substream;
900 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
901 return 0;
902}
903
904
905
906static int snd_usX2Y_pcm_close(snd_pcm_substream_t *substream)
907{
908 snd_pcm_runtime_t *runtime = substream->runtime;
909 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
910 int err = 0;
911
912 subs->pcm_substream = NULL;
913
914 return err;
915}
916
917
918static snd_pcm_ops_t snd_usX2Y_pcm_ops =
919{
920 .open = snd_usX2Y_pcm_open,
921 .close = snd_usX2Y_pcm_close,
922 .ioctl = snd_pcm_lib_ioctl,
923 .hw_params = snd_usX2Y_pcm_hw_params,
924 .hw_free = snd_usX2Y_pcm_hw_free,
925 .prepare = snd_usX2Y_pcm_prepare,
926 .trigger = snd_usX2Y_pcm_trigger,
927 .pointer = snd_usX2Y_pcm_pointer,
928};
929
930
931/*
932 * free a usb stream instance
933 */
934static void usX2Y_audio_stream_free(snd_usX2Y_substream_t **usX2Y_substream)
935{
936 if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) {
937 kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
938 usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
939 }
940 kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);
941 usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;
942}
943
944static void snd_usX2Y_pcm_private_free(snd_pcm_t *pcm)
945{
946 snd_usX2Y_substream_t **usX2Y_stream = pcm->private_data;
947 if (usX2Y_stream) {
948 snd_pcm_lib_preallocate_free_for_all(pcm);
949 usX2Y_audio_stream_free(usX2Y_stream);
950 }
951}
952
953static int usX2Y_audio_stream_new(snd_card_t *card, int playback_endpoint, int capture_endpoint)
954{
955 snd_pcm_t *pcm;
956 int err, i;
957 snd_usX2Y_substream_t **usX2Y_substream =
958 usX2Y(card)->subs + 2 * usX2Y(card)->chip.pcm_devs;
959
960 for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
961 i <= SNDRV_PCM_STREAM_CAPTURE; ++i) {
962 usX2Y_substream[i] = kcalloc(1, sizeof(snd_usX2Y_substream_t), GFP_KERNEL);
963 if (NULL == usX2Y_substream[i]) {
964 snd_printk(KERN_ERR "cannot malloc\n");
965 return -ENOMEM;
966 }
967 usX2Y_substream[i]->usX2Y = usX2Y(card);
968 }
969
970 if (playback_endpoint)
971 usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]->endpoint = playback_endpoint;
972 usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]->endpoint = capture_endpoint;
973
974 err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usX2Y(card)->chip.pcm_devs,
975 playback_endpoint ? 1 : 0, 1,
976 &pcm);
977 if (err < 0) {
978 usX2Y_audio_stream_free(usX2Y_substream);
979 return err;
980 }
981
982 if (playback_endpoint)
983 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_pcm_ops);
984 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_pcm_ops);
985
986 pcm->private_data = usX2Y_substream;
987 pcm->private_free = snd_usX2Y_pcm_private_free;
988 pcm->info_flags = 0;
989
990 sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usX2Y(card)->chip.pcm_devs);
991
992 if ((playback_endpoint &&
993 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
994 SNDRV_DMA_TYPE_CONTINUOUS,
995 snd_dma_continuous_data(GFP_KERNEL),
996 64*1024, 128*1024))) ||
997 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
998 SNDRV_DMA_TYPE_CONTINUOUS,
999 snd_dma_continuous_data(GFP_KERNEL),
1000 64*1024, 128*1024))) {
1001 snd_usX2Y_pcm_private_free(pcm);
1002 return err;
1003 }
1004 usX2Y(card)->chip.pcm_devs++;
1005
1006 return 0;
1007}
1008
1009/*
1010 * create a chip instance and set its names.
1011 */
1012int usX2Y_audio_create(snd_card_t* card)
1013{
1014 int err = 0;
1015
1016 INIT_LIST_HEAD(&usX2Y(card)->chip.pcm_list);
1017
1018 if (0 > (err = usX2Y_audio_stream_new(card, 0xA, 0x8)))
1019 return err;
1020 if (le16_to_cpu(usX2Y(card)->chip.dev->descriptor.idProduct) == USB_ID_US428)
1021 if (0 > (err = usX2Y_audio_stream_new(card, 0, 0xA)))
1022 return err;
1023 if (le16_to_cpu(usX2Y(card)->chip.dev->descriptor.idProduct) != USB_ID_US122)
1024 err = usX2Y_rate_set(usX2Y(card), 44100); // Lets us428 recognize output-volume settings, disturbs us122.
1025 return err;
1026}
diff --git a/sound/usb/usx2y/usx2y.h b/sound/usb/usx2y/usx2y.h
new file mode 100644
index 000000000000..7e59263dd895
--- /dev/null
+++ b/sound/usb/usx2y/usx2y.h
@@ -0,0 +1,51 @@
1/*
2 * Driver for Tascam US-X2Y USB soundcards
3 *
4 * Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#ifndef __SOUND_USX2Y_COMMON_H
22#define __SOUND_USX2Y_COMMON_H
23
24
25#define USX2Y_DRIVER_VERSION 0x0100 /* 0.1.0 */
26
27
28/* hwdep id string */
29#define SND_USX2Y_LOADER_ID "USX2Y Loader"
30#define SND_USX2Y_USBPCM_ID "USX2Y USBPCM"
31
32/* hardware type */
33enum {
34 USX2Y_TYPE_122,
35 USX2Y_TYPE_224,
36 USX2Y_TYPE_428,
37 USX2Y_TYPE_NUMS
38};
39
40#define USB_ID_US122 0x8007
41#define USB_ID_US224 0x8005
42#define USB_ID_US428 0x8001
43
44/* chip status */
45enum {
46 USX2Y_STAT_CHIP_INIT = (1 << 0), /* all operational */
47 USX2Y_STAT_CHIP_MMAP_PCM_URBS = (1 << 1), /* pcm transport over mmaped urbs */
48 USX2Y_STAT_CHIP_HUP = (1 << 31), /* all operational */
49};
50
51#endif /* __SOUND_USX2Y_COMMON_H */
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
new file mode 100644
index 000000000000..bb2c8e9000c6
--- /dev/null
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -0,0 +1,807 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17/* USX2Y "rawusb" aka hwdep_pcm implementation
18
19 Its usb's unableness to atomically handle power of 2 period sized data chuncs
20 at standard samplerates,
21 what led to this part of the usx2y module:
22 It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
23 The pair uses a hardware dependant alsa-device for mmaped pcm transport.
24 Advantage achieved:
25 The usb_hc moves pcm data from/into memory via DMA.
26 That memory is mmaped by jack's usx2y driver.
27 Jack's usx2y driver is the first/last to read/write pcm data.
28 Read/write is a combination of power of 2 period shaping and
29 float/int conversation.
30 Compared to mainline alsa/jack we leave out power of 2 period shaping inside
31 snd-usb-usx2y which needs memcpy() and additional buffers.
32 As a side effect possible unwanted pcm-data coruption resulting of
33 standard alsa's snd-usb-usx2y period shaping scheme falls away.
34 Result is sane jack operation at buffering schemes down to 128frames,
35 2 periods.
36 plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
37 cost of easier triggered i.e. aeolus xruns (128 or 256frames,
38 2periods works but is useless cause of crackling).
39
40 This is a first "proof of concept" implementation.
41 Later, funcionalities should migrate to more apropriate places:
42 Userland:
43 - The jackd could mmap its float-pcm buffers directly from alsa-lib.
44 - alsa-lib could provide power of 2 period sized shaping combined with int/float
45 conversation.
46 Currently the usx2y jack driver provides above 2 services.
47 Kernel:
48 - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
49 devices can use it.
50 Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
51*/
52
53#include "usbusx2yaudio.c"
54
55#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
56
57#include <sound/hwdep.h>
58
59
60static int usX2Y_usbpcm_urb_capt_retire(snd_usX2Y_substream_t *subs)
61{
62 struct urb *urb = subs->completed_urb;
63 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
64 int i, lens = 0, hwptr_done = subs->hwptr_done;
65 usX2Ydev_t *usX2Y = subs->usX2Y;
66 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
67 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
68 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
69 head = 0;
70 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
71 snd_printdd("cap start %i\n", head);
72 }
73 for (i = 0; i < nr_of_packs(); i++) {
74 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
75 snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
76 return urb->iso_frame_desc[i].status;
77 }
78 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
79 }
80 if ((hwptr_done += lens) >= runtime->buffer_size)
81 hwptr_done -= runtime->buffer_size;
82 subs->hwptr_done = hwptr_done;
83 subs->transfer_done += lens;
84 /* update the pointer, call callback if necessary */
85 if (subs->transfer_done >= runtime->period_size) {
86 subs->transfer_done -= runtime->period_size;
87 snd_pcm_period_elapsed(subs->pcm_substream);
88 }
89 return 0;
90}
91
92static inline int usX2Y_iso_frames_per_buffer(snd_pcm_runtime_t *runtime, usX2Ydev_t * usX2Y)
93{
94 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
95}
96
97/*
98 * prepare urb for playback data pipe
99 *
100 * we copy the data directly from the pcm buffer.
101 * the current position to be copied is held in hwptr field.
102 * since a urb can handle only a single linear buffer, if the total
103 * transferred area overflows the buffer boundary, we cannot send
104 * it directly from the buffer. thus the data is once copied to
105 * a temporary buffer and urb points to that.
106 */
107static int usX2Y_hwdep_urb_play_prepare(snd_usX2Y_substream_t *subs,
108 struct urb *urb)
109{
110 int count, counts, pack;
111 usX2Ydev_t *usX2Y = subs->usX2Y;
112 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
113 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
114
115 if (0 > shm->playback_iso_start) {
116 shm->playback_iso_start = shm->captured_iso_head -
117 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
118 if (0 > shm->playback_iso_start)
119 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
120 shm->playback_iso_head = shm->playback_iso_start;
121 }
122
123 count = 0;
124 for (pack = 0; pack < nr_of_packs(); pack++) {
125 /* calculate the size of a packet */
126 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
127 if (counts < 43 || counts > 50) {
128 snd_printk("should not be here with counts=%i\n", counts);
129 return -EPIPE;
130 }
131 /* set up descriptor */
132 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
133 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
134 if (atomic_read(&subs->state) != state_RUNNING)
135 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
136 urb->iso_frame_desc[pack].length);
137 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
138 shm->playback_iso_head = 0;
139 count += counts;
140 }
141 urb->transfer_buffer_length = count * usX2Y->stride;
142 return 0;
143}
144
145
146static inline void usX2Y_usbpcm_urb_capt_iso_advance(snd_usX2Y_substream_t *subs, struct urb *urb)
147{
148 int pack;
149 for (pack = 0; pack < nr_of_packs(); ++pack) {
150 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
151 if (NULL != subs) {
152 snd_usX2Y_hwdep_pcm_shm_t *shm = subs->usX2Y->hwdep_pcm_shm;
153 int head = shm->captured_iso_head + 1;
154 if (head >= ARRAY_SIZE(shm->captured_iso))
155 head = 0;
156 shm->captured_iso[head].frame = urb->start_frame + pack;
157 shm->captured_iso[head].offset = desc->offset;
158 shm->captured_iso[head].length = desc->actual_length;
159 shm->captured_iso_head = head;
160 shm->captured_iso_frames++;
161 }
162 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
163 desc->length >= SSS)
164 desc->offset -= (SSS - desc->length);
165 }
166}
167
168static inline int usX2Y_usbpcm_usbframe_complete(snd_usX2Y_substream_t *capsubs,
169 snd_usX2Y_substream_t *capsubs2,
170 snd_usX2Y_substream_t *playbacksubs, int frame)
171{
172 int err, state;
173 struct urb *urb = playbacksubs->completed_urb;
174
175 state = atomic_read(&playbacksubs->state);
176 if (NULL != urb) {
177 if (state == state_RUNNING)
178 usX2Y_urb_play_retire(playbacksubs, urb);
179 else
180 if (state >= state_PRERUNNING) {
181 atomic_inc(&playbacksubs->state);
182 }
183 } else {
184 switch (state) {
185 case state_STARTING1:
186 urb = playbacksubs->urb[0];
187 atomic_inc(&playbacksubs->state);
188 break;
189 case state_STARTING2:
190 urb = playbacksubs->urb[1];
191 atomic_inc(&playbacksubs->state);
192 break;
193 }
194 }
195 if (urb) {
196 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
197 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
198 return err;
199 }
200 }
201
202 playbacksubs->completed_urb = NULL;
203
204 state = atomic_read(&capsubs->state);
205 if (state >= state_PREPARED) {
206 if (state == state_RUNNING) {
207 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
208 return err;
209 } else {
210 if (state >= state_PRERUNNING)
211 atomic_inc(&capsubs->state);
212 }
213 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
214 if (NULL != capsubs2)
215 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
216 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
217 return err;
218 if (NULL != capsubs2)
219 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
220 return err;
221 }
222 capsubs->completed_urb = NULL;
223 if (NULL != capsubs2)
224 capsubs2->completed_urb = NULL;
225 return 0;
226}
227
228
229static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
230{
231 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
232 usX2Ydev_t *usX2Y = subs->usX2Y;
233 snd_usX2Y_substream_t *capsubs, *capsubs2, *playbacksubs;
234
235 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
236 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", usb_get_current_frame_number(usX2Y->chip.dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", urb->status, urb->start_frame);
237 return;
238 }
239 if (unlikely(urb->status)) {
240 usX2Y_error_urb_status(usX2Y, subs, urb);
241 return;
242 }
243 if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
244 subs->completed_urb = urb;
245 else {
246 usX2Y_error_sequence(usX2Y, subs, urb);
247 return;
248 }
249
250 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
251 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
252 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
253 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
254 (NULL == capsubs2 || capsubs2->completed_urb) &&
255 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
256 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
257 if (nr_of_packs() <= urb->start_frame &&
258 urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
259 usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
260 else
261 usX2Y->wait_iso_frame += nr_of_packs();
262 } else {
263 snd_printdd("\n");
264 usX2Y_clients_stop(usX2Y);
265 }
266 }
267}
268
269
270static void usX2Y_hwdep_urb_release(struct urb** urb)
271{
272 usb_kill_urb(*urb);
273 usb_free_urb(*urb);
274 *urb = NULL;
275}
276
277/*
278 * release a substream
279 */
280static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
281{
282 int i;
283 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
284 for (i = 0; i < NRURBS; i++)
285 usX2Y_hwdep_urb_release(subs->urb + i);
286}
287
288static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
289{
290 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
291 usX2Y->prepare_subs = NULL;
292}
293
294static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
295{
296 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
297 usX2Ydev_t *usX2Y = subs->usX2Y;
298 snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
299 if (NULL != prepare_subs &&
300 urb->start_frame == prepare_subs->urb[0]->start_frame) {
301 atomic_inc(&prepare_subs->state);
302 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
303 snd_usX2Y_substream_t *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
304 if (cap_subs2 != NULL)
305 atomic_inc(&cap_subs2->state);
306 }
307 usX2Y_usbpcm_subs_startup_finish(usX2Y);
308 wake_up(&usX2Y->prepare_wait_queue);
309 }
310
311 i_usX2Y_usbpcm_urb_complete(urb, regs);
312}
313
314/*
315 * initialize a substream's urbs
316 */
317static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
318{
319 int i;
320 unsigned int pipe;
321 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
322 struct usb_device *dev = subs->usX2Y->chip.dev;
323
324 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
325 usb_rcvisocpipe(dev, subs->endpoint);
326 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
327 if (!subs->maxpacksize)
328 return -EINVAL;
329
330 /* allocate and initialize data urbs */
331 for (i = 0; i < NRURBS; i++) {
332 struct urb** purb = subs->urb + i;
333 if (*purb) {
334 usb_kill_urb(*purb);
335 continue;
336 }
337 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
338 if (NULL == *purb) {
339 usX2Y_usbpcm_urbs_release(subs);
340 return -ENOMEM;
341 }
342 (*purb)->transfer_buffer = is_playback ?
343 subs->usX2Y->hwdep_pcm_shm->playback : (
344 subs->endpoint == 0x8 ?
345 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
346 subs->usX2Y->hwdep_pcm_shm->capture0xA);
347
348 (*purb)->dev = dev;
349 (*purb)->pipe = pipe;
350 (*purb)->number_of_packets = nr_of_packs();
351 (*purb)->context = subs;
352 (*purb)->interval = 1;
353 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
354 }
355 return 0;
356}
357
358/*
359 * free the buffer
360 */
361static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
362{
363 snd_pcm_runtime_t *runtime = substream->runtime;
364 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data,
365 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
366 down(&subs->usX2Y->prepare_mutex);
367 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
368
369 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
370 snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
371 atomic_set(&subs->state, state_STOPPED);
372 usX2Y_usbpcm_urbs_release(subs);
373 if (!cap_subs->pcm_substream ||
374 !cap_subs->pcm_substream->runtime ||
375 !cap_subs->pcm_substream->runtime->status ||
376 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
377 atomic_set(&cap_subs->state, state_STOPPED);
378 if (NULL != cap_subs2)
379 atomic_set(&cap_subs2->state, state_STOPPED);
380 usX2Y_usbpcm_urbs_release(cap_subs);
381 if (NULL != cap_subs2)
382 usX2Y_usbpcm_urbs_release(cap_subs2);
383 }
384 } else {
385 snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
386 if (atomic_read(&playback_subs->state) < state_PREPARED) {
387 atomic_set(&subs->state, state_STOPPED);
388 if (NULL != cap_subs2)
389 atomic_set(&cap_subs2->state, state_STOPPED);
390 usX2Y_usbpcm_urbs_release(subs);
391 if (NULL != cap_subs2)
392 usX2Y_usbpcm_urbs_release(cap_subs2);
393 }
394 }
395 up(&subs->usX2Y->prepare_mutex);
396 return snd_pcm_lib_free_pages(substream);
397}
398
399static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
400{
401 usX2Ydev_t * usX2Y = subs->usX2Y;
402 usX2Y->prepare_subs = subs;
403 subs->urb[0]->start_frame = -1;
404 smp_wmb(); // Make shure above modifications are seen by i_usX2Y_subs_startup()
405 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
406}
407
408static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
409{
410 int p, u, err,
411 stream = subs->pcm_substream->stream;
412 usX2Ydev_t *usX2Y = subs->usX2Y;
413
414 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
415 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
416 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
417 }
418
419 for (p = 0; 3 >= (stream + p); p += 2) {
420 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
421 if (subs != NULL) {
422 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
423 return err;
424 subs->completed_urb = NULL;
425 }
426 }
427
428 for (p = 0; p < 4; p++) {
429 snd_usX2Y_substream_t *subs = usX2Y->subs[p];
430 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
431 goto start;
432 }
433 usX2Y->wait_iso_frame = -1;
434
435 start:
436 usX2Y_usbpcm_subs_startup(subs);
437 for (u = 0; u < NRURBS; u++) {
438 for (p = 0; 3 >= (stream + p); p += 2) {
439 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
440 if (subs != NULL) {
441 struct urb *urb = subs->urb[u];
442 if (usb_pipein(urb->pipe)) {
443 unsigned long pack;
444 if (0 == u)
445 atomic_set(&subs->state, state_STARTING3);
446 urb->dev = usX2Y->chip.dev;
447 urb->transfer_flags = URB_ISO_ASAP;
448 for (pack = 0; pack < nr_of_packs(); pack++) {
449 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
450 urb->iso_frame_desc[pack].length = subs->maxpacksize;
451 }
452 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
453 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
454 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
455 err = -EPIPE;
456 goto cleanup;
457 } else {
458 snd_printdd("%i\n", urb->start_frame);
459 if (0 > usX2Y->wait_iso_frame)
460 usX2Y->wait_iso_frame = urb->start_frame;
461 }
462 urb->transfer_flags = 0;
463 } else {
464 atomic_set(&subs->state, state_STARTING1);
465 break;
466 }
467 }
468 }
469 }
470 err = 0;
471 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
472 if (atomic_read(&subs->state) != state_PREPARED)
473 err = -EPIPE;
474
475 cleanup:
476 if (err) {
477 usX2Y_subs_startup_finish(usX2Y); // Call it now
478 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
479 }
480 return err;
481}
482
483/*
484 * prepare callback
485 *
486 * set format and initialize urbs
487 */
488static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
489{
490 snd_pcm_runtime_t *runtime = substream->runtime;
491 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
492 usX2Ydev_t *usX2Y = subs->usX2Y;
493 snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
494 int err = 0;
495 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
496
497 if (NULL == usX2Y->hwdep_pcm_shm) {
498 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(snd_usX2Y_hwdep_pcm_shm_t), GFP_KERNEL)))
499 return -ENOMEM;
500 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
501 }
502
503 down(&usX2Y->prepare_mutex);
504 usX2Y_subs_prepare(subs);
505// Start hardware streams
506// SyncStream first....
507 if (atomic_read(&capsubs->state) < state_PREPARED) {
508 if (usX2Y->format != runtime->format)
509 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
510 goto up_prepare_mutex;
511 if (usX2Y->rate != runtime->rate)
512 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
513 goto up_prepare_mutex;
514 snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
515 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
516 goto up_prepare_mutex;
517 }
518
519 if (subs != capsubs) {
520 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
521 if (atomic_read(&subs->state) < state_PREPARED) {
522 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
523 signed long timeout;
524 snd_printd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
525 set_current_state(TASK_INTERRUPTIBLE);
526 timeout = schedule_timeout(HZ/100 + 1);
527 if (signal_pending(current)) {
528 err = -ERESTARTSYS;
529 goto up_prepare_mutex;
530 }
531 }
532 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
533 goto up_prepare_mutex;
534 }
535 snd_printd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
536 } else
537 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
538
539 up_prepare_mutex:
540 up(&usX2Y->prepare_mutex);
541 return err;
542}
543
544static snd_pcm_hardware_t snd_usX2Y_4c =
545{
546 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
547 SNDRV_PCM_INFO_BLOCK_TRANSFER |
548 SNDRV_PCM_INFO_MMAP_VALID),
549 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
550 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
551 .rate_min = 44100,
552 .rate_max = 48000,
553 .channels_min = 2,
554 .channels_max = 4,
555 .buffer_bytes_max = (2*128*1024),
556 .period_bytes_min = 64,
557 .period_bytes_max = (128*1024),
558 .periods_min = 2,
559 .periods_max = 1024,
560 .fifo_size = 0
561};
562
563
564
565static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
566{
567 snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **)
568 snd_pcm_substream_chip(substream))[substream->stream];
569 snd_pcm_runtime_t *runtime = substream->runtime;
570
571 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
572 return -EBUSY;
573
574 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
575 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
576 runtime->private_data = subs;
577 subs->pcm_substream = substream;
578 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
579 return 0;
580}
581
582
583static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
584{
585 snd_pcm_runtime_t *runtime = substream->runtime;
586 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
587 int err = 0;
588 snd_printd("\n");
589 subs->pcm_substream = NULL;
590 return err;
591}
592
593
594static snd_pcm_ops_t snd_usX2Y_usbpcm_ops =
595{
596 .open = snd_usX2Y_usbpcm_open,
597 .close = snd_usX2Y_usbpcm_close,
598 .ioctl = snd_pcm_lib_ioctl,
599 .hw_params = snd_usX2Y_pcm_hw_params,
600 .hw_free = snd_usX2Y_usbpcm_hw_free,
601 .prepare = snd_usX2Y_usbpcm_prepare,
602 .trigger = snd_usX2Y_pcm_trigger,
603 .pointer = snd_usX2Y_pcm_pointer,
604};
605
606
607static int usX2Y_pcms_lock_check(snd_card_t *card)
608{
609 struct list_head *list;
610 snd_device_t *dev;
611 snd_pcm_t *pcm;
612 int err = 0;
613 list_for_each(list, &card->devices) {
614 dev = snd_device(list);
615 if (dev->type != SNDRV_DEV_PCM)
616 continue;
617 pcm = dev->device_data;
618 down(&pcm->open_mutex);
619 }
620 list_for_each(list, &card->devices) {
621 int s;
622 dev = snd_device(list);
623 if (dev->type != SNDRV_DEV_PCM)
624 continue;
625 pcm = dev->device_data;
626 for (s = 0; s < 2; ++s) {
627 snd_pcm_substream_t *substream;
628 substream = pcm->streams[s].substream;
629 if (substream && substream->open_flag)
630 err = -EBUSY;
631 }
632 }
633 return err;
634}
635
636
637static void usX2Y_pcms_unlock(snd_card_t *card)
638{
639 struct list_head *list;
640 snd_device_t *dev;
641 snd_pcm_t *pcm;
642 list_for_each(list, &card->devices) {
643 dev = snd_device(list);
644 if (dev->type != SNDRV_DEV_PCM)
645 continue;
646 pcm = dev->device_data;
647 up(&pcm->open_mutex);
648 }
649}
650
651
652static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
653{
654 // we need to be the first
655 snd_card_t *card = hw->card;
656 int err = usX2Y_pcms_lock_check(card);
657 if (0 == err)
658 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
659 usX2Y_pcms_unlock(card);
660 return err;
661}
662
663
664static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
665{
666 snd_card_t *card = hw->card;
667 int err = usX2Y_pcms_lock_check(card);
668 if (0 == err)
669 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
670 usX2Y_pcms_unlock(card);
671 return err;
672}
673
674
675static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
676{
677}
678
679
680static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
681{
682}
683
684
685static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
686{
687 unsigned long offset;
688 struct page *page;
689 void *vaddr;
690
691 offset = area->vm_pgoff << PAGE_SHIFT;
692 offset += address - area->vm_start;
693 snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
694 vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->hwdep_pcm_shm + offset;
695 page = virt_to_page(vaddr);
696
697 if (type)
698 *type = VM_FAULT_MINOR;
699
700 return page;
701}
702
703
704static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
705 .open = snd_usX2Y_hwdep_pcm_vm_open,
706 .close = snd_usX2Y_hwdep_pcm_vm_close,
707 .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
708};
709
710
711static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
712{
713 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
714 usX2Ydev_t *usX2Y = (usX2Ydev_t*)hw->private_data;
715
716 if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
717 return -EBUSY;
718
719 /* if userspace tries to mmap beyond end of our buffer, fail */
720 if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
721 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t));
722 return -EINVAL;
723 }
724
725 if (!usX2Y->hwdep_pcm_shm) {
726 return -ENODEV;
727 }
728 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
729 area->vm_flags |= VM_RESERVED;
730 snd_printd("vm_flags=0x%lX\n", area->vm_flags);
731 area->vm_private_data = hw->private_data;
732 return 0;
733}
734
735
736static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
737{
738 usX2Ydev_t *usX2Y = (usX2Ydev_t *)hwdep->private_data;
739 if (NULL != usX2Y->hwdep_pcm_shm)
740 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
741}
742
743
744static void snd_usX2Y_usbpcm_private_free(snd_pcm_t *pcm)
745{
746 snd_pcm_lib_preallocate_free_for_all(pcm);
747}
748
749
750int usX2Y_hwdep_pcm_new(snd_card_t* card)
751{
752 int err;
753 snd_hwdep_t *hw;
754 snd_pcm_t *pcm;
755 struct usb_device *dev = usX2Y(card)->chip.dev;
756 if (1 != nr_of_packs())
757 return 0;
758
759 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0) {
760 snd_printd("\n");
761 return err;
762 }
763 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
764 hw->private_data = usX2Y(card);
765 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
766 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
767 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
768 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
769 hw->exclusive = 1;
770 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
771
772 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
773 if (err < 0) {
774 return err;
775 }
776 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
777 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
778
779 pcm->private_data = usX2Y(card)->subs;
780 pcm->private_free = snd_usX2Y_usbpcm_private_free;
781 pcm->info_flags = 0;
782
783 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
784 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
785 SNDRV_DMA_TYPE_CONTINUOUS,
786 snd_dma_continuous_data(GFP_KERNEL),
787 64*1024, 128*1024)) ||
788 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
789 SNDRV_DMA_TYPE_CONTINUOUS,
790 snd_dma_continuous_data(GFP_KERNEL),
791 64*1024, 128*1024))) {
792 snd_usX2Y_usbpcm_private_free(pcm);
793 return err;
794 }
795
796
797 return 0;
798}
799
800#else
801
802int usX2Y_hwdep_pcm_new(snd_card_t* card)
803{
804 return 0;
805}
806
807#endif
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.h b/sound/usb/usx2y/usx2yhwdeppcm.h
new file mode 100644
index 000000000000..d68f0cbdbbe2
--- /dev/null
+++ b/sound/usb/usx2y/usx2yhwdeppcm.h
@@ -0,0 +1,21 @@
1#define MAXPACK 50
2#define MAXBUFFERMS 100
3#define MAXSTRIDE 3
4
5#define SSS (((MAXPACK*MAXBUFFERMS*MAXSTRIDE + 4096) / 4096) * 4096)
6struct snd_usX2Y_hwdep_pcm_shm {
7 char playback[SSS];
8 char capture0x8[SSS];
9 char capture0xA[SSS];
10 volatile int playback_iso_head;
11 int playback_iso_start;
12 struct {
13 int frame,
14 offset,
15 length;
16 } captured_iso[128];
17 volatile int captured_iso_head;
18 volatile unsigned captured_iso_frames;
19 int capture_iso_start;
20};
21typedef struct snd_usX2Y_hwdep_pcm_shm snd_usX2Y_hwdep_pcm_shm_t;