diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/usb/usx2y/usX2Yhwdep.c |
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/usX2Yhwdep.c')
-rw-r--r-- | sound/usb/usx2y/usX2Yhwdep.c | 280 |
1 files changed, 280 insertions, 0 deletions
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 | |||
34 | int usX2Y_hwdep_pcm_new(snd_card_t* card); | ||
35 | |||
36 | |||
37 | static 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 | |||
63 | static struct vm_operations_struct us428ctls_vm_ops = { | ||
64 | .nopage = snd_us428ctls_vm_nopage, | ||
65 | }; | ||
66 | |||
67 | static 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 | |||
96 | static 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 | |||
113 | static int snd_usX2Y_hwdep_open(snd_hwdep_t *hw, struct file *file) | ||
114 | { | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int snd_usX2Y_hwdep_release(snd_hwdep_t *hw, struct file *file) | ||
119 | { | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static 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 | |||
154 | static 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 | |||
190 | static 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 | |||
210 | static 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 | |||
260 | int 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 | |||