xref: /haiku/src/add-ons/kernel/drivers/midi/usb_midi/usb_midi.cpp (revision 3ecb7fb4415b319b6aac606551d51efad21037df)
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_READ_AREA | B_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 		if (port->open_fd != NULL) {
468 			remove_port_info(port);
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 = midiseq[0];	/* preserved for reference */
632 	status_t status;
633 	size_t buff_lim;
634 	uint8 cin = ((midicode & 0xF0) == 0xF0) ? CINcode[midicode & 0x0F]
635 		 : (midicode >> 4);
636 
637 	assert(cookie != NULL);
638 	usbmidi_port_info* port = cookie->port;
639 	usbmidi_device_info* midiDevice = cookie->device;
640 
641 	if (!midiDevice || !midiDevice->active)
642 		return B_ERROR;		/* already unplugged */
643 
644 	buff_lim = midiDevice->outMaxPkt * 3 / 4;
645 		/* max MIDI bytes buffer space */
646 
647 	DPRINTF_DEBUG((MY_ID "MIDI write (%" B_PRIuSIZE " bytes at %" B_PRIdOFF
648 		")\n", *num_bytes, position));
649 	if (*num_bytes > 3 && midicode != 0xF0) {
650 		DPRINTF_ERR((MY_ID "Non-SysEx packet of %ld bytes"
651 			" -- too big to handle\n", *num_bytes));
652 		return B_ERROR;
653 	}
654 
655 	size_t bytes_left = *num_bytes;
656 	while (bytes_left) {
657 		size_t xfer_bytes = (bytes_left < buff_lim) ?  bytes_left : buff_lim;
658 		usb_midi_event_packet* pkt = midiDevice->out_buffer;
659 		int packet_count = 0;
660 
661 		status = acquire_sem_etc(midiDevice->sem_send,
662 			1, B_RELATIVE_TIMEOUT, 2000000LL);
663 		if (status != B_OK)
664 			return status;
665 
666 		while (xfer_bytes) {
667 			uint8 pkt_bytes = CINbytes[cin];
668 			memset(pkt, 0, sizeof(usb_midi_event_packet));
669 			pkt->cin = cin;
670 			pkt->cn = port->cable;
671 			DPRINTF_DEBUG((MY_ID "using packet data (code %x -- %d bytes)"
672 				" %x %x %x\n", pkt->cin, CINbytes[pkt->cin],
673 				midiseq[0], midiseq[1], midiseq[2]));
674 			memcpy(pkt->midi, midiseq, pkt_bytes);
675 			DPRINTF_DEBUG((MY_ID "built packet %p %x:%d %x %x %x\n",
676 				pkt, pkt->cin, pkt->cn,
677 				pkt->midi[0], pkt->midi[1], pkt->midi[2]));
678 			xfer_bytes -= pkt_bytes;
679 			bytes_left -= pkt_bytes;
680 			midiseq += pkt_bytes;
681 			packet_count++;
682 			pkt++;
683 			if (midicode == 0xF0 && bytes_left < 4) cin = 4 + bytes_left;
684 				/* see USB-MIDI Spec */
685 		}
686 		status = usb->queue_bulk(midiDevice->ept_out->handle,
687 			midiDevice->out_buffer,	sizeof(usb_midi_event_packet)
688 			* packet_count, (usb_callback_func)midi_usb_write_callback,
689 			midiDevice);
690 		if (status != B_OK) {
691 			DPRINTF_ERR((MY_ID "midi write queue_bulk() error 0x%" B_PRIx32
692 				"\n", status));
693 			return B_ERROR;
694 		}
695 	}
696 	return B_OK;
697 }
698 
699 
700 static status_t
701 usb_midi_control(void* cookie, uint32 iop, void* data, size_t len)
702 {
703 	return B_ERROR;
704 }
705 
706 
707 static status_t
708 usb_midi_close(driver_cookie* cookie)
709 {
710 	assert(cookie != NULL);
711 	delete_sem(cookie->sem_cb);
712 
713 	usbmidi_port_info* port = cookie->port;
714 	usbmidi_device_info* midiDevice = cookie->device;
715 
716 	DPRINTF_INFO((MY_ID "usb_midi_close(%p device=%p port=%p)\n",
717 		cookie, midiDevice, port));
718 
719 	acquire_sem(usbmidi_port_list_lock);
720 	if (port != NULL) {
721 		/* detach the cookie from port */
722 		port->open_fd = NULL;
723 		--port->open;
724 	}
725 	release_sem(usbmidi_port_list_lock);
726 	DPRINTF_DEBUG((MY_ID "usb_midi_close: complete\n"));
727 
728 	return B_OK;
729 }
730 
731 
732 static status_t
733 usb_midi_free(driver_cookie* cookie)
734 {
735 	assert(cookie != NULL);
736 	usbmidi_device_info* midiDevice = cookie->device;
737 	DPRINTF_INFO((MY_ID "usb_midi_free(%p device=%p)\n", cookie, midiDevice));
738 
739 	free(cookie);
740 
741 	return B_OK;
742 }
743 
744 
745 static device_hooks usb_midi_hooks = {
746 	(device_open_hook)usb_midi_open,
747 	(device_close_hook)usb_midi_close,
748 	(device_free_hook)usb_midi_free,
749 	(device_control_hook)usb_midi_control,
750 	(device_read_hook)usb_midi_read,
751 	(device_write_hook)usb_midi_write,
752 	NULL, NULL, NULL, NULL
753 };
754 
755 
756 /*
757 	Driver Registration
758  */
759 
760 _EXPORT status_t
761 init_hardware(void)
762 {
763 	DPRINTF_DEBUG((MY_ID "init_hardware() version:"
764 		__DATE__ " " __TIME__ "\n"));
765 	return B_OK;
766 }
767 
768 
769 _EXPORT status_t
770 init_driver(void)
771 {
772 	DPRINTF_INFO((MY_ID "init_driver() version:" __DATE__ " " __TIME__ "\n"));
773 
774 	if (get_module(B_USB_MODULE_NAME, (module_info**)&usb) != B_OK)
775 		return B_ERROR;
776 
777 	if ((usbmidi_port_list_lock = create_sem(1, "dev_list_lock")) < 0) {
778 		put_module(B_USB_MODULE_NAME);
779 		return usbmidi_port_list_lock;		/* error code */
780 	}
781 
782 	usb->register_driver(usb_midi_driver_name, my_supported_devices,
783 		SUPPORTED_DEVICES, NULL);
784 	usb->install_notify(usb_midi_driver_name, &my_notify_hooks);
785 	DPRINTF_INFO((MY_ID "init_driver() OK\n"));
786 
787 	return B_OK;
788 }
789 
790 
791 _EXPORT void
792 uninit_driver(void)
793 {
794 	DPRINTF_INFO((MY_ID "uninit_driver()\n"));
795 	usb->uninstall_notify(usb_midi_driver_name);
796 
797 	delete_sem(usbmidi_port_list_lock);
798 	put_module(B_USB_MODULE_NAME);
799 	free_port_names();
800 	DPRINTF_INFO((MY_ID "uninit complete\n"));
801 }
802 
803 
804 _EXPORT const char**
805 publish_devices(void)
806 {
807 	DPRINTF_INFO((MY_ID "publish_devices()\n"));
808 
809 	if (usbmidi_port_list_changed) {
810 		free_port_names();
811 		alloc_port_names();
812 		if (usbmidi_port_names != NULL)
813 			rebuild_port_names();
814 		usbmidi_port_list_changed = false;
815 	}
816 	assert(usbmidi_port_names != NULL);
817 	return (const char**)usbmidi_port_names;
818 }
819 
820 
821 _EXPORT device_hooks*
822 find_device(const char* name)
823 {
824 	assert(name != NULL);
825 	DPRINTF_INFO((MY_ID "find_device(%s)\n", name));
826 	if (search_port_info(name) == NULL)
827 		return NULL;
828 	return &usb_midi_hooks;
829 }
830 
831