Ticket #4107: usb_midi.c

File usb_midi.c, 14.5 KB (added by Pete, 15 years ago)

modified test version of usb_midi.c

Line 
1/*****************************************************************************/
2// midi usb driver
3// Written by Jérôme Duval
4//
5// usb_midi.c
6//
7// Copyright (c) 2006 Haiku Project
8//
9// Some portions of code are copyrighted by
10// USB Joystick driver for BeOS R5
11// Copyright 2000 (C) ITO, Takayuki
12// All rights reserved
13//
14// Permission is hereby granted, free of charge, to any person obtaining a
15// copy of this software and associated documentation files (the "Software"),
16// to deal in the Software without restriction, including without limitation
17// the rights to use, copy, modify, merge, publish, distribute, sublicense,
18// and/or sell copies of the Software, and to permit persons to whom the
19// Software is furnished to do so, subject to the following conditions:
20//
21// The above copyright notice and this permission notice shall be included
22// in all copies or substantial portions of the Software.
23//
24// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30// DEALINGS IN THE SOFTWARE.
31/*****************************************************************************/
32#include <support/Debug.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include "usb_midi.h"
37#include "usbdevs.h"
38#include "usbdevs_data.h"
39
40static int midi_device_number = 0;
41const char *midi_base_name = "midi/usb/";
42
43my_device_info *
44create_device(const usb_device *dev, const usb_interface_info *ii, uint16 ifno)
45{
46 my_device_info *my_dev = NULL;
47 int number;
48 area_id area;
49 sem_id sem;
50 char area_name [32];
51 const char *base_name;
52
53 assert (usb != NULL && dev != NULL);
54
55 number = midi_device_number++;
56 base_name = midi_base_name;
57
58 my_dev = malloc (sizeof (my_device_info));
59 if (my_dev == NULL)
60 return NULL;
61
62 my_dev->sem_cb = sem = create_sem (0, DRIVER_NAME "_cb");
63 if (sem < 0) {
64 DPRINTF_ERR ((MY_ID "create_sem() failed %d\n", (int) sem));
65 free (my_dev);
66 return NULL;
67 }
68
69 my_dev->sem_lock = sem = create_sem (1, DRIVER_NAME "_lock");
70 if (sem < 0) {
71 DPRINTF_ERR ((MY_ID "create_sem() failed %d\n", (int) sem));
72 delete_sem (my_dev->sem_cb);
73 free (my_dev);
74 return NULL;
75 }
76
77 sprintf (area_name, DRIVER_NAME "_buffer%d", number);
78 my_dev->buffer_area = area = create_area (area_name,
79 (void **) &my_dev->buffer, B_ANY_KERNEL_ADDRESS,
80 B_PAGE_SIZE, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
81 if (area < 0) {
82 DPRINTF_ERR ((MY_ID "create_area() failed %d\n", (int) area));
83 delete_sem (my_dev->sem_cb);
84 delete_sem (my_dev->sem_lock);
85 free (my_dev);
86 return NULL;
87 }
88
89 sprintf(my_dev->name, "%s%d", base_name, number);
90 my_dev->dev = dev;
91 my_dev->ifno = ifno;
92 my_dev->open = 0;
93 my_dev->open_fds = NULL;
94 my_dev->active = true;
95 my_dev->flags = 0;
96 my_dev->rbuf = create_ring_buffer(384);
97 my_dev->total_report_size = sizeof(usb_midi_event_packet);
98
99 return my_dev;
100}
101
102void
103remove_device (my_device_info *my_dev)
104{
105 assert (my_dev != NULL);
106 if (my_dev->rbuf != NULL) {
107 delete_ring_buffer(my_dev->rbuf);
108 my_dev->rbuf = NULL;
109 }
110
111 delete_area (my_dev->buffer_area);
112 delete_sem (my_dev->sem_cb);
113 delete_sem (my_dev->sem_lock);
114 free (my_dev);
115}
116
117
118/* driver cookie (per open) */
119
120typedef struct driver_cookie
121{
122 struct driver_cookie *next;
123 my_device_info *my_dev;
124} driver_cookie;
125
126/* NB global variables are valid only while driver is loaded */
127
128_EXPORT int32 api_version = B_CUR_DRIVER_API_VERSION;
129
130const char *usb_midi_driver_name = "usb_midi";
131
132usb_module_info *usb;
133
134static void
135interpret_midi_buffer(my_device_info *my_dev)
136{
137 usb_midi_event_packet *packet = my_dev->buffer;
138
139 ring_buffer_write(my_dev->rbuf, packet->midi, sizeof(packet->midi));
140 release_sem_etc(my_dev->sem_cb, 3, B_DO_NOT_RESCHEDULE);
141}
142
143
144/*
145 callback: got a report, issue next request
146*/
147
148static void
149midi_usb_callback(void *cookie, uint32 status,
150 void *data, uint32 actual_len)
151{
152 status_t st;
153 my_device_info *my_dev = cookie;
154
155 assert (cookie != NULL);
156 DPRINTF_INFO ((MY_ID "midi_usb_callback()\n"));
157
158 acquire_sem (my_dev->sem_lock);
159 my_dev->actual_length = actual_len;
160 my_dev->bus_status = status; /* B_USB_STATUS_* */
161 if (status != B_OK) {
162 /* request failed */
163 release_sem (my_dev->sem_lock);
164 DPRINTF_ERR ((MY_ID "bus status %d\n", (int)status));
165 if (status == B_CANCELED) {
166 /* cancelled: device is unplugged */
167 return;
168 }
169#if 0
170 st = usb->clear_feature (my_dev->ept->handle, USB_FEATURE_ENDPOINT_HALT);
171 if (st != B_OK)
172 DPRINTF_ERR ((MY_ID "clear_feature() error %d\n", (int)st));
173#endif
174 } else {
175 /* got a report */
176//#if 0
177 uint32 i;
178 char linbuf [256];
179 uint8 *buffer = my_dev->buffer;
180
181 for (i = 0; i < my_dev->total_report_size; i++)
182 sprintf (&linbuf[i*3], "%02X ", buffer [i]);
183 DPRINTF_INFO ((MY_ID "report: %s\n", linbuf));
184//#endif
185 my_dev->timestamp = system_time ();
186
187 interpret_midi_buffer(my_dev);
188 release_sem (my_dev->sem_lock);
189 }
190
191 /* issue next request */
192
193 DPRINTF_INFO ((MY_ID "in callback: my_dev = %p endpoint = %p (base %p)\n",
194 my_dev, my_dev->ept, *(void **)my_dev));
195 if (my_dev->ept == 0xcccccccc) return; /* prevent KDL */
196 st = usb->queue_bulk (my_dev->ept->handle, my_dev->buffer,
197 my_dev->total_report_size, midi_usb_callback, my_dev);
198 if (st != B_OK) {
199 /* XXX probably endpoint stall */
200 DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)st));
201 }
202}
203
204/*
205 USB specific device hooks
206*/
207
208static status_t
209usb_midi_added(const usb_device *dev, void **cookie)
210{
211 my_device_info *my_dev;
212 const usb_device_descriptor *dev_desc;
213 const usb_configuration_info *conf;
214 const usb_interface_info *intf;
215 status_t st;
216 uint16 ifno, i;
217 int alt;
218
219 assert (dev != NULL && cookie != NULL);
220 DPRINTF_INFO ((MY_ID "device_added()\n"));
221
222 dev_desc = usb->get_device_descriptor (dev);
223
224 DPRINTF_INFO ((MY_ID "vendor ID 0x%04X, product ID 0x%04X\n",
225 dev_desc->vendor_id, dev_desc->product_id));
226
227 /* check interface class */
228
229 if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION)) == NULL) {
230 DPRINTF_ERR ((MY_ID "cannot get default configuration\n"));
231 return B_ERROR;
232 }
233
234 for (ifno = 0; ifno < conf->interface_count; ifno++) {
235 /* This is C; I can use "class" :-> */
236 int class, subclass, protocol;
237
238 for (alt = 0; alt < conf->interface[ifno].alt_count; alt++) {
239 intf = &conf->interface [ifno].alt[alt];
240 class = intf->descr->interface_class;
241 subclass = intf->descr->interface_subclass;
242 protocol = intf->descr->interface_protocol;
243 DPRINTF_INFO ((MY_ID "interface %d, alt : %d: class %d, subclass %d, protocol %d\n",
244 ifno, alt, class, subclass, protocol));
245
246 if (class == USB_AUDIO_DEVICE_CLASS
247 && subclass == USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS)
248 goto got_one;
249 }
250 }
251
252 DPRINTF_INFO ((MY_ID "Midi interface not found\n"));
253 return B_ERROR;
254
255got_one:
256
257 for (i=0; i<sizeof(usb_knowndevs)/sizeof(struct usb_knowndev) ; i++) {
258 if (usb_knowndevs[i].vendor == dev_desc->vendor_id
259 && usb_knowndevs[i].product == dev_desc->product_id) {
260 DPRINTF_INFO ((MY_ID "vendor %s, product %s\n",
261 usb_knowndevs[i].vendorname, usb_knowndevs[i].productname));
262 }
263 }
264
265 if(alt && (st = usb->set_alt_interface(dev, intf)) != B_OK) {
266 DPRINTF_ERR ((MY_ID "set_alt_interface() failed %d\n", (int)st));
267 return B_ERROR;
268 }
269
270 /* configuration */
271 if ((st = usb->set_configuration (dev, conf)) != B_OK) {
272 DPRINTF_ERR ((MY_ID "set_configuration() failed %d\n", (int)st));
273 return B_ERROR;
274 }
275
276 if ((my_dev = create_device (dev, intf, ifno)) == NULL) {
277 return B_ERROR;
278 }
279
280 DPRINTF_INFO ((MY_ID "my_dev = %p interface endpoint = %p\n",
281 my_dev, &intf->endpoint [0]));
282 st = usb->queue_request(dev,
283 USB_REQTYPE_ENDPOINT_OUT | USB_REQTYPE_STANDARD,
284 USB_REQUEST_CLEAR_FEATURE,
285 USB_FEATURE_ENDPOINT_HALT, 1, my_dev->total_report_size,
286 my_dev->buffer, my_dev->total_report_size, midi_usb_callback, my_dev);
287 if (st != B_OK) {
288 DPRINTF_ERR ((MY_ID "queue_request() error %d\n", (int)st));
289 return B_ERROR;
290 }
291
292 my_dev->timestamp = system_time ();
293
294 DPRINTF_INFO ((MY_ID "queueing bulk xfer ep 0\n"));
295 /* issue bulk transfer */
296 my_dev->ept = &intf->endpoint [0]; /* interrupt IN */
297/* DPRINTF_INFO ((MY_ID "my_dev = %p endpoint = %p interface endpoint = %p\n",*/
298/* my_dev, my_dev->ept, &intf->endpoint [0]));*/
299 st = usb->queue_bulk (my_dev->ept->handle, my_dev->buffer,
300 my_dev->total_report_size, midi_usb_callback, my_dev);
301 if (st != B_OK) {
302 DPRINTF_ERR ((MY_ID "queue_bulk() error %d\n", (int)st));
303 return B_ERROR;
304 }
305
306 /* create a port */
307 add_device_info (my_dev);
308
309 *cookie = my_dev;
310 DPRINTF_INFO ((MY_ID "added %s\n", my_dev->name));
311
312 return B_OK;
313}
314
315static status_t
316usb_midi_removed (void *cookie)
317{
318 my_device_info *my_dev = cookie;
319
320 assert (cookie != NULL);
321
322 DPRINTF_INFO ((MY_ID "device_removed(%s)\n", my_dev->name));
323 usb->cancel_queued_transfers (my_dev->ept->handle);
324 remove_device_info (my_dev);
325 if (my_dev->open == 0) {
326 remove_device (my_dev);
327 } else {
328 DPRINTF_INFO ((MY_ID "%s still open\n", my_dev->name));
329 my_dev->active = false;
330 }
331 return B_OK;
332}
333
334static usb_notify_hooks my_notify_hooks =
335{
336 usb_midi_added, usb_midi_removed
337};
338
339#define SUPPORTED_DEVICES 1
340usb_support_descriptor my_supported_devices [SUPPORTED_DEVICES] =
341{
342 { USB_AUDIO_DEVICE_CLASS, USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS, 0, 0, 0 },
343};
344
345
346/* ----------
347 usb_midi_open - handle open() calls
348----- */
349
350static status_t
351usb_midi_open(const char *name, uint32 flags,
352 driver_cookie **out_cookie)
353{
354 driver_cookie *cookie;
355 my_device_info *my_dev;
356
357 assert (name != NULL);
358 assert (out_cookie != NULL);
359 DPRINTF_INFO ((MY_ID "open(%s)\n", name));
360
361 if ((my_dev = search_device_info (name)) == NULL)
362 return B_ENTRY_NOT_FOUND;
363 if ((cookie = malloc (sizeof (driver_cookie))) == NULL)
364 return B_NO_MEMORY;
365
366 acquire_sem (my_dev->sem_lock);
367 cookie->my_dev = my_dev;
368 cookie->next = my_dev->open_fds;
369 my_dev->open_fds = cookie;
370 my_dev->open++;
371 release_sem (my_dev->sem_lock);
372
373 *out_cookie = cookie;
374 DPRINTF_INFO ((MY_ID "device %s open (%d)\n", name, my_dev->open));
375 return B_OK;
376}
377
378
379/* ----------
380 usb_midi_read - handle read() calls
381----- */
382
383static status_t
384usb_midi_read(driver_cookie *cookie, off_t position,
385 void *buf, size_t *num_bytes)
386{
387 status_t err = B_ERROR;
388 my_device_info *my_dev;
389
390 assert (cookie != NULL);
391 my_dev = cookie->my_dev;
392 assert (my_dev != NULL);
393
394 if (!my_dev->active)
395 return B_ERROR; /* already unplugged */
396
397 err = acquire_sem_etc(my_dev->sem_cb, 1, B_CAN_INTERRUPT, 0LL);
398 if (err != B_OK)
399 return err;
400 acquire_sem (my_dev->sem_lock);
401 ring_buffer_user_read(my_dev->rbuf, buf, 1);
402 release_sem (my_dev->sem_lock);
403 *num_bytes = 1;
404 return err;
405}
406
407
408/* ----------
409 usb_midi_write - handle write() calls
410----- */
411
412static status_t
413usb_midi_write(driver_cookie *cookie, off_t position,
414 const void *buf, size_t *num_bytes)
415{
416 return B_ERROR;
417}
418
419/* ----------
420 usb_midi_control - handle ioctl calls
421----- */
422
423static status_t
424usb_midi_control(void * cookie, uint32 iop,
425 void * data, size_t len)
426{
427 return B_ERROR;
428}
429
430/* ----------
431 usb_midi_close - handle close() calls
432----- */
433
434static status_t
435usb_midi_close(driver_cookie *cookie)
436{
437 my_device_info *my_dev;
438
439 assert (cookie != NULL && cookie->my_dev != NULL);
440 my_dev = cookie->my_dev;
441 DPRINTF_INFO ((MY_ID "close(%s)\n", my_dev->name));
442
443 /* detach the cookie from list */
444
445 acquire_sem (my_dev->sem_lock);
446 if (my_dev->open_fds == cookie)
447 my_dev->open_fds = cookie->next;
448 else {
449 driver_cookie *p;
450 for (p = my_dev->open_fds; p != NULL; p = p->next) {
451 if (p->next == cookie) {
452 p->next = cookie->next;
453 break;
454 }
455 }
456 }
457 --my_dev->open;
458 release_sem (my_dev->sem_lock);
459
460 return B_OK;
461}
462
463
464/* -----
465 usb_midi_free - called after the last device is closed, and after
466 all i/o is complete.
467----- */
468static status_t
469usb_midi_free(driver_cookie *cookie)
470{
471 my_device_info *my_dev;
472
473 assert (cookie != NULL && cookie->my_dev != NULL);
474 my_dev = cookie->my_dev;
475 DPRINTF_INFO ((MY_ID "free(%s)\n", my_dev->name));
476
477 free (cookie);
478 if (my_dev->open > 0)
479 DPRINTF_INFO ((MY_ID "%d opens left\n", my_dev->open));
480 else if (!my_dev->active) {
481 DPRINTF_INFO ((MY_ID "removed %s\n", my_dev->name));
482 remove_device (my_dev);
483 }
484
485 return B_OK;
486}
487
488
489/* -----
490 function pointers for the device hooks entry points
491----- */
492
493static device_hooks usb_midi_hooks = {
494 (device_open_hook) usb_midi_open,
495 (device_close_hook) usb_midi_close,
496 (device_free_hook) usb_midi_free,
497 (device_control_hook) usb_midi_control,
498 (device_read_hook) usb_midi_read,
499 (device_write_hook) usb_midi_write,
500 NULL, NULL, NULL, NULL
501};
502
503
504/* ----------
505 init_hardware - called once the first time the driver is loaded
506----- */
507_EXPORT status_t
508init_hardware (void)
509{
510 DPRINTF_INFO ((MY_ID "init_hardware() " __DATE__ " " __TIME__ "\n"));
511 return B_OK;
512}
513
514/* ----------
515 init_driver - optional function - called every time the driver
516 is loaded.
517----- */
518_EXPORT status_t
519init_driver (void)
520{
521
522 DPRINTF_INFO ((MY_ID "init_driver() " __DATE__ " " __TIME__ "\n"));
523
524 if (get_module (B_USB_MODULE_NAME, (module_info **) &usb) != B_OK)
525 return B_ERROR;
526
527 if ((my_device_list_lock = create_sem (1, "dev_list_lock")) < 0) {
528 put_module (B_USB_MODULE_NAME);
529 return my_device_list_lock; /* error code */
530 }
531
532 usb->register_driver (usb_midi_driver_name, my_supported_devices,
533 SUPPORTED_DEVICES, NULL);
534 usb->install_notify (usb_midi_driver_name, &my_notify_hooks);
535 DPRINTF_INFO ((MY_ID "init_driver() OK\n"));
536
537 return B_OK;
538}
539
540
541/* ----------
542 uninit_driver - optional function - called every time the driver
543 is unloaded
544----- */
545_EXPORT void
546uninit_driver (void)
547{
548 DPRINTF_INFO ((MY_ID "uninit_driver()\n"));
549 usb->uninstall_notify (usb_midi_driver_name);
550
551 delete_sem(my_device_list_lock);
552 put_module(B_USB_MODULE_NAME);
553 free_device_names();
554}
555
556/*
557 publish_devices
558 device names are generated dynamically
559*/
560
561_EXPORT const char **
562publish_devices (void)
563{
564 DPRINTF_INFO ((MY_ID "publish_devices()\n"));
565
566 if (my_device_list_changed) {
567 free_device_names ();
568 alloc_device_names ();
569 if (my_device_names != NULL)
570 rebuild_device_names ();
571 my_device_list_changed = false;
572 }
573 assert (my_device_names != NULL);
574 return (const char **) my_device_names;
575}
576
577/* ----------
578 find_device - return ptr to device hooks structure for a
579 given device name
580----- */
581
582_EXPORT device_hooks *
583find_device(const char *name)
584{
585 assert (name != NULL);
586 DPRINTF_INFO ((MY_ID "find_device(%s)\n", name));
587 if (search_device_info(name) == NULL)
588 return NULL;
589 return &usb_midi_hooks;
590}
591