1 /*
2 * midi usb driver
3 * usb_midi.c
4 *
5 * Copyright 2006-2013 Haiku Inc. All rights reserved.
6 * Distributed under the terms of the MIT Licence.
7 *
8 * Authors:
9 * Jérôme Duval
10 * Pete Goodeve, pete.goodeve@computer.org
11 *
12 * Some portions of this code were originally derived from
13 * USB Joystick driver for BeOS R5
14 * Copyright 2000 (C) ITO, Takayuki
15 * All rights reserved
16 *
17 */
18
19
20 /* #define DEBUG 1 */ /* Define this to enable DPRINTF_DEBUG statements */
21 /* (Other categories of printout set in usb_midi.h) */
22
23 #include "usb_midi.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30
31 const char* midi_base_name = "midi/usb/";
32
33
34 usbmidi_port_info*
create_usbmidi_port(usbmidi_device_info * devinfo,int cable,bool has_in,bool has_out)35 create_usbmidi_port(usbmidi_device_info* devinfo,
36 int cable, bool has_in, bool has_out)
37 {
38 usbmidi_port_info* port = NULL;
39 assert(usb != NULL && devinfo != NULL);
40
41 port = (usbmidi_port_info*)malloc(sizeof(usbmidi_port_info));
42 if (port == NULL)
43 return NULL;
44
45 sprintf(port->name, "%s-%d", devinfo->name, cable);
46 port->device = devinfo;
47 port->cable = cable;
48 port->next = NULL;
49 port->open = 0;
50 port->open_fd = NULL;
51 port->has_in = has_in;
52 port->has_out = has_out;
53 port->rbuf = create_ring_buffer(1024);
54
55 devinfo->ports[cable] = port;
56
57 DPRINTF_INFO((MY_ID "Created port %p cable %d: %s\n",
58 port, cable, port->name));
59
60 return port;
61 }
62
63
64 void
remove_port(usbmidi_port_info * port)65 remove_port(usbmidi_port_info* port)
66 {
67 assert(port != NULL);
68 if (port->rbuf != NULL) {
69 delete_ring_buffer(port->rbuf);
70 port->rbuf = NULL;
71 }
72 DPRINTF_INFO((MY_ID "remove_port %p done\n", port));
73
74 free(port);
75 }
76
77
78 usbmidi_device_info*
create_device(const usb_device * dev,uint16 ifno)79 create_device(const usb_device* dev, uint16 ifno)
80 {
81 usbmidi_device_info* midiDevice = NULL;
82 int number;
83 area_id area;
84 sem_id sem;
85 char area_name[32];
86
87 assert(usb != NULL && dev != NULL);
88
89 number = find_free_device_number();
90
91 midiDevice = (usbmidi_device_info*)malloc(sizeof(usbmidi_device_info));
92 if (midiDevice == NULL)
93 return NULL;
94
95 midiDevice->sem_lock = sem = create_sem(1, DRIVER_NAME "_lock");
96 if (sem < 0) {
97 DPRINTF_ERR((MY_ID "create_sem() failed 0x%" B_PRIx32 "\n", sem));
98 free(midiDevice);
99 return NULL;
100 }
101
102 sprintf(area_name, DRIVER_NAME "_buffer%d", number);
103 midiDevice->buffer_area = area = create_area(area_name,
104 (void**)&midiDevice->buffer, B_ANY_KERNEL_ADDRESS,
105 B_PAGE_SIZE, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
106 if (area < 0) {
107 DPRINTF_ERR((MY_ID "create_area() failed 0x%" B_PRIx32 "\n", area));
108 delete_sem(midiDevice->sem_lock);
109 free(midiDevice);
110 return NULL;
111 }
112 /* use half of reserved area for each of in and out buffers: */
113 midiDevice->out_buffer =
114 (usb_midi_event_packet*)((uint8*)midiDevice->buffer + B_PAGE_SIZE / 2);
115 midiDevice->sem_send = sem = create_sem(1, DRIVER_NAME "_send");
116 if (sem < 0) {
117 DPRINTF_ERR((MY_ID "create_sem() failed 0x%" B_PRIx32 "\n", sem));
118 delete_sem(midiDevice->sem_lock);
119 delete_area(area);
120 free(midiDevice);
121 return NULL;
122 }
123 {
124 int32 bc;
125 get_sem_count(sem, &bc);
126 DPRINTF_DEBUG((MY_ID "Allocated %" B_PRId32 " write buffers\n", bc));
127 }
128
129
130 sprintf(midiDevice->name, "%s%d", midi_base_name, number);
131 midiDevice->dev = dev;
132 midiDevice->devnum = number;
133 midiDevice->ifno = ifno;
134 midiDevice->active = true;
135 midiDevice->flags = 0;
136 memset(midiDevice->ports, 0, sizeof(midiDevice->ports));
137 midiDevice->inMaxPkt = midiDevice->outMaxPkt = B_PAGE_SIZE / 2;
138 /* Initially -- will get reduced */
139 DPRINTF_INFO((MY_ID "Created device %p\n", midiDevice));
140
141 return midiDevice;
142 }
143
144
145 void
remove_device(usbmidi_device_info * midiDevice)146 remove_device(usbmidi_device_info* midiDevice)
147 {
148 assert(midiDevice != NULL);
149 DPRINTF_INFO((MY_ID "remove_device %p\n", midiDevice));
150
151 delete_area(midiDevice->buffer_area);
152 delete_sem(midiDevice->sem_lock);
153 delete_sem(midiDevice->sem_send);
154 free(midiDevice);
155 }
156
157
158 /* driver cookie (per open -- but only one open per port allowed!) */
159
160 typedef struct driver_cookie
161 {
162 struct driver_cookie* next;
163 usbmidi_device_info* device; /* a bit redundant, but convenient */
164 usbmidi_port_info* port;
165 sem_id sem_cb;
166 } driver_cookie;
167
168
169 /* NB global variables are valid only while driver is loaded */
170
171 _EXPORT int32 api_version = B_CUR_DRIVER_API_VERSION;
172
173 static const char* usb_midi_driver_name = "usb_midi";
174
175 static const int CINbytes[] = { /* See USB-MIDI Spec */
176 0, /* 0x0 -- undefined Misc -- Reserved */
177 0, /* 0x1 -- undefined Cable -- Reserved */
178 2, /* 0x2 -- 2-byte system common */
179 3, /* 0x3 -- 3-byte system common */
180 3, /* 0x4 -- SysEx start/continue */
181 1, /* 0x5 -- SysEx single-byte-end, or 1-byte Common */
182 2, /* 0x6 -- SysEx two-byte-end */
183 3, /* 0x7 -- SysEx three-byte-end */
184 3, /* 0x8 -- Note Off */
185 3, /* 0x9 -- Note On */
186 3, /* 0xA -- Poly KeyPress*/
187 3, /* 0xB -- Control Change */
188 2, /* 0xC -- Program Change */
189 2, /* 0xD -- Channel Pressure */
190 3, /* 0xE -- Pitch Bend */
191 1, /* 0xF -- Single Byte */
192 };
193
194 usb_module_info* usb;
195
196
197 static void
interpret_midi_buffer(usbmidi_device_info * midiDevice)198 interpret_midi_buffer(usbmidi_device_info* midiDevice)
199 {
200 usb_midi_event_packet* packet = midiDevice->buffer;
201 size_t bytes_left = midiDevice->actual_length;
202
203 /* buffer may have several packets */
204 while (bytes_left >= sizeof(usb_midi_event_packet)) {
205 DPRINTF_DEBUG((MY_ID "received packet %x:%d %x %x %x\n",
206 packet->cin, packet->cn,
207 packet->midi[0], packet->midi[1], packet->midi[2]));
208
209 int pktlen = CINbytes[packet->cin];
210 usbmidi_port_info* port = midiDevice->ports[packet->cn];
211
212 /* port matching 'cable number' */
213 if (port == NULL) {
214 DPRINTF_ERR((MY_ID "no port matching cable number %d!\n",
215 packet->cn));
216 } else if (port->open_fd == NULL) {
217 DPRINTF_ERR((MY_ID "received data for port %d but it is closed!\n",
218 packet->cn));
219 } else {
220 ring_buffer_write(port->rbuf, packet->midi, pktlen);
221 release_sem_etc(port->open_fd->sem_cb, pktlen,
222 B_DO_NOT_RESCHEDULE);
223 }
224
225 packet++;
226 bytes_left -= sizeof(usb_midi_event_packet);
227 }
228 }
229
230
231 /*
232 callback: got a report, issue next request
233 */
234
235 static void
midi_usb_read_callback(void * cookie,status_t status,void * data,size_t actual_len)236 midi_usb_read_callback(void* cookie, status_t status,
237 void* data, size_t actual_len)
238 {
239 status_t st;
240 usbmidi_device_info* midiDevice = (usbmidi_device_info*)cookie;
241
242 assert(cookie != NULL);
243 if (actual_len > 0) {
244 DPRINTF_DEBUG((MY_ID "midi_usb_read_callback() -- packet length %ld\n",
245 actual_len));
246 }
247
248 acquire_sem(midiDevice->sem_lock);
249 midiDevice->actual_length = actual_len;
250 midiDevice->bus_status = status; /* B_USB_STATUS_* */
251 if (status != B_OK) {
252 /* request failed */
253 DPRINTF_DEBUG((MY_ID "bus status 0x%" B_PRIx32 "\n", status));
254 if (status == B_CANCELED || !midiDevice->active) {
255 /* cancelled: device is unplugged */
256 DPRINTF_DEBUG((MY_ID "midi_usb_read_callback: cancelled"
257 "(status=%" B_PRIx32 " active=%d -- deleting sem_cbs\n",
258 status, midiDevice->active));
259
260 // Free any read() still blocked on semaphore
261 for (int cable = 0; cable < 16; cable++) {
262 usbmidi_port_info* port = midiDevice->ports[cable];
263 if (port == NULL)
264 break;
265 if (port->open_fd != NULL)
266 delete_sem(port->open_fd->sem_cb);
267 }
268 release_sem(midiDevice->sem_lock);
269 return;
270 }
271 release_sem(midiDevice->sem_lock);
272 } else {
273 /* got a report */
274 midiDevice->timestamp = system_time(); /* not used... */
275
276 interpret_midi_buffer(midiDevice);
277 release_sem(midiDevice->sem_lock);
278 }
279
280 /* issue next request */
281 st = usb->queue_bulk(midiDevice->ept_in->handle,
282 midiDevice->buffer, midiDevice->inMaxPkt,
283 (usb_callback_func)midi_usb_read_callback, midiDevice);
284 if (st != B_OK) {
285 /* probably endpoint stall */
286 DPRINTF_ERR((MY_ID "queue_bulk() error 0x%" B_PRIx32 "\n", st));
287 }
288 }
289
290
291 static void
midi_usb_write_callback(void * cookie,status_t status,void * data,size_t actual_len)292 midi_usb_write_callback(void* cookie, status_t status,
293 void* data, size_t actual_len)
294 {
295 usbmidi_device_info* midiDevice = (usbmidi_device_info*)cookie;
296 #ifdef DEBUG
297 usb_midi_event_packet* pkt = (usb_midi_event_packet*)data;
298 #endif
299
300 assert(cookie != NULL);
301 DPRINTF_DEBUG((MY_ID "midi_usb_write_callback()"
302 " status %" B_PRId32 " length %" B_PRIuSIZE " pkt %p cin %x\n",
303 status, actual_len, pkt, pkt->cin));
304 release_sem(midiDevice->sem_send); /* done with buffer */
305 }
306
307
308 /*
309 USB specific device hooks
310 */
311
312 static status_t
usb_midi_added(const usb_device * dev,void ** cookie)313 usb_midi_added(const usb_device* dev, void** cookie)
314 {
315 /* This seems overcomplicated, but endpoints can be in either order...
316 and could possibly have different number of connectors! */
317 int in_cables = 0, out_cables = 0;
318 int cable_count[2] = {0, 0};
319 int iep = 0;
320 status_t status;
321
322 assert(dev != NULL && cookie != NULL);
323 DPRINTF_INFO((MY_ID "usb_midi_added(%p, %p)\n", dev, cookie));
324
325 const usb_device_descriptor* dev_desc = usb->get_device_descriptor(dev);
326
327 DPRINTF_INFO((MY_ID "vendor ID 0x%04X, product ID 0x%04X\n",
328 dev_desc->vendor_id, dev_desc->product_id));
329
330 /* check interface class */
331 const usb_configuration_info* conf;
332 if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION))
333 == NULL) {
334 DPRINTF_ERR((MY_ID "cannot get default configuration\n"));
335 return B_ERROR;
336 }
337 DPRINTF_INFO((MY_ID "Interface count = %ld\n", conf->interface_count));
338
339 uint16 alt, ifno;
340 const usb_interface_info* intf;
341 for (ifno = 0; ifno < conf->interface_count; ifno++) {
342 int devclass, subclass, protocol;
343
344 for (alt = 0; alt < conf->interface[ifno].alt_count; alt++) {
345 intf = &conf->interface[ifno].alt[alt];
346 devclass = intf->descr->interface_class;
347 subclass = intf->descr->interface_subclass;
348 protocol = intf->descr->interface_protocol;
349 DPRINTF_INFO((
350 MY_ID "interface %d, alt : %d: class %d,"
351 " subclass %d, protocol %d\n",
352 ifno, alt, devclass, subclass, protocol));
353
354 if (devclass == USB_AUDIO_DEVICE_CLASS
355 && subclass == USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS)
356 goto got_one;
357 }
358 }
359
360 DPRINTF_INFO((MY_ID "Midi interface not found\n"));
361 return B_ERROR;
362
363 got_one:
364
365 if ((status = usb->set_configuration(dev, conf)) != B_OK) {
366 DPRINTF_ERR((MY_ID "set_configuration() failed 0x%" B_PRIx32 "\n",
367 status));
368 return B_ERROR;
369 }
370
371 usbmidi_device_info* midiDevice;
372 if ((midiDevice = create_device(dev, ifno)) == NULL) {
373 return B_ERROR;
374 }
375
376 /* get the actual number of ports in and out */
377 for (uint16 i = 0; i < intf->generic_count; i++) {
378 usb_generic_descriptor *generic = &intf->generic[i]->generic;
379 DPRINTF_DEBUG((MY_ID "descriptor %d: type %x sub %x\n",
380 i, generic->descriptor_type, generic->data[0]));
381 if (generic->descriptor_type == USB_DESCRIPTOR_CS_ENDPOINT
382 && generic->data[0] == USB_MS_GENERAL_DESCRIPTOR) {
383 /* These *better* be in the same order as the endpoints! */
384 cable_count[iep] = generic->data[1];
385 iep = 1;
386 }
387 }
388
389 DPRINTF_DEBUG((MY_ID "midiDevice = %p endpoint count = %ld\n",
390 midiDevice, intf->endpoint_count));
391 midiDevice->ept_in = midiDevice->ept_out = NULL;
392
393 for (uint16 i = 0; i < intf->endpoint_count && i < 2; i++) {
394 /* we are actually assuming max one IN, one OUT endpoint... */
395 DPRINTF_INFO((MY_ID "endpoint %d = %p %s maxPkt=%d\n",
396 i, &intf->endpoint[i],
397 (intf->endpoint[i].descr->endpoint_address & 0x80) != 0
398 ? "IN" : "OUT", intf->endpoint[i].descr->max_packet_size));
399 if ((intf->endpoint[i].descr->endpoint_address & 0x80) != 0) {
400 if (midiDevice->ept_in == NULL) {
401 midiDevice->ept_in = &intf->endpoint[i];
402 in_cables = cable_count[i];
403 if (intf->endpoint[i].descr->max_packet_size
404 < midiDevice->inMaxPkt)
405 midiDevice->inMaxPkt = intf->endpoint[i].descr->max_packet_size;
406 }
407 } else {
408 if (midiDevice->ept_out == NULL) {
409 midiDevice->ept_out = &intf->endpoint[i];
410 out_cables = cable_count[i];
411 if (intf->endpoint[i].descr->max_packet_size
412 < midiDevice->outMaxPkt)
413 midiDevice->outMaxPkt = intf->endpoint[i].descr->max_packet_size;
414 }
415 }
416 }
417
418 midiDevice->timestamp = system_time(); /* This never seems to be used */
419
420 /* Create the actual device ports */
421 usbmidi_port_info* port;
422 for (uint16 i = 0; in_cables > 0 || out_cables > 0; i++) {
423 port = create_usbmidi_port(midiDevice, i,
424 (bool)in_cables, (bool)out_cables);
425 midiDevice->ports[i] = port;
426 if (in_cables)
427 in_cables--;
428 if (out_cables)
429 out_cables--;
430 add_port_info(port);
431 }
432
433 /* issue bulk transfer */
434 if (midiDevice->ept_in != NULL) {
435 DPRINTF_DEBUG((MY_ID "queueing bulk xfer IN endpoint\n"));
436 status = usb->queue_bulk(midiDevice->ept_in->handle, midiDevice->buffer,
437 midiDevice->inMaxPkt,
438 (usb_callback_func)midi_usb_read_callback, midiDevice);
439 if (status != B_OK) {
440 DPRINTF_ERR((MY_ID "queue_bulk() error 0x%" B_PRIx32 "\n", status));
441 return B_ERROR;
442 }
443 }
444
445 *cookie = midiDevice;
446 DPRINTF_INFO((MY_ID "usb_midi_added: %s\n", midiDevice->name));
447
448 return B_OK;
449 }
450
451
452 static status_t
usb_midi_removed(void * cookie)453 usb_midi_removed(void* cookie)
454 {
455 usbmidi_device_info* midiDevice = (usbmidi_device_info*)cookie;
456
457 assert(cookie != NULL);
458
459 DPRINTF_INFO((MY_ID "usb_midi_removed(%s)\n", midiDevice->name));
460 midiDevice->active = false;
461 for (int cable = 0; cable < 16; cable++) {
462 usbmidi_port_info* port = midiDevice->ports[cable];
463 if (port == NULL)
464 break;
465 midiDevice->ports[cable] = NULL;
466 DPRINTF_DEBUG((MY_ID "removing port %d\n", cable));
467 remove_port_info(port);
468 if (port->open_fd != NULL) {
469 port->open_fd->port = NULL;
470 port->open_fd->device = NULL;
471 delete_sem(port->open_fd->sem_cb);
472 /* done here to ensure read is freed */
473 }
474 remove_port(port);
475 }
476
477 if (midiDevice->ept_in != NULL)
478 usb->cancel_queued_transfers(midiDevice->ept_in->handle);
479 if (midiDevice->ept_out != NULL)
480 usb->cancel_queued_transfers(midiDevice->ept_out->handle);
481 DPRINTF_DEBUG((MY_ID "usb_midi_removed: doing remove: %s\n",
482 midiDevice->name));
483 remove_device(midiDevice);
484 return B_OK;
485 }
486
487
488 static usb_notify_hooks my_notify_hooks =
489 {
490 usb_midi_added, usb_midi_removed
491 };
492
493 #define SUPPORTED_DEVICES 1
494 usb_support_descriptor my_supported_devices[SUPPORTED_DEVICES] =
495 {
496 {
497 USB_AUDIO_DEVICE_CLASS,
498 USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS,
499 0, 0, 0
500 },
501 };
502
503
504 /*
505 Device Driver Hook Functions
506 -- open, read, write, close, and free
507 */
508
509 static status_t
usb_midi_open(const char * name,uint32 flags,driver_cookie ** out_cookie)510 usb_midi_open(const char* name, uint32 flags,
511 driver_cookie** out_cookie)
512 {
513 driver_cookie* cookie;
514 usbmidi_port_info* port;
515 int mode = flags & O_RWMASK;
516
517 assert(name != NULL);
518 assert(out_cookie != NULL);
519 DPRINTF_INFO((MY_ID "usb_midi_open(%s) flags=%" B_PRIx32 "\n", name,
520 flags));
521
522 if ((port = search_port_info(name)) == NULL)
523 return B_ENTRY_NOT_FOUND;
524
525 if (!port->has_in && mode != O_WRONLY)
526 return B_PERMISSION_DENIED; /* == EACCES */
527 else if (!port->has_out && mode != O_RDONLY)
528 return B_PERMISSION_DENIED;
529
530 if ((cookie = (driver_cookie*)malloc(sizeof(driver_cookie))) == NULL)
531 return B_NO_MEMORY;
532
533 cookie->sem_cb = create_sem(0, DRIVER_NAME "_cb");
534 if (cookie->sem_cb < 0) {
535 DPRINTF_ERR((MY_ID "create_sem() failed 0x%" B_PRIx32 "\n",
536 cookie->sem_cb));
537 free(cookie);
538 return B_ERROR;
539 }
540
541 cookie->port = port;
542 cookie->device = port->device;
543
544 acquire_sem(usbmidi_port_list_lock);
545 if (port->open_fd != NULL) {
546 /* there can only be one open channel to the device */
547 delete_sem(cookie->sem_cb);
548 free(cookie);
549 release_sem(usbmidi_port_list_lock);
550 return B_BUSY;
551 }
552 port->open_fd = cookie;
553 port->open++;
554 release_sem(usbmidi_port_list_lock);
555
556 *out_cookie = cookie;
557 DPRINTF_INFO((MY_ID "usb_midi_open: device %s open (%d)\n",
558 name, port->open));
559 return B_OK;
560 }
561
562
563 static status_t
usb_midi_read(driver_cookie * cookie,off_t position,void * buf,size_t * num_bytes)564 usb_midi_read(driver_cookie* cookie, off_t position,
565 void* buf, size_t* num_bytes)
566 {
567 assert(cookie != NULL);
568 status_t err = B_ERROR;
569 usbmidi_port_info* port = cookie->port;
570 usbmidi_device_info* midiDevice = cookie->device;
571
572 if (midiDevice == NULL || !midiDevice->active)
573 return B_ERROR; /* already unplugged */
574
575 DPRINTF_DEBUG((MY_ID "usb_midi_read: (%" B_PRIuSIZE " byte buffer at %"
576 B_PRIdOFF " cookie %p)\n", *num_bytes, position, cookie));
577 while (midiDevice && midiDevice->active) {
578 ZDPRINTF_DEBUG((MY_ID "waiting on acquire_sem_etc\n"));
579 err = acquire_sem_etc(cookie->sem_cb, 1,
580 B_RELATIVE_TIMEOUT, 1000000);
581 if (err == B_TIMED_OUT) {
582 ZDPRINTF_DEBUG((MY_ID "acquire_sem_etc timed out\n"));
583 continue; /* see if we're still active */
584 }
585 if (err != B_OK) {
586 *num_bytes = 0;
587 DPRINTF_DEBUG((MY_ID "acquire_sem_etc aborted\n"));
588 break;
589 }
590 DPRINTF_DEBUG((MY_ID "reading from ringbuffer\n"));
591 acquire_sem(midiDevice->sem_lock);
592 /* a global semaphore -- OK, I think */
593 ring_buffer_user_read(port->rbuf, (uint8*)buf, 1);
594 release_sem(midiDevice->sem_lock);
595 *num_bytes = 1;
596 DPRINTF_DEBUG((MY_ID "read byte %x -- cookie %p)\n",
597 *(uint8*)buf, cookie));
598 return B_OK;
599 }
600 DPRINTF_INFO((MY_ID "usb_midi_read: loop terminated"
601 " -- Device no longer active\n"));
602 return B_CANCELED;
603 }
604
605
606 const uint8 CINcode[] = { /* see USB-MIDI Spec */
607 0x4, /* 0x0 - sysex start */
608 0, /* 0x1 -- undefined */
609 0x3, /* 0x2 -- song pos */
610 0x2, /* 0x3 -- song select */
611 0, /* 0x4 -- undefined */
612 0, /* 0x5 -- undefined */
613 0x2, /* 0x6 -- tune request */
614 0x5, /* 0x7 -- sysex end */
615 0x5, /* 0x8 -- clock */
616 0, /* 0x9 -- undefined */
617 0x5, /* 0xA -- start */
618 0x5, /* 0xB -- continue */
619 0x5, /* 0xC -- stop */
620 0, /* 0xD -- undefined */
621 0x5, /* 0xE -- active sensing */
622 0x5, /* 0x0 -- system reset */
623 };
624
625
626 static status_t
usb_midi_write(driver_cookie * cookie,off_t position,const void * buf,size_t * num_bytes)627 usb_midi_write(driver_cookie* cookie, off_t position,
628 const void* buf, size_t* num_bytes)
629 {
630 uint8* midiseq = (uint8*)buf;
631 uint8 midicode; /* preserved for reference */
632 if (user_memcpy(&midicode, midiseq, sizeof(midicode)) != B_OK)
633 return B_BAD_ADDRESS;
634 status_t status;
635 size_t buff_lim;
636 uint8 cin = ((midicode & 0xF0) == 0xF0) ? CINcode[midicode & 0x0F]
637 : (midicode >> 4);
638
639 assert(cookie != NULL);
640 usbmidi_port_info* port = cookie->port;
641 usbmidi_device_info* midiDevice = cookie->device;
642
643 if (!midiDevice || !midiDevice->active)
644 return B_ERROR; /* already unplugged */
645
646 buff_lim = midiDevice->outMaxPkt * 3 / 4;
647 /* max MIDI bytes buffer space */
648
649 DPRINTF_DEBUG((MY_ID "MIDI write (%" B_PRIuSIZE " bytes at %" B_PRIdOFF
650 ")\n", *num_bytes, position));
651 // Make sure we always write exactly one MIDI event at a time.
652 // SysEx can be of arbitrary sizes, for all others, we check that the
653 // complete event was passed in and do not use more than that.
654 // TODO add a loop to allow writing multiple events in a single write()
655 // call if desired.
656 if (midicode != 0xF0) {
657 if ((int)*num_bytes < CINbytes[cin]) {
658 DPRINTF_ERR((MY_ID "Expected %d bytes for MIDI command %" B_PRIu8
659 " but got only %" B_PRIuSIZE ".\n", CINbytes[cin], cin,
660 *num_bytes));
661 return B_BAD_DATA;
662 }
663 *num_bytes = CINbytes[cin];
664 }
665
666 size_t bytes_left = *num_bytes;
667 while (bytes_left) {
668 size_t xfer_bytes = (bytes_left < buff_lim) ? bytes_left : buff_lim;
669 usb_midi_event_packet* pkt = midiDevice->out_buffer;
670 int packet_count = 0;
671
672 status = acquire_sem_etc(midiDevice->sem_send,
673 1, B_RELATIVE_TIMEOUT, 2000000LL);
674 if (status != B_OK)
675 return status;
676
677 while (xfer_bytes) {
678 uint8 pkt_bytes = CINbytes[cin];
679 memset(pkt, 0, sizeof(usb_midi_event_packet));
680 pkt->cin = cin;
681 pkt->cn = port->cable;
682 DPRINTF_DEBUG((MY_ID "using packet data (code %x -- %d bytes)"
683 " %x %x %x\n", pkt->cin, CINbytes[pkt->cin],
684 midiseq[0], midiseq[1], midiseq[2]));
685 if (user_memcpy(pkt->midi, midiseq, pkt_bytes) != B_OK)
686 return B_BAD_ADDRESS;
687 DPRINTF_DEBUG((MY_ID "built packet %p %x:%d %x %x %x\n",
688 pkt, pkt->cin, pkt->cn,
689 pkt->midi[0], pkt->midi[1], pkt->midi[2]));
690 xfer_bytes -= pkt_bytes;
691 bytes_left -= pkt_bytes;
692 midiseq += pkt_bytes;
693 packet_count++;
694 pkt++;
695 if (midicode == 0xF0 && bytes_left < 4) cin = 4 + bytes_left;
696 /* see USB-MIDI Spec */
697 }
698 status = usb->queue_bulk(midiDevice->ept_out->handle,
699 midiDevice->out_buffer, sizeof(usb_midi_event_packet)
700 * packet_count, (usb_callback_func)midi_usb_write_callback,
701 midiDevice);
702 if (status != B_OK) {
703 DPRINTF_ERR((MY_ID "midi write queue_bulk() error 0x%" B_PRIx32
704 "\n", status));
705 return B_ERROR;
706 }
707 }
708 return B_OK;
709 }
710
711
712 static status_t
usb_midi_control(void * cookie,uint32 iop,void * data,size_t len)713 usb_midi_control(void* cookie, uint32 iop, void* data, size_t len)
714 {
715 return B_ERROR;
716 }
717
718
719 static status_t
usb_midi_close(driver_cookie * cookie)720 usb_midi_close(driver_cookie* cookie)
721 {
722 assert(cookie != NULL);
723 delete_sem(cookie->sem_cb);
724
725 usbmidi_port_info* port = cookie->port;
726 usbmidi_device_info* midiDevice = cookie->device;
727
728 DPRINTF_INFO((MY_ID "usb_midi_close(%p device=%p port=%p)\n",
729 cookie, midiDevice, port));
730
731 acquire_sem(usbmidi_port_list_lock);
732 if (port != NULL) {
733 /* detach the cookie from port */
734 port->open_fd = NULL;
735 --port->open;
736 }
737 release_sem(usbmidi_port_list_lock);
738 DPRINTF_DEBUG((MY_ID "usb_midi_close: complete\n"));
739
740 return B_OK;
741 }
742
743
744 static status_t
usb_midi_free(driver_cookie * cookie)745 usb_midi_free(driver_cookie* cookie)
746 {
747 assert(cookie != NULL);
748 usbmidi_device_info* midiDevice = cookie->device;
749 DPRINTF_INFO((MY_ID "usb_midi_free(%p device=%p)\n", cookie, midiDevice));
750
751 free(cookie);
752
753 return B_OK;
754 }
755
756
757 static device_hooks usb_midi_hooks = {
758 (device_open_hook)usb_midi_open,
759 (device_close_hook)usb_midi_close,
760 (device_free_hook)usb_midi_free,
761 (device_control_hook)usb_midi_control,
762 (device_read_hook)usb_midi_read,
763 (device_write_hook)usb_midi_write,
764 NULL, NULL, NULL, NULL
765 };
766
767
768 /*
769 Driver Registration
770 */
771
772 _EXPORT status_t
init_hardware(void)773 init_hardware(void)
774 {
775 DPRINTF_DEBUG((MY_ID "init_hardware() version:"
776 __DATE__ " " __TIME__ "\n"));
777 return B_OK;
778 }
779
780
781 _EXPORT status_t
init_driver(void)782 init_driver(void)
783 {
784 DPRINTF_INFO((MY_ID "init_driver() version:" __DATE__ " " __TIME__ "\n"));
785
786 if (get_module(B_USB_MODULE_NAME, (module_info**)&usb) != B_OK)
787 return B_ERROR;
788
789 if ((usbmidi_port_list_lock = create_sem(1, "dev_list_lock")) < 0) {
790 put_module(B_USB_MODULE_NAME);
791 return usbmidi_port_list_lock; /* error code */
792 }
793
794 usb->register_driver(usb_midi_driver_name, my_supported_devices,
795 SUPPORTED_DEVICES, NULL);
796 usb->install_notify(usb_midi_driver_name, &my_notify_hooks);
797 DPRINTF_INFO((MY_ID "init_driver() OK\n"));
798
799 return B_OK;
800 }
801
802
803 _EXPORT void
uninit_driver(void)804 uninit_driver(void)
805 {
806 DPRINTF_INFO((MY_ID "uninit_driver()\n"));
807 usb->uninstall_notify(usb_midi_driver_name);
808
809 delete_sem(usbmidi_port_list_lock);
810 put_module(B_USB_MODULE_NAME);
811 free_port_names();
812 DPRINTF_INFO((MY_ID "uninit complete\n"));
813 }
814
815
816 _EXPORT const char**
publish_devices(void)817 publish_devices(void)
818 {
819 DPRINTF_INFO((MY_ID "publish_devices()\n"));
820
821 if (usbmidi_port_list_changed) {
822 free_port_names();
823 alloc_port_names();
824 if (usbmidi_port_names != NULL)
825 rebuild_port_names();
826 usbmidi_port_list_changed = false;
827 }
828 assert(usbmidi_port_names != NULL);
829 return (const char**)usbmidi_port_names;
830 }
831
832
833 _EXPORT device_hooks*
find_device(const char * name)834 find_device(const char* name)
835 {
836 assert(name != NULL);
837 DPRINTF_INFO((MY_ID "find_device(%s)\n", name));
838 if (search_port_info(name) == NULL)
839 return NULL;
840 return &usb_midi_hooks;
841 }
842
843