xref: /haiku/src/add-ons/kernel/drivers/midi/usb_midi/usb_midi.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
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*
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%" 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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**
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*
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