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