xref: /netbsd-src/sys/dev/usb/umidi_quirks.c (revision 4b71a66d0f279143147d63ebfcfd8a59499a3684)
1 /*	$NetBSD: umidi_quirks.c,v 1.15 2008/04/28 20:24:00 martin Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Takuya SHIOZAKI (tshiozak@NetBSD.org).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: umidi_quirks.c,v 1.15 2008/04/28 20:24:00 martin Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/device.h>
40 #include <sys/ioctl.h>
41 #include <sys/conf.h>
42 #include <sys/file.h>
43 #include <sys/select.h>
44 #include <sys/proc.h>
45 #include <sys/vnode.h>
46 #include <sys/poll.h>
47 
48 #include <dev/usb/usb.h>
49 #include <dev/usb/usbdi.h>
50 #include <dev/usb/usbdi_util.h>
51 
52 #include <dev/usb/usbdevs.h>
53 #include <dev/usb/uaudioreg.h>
54 #include <dev/usb/umidireg.h>
55 #include <dev/usb/umidivar.h>
56 #include <dev/usb/umidi_quirks.h>
57 
58 /*
59  * quirk codes for UMIDI
60  */
61 
62 #ifdef UMIDIQUIRK_DEBUG
63 #define DPRINTF(x)	if (umidiquirkdebug) printf x
64 #define DPRINTFN(n,x)	if (umidiquirkdebug >= (n)) printf x
65 int	umidiquirkdebug = 1;
66 #else
67 #define DPRINTF(x)
68 #define DPRINTFN(n,x)
69 #endif
70 
71 
72 /*
73  * YAMAHA UX-256
74  *  --- this is a typical yamaha device, but has a broken descriptor :-<
75  */
76 
77 UMQ_FIXED_EP_DEF(YAMAHA, YAMAHA_UX256, ANYIFACE, 1, 1) = {
78 	/* out */
79 	{ 0, 16 },
80 	/* in */
81 	{ 1, 8 }
82 };
83 
84 UMQ_DEF(YAMAHA, YAMAHA_UX256, ANYIFACE) = {
85 	UMQ_FIXED_EP_REG(YAMAHA, YAMAHA_UX256, ANYIFACE),
86 #if 0
87 	UMQ_YAMAHA_REG(YAMAHA, ANYPRODUCT, ANYIFACE),
88 #endif
89 	UMQ_TERMINATOR
90 };
91 
92 
93 /*
94  * YAMAHA generic
95  */
96 UMQ_DEF(YAMAHA, ANYPRODUCT, ANYIFACE) = {
97 	UMQ_YAMAHA_REG(YAMAHA, ANYPRODUCT, ANYIFACE),
98 	UMQ_TERMINATOR
99 };
100 
101 
102 /*
103  * ROLAND UM-1
104  */
105 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM1, 2, 1, 1) = {
106 	/* out */
107 	{ 0, 1 },
108 	/* in */
109 	{ 1, 1 }
110 };
111 
112 UMQ_DEF(ROLAND, ROLAND_UM1, 2) = {
113 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM1, 2),
114 	UMQ_TERMINATOR
115 };
116 
117 /*
118  * ROLAND SC-8850
119  */
120 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SC8850, 2, 1, 1) = {
121 	/* out */
122 	{ 0, 6 },
123 	/* in */
124 	{ 1, 6 }
125 };
126 
127 UMQ_DEF(ROLAND, ROLAND_SC8850, 2) = {
128 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SC8850, 2),
129 	UMQ_TERMINATOR
130 };
131 
132 /*
133  * ROLAND SD-90
134  */
135 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SD90, 2, 1, 1) = {
136 	/* out */
137 	{ 0, 4 },
138 	/* in */
139 	{ 1, 4 }
140 };
141 
142 UMQ_DEF(ROLAND, ROLAND_SD90, 2) = {
143 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SD90, 2),
144 	UMQ_TERMINATOR
145 };
146 
147 
148 /*
149  * ROLAND UM-880 (native mode)
150  */
151 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM880N, 0, 1, 1) = {
152 	/* out */
153 	{ 0, 9 },
154 	/* in */
155 	{ 1, 9 }
156 };
157 
158 UMQ_DEF(ROLAND, ROLAND_UM880N, 0) = {
159 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM880N, 0),
160 	UMQ_TERMINATOR
161 };
162 
163 /*
164  * ROLAND UA-100
165  */
166 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA100, 2, 1, 1) = {
167 	/* out */
168 	{ 0, 3 },
169 	/* in */
170 	{ 1, 3 }
171 };
172 
173 UMQ_DEF(ROLAND, ROLAND_UA100, 2) = {
174 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA100, 2),
175 	UMQ_TERMINATOR
176 };
177 
178 /*
179  * ROLAND UM-4
180  */
181 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM4, 2, 1, 1) = {
182 	/* out */
183 	{ 0, 4 },
184 	/* in */
185 	{ 1, 4 }
186 };
187 
188 UMQ_DEF(ROLAND, ROLAND_UM4, 2) = {
189 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM4, 2),
190 	UMQ_TERMINATOR
191 };
192 
193 /*
194  * ROLAND U-8
195  */
196 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_U8, 2, 1, 1) = {
197 	/* out */
198 	{ 0, 2 },
199 	/* in */
200 	{ 1, 2 }
201 };
202 
203 UMQ_DEF(ROLAND, ROLAND_U8, 2) = {
204 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_U8, 2),
205 	UMQ_TERMINATOR
206 };
207 
208 /*
209  * ROLAND UM-2
210  */
211 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM2, 2, 1, 1) = {
212 	/* out */
213 	{ 0, 2 },
214 	/* in */
215 	{ 1, 2 }
216 };
217 
218 UMQ_DEF(ROLAND, ROLAND_UM2, 2) = {
219 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM2, 2),
220 	UMQ_TERMINATOR
221 };
222 
223 /*
224  * ROLAND SC-8820
225  */
226 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SC8820, 2, 1, 1) = {
227 	/* out */
228 	{ 0, 5 }, /* cables 0, 1, 4 only */
229 	/* in */
230 	{ 1, 5 } /* do. */
231 };
232 
233 UMQ_DEF(ROLAND, ROLAND_SC8820, 2) = {
234 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SC8820, 2),
235 	UMQ_TERMINATOR
236 };
237 
238 /*
239  * ROLAND PC-300
240  */
241 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_PC300, 2, 1, 1) = {
242 	/* out */
243 	{ 0, 1 },
244 	/* in */
245 	{ 1, 1 }
246 };
247 
248 UMQ_DEF(ROLAND, ROLAND_PC300, 2) = {
249 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_PC300, 2),
250 	UMQ_TERMINATOR
251 };
252 
253 /*
254  * ROLAND SK-500
255  */
256 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SK500, 2, 1, 1) = {
257 	/* out */
258 	{ 0, 5 }, /* cables 0, 1, 4 only */
259 	/* in */
260 	{ 1, 5 } /* do. */
261 };
262 
263 UMQ_DEF(ROLAND, ROLAND_SK500, 2) = {
264 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SK500, 2),
265 	UMQ_TERMINATOR
266 };
267 
268 /*
269  * ROLAND SC-D70
270  */
271 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SCD70, 2, 1, 1) = {
272 	/* out */
273 	{ 0, 3 },
274 	/* in */
275 	{ 1, 3 }
276 };
277 
278 UMQ_DEF(ROLAND, ROLAND_SCD70, 2) = {
279 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SCD70, 2),
280 	UMQ_TERMINATOR
281 };
282 
283 /*
284  * ROLAND XV-5050
285  */
286 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_XV5050, 0, 1, 1) = {
287 	/* out */
288 	{ 0, 1 },
289 	/* in */
290 	{ 1, 1 }
291 };
292 
293 UMQ_DEF(ROLAND, ROLAND_XV5050, 0) = {
294 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_XV5050, 0),
295 	UMQ_TERMINATOR
296 };
297 
298 /*
299  * ROLAND UM-550
300  */
301 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM550, 0, 1, 1) = {
302 	/* out */
303 	{ 0, 6 },
304 	/* in */
305 	{ 1, 6 }
306 };
307 
308 UMQ_DEF(ROLAND, ROLAND_UM550, 0) = {
309 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM550, 0),
310 	UMQ_TERMINATOR
311 };
312 
313 /*
314  * ROLAND SD-20
315  */
316 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SD20, 0, 1, 1) = {
317 	/* out */
318 	{ 0, 2 },
319 	/* in */
320 	{ 1, 3 }
321 };
322 
323 UMQ_DEF(ROLAND, ROLAND_SD20, 0) = {
324 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SD20, 0),
325 	UMQ_TERMINATOR
326 };
327 
328 /*
329  * ROLAND SD-80
330  */
331 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SD80, 0, 1, 1) = {
332 	/* out */
333 	{ 0, 4 },
334 	/* in */
335 	{ 1, 4 }
336 };
337 
338 UMQ_DEF(ROLAND, ROLAND_SD80, 0) = {
339 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SD80, 0),
340 	UMQ_TERMINATOR
341 };
342 
343 /*
344  * ROLAND UA-700
345  */
346 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA700, 3, 1, 1) = {
347 	/* out */
348 	{ 0, 2 },
349 	/* in */
350 	{ 1, 2 }
351 };
352 
353 UMQ_DEF(ROLAND, ROLAND_UA700, 3) = {
354 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA700, 3),
355 	UMQ_TERMINATOR
356 };
357 
358 /*
359  * ROLAND UA-1000
360  */
361 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA1000, 3, 1, 1) = {
362 	/* out */
363 	{ 0, 2 },
364 	/* in */
365 	{ 1, 2 }
366 };
367 
368 UMQ_DEF(ROLAND, ROLAND_UA1000, 3) = {
369 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA1000, 3),
370 	UMQ_TERMINATOR
371 };
372 
373 /*
374  * ROLAND UA-101
375  */
376 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA101, 2, 1, 1) = {
377 	/* out */
378 	{ 0, 2 },
379 	/* in */
380 	{ 1, 2 }
381 };
382 
383 UMQ_DEF(ROLAND, ROLAND_UA101, 2) = {
384 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA101, 2),
385 	UMQ_TERMINATOR
386 };
387 
388 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA101F, 2, 1, 1) = {
389 	/* out */
390 	{ 0, 2 },
391 	/* in */
392 	{ 1, 2 }
393 };
394 
395 UMQ_DEF(ROLAND, ROLAND_UA101F, 2) = {
396 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA101F, 2),
397 	UMQ_TERMINATOR
398 };
399 
400 /*
401  * ROLAND Fantom-X
402  */
403 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_FANTOMX, 0, 1, 1) = {
404 	/* out */
405 	{ 0, 1 },
406 	/* in */
407 	{ 1, 1 }
408 };
409 
410 UMQ_DEF(ROLAND, ROLAND_FANTOMX, 0) = {
411 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_FANTOMX, 0),
412 	UMQ_TERMINATOR
413 };
414 
415 /*
416  * ROLAND PCR
417  */
418 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_PCR, 0, 1, 1) = {
419 	/* out */
420 	{ 0, 3 },
421 	/* in */
422 	{ 1, 3 }
423 };
424 
425 UMQ_DEF(ROLAND, ROLAND_PCR, 0) = {
426 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_PCR, 0),
427 	UMQ_TERMINATOR
428 };
429 
430 /*
431  * ROLAND UM-3EX
432  */
433 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM3, 0, 1, 1) = {
434 	/* out */
435 	{ 0, 3 },
436 	/* in */
437 	{ 1, 3 }
438 };
439 
440 UMQ_DEF(ROLAND, ROLAND_UM3, 0) = {
441 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM3, 0),
442 	UMQ_TERMINATOR
443 };
444 
445 /*
446  * ROLAND UA-25
447  */
448 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA25, 2, 1, 1) = {
449 	/* out */
450 	{ 0, 1 },
451 	/* in */
452 	{ 1, 1 }
453 };
454 
455 UMQ_DEF(ROLAND, ROLAND_UA25, 2) = {
456 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA25, 2),
457 	UMQ_TERMINATOR
458 };
459 
460 /*
461  * ROLAND UA-4FX
462  */
463 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UA4FX, 2, 1, 1) = {
464 	/* out */
465 	{ 0, 1 },
466 	/* in */
467 	{ 1, 1 }
468 };
469 
470 UMQ_DEF(ROLAND, ROLAND_UA4FX, 2) = {
471 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_UA4FX, 2),
472 	UMQ_TERMINATOR
473 };
474 
475 /*
476  * ROLAND SonicCell
477  */
478 UMQ_FIXED_EP_DEF(ROLAND, ROLAND_SONICCELL, 2, 1, 1) = {
479 	/* out */
480 	{ 0, 1 },
481 	/* in */
482 	{ 1, 1 }
483 };
484 
485 UMQ_DEF(ROLAND, ROLAND_SONICCELL, 2) = {
486 	UMQ_FIXED_EP_REG(ROLAND, ROLAND_SONICCELL, 2),
487 	UMQ_TERMINATOR
488 };
489 
490 /*
491  * Midiman Midisport 2x4. This has 2 physical MIDI IN jacks that are read
492  * on endpoint 0x81 (descriptor index 0). It has 4 physical MIDI OUT jacks
493  * that can be written on endpoints 2 or 4 (at descriptor index 2 or 4,
494  * coincidentally) interchangeably: either endpoint will accept a Cable Number
495  * field of 0 to 3, and data for a given CN will be routed to the same
496  * physical output regardless of the endpoint used for the transfer. But
497  * there's a catch: flow-control feedback only goes to endpoint 2 for
498  * CN 0 and 2, and only to endpoint 4 for CN 1 and 3. If you send output at
499  * high rates for CN 0 or 2 over endpoint 4, or for CN 1 or 3 over endpoint 2,
500  * the USB transfers complete as fast as possible, giving you an apparent data
501  * rate much higher than MIDI's 3125 cps (easy to measure using dd to blast a
502  * bunch of midi data to the rmidi device). Of course that isn't a way to make
503  * MIDI faster, just a way to overrun the device buffer and spray bits on the
504  * floor. So this device needs the fixed endpoint quirk, the fixed cable number
505  * quirk (to make sure CNs 0 and 2 are put on the first endpoint and 1 and 3
506  * on the other), and then the fixed mididev-assignment quirk (to match jacks
507  * to mididevs so the rmidi devices match the order of the blinkenlights).
508  */
509 UMQ_FIXED_EP_DEF(MIDIMAN, MIDIMAN_MIDISPORT2X4, ANYIFACE, 2, 1) = {
510 	/* out: ep# jacks */
511 	{ 2, 2 },
512 	{ 4, 2 },
513 	/* in: ep# jacks */
514 	{ 0, 2 }
515 };
516 UMQ_DEF(MIDIMAN, MIDIMAN_MIDISPORT2X4, ANYIFACE) = {
517 	UMQ_FIXED_EP_REG(MIDIMAN, MIDIMAN_MIDISPORT2X4, ANYIFACE),
518 	UMQ_CN_FIXED_REG(0, 2, 1, 3, 0, 1),
519 	UMQ_MD_FIXED_REG(0, 0, 2, 1, 1, -1, 3, -1),
520 	UMQ_TYPE(MIDIMAN_GARBLE),
521 	UMQ_TERMINATOR
522 };
523 
524 /*
525  * quirk list
526  */
527 struct umidi_quirk umidi_quirklist[] = {
528 	UMQ_REG(YAMAHA, YAMAHA_UX256, ANYIFACE),
529 	UMQ_REG(YAMAHA, ANYPRODUCT, ANYIFACE),
530 	UMQ_REG(ROLAND, ROLAND_UM1, 2),
531 	UMQ_REG(ROLAND, ROLAND_SC8850, 2),
532 	UMQ_REG(ROLAND, ROLAND_SD90, 2),
533 	UMQ_REG(ROLAND, ROLAND_UM880N, 0),
534 	UMQ_REG(ROLAND, ROLAND_UA100, 2),
535 	UMQ_REG(ROLAND, ROLAND_UM4, 2),
536 	UMQ_REG(ROLAND, ROLAND_U8, 2),
537 	UMQ_REG(ROLAND, ROLAND_UM2, 2),
538 	UMQ_REG(ROLAND, ROLAND_SC8820, 2),
539 	UMQ_REG(ROLAND, ROLAND_PC300, 2),
540 	UMQ_REG(ROLAND, ROLAND_SK500, 2),
541 	UMQ_REG(ROLAND, ROLAND_SCD70, 2),
542 	UMQ_REG(ROLAND, ROLAND_XV5050, 0),
543 	UMQ_REG(ROLAND, ROLAND_UM550, 0),
544 	UMQ_REG(ROLAND, ROLAND_SD20, 0),
545 	UMQ_REG(ROLAND, ROLAND_SD80, 0),
546 	UMQ_REG(ROLAND, ROLAND_UA700, 3),
547 	UMQ_REG(ROLAND, ROLAND_UA1000, 3),
548 	UMQ_REG(ROLAND, ROLAND_UA101, 2),
549 	UMQ_REG(ROLAND, ROLAND_UA101F, 2),
550 	UMQ_REG(ROLAND, ROLAND_FANTOMX, 0),
551 	UMQ_REG(ROLAND, ROLAND_PCR, 0),
552 	UMQ_REG(ROLAND, ROLAND_UM3, 0),
553 	UMQ_REG(ROLAND, ROLAND_UA25, 2),
554 	UMQ_REG(ROLAND, ROLAND_UA4FX, 2),
555 	UMQ_REG(ROLAND, ROLAND_SONICCELL, 2),
556 	UMQ_REG(MIDIMAN, MIDIMAN_MIDISPORT2X4, ANYIFACE),
557 	{ .vendor = 0 },
558 };
559 
560 
561 /*
562  * quirk utilities
563  */
564 
565 struct umidi_quirk *
566 umidi_search_quirk(int vendor, int product, int ifaceno)
567 {
568 	struct umidi_quirk *p;
569 	struct umq_data *q;
570 
571 	DPRINTF(("umidi_search_quirk: v=%d, p=%d, i=%d\n",
572 		 vendor, product, ifaceno));
573 
574 	for (p=&umidi_quirklist[0]; p->vendor; p++) {
575 		DPRINTFN(10, ("\tv=%d, p=%d, i=%d",
576 			      p->vendor, p->product, p->iface));
577 		if ((p->vendor==vendor || p->vendor==ANYVENDOR) &&
578 		    (p->product==product || p->product==ANYPRODUCT) &&
579 		    (p->iface==ifaceno || p->iface==ANYIFACE)) {
580 			DPRINTFN(10, (" found\n"));
581 			if (!p->type_mask)
582 				/* make quirk mask */
583 				for (q=p->quirks; q->type; q++)
584 					p->type_mask |= 1<<(q->type-1);
585 			return p;
586 		}
587 		DPRINTFN(10, ("\n"));
588 	}
589 
590 	return NULL;
591 }
592 
593 static const char *quirk_name[] = {
594 	"NULL",
595 	"Fixed Endpoint",
596 	"Yamaha Specific",
597 	"Midiman Packet Garbling",
598 	"Cable Numbers per Endpoint",
599 	"Cable Numbers Global",
600 	"Cable Numbers Fixed",
601 	"Unit Mapping Fixed",
602 };
603 
604 void
605 umidi_print_quirk(struct umidi_quirk *q)
606 {
607 	struct umq_data *qd;
608 	if (q) {
609 		printf("(");
610 		for (qd=q->quirks; qd->type; qd++)
611 			printf("%s%s", quirk_name[qd->type],
612 			       (qd+1)->type?", ":")\n");
613 	} else {
614 		printf("(genuine USB-MIDI)\n");
615 	}
616 }
617 
618 void *
619 umidi_get_quirk_data_from_type(struct umidi_quirk *q, u_int32_t type)
620 {
621 	struct umq_data *qd;
622 	if (q) {
623 		for (qd=q->quirks; qd->type; qd++)
624 			if (qd->type == type)
625 				return qd->data;
626 	}
627 	return NULL;
628 }
629