xref: /haiku/src/add-ons/kernel/drivers/midi/usb_midi/usb_midi.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
1 /*
2  * midi usb driver
3  * usb_midi.c
4  *
5  * Copyright 2006-2011 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*
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
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*
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%lx\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_READ_AREA | B_WRITE_AREA);
106 	if (area < 0) {
107 		DPRINTF_ERR((MY_ID "create_area() failed 0x%lx\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%lx\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 %ld 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->buffer_size = B_PAGE_SIZE / 2;
138 	DPRINTF_INFO((MY_ID "Created device %p\n", midiDevice));
139 
140 	return midiDevice;
141 }
142 
143 
144 void
145 remove_device(usbmidi_device_info* midiDevice)
146 {
147 	assert(midiDevice != NULL);
148 	DPRINTF_INFO((MY_ID "remove_device %p\n", midiDevice));
149 
150 	delete_area(midiDevice->buffer_area);
151 	delete_sem(midiDevice->sem_lock);
152 	delete_sem(midiDevice->sem_send);
153 	free(midiDevice);
154 }
155 
156 
157 /* driver cookie (per open -- but only one open per port allowed!) */
158 
159 typedef struct driver_cookie
160 {
161 	struct driver_cookie*	next;
162 	usbmidi_device_info*	device;	/* a bit redundant, but convenient */
163 	usbmidi_port_info*		port;
164 	sem_id sem_cb;
165 } driver_cookie;
166 
167 
168 /* NB global variables are valid only while driver is loaded */
169 
170 _EXPORT int32	api_version = B_CUR_DRIVER_API_VERSION;
171 
172 const char* usb_midi_driver_name = "usb_midi";
173 
174 const int CINbytes[] = {	/* See USB-MIDI Spec */
175 	0,	/* 0x0 -- undefined Misc -- Reserved */
176 	0,	/* 0x1 -- undefined  Cable -- Reserved */
177 	2,	/* 0x2 -- 2-byte system common */
178 	3,	/* 0x3 -- 3-byte system common */
179 	3,	/* 0x4 -- SysEx start/continue */
180 	1,	/* 0x5 -- SysEx single-byte-end, or 1-byte Common */
181 	2,	/* 0x6 --  SysEx two-byte-end */
182 	3,	/* 0x7 --  SysEx three-byte-end */
183 	3,	/* 0x8 --  Note Off */
184 	3,	/* 0x9 --  Note On */
185 	3,	/* 0xA --  Poly KeyPress*/
186 	3,	/* 0xB --  Control Change */
187 	2,	/* 0xC --  Program Change */
188 	2,	/* 0xD --  Channel Pressure */
189 	3,	/* 0xE --  Pitch Bend */
190 	1,	/* 0xF --  Single Byte */
191 };
192 
193 usb_module_info* usb;
194 
195 
196 static void
197 interpret_midi_buffer(usbmidi_device_info* midiDevice)
198 {
199 	usb_midi_event_packet* packet = midiDevice->buffer;
200 	size_t bytes_left = midiDevice->actual_length;
201 	while (bytes_left) {	/* buffer may have several packets */
202 		int pktlen = CINbytes[packet->cin];
203 		usbmidi_port_info* port = midiDevice->ports[packet->cn];
204 
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 		/* port matching 'cable number' */
210 		if (port == NULL) {
211 			DPRINTF_ERR((MY_ID "no port matching cable number %d!\n",
212 				packet->cn));
213 		} else {
214 			ring_buffer_write(port->rbuf, packet->midi, pktlen);
215 			release_sem_etc(port->open_fd->sem_cb, pktlen,
216 				B_DO_NOT_RESCHEDULE);
217 		}
218 
219 		packet++;
220 		bytes_left -= sizeof(usb_midi_event_packet);
221 	}
222 }
223 
224 
225 /*
226 	callback: got a report, issue next request
227 */
228 
229 static void
230 midi_usb_read_callback(void* cookie, status_t status,
231 	void* data, size_t actual_len)
232 {
233 	status_t st;
234 	usbmidi_device_info* midiDevice = (usbmidi_device_info*)cookie;
235 
236 	assert(cookie != NULL);
237 	DPRINTF_DEBUG((MY_ID "midi_usb_read_callback() -- packet length %ld\n",
238 		actual_len));
239 
240 	acquire_sem(midiDevice->sem_lock);
241 	midiDevice->actual_length = actual_len;
242 	midiDevice->bus_status = status;	/* B_USB_STATUS_* */
243 	if (status != B_OK) {
244 		/* request failed */
245 		DPRINTF_DEBUG((MY_ID "bus status 0x%lx\n", status));
246 		if (status == B_CANCELED || !midiDevice->active) {
247 			/* cancelled: device is unplugged */
248 			DPRINTF_DEBUG((MY_ID "midi_usb_read_callback: cancelled"
249 				"(status=%lx active=%d -- deleting sem_cbs\n",
250 				status, midiDevice->active));
251 
252 			// Free any read() still blocked on semaphore
253 			for (int cable = 0; cable < 16; cable++) {
254 				usbmidi_port_info* port = midiDevice->ports[cable];
255 				if (port == NULL)
256 					break;
257 				if (port->open_fd != NULL)
258 					delete_sem(port->open_fd->sem_cb);
259 			}
260 			release_sem(midiDevice->sem_lock);
261 			return;
262 		}
263 		release_sem(midiDevice->sem_lock);
264 	} else {
265 		/* got a report */
266 		midiDevice->timestamp = system_time();	/* not used... */
267 
268 		interpret_midi_buffer(midiDevice);
269 		release_sem(midiDevice->sem_lock);
270 	}
271 
272 	/* issue next request */
273 	st = usb->queue_bulk(midiDevice->ept_in->handle,
274 		midiDevice->buffer, midiDevice->buffer_size,
275 		(usb_callback_func)midi_usb_read_callback, midiDevice);
276 	if (st != B_OK) {
277 		/* probably endpoint stall */
278 		DPRINTF_ERR((MY_ID "queue_bulk() error 0x%lx\n", st));
279 	}
280 }
281 
282 
283 static void
284 midi_usb_write_callback(void* cookie, status_t status,
285 	void* data, size_t actual_len)
286 {
287 	usbmidi_device_info* midiDevice = (usbmidi_device_info*)cookie;
288 #ifdef DEBUG
289 	usb_midi_event_packet* pkt = data;
290 #endif
291 
292 	assert(cookie != NULL);
293 	DPRINTF_DEBUG((MY_ID "midi_usb_write_callback()"
294 		" status %ld length %ld  pkt %p cin %x\n",
295 		status, actual_len, pkt, pkt->cin));
296 	release_sem(midiDevice->sem_send); /* done with buffer */
297 }
298 
299 
300 /*
301 	USB specific device hooks
302 */
303 
304 static status_t
305 usb_midi_added(const usb_device* dev, void** cookie)
306 {
307 	/* This seems overcomplicated, but endpoints can be in either order...
308 	and could possibly have different number of connectors! */
309 	int in_cables = 0, out_cables = 0;
310 	int cable_count[2] = {0, 0};
311 	int iep = 0;
312 	status_t status;
313 
314 	assert(dev != NULL && cookie != NULL);
315 	DPRINTF_INFO((MY_ID "usb_midi_added(%p, %p)\n", dev, cookie));
316 
317 	const usb_device_descriptor* dev_desc = usb->get_device_descriptor(dev);
318 
319 	DPRINTF_INFO((MY_ID "vendor ID 0x%04X, product ID 0x%04X\n",
320 		dev_desc->vendor_id, dev_desc->product_id));
321 
322 	/* check interface class */
323 	const usb_configuration_info* conf;
324 	if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION))
325 		== NULL) {
326 		DPRINTF_ERR((MY_ID "cannot get default configuration\n"));
327 		return B_ERROR;
328 	}
329 	DPRINTF_INFO((MY_ID "Interface count = %ld\n", conf->interface_count));
330 
331 	uint16 alt, ifno;
332 	const usb_interface_info* intf;
333 	for (ifno = 0; ifno < conf->interface_count; ifno++) {
334 		int devclass, subclass, protocol;
335 
336 		for (alt = 0; alt < conf->interface[ifno].alt_count; alt++) {
337 			intf = &conf->interface[ifno].alt[alt];
338 			devclass    = intf->descr->interface_class;
339 			subclass = intf->descr->interface_subclass;
340 			protocol = intf->descr->interface_protocol;
341 			DPRINTF_INFO((
342 				MY_ID "interface %d, alt : %d: class %d,"
343 				" subclass %d, protocol %d\n",
344                 ifno, alt, devclass, subclass, protocol));
345 
346 			if (devclass == USB_AUDIO_DEVICE_CLASS
347 				&& subclass == USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS)
348 				goto got_one;
349 		}
350 	}
351 
352 	DPRINTF_INFO((MY_ID "Midi interface not found\n"));
353 	return B_ERROR;
354 
355 got_one:
356 
357 	if ((status = usb->set_configuration(dev, conf)) != B_OK) {
358 		DPRINTF_ERR((MY_ID "set_configuration() failed 0x%lx\n", status));
359 		return B_ERROR;
360 	}
361 
362 	usbmidi_device_info* midiDevice;
363 	if ((midiDevice = create_device(dev, ifno)) == NULL) {
364 		return B_ERROR;
365 	}
366 
367 	/* get the actual number of  ports in and out */
368 	for (uint16 i = 0; i < intf->generic_count; i++) {
369 		usb_generic_descriptor *generic = &intf->generic[i]->generic;
370 		DPRINTF_DEBUG((MY_ID "descriptor %d: type %x sub %x\n",
371 			i, generic->descriptor_type, generic->data[0]));
372 		if (generic->descriptor_type == USB_DESCRIPTOR_CS_ENDPOINT
373 			&& generic->data[0] == USB_MS_GENERAL_DESCRIPTOR) {
374 			/* These *better* be in the same order as the endpoints! */
375 			cable_count[iep] = generic->data[1];
376 			iep = 1;
377 		}
378 	}
379 
380 	DPRINTF_DEBUG((MY_ID "midiDevice = %p endpoint count = %ld\n",
381 		midiDevice, intf->endpoint_count));
382 	midiDevice->ept_in = midiDevice->ept_out = NULL;
383 
384 	for (uint16 i = 0; i < intf->endpoint_count && i < 2; i++) {
385 		/* we are actually assuming max one IN, one OUT endpoint... */
386 		DPRINTF_INFO((MY_ID "endpoint %d = %p  %s\n",
387 			i, &intf->endpoint[i],
388 			(intf->endpoint[i].descr->endpoint_address & 0x80) != 0
389 			 ? "IN" : "OUT"));
390 		if ((intf->endpoint[i].descr->endpoint_address & 0x80) != 0) {
391 			if (midiDevice->ept_in == NULL) {
392 				midiDevice->ept_in = &intf->endpoint[i];
393 				in_cables = cable_count[i];
394 			}
395 		} else if (midiDevice->ept_out == NULL) {
396 			midiDevice->ept_out = &intf->endpoint[i];
397 			out_cables = cable_count[i];
398 		}
399 	}
400 
401 	midiDevice->timestamp = system_time();	/* This never seems to be used */
402 
403 	/* Create the actual device ports */
404 	usbmidi_port_info* port;
405 	for (uint16 i = 0; in_cables > 0 || out_cables > 0; i++) {
406 		port = create_usbmidi_port(midiDevice, i,
407 			(bool)in_cables, (bool)out_cables);
408 		midiDevice->ports[i] = port;
409 		if (in_cables)
410 			in_cables--;
411 		if (out_cables)
412 			out_cables--;
413 		add_port_info(port);
414 	}
415 
416 	/* issue bulk transfer */
417 	DPRINTF_DEBUG((MY_ID "queueing bulk xfer IN endpoint\n"));
418 	status = usb->queue_bulk(midiDevice->ept_in->handle, midiDevice->buffer,
419 		midiDevice->buffer_size,
420 		(usb_callback_func)midi_usb_read_callback, midiDevice);
421 	if (status != B_OK) {
422 		DPRINTF_ERR((MY_ID "queue_bulk() error 0x%lx\n", status));
423 		return B_ERROR;
424 	}
425 
426 	*cookie = midiDevice;
427 	DPRINTF_INFO((MY_ID "usb_midi_added: %s\n", midiDevice->name));
428 
429 	return B_OK;
430 }
431 
432 
433 static status_t
434 usb_midi_removed(void* cookie)
435 {
436 	usbmidi_device_info* midiDevice = (usbmidi_device_info*)cookie;
437 
438 	assert(cookie != NULL);
439 
440 	DPRINTF_INFO((MY_ID "usb_midi_removed(%s)\n", midiDevice->name));
441 	midiDevice->active = false;
442 	for (int cable = 0; cable < 16; cable++) {
443 		usbmidi_port_info* port = midiDevice->ports[cable];
444 		if (port == NULL)
445 			break;
446 		DPRINTF_DEBUG((MY_ID "removing port %d\n", cable));
447 		if (port->open_fd != NULL) {
448 			remove_port_info(port);
449 			port->open_fd->port = NULL;
450 			port->open_fd->device = NULL;
451 			delete_sem(port->open_fd->sem_cb);
452 				/* done here to ensure read is freed */
453 		}
454 		remove_port(port);
455 	}
456 	usb->cancel_queued_transfers(midiDevice->ept_in->handle);
457 	usb->cancel_queued_transfers(midiDevice->ept_out->handle);
458 	DPRINTF_DEBUG((MY_ID "usb_midi_removed: doing remove: %s\n",
459 		midiDevice->name));
460 	remove_device(midiDevice);
461 	return B_OK;
462 }
463 
464 
465 static usb_notify_hooks my_notify_hooks =
466 {
467 	usb_midi_added, usb_midi_removed
468 };
469 
470 #define	SUPPORTED_DEVICES	1
471 usb_support_descriptor my_supported_devices[SUPPORTED_DEVICES] =
472 {
473 	{
474 		USB_AUDIO_DEVICE_CLASS,
475 		USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS,
476 		0, 0, 0
477 	},
478 };
479 
480 
481 /*
482 	Device Driver Hook Functions
483 	-- open, read, write, close, and free
484  */
485 
486 static status_t
487 usb_midi_open(const char* name, uint32 flags,
488 	driver_cookie** out_cookie)
489 {
490 	driver_cookie* cookie;
491 	usbmidi_port_info* port;
492 	int mode = flags & O_RWMASK;
493 
494 	assert(name != NULL);
495 	assert(out_cookie != NULL);
496 	DPRINTF_INFO((MY_ID "usb_midi_open(%s) flags=%lx\n", name, flags));
497 
498 	if ((port = search_port_info(name)) == NULL)
499 		return B_ENTRY_NOT_FOUND;
500 
501 	if (!port->has_in && mode != O_RDONLY)
502 		return B_PERMISSION_DENIED;	 /* == EACCES */
503 	else if (!port->has_out && mode != O_WRONLY)
504 		return B_PERMISSION_DENIED;
505 
506 	if ((cookie = (driver_cookie*)malloc(sizeof(driver_cookie))) == NULL)
507 		return B_NO_MEMORY;
508 
509 	cookie->sem_cb = create_sem(0, DRIVER_NAME "_cb");
510 	if (cookie->sem_cb < 0) {
511 		DPRINTF_ERR((MY_ID "create_sem() failed 0x%lx\n", cookie->sem_cb));
512 		free(cookie);
513 		return B_ERROR;
514 	}
515 
516 	cookie->port = port;
517 	cookie->device = port->device;
518 
519 	acquire_sem(usbmidi_port_list_lock);
520 	if (port->open_fd != NULL) {
521 		/* there can only be one open channel to the device */
522 		delete_sem(cookie->sem_cb);
523 		free(cookie);
524 		release_sem(usbmidi_port_list_lock);
525 		return B_BUSY;
526 	}
527 	port->open_fd = cookie;
528 	port->open++;
529 	release_sem(usbmidi_port_list_lock);
530 
531 	*out_cookie = cookie;
532 	DPRINTF_INFO((MY_ID "usb_midi_open: device %s open (%d)\n",
533 		name, port->open));
534 	return B_OK;
535 }
536 
537 
538 static status_t
539 usb_midi_read(driver_cookie* cookie, off_t position,
540 	void* buf,	size_t* num_bytes)
541 {
542 	assert(cookie != NULL);
543 	status_t err = B_ERROR;
544 	usbmidi_port_info* port = cookie->port;
545 	usbmidi_device_info* midiDevice = cookie->device;
546 
547 	if (midiDevice == NULL || !midiDevice->active)
548 		return B_ERROR;	/* already unplugged */
549 
550 	DPRINTF_DEBUG((MY_ID "usb_midi_read: (%ld byte buffer at %lld cookie %p)"
551 		"\n", *num_bytes, position, cookie));
552 	while (midiDevice && midiDevice->active) {
553 		DPRINTF_DEBUG((MY_ID "waiting on acquire_sem_etc\n"));
554 		err = acquire_sem_etc(cookie->sem_cb, 1,
555 			 B_RELATIVE_TIMEOUT, 1000000);
556 		if (err == B_TIMED_OUT) {
557 			DPRINTF_DEBUG((MY_ID "acquire_sem_etc timed out\n"));
558 			continue;	/* see if we're still active */
559 		}
560 		if (err != B_OK) {
561 			*num_bytes = 0;
562 			DPRINTF_DEBUG((MY_ID "acquire_sem_etc aborted\n"));
563 			break;
564 		}
565 		DPRINTF_DEBUG((MY_ID "reading from ringbuffer\n"));
566 		acquire_sem(midiDevice->sem_lock);
567 			/* a global semaphore -- OK, I think */
568 		ring_buffer_user_read(port->rbuf, (uint8*)buf, 1);
569 		release_sem(midiDevice->sem_lock);
570 		*num_bytes = 1;
571 		DPRINTF_DEBUG((MY_ID "read byte %x -- cookie %p)\n",
572 			*(uint8*)buf, cookie));
573 		return B_OK;
574 	}
575 	DPRINTF_INFO((MY_ID "usb_midi_read: loop terminated"
576 		" -- Device no longer active\n"));
577 	return B_CANCELED;
578 }
579 
580 
581 const uint8 CINcode[] = {	/* see USB-MIDI Spec */
582 	0x4,	/* 0x0 - sysex start */
583 	0,	/* 0x1 -- undefined */
584 	0x3,	/* 0x2  -- song pos */
585 	0x2,	/* 0x3 -- song select */
586 	0,	/* 0x4 -- undefined */
587 	0,	/* 0x5 -- undefined */
588 	0x2,	/* 0x6  -- tune request */
589 	0x5,	/* 0x7 -- sysex end */
590 	0x5,	/* 0x8 -- clock */
591 	0,	/* 0x9 -- undefined */
592 	0x5,	/* 0xA -- start */
593 	0x5,	/* 0xB -- continue */
594 	0x5,	/* 0xC -- stop */
595 	0,	/* 0xD -- undefined */
596 	0x5,	/* 0xE -- active sensing */
597 	0x5,	/* 0x0 -- system reset */
598 };
599 
600 
601 static status_t
602 usb_midi_write(driver_cookie* cookie, off_t position,
603 	const void* buf, size_t* num_bytes)
604 {
605 	uint8* midiseq = (uint8*)buf;
606 	uint8 midicode = midiseq[0];	/* preserved for reference */
607 	status_t status;
608 	size_t buff_lim;
609 	uint8 cin = ((midicode & 0xF0) == 0xF0) ? CINcode[midicode & 0x0F]
610 		 : (midicode >> 4);
611 
612 	assert(cookie != NULL);
613 	usbmidi_port_info* port = cookie->port;
614 	usbmidi_device_info* midiDevice = cookie->device;
615 
616 	if (!midiDevice || !midiDevice->active)
617 		return B_ERROR;		/* already unplugged */
618 
619 	buff_lim = midiDevice->buffer_size * 3 / 4;
620 		/* max MIDI bytes buffer space */
621 
622 	DPRINTF_DEBUG((MY_ID "MIDI write (%ld bytes at %lld)\n",
623 		*num_bytes, position));
624 	if (*num_bytes > 3 && midicode != 0xF0) {
625 		DPRINTF_ERR((MY_ID "Non-SysEx packet of %ld bytes"
626 			" -- too big to handle\n", *num_bytes));
627 		return B_ERROR;
628 	}
629 
630 	size_t bytes_left = *num_bytes;
631 	while (bytes_left) {
632 		size_t xfer_bytes = (bytes_left < buff_lim) ?  bytes_left : buff_lim;
633 		usb_midi_event_packet* pkt = midiDevice->out_buffer;
634 		int packet_count = 0;
635 
636 		status = acquire_sem_etc(midiDevice->sem_send,
637 			1, B_RELATIVE_TIMEOUT, 2000000LL);
638 		if (status != B_OK)
639 			return status;
640 
641 		while (xfer_bytes) {
642 			uint8 pkt_bytes = CINbytes[cin];
643 			memset(pkt, 0, sizeof(usb_midi_event_packet));
644 			pkt->cin = cin;
645 			pkt->cn = port->cable;
646 			DPRINTF_DEBUG((MY_ID "using packet data (code %x -- %d bytes)"
647 				" %x %x %x\n", pkt->cin, CINbytes[pkt->cin],
648 				midiseq[0], midiseq[1], midiseq[2]));
649 			memcpy(pkt->midi, midiseq, pkt_bytes);
650 			DPRINTF_DEBUG((MY_ID "built packet %p %x:%d %x %x %x\n",
651 				pkt, pkt->cin, pkt->cn,
652 				pkt->midi[0], pkt->midi[1], pkt->midi[2]));
653 			xfer_bytes -= pkt_bytes;
654 			bytes_left -= pkt_bytes;
655 			midiseq += pkt_bytes;
656 			packet_count++;
657 			pkt++;
658 			if (midicode == 0xF0 && bytes_left < 4) cin = 4 + bytes_left;
659 				/* see USB-MIDI Spec */
660 		}
661 		status = usb->queue_bulk(midiDevice->ept_out->handle,
662 			midiDevice->out_buffer,	sizeof(usb_midi_event_packet)
663 			* packet_count, (usb_callback_func)midi_usb_write_callback,
664 			midiDevice);
665 		if (status != B_OK) {
666 			DPRINTF_ERR((MY_ID "midi write queue_bulk() error 0x%lx\n",
667 				status));
668 			return B_ERROR;
669 		}
670 	}
671 	return B_OK;
672 }
673 
674 
675 static status_t
676 usb_midi_control(void* cookie, uint32 iop, void* data, size_t len)
677 {
678 	return B_ERROR;
679 }
680 
681 
682 static status_t
683 usb_midi_close(driver_cookie* cookie)
684 {
685 	assert(cookie != NULL);
686 	delete_sem(cookie->sem_cb);
687 
688 	usbmidi_port_info* port = cookie->port;
689 	usbmidi_device_info* midiDevice = cookie->device;
690 
691 	DPRINTF_INFO((MY_ID "usb_midi_close(%p device=%p port=%p)\n",
692 		cookie, midiDevice, port));
693 
694 	acquire_sem(usbmidi_port_list_lock);
695 	if (port != NULL) {
696 		/* detach the cookie from port */
697 		port->open_fd = NULL;
698 		--port->open;
699 	}
700 	release_sem(usbmidi_port_list_lock);
701 	DPRINTF_DEBUG((MY_ID "usb_midi_close: complete\n"));
702 
703 	return B_OK;
704 }
705 
706 
707 static status_t
708 usb_midi_free(driver_cookie* cookie)
709 {
710 	assert(cookie != NULL);
711 	usbmidi_device_info* midiDevice = cookie->device;
712 	DPRINTF_INFO((MY_ID "usb_midi_free(%p device=%p)\n", cookie, midiDevice));
713 
714 	free(cookie);
715 
716 	return B_OK;
717 }
718 
719 
720 static device_hooks usb_midi_hooks = {
721 	(device_open_hook)usb_midi_open,
722 	(device_close_hook)usb_midi_close,
723 	(device_free_hook)usb_midi_free,
724 	(device_control_hook)usb_midi_control,
725 	(device_read_hook)usb_midi_read,
726 	(device_write_hook)usb_midi_write,
727 	NULL, NULL, NULL, NULL
728 };
729 
730 
731 /*
732 	Driver Registration
733  */
734 
735 _EXPORT status_t
736 init_hardware(void)
737 {
738 	DPRINTF_DEBUG((MY_ID "init_hardware() version:"
739 		__DATE__ " " __TIME__ "\n"));
740 	return B_OK;
741 }
742 
743 
744 _EXPORT status_t
745 init_driver(void)
746 {
747 	DPRINTF_INFO((MY_ID "init_driver() version:" __DATE__ " " __TIME__ "\n"));
748 
749 	if (get_module(B_USB_MODULE_NAME, (module_info**)&usb) != B_OK)
750 		return B_ERROR;
751 
752 	if ((usbmidi_port_list_lock = create_sem(1, "dev_list_lock")) < 0) {
753 		put_module(B_USB_MODULE_NAME);
754 		return usbmidi_port_list_lock;		/* error code */
755 	}
756 
757 	usb->register_driver(usb_midi_driver_name, my_supported_devices,
758 		SUPPORTED_DEVICES, NULL);
759 	usb->install_notify(usb_midi_driver_name, &my_notify_hooks);
760 	DPRINTF_INFO((MY_ID "init_driver() OK\n"));
761 
762 	return B_OK;
763 }
764 
765 
766 _EXPORT void
767 uninit_driver(void)
768 {
769 	DPRINTF_INFO((MY_ID "uninit_driver()\n"));
770 	usb->uninstall_notify(usb_midi_driver_name);
771 
772 	delete_sem(usbmidi_port_list_lock);
773 	put_module(B_USB_MODULE_NAME);
774 	free_port_names();
775 	DPRINTF_INFO((MY_ID "uninit complete\n"));
776 }
777 
778 
779 _EXPORT const char**
780 publish_devices(void)
781 {
782 	DPRINTF_INFO((MY_ID "publish_devices()\n"));
783 
784 	if (usbmidi_port_list_changed) {
785 		free_port_names();
786 		alloc_port_names();
787 		if (usbmidi_port_names != NULL)
788 			rebuild_port_names();
789 		usbmidi_port_list_changed = false;
790 	}
791 	assert(usbmidi_port_names != NULL);
792 	return (const char**)usbmidi_port_names;
793 }
794 
795 
796 _EXPORT device_hooks*
797 find_device(const char* name)
798 {
799 	assert(name != NULL);
800 	DPRINTF_INFO((MY_ID "find_device(%s)\n", name));
801 	if (search_port_info(name) == NULL)
802 		return NULL;
803 	return &usb_midi_hooks;
804 }
805 
806