aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMario Kicherer <dev@kicherer.org>2014-04-04 14:40:50 -0400
committerTakashi Iwai <tiwai@suse.de>2014-04-07 10:05:41 -0400
commitb47a22290d581277be70e8a597824a4985d39e83 (patch)
tree5ddf29073ab5fd60dee1376407f1fae31003f392
parentde9481cb40339d9bfc1104b53649876fc5f3d432 (diff)
ALSA: MIDI driver for Behringer BCD2000 USB device
This patch adds initial support for the Behringer BCD2000 USB DJ controller. At the moment, only the MIDI part of the device is working, i.e. knobs, buttons and LEDs. I also plan to add support for the audio part, but I assume that this will require more effort than the rather simple MIDI interface. Progress can be tracked at https://github.com/anyc/snd-usb-bcd2000. Signed-off-by: Mario Kicherer <dev@kicherer.org> Reviewed-by: Daniel Mack <daniel@zonque.org> Reviewed-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/usb/Kconfig13
-rw-r--r--sound/usb/Makefile2
-rw-r--r--sound/usb/bcd2000/Makefile3
-rw-r--r--sound/usb/bcd2000/bcd2000.c461
4 files changed, 478 insertions, 1 deletions
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index e05a86b7c0da..d393153c474f 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -147,5 +147,18 @@ config SND_USB_HIFACE
147 To compile this driver as a module, choose M here: the module 147 To compile this driver as a module, choose M here: the module
148 will be called snd-usb-hiface. 148 will be called snd-usb-hiface.
149 149
150config SND_BCD2000
151 tristate "Behringer BCD2000 MIDI driver"
152 select SND_RAWMIDI
153 help
154 Say Y here to include MIDI support for the Behringer BCD2000 DJ
155 controller.
156
157 Audio support is still work-in-progress at
158 https://github.com/anyc/snd-usb-bcd2000
159
160 To compile this driver as a module, choose M here: the module
161 will be called snd-bcd2000.
162
150endif # SND_USB 163endif # SND_USB
151 164
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index abe668f660d1..2b92f0dcbc4c 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
23obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o 23obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
24obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o 24obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
25 25
26obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ 26obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
diff --git a/sound/usb/bcd2000/Makefile b/sound/usb/bcd2000/Makefile
new file mode 100644
index 000000000000..f09ccc0af6ff
--- /dev/null
+++ b/sound/usb/bcd2000/Makefile
@@ -0,0 +1,3 @@
1snd-bcd2000-y := bcd2000.o
2
3obj-$(CONFIG_SND_BCD2000) += snd-bcd2000.o \ No newline at end of file
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
new file mode 100644
index 000000000000..820d6ca8c458
--- /dev/null
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -0,0 +1,461 @@
1/*
2 * Behringer BCD2000 driver
3 *
4 * Copyright (C) 2014 Mario Kicherer (dev@kicherer.org)
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
17#include <linux/kernel.h>
18#include <linux/errno.h>
19#include <linux/init.h>
20#include <linux/slab.h>
21#include <linux/module.h>
22#include <linux/bitmap.h>
23#include <linux/usb.h>
24#include <linux/usb/audio.h>
25#include <sound/core.h>
26#include <sound/initval.h>
27#include <sound/rawmidi.h>
28
29#define PREFIX "snd-bcd2000: "
30#define BUFSIZE 64
31
32static struct usb_device_id id_table[] = {
33 { USB_DEVICE(0x1397, 0x00bd) },
34 { },
35};
36
37static unsigned char device_cmd_prefix[] = {0x03, 0x00};
38
39static unsigned char bcd2000_init_sequence[] = {
40 0x07, 0x00, 0x00, 0x00, 0x78, 0x48, 0x1c, 0x81,
41 0xc4, 0x00, 0x00, 0x00, 0x5e, 0x53, 0x4a, 0xf7,
42 0x18, 0xfa, 0x11, 0xff, 0x6c, 0xf3, 0x90, 0xff,
43 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
44 0x18, 0xfa, 0x11, 0xff, 0x14, 0x00, 0x00, 0x00,
45 0x00, 0x00, 0x00, 0x00, 0xf2, 0x34, 0x4a, 0xf7,
46 0x18, 0xfa, 0x11, 0xff
47};
48
49struct bcd2000 {
50 struct usb_device *dev;
51 struct snd_card *card;
52 struct usb_interface *intf;
53 int card_index;
54
55 int midi_out_active;
56 struct snd_rawmidi *rmidi;
57 struct snd_rawmidi_substream *midi_receive_substream;
58 struct snd_rawmidi_substream *midi_out_substream;
59
60 unsigned char midi_in_buf[BUFSIZE];
61 unsigned char midi_out_buf[BUFSIZE];
62
63 struct urb *midi_out_urb;
64 struct urb *midi_in_urb;
65
66 struct usb_anchor anchor;
67};
68
69static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
70static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
71
72static DEFINE_MUTEX(devices_mutex);
73DECLARE_BITMAP(devices_used, SNDRV_CARDS);
74static struct usb_driver bcd2000_driver;
75
76#ifdef CONFIG_SND_DEBUG
77static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len)
78{
79 print_hex_dump(KERN_DEBUG, prefix,
80 DUMP_PREFIX_NONE, 16, 1,
81 buf, len, false);
82}
83#else
84static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len) {}
85#endif
86
87static int bcd2000_midi_input_open(struct snd_rawmidi_substream *substream)
88{
89 return 0;
90}
91
92static int bcd2000_midi_input_close(struct snd_rawmidi_substream *substream)
93{
94 return 0;
95}
96
97/* (de)register midi substream from client */
98static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream,
99 int up)
100{
101 struct bcd2000 *bcd2k = substream->rmidi->private_data;
102 bcd2k->midi_receive_substream = up ? substream : NULL;
103}
104
105static void bcd2000_midi_handle_input(struct bcd2000 *bcd2k,
106 const unsigned char *buf, unsigned int buf_len)
107{
108 unsigned int payload_length, tocopy;
109 struct snd_rawmidi_substream *midi_receive_substream;
110
111 midi_receive_substream = ACCESS_ONCE(bcd2k->midi_receive_substream);
112 if (!midi_receive_substream)
113 return;
114
115 bcd2000_dump_buffer(PREFIX "received from device: ", buf, buf_len);
116
117 if (buf_len < 2)
118 return;
119
120 payload_length = buf[0];
121
122 /* ignore packets without payload */
123 if (payload_length == 0)
124 return;
125
126 tocopy = min(payload_length, buf_len-1);
127
128 bcd2000_dump_buffer(PREFIX "sending to userspace: ",
129 &buf[1], tocopy);
130
131 snd_rawmidi_receive(midi_receive_substream,
132 &buf[1], tocopy);
133}
134
135static void bcd2000_midi_send(struct bcd2000 *bcd2k)
136{
137 int len, ret;
138 struct snd_rawmidi_substream *midi_out_substream;
139
140 BUILD_BUG_ON(sizeof(device_cmd_prefix) >= BUFSIZE);
141
142 midi_out_substream = ACCESS_ONCE(bcd2k->midi_out_substream);
143 if (!midi_out_substream)
144 return;
145
146 /* copy command prefix bytes */
147 memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
148 sizeof(device_cmd_prefix));
149
150 /*
151 * get MIDI packet and leave space for command prefix
152 * and payload length
153 */
154 len = snd_rawmidi_transmit(midi_out_substream,
155 bcd2k->midi_out_buf + 3, BUFSIZE - 3);
156
157 if (len < 0)
158 dev_err(&bcd2k->dev->dev, "%s: snd_rawmidi_transmit error %d\n",
159 __func__, len);
160
161 if (len <= 0)
162 return;
163
164 /* set payload length */
165 bcd2k->midi_out_buf[2] = len;
166 bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
167
168 bcd2000_dump_buffer(PREFIX "sending to device: ",
169 bcd2k->midi_out_buf, len+3);
170
171 /* send packet to the BCD2000 */
172 ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);
173 if (ret < 0)
174 dev_err(&bcd2k->dev->dev, PREFIX
175 "%s (%p): usb_submit_urb() failed, ret=%d, len=%d\n",
176 __func__, midi_out_substream, ret, len);
177 else
178 bcd2k->midi_out_active = 1;
179}
180
181static int bcd2000_midi_output_open(struct snd_rawmidi_substream *substream)
182{
183 return 0;
184}
185
186static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream)
187{
188 struct bcd2000 *bcd2k = substream->rmidi->private_data;
189
190 if (bcd2k->midi_out_active) {
191 usb_kill_urb(bcd2k->midi_out_urb);
192 bcd2k->midi_out_active = 0;
193 }
194
195 return 0;
196}
197
198/* (de)register midi substream from client */
199static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream,
200 int up)
201{
202 struct bcd2000 *bcd2k = substream->rmidi->private_data;
203
204 if (up) {
205 bcd2k->midi_out_substream = substream;
206 /* check if there is data userspace wants to send */
207 if (!bcd2k->midi_out_active)
208 bcd2000_midi_send(bcd2k);
209 } else {
210 bcd2k->midi_out_substream = NULL;
211 }
212}
213
214static void bcd2000_output_complete(struct urb *urb)
215{
216 struct bcd2000 *bcd2k = urb->context;
217
218 bcd2k->midi_out_active = 0;
219
220 if (urb->status)
221 dev_warn(&urb->dev->dev,
222 PREFIX "output urb->status: %d\n", urb->status);
223
224 if (urb->status == -ESHUTDOWN)
225 return;
226
227 /* check if there is more data userspace wants to send */
228 bcd2000_midi_send(bcd2k);
229}
230
231static void bcd2000_input_complete(struct urb *urb)
232{
233 int ret;
234 struct bcd2000 *bcd2k = urb->context;
235
236 if (urb->status)
237 dev_warn(&urb->dev->dev,
238 PREFIX "input urb->status: %i\n", urb->status);
239
240 if (!bcd2k || urb->status == -ESHUTDOWN)
241 return;
242
243 if (urb->actual_length > 0)
244 bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer,
245 urb->actual_length);
246
247 /* return URB to device */
248 ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_ATOMIC);
249 if (ret < 0)
250 dev_err(&bcd2k->dev->dev, PREFIX
251 "%s: usb_submit_urb() failed, ret=%d\n",
252 __func__, ret);
253}
254
255static struct snd_rawmidi_ops bcd2000_midi_output = {
256 .open = bcd2000_midi_output_open,
257 .close = bcd2000_midi_output_close,
258 .trigger = bcd2000_midi_output_trigger,
259};
260
261static struct snd_rawmidi_ops bcd2000_midi_input = {
262 .open = bcd2000_midi_input_open,
263 .close = bcd2000_midi_input_close,
264 .trigger = bcd2000_midi_input_trigger,
265};
266
267static void bcd2000_init_device(struct bcd2000 *bcd2k)
268{
269 int ret;
270
271 init_usb_anchor(&bcd2k->anchor);
272 usb_anchor_urb(bcd2k->midi_out_urb, &bcd2k->anchor);
273 usb_anchor_urb(bcd2k->midi_in_urb, &bcd2k->anchor);
274
275 /* copy init sequence into buffer */
276 memcpy(bcd2k->midi_out_buf, bcd2000_init_sequence, 52);
277 bcd2k->midi_out_urb->transfer_buffer_length = 52;
278
279 /* submit sequence */
280 ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_KERNEL);
281 if (ret < 0)
282 dev_err(&bcd2k->dev->dev, PREFIX
283 "%s: usb_submit_urb() out failed, ret=%d: ",
284 __func__, ret);
285 else
286 bcd2k->midi_out_active = 1;
287
288 /* pass URB to device to enable button and controller events */
289 ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_KERNEL);
290 if (ret < 0)
291 dev_err(&bcd2k->dev->dev, PREFIX
292 "%s: usb_submit_urb() in failed, ret=%d: ",
293 __func__, ret);
294
295 /* ensure initialization is finished */
296 usb_wait_anchor_empty_timeout(&bcd2k->anchor, 1000);
297}
298
299static int bcd2000_init_midi(struct bcd2000 *bcd2k)
300{
301 int ret;
302 struct snd_rawmidi *rmidi;
303
304 ret = snd_rawmidi_new(bcd2k->card, bcd2k->card->shortname, 0,
305 1, /* output */
306 1, /* input */
307 &rmidi);
308
309 if (ret < 0)
310 return ret;
311
312 strlcpy(rmidi->name, bcd2k->card->shortname, sizeof(rmidi->name));
313
314 rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX;
315 rmidi->private_data = bcd2k;
316
317 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
318 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
319 &bcd2000_midi_output);
320
321 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
322 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
323 &bcd2000_midi_input);
324
325 bcd2k->rmidi = rmidi;
326
327 bcd2k->midi_in_urb = usb_alloc_urb(0, GFP_KERNEL);
328 bcd2k->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL);
329
330 if (!bcd2k->midi_in_urb || !bcd2k->midi_out_urb) {
331 dev_err(&bcd2k->dev->dev, PREFIX "usb_alloc_urb failed\n");
332 return -ENOMEM;
333 }
334
335 usb_fill_int_urb(bcd2k->midi_in_urb, bcd2k->dev,
336 usb_rcvintpipe(bcd2k->dev, 0x81),
337 bcd2k->midi_in_buf, BUFSIZE,
338 bcd2000_input_complete, bcd2k, 1);
339
340 usb_fill_int_urb(bcd2k->midi_out_urb, bcd2k->dev,
341 usb_sndintpipe(bcd2k->dev, 0x1),
342 bcd2k->midi_out_buf, BUFSIZE,
343 bcd2000_output_complete, bcd2k, 1);
344
345 bcd2000_init_device(bcd2k);
346
347 return 0;
348}
349
350static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
351 struct usb_interface *interface)
352{
353 /* usb_kill_urb not necessary, urb is aborted automatically */
354
355 usb_free_urb(bcd2k->midi_out_urb);
356 usb_free_urb(bcd2k->midi_in_urb);
357
358 if (bcd2k->intf) {
359 usb_set_intfdata(bcd2k->intf, NULL);
360 bcd2k->intf = NULL;
361 }
362}
363
364static int bcd2000_probe(struct usb_interface *interface,
365 const struct usb_device_id *usb_id)
366{
367 struct snd_card *card;
368 struct bcd2000 *bcd2k;
369 unsigned int card_index;
370 char usb_path[32];
371 int err;
372
373 mutex_lock(&devices_mutex);
374
375 for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
376 if (!test_bit(card_index, devices_used))
377 break;
378
379 if (card_index >= SNDRV_CARDS) {
380 mutex_unlock(&devices_mutex);
381 return -ENOENT;
382 }
383
384 err = snd_card_new(&interface->dev, index[card_index], id[card_index],
385 THIS_MODULE, sizeof(*bcd2k), &card);
386 if (err < 0) {
387 mutex_unlock(&devices_mutex);
388 return err;
389 }
390
391 bcd2k = card->private_data;
392 bcd2k->dev = interface_to_usbdev(interface);
393 bcd2k->card = card;
394 bcd2k->card_index = card_index;
395 bcd2k->intf = interface;
396
397 snd_card_set_dev(card, &interface->dev);
398
399 strncpy(card->driver, "snd-bcd2000", sizeof(card->driver));
400 strncpy(card->shortname, "BCD2000", sizeof(card->shortname));
401 usb_make_path(bcd2k->dev, usb_path, sizeof(usb_path));
402 snprintf(bcd2k->card->longname, sizeof(bcd2k->card->longname),
403 "Behringer BCD2000 at %s",
404 usb_path);
405
406 err = bcd2000_init_midi(bcd2k);
407 if (err < 0)
408 goto probe_error;
409
410 err = snd_card_register(card);
411 if (err < 0)
412 goto probe_error;
413
414 usb_set_intfdata(interface, bcd2k);
415 set_bit(card_index, devices_used);
416
417 mutex_unlock(&devices_mutex);
418 return 0;
419
420probe_error:
421 dev_info(&bcd2k->dev->dev, PREFIX "error during probing");
422 bcd2000_free_usb_related_resources(bcd2k, interface);
423 snd_card_free(card);
424 mutex_unlock(&devices_mutex);
425 return err;
426}
427
428static void bcd2000_disconnect(struct usb_interface *interface)
429{
430 struct bcd2000 *bcd2k = usb_get_intfdata(interface);
431
432 if (!bcd2k)
433 return;
434
435 mutex_lock(&devices_mutex);
436
437 /* make sure that userspace cannot create new requests */
438 snd_card_disconnect(bcd2k->card);
439
440 bcd2000_free_usb_related_resources(bcd2k, interface);
441
442 clear_bit(bcd2k->card_index, devices_used);
443
444 snd_card_free_when_closed(bcd2k->card);
445
446 mutex_unlock(&devices_mutex);
447}
448
449static struct usb_driver bcd2000_driver = {
450 .name = "snd-bcd2000",
451 .probe = bcd2000_probe,
452 .disconnect = bcd2000_disconnect,
453 .id_table = id_table,
454};
455
456module_usb_driver(bcd2000_driver);
457
458MODULE_DEVICE_TABLE(usb, id_table);
459MODULE_AUTHOR("Mario Kicherer, dev@kicherer.org");
460MODULE_DESCRIPTION("Behringer BCD2000 driver");
461MODULE_LICENSE("GPL");