Annotation of src/sys/dev/usb/umidi.c, Revision 1.23.2.4
1.23.2.4! yamt 1: /* $NetBSD: umidi.c,v 1.23.2.3 2007/09/03 14:39:18 yamt Exp $ */
1.1 tshiozak 2: /*
3: * Copyright (c) 2001 The NetBSD Foundation, Inc.
4: * All rights reserved.
5: *
6: * This code is derived from software contributed to The NetBSD Foundation
1.23.2.2 yamt 7: * by Takuya SHIOZAKI (tshiozak@NetBSD.org) and (full-size transfers, extended
8: * hw_if) Chapman Flack (chap@NetBSD.org).
1.1 tshiozak 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: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the NetBSD
21: * Foundation, Inc. and its contributors.
22: * 4. Neither the name of The NetBSD Foundation nor the names of its
23: * contributors may be used to endorse or promote products derived
24: * from this software without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36: * POSSIBILITY OF SUCH DAMAGE.
37: */
1.10 lukem 38:
39: #include <sys/cdefs.h>
1.23.2.4! yamt 40: __KERNEL_RCSID(0, "$NetBSD: umidi.c,v 1.23.2.3 2007/09/03 14:39:18 yamt Exp $");
1.1 tshiozak 41:
1.23.2.2 yamt 42: #include <sys/types.h>
1.1 tshiozak 43: #include <sys/param.h>
44: #include <sys/systm.h>
45: #include <sys/kernel.h>
46: #include <sys/malloc.h>
47: #include <sys/device.h>
48: #include <sys/ioctl.h>
49: #include <sys/conf.h>
50: #include <sys/file.h>
51: #include <sys/select.h>
52: #include <sys/proc.h>
53: #include <sys/vnode.h>
54: #include <sys/poll.h>
55: #include <sys/lock.h>
1.23.2.4! yamt 56: #include <sys/intr.h>
1.23.2.2 yamt 57:
1.1 tshiozak 58: #include <dev/usb/usb.h>
59: #include <dev/usb/usbdi.h>
60: #include <dev/usb/usbdi_util.h>
61:
62: #include <dev/usb/usbdevs.h>
63: #include <dev/usb/uaudioreg.h>
64: #include <dev/usb/umidireg.h>
65: #include <dev/usb/umidivar.h>
66: #include <dev/usb/umidi_quirks.h>
67:
68: #include <dev/midi_if.h>
69:
70: #ifdef UMIDI_DEBUG
71: #define DPRINTF(x) if (umididebug) printf x
72: #define DPRINTFN(n,x) if (umididebug >= (n)) printf x
1.23.2.2 yamt 73: #include <sys/time.h>
74: static struct timeval umidi_tv;
1.1 tshiozak 75: int umididebug = 0;
76: #else
77: #define DPRINTF(x)
78: #define DPRINTFN(n,x)
79: #endif
80:
81:
82: static int umidi_open(void *, int,
83: void (*)(void *, int), void (*)(void *), void *);
84: static void umidi_close(void *);
1.23.2.2 yamt 85: static int umidi_channelmsg(void *, int, int, u_char *, int);
86: static int umidi_commonmsg(void *, int, u_char *, int);
87: static int umidi_sysex(void *, u_char *, int);
88: static int umidi_rtmsg(void *, int);
1.1 tshiozak 89: static void umidi_getinfo(void *, struct midi_info *);
90:
1.3 tshiozak 91: static usbd_status alloc_pipe(struct umidi_endpoint *);
92: static void free_pipe(struct umidi_endpoint *);
1.1 tshiozak 93:
94: static usbd_status alloc_all_endpoints(struct umidi_softc *);
95: static void free_all_endpoints(struct umidi_softc *);
96:
97: static usbd_status alloc_all_jacks(struct umidi_softc *);
98: static void free_all_jacks(struct umidi_softc *);
99: static usbd_status bind_jacks_to_mididev(struct umidi_softc *,
100: struct umidi_jack *,
101: struct umidi_jack *,
102: struct umidi_mididev *);
1.4 tshiozak 103: static void unbind_jacks_from_mididev(struct umidi_mididev *);
104: static void unbind_all_jacks(struct umidi_softc *);
1.1 tshiozak 105: static usbd_status assign_all_jacks_automatically(struct umidi_softc *);
1.4 tshiozak 106: static usbd_status open_out_jack(struct umidi_jack *, void *,
107: void (*)(void *));
108: static usbd_status open_in_jack(struct umidi_jack *, void *,
109: void (*)(void *, int));
110: static void close_out_jack(struct umidi_jack *);
111: static void close_in_jack(struct umidi_jack *);
1.1 tshiozak 112:
1.23.2.2 yamt 113: static usbd_status attach_mididev(struct umidi_softc *, struct umidi_mididev *);
1.1 tshiozak 114: static usbd_status detach_mididev(struct umidi_mididev *, int);
115: static usbd_status deactivate_mididev(struct umidi_mididev *);
116: static usbd_status alloc_all_mididevs(struct umidi_softc *, int);
117: static void free_all_mididevs(struct umidi_softc *);
118: static usbd_status attach_all_mididevs(struct umidi_softc *);
119: static usbd_status detach_all_mididevs(struct umidi_softc *, int);
120: static usbd_status deactivate_all_mididevs(struct umidi_softc *);
1.23.2.2 yamt 121: static char *describe_mididev(struct umidi_mididev *);
1.1 tshiozak 122:
123: #ifdef UMIDI_DEBUG
124: static void dump_sc(struct umidi_softc *);
125: static void dump_ep(struct umidi_endpoint *);
126: static void dump_jack(struct umidi_jack *);
127: #endif
128:
129: static usbd_status start_input_transfer(struct umidi_endpoint *);
130: static usbd_status start_output_transfer(struct umidi_endpoint *);
1.23.2.2 yamt 131: static int out_jack_output(struct umidi_jack *, u_char *, int, int);
1.1 tshiozak 132: static void in_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
133: static void out_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
1.23.2.2 yamt 134: static void out_solicit(void *); /* struct umidi_endpoint* for softintr */
1.1 tshiozak 135:
136:
1.22 yamt 137: const struct midi_hw_if umidi_hw_if = {
1.1 tshiozak 138: umidi_open,
139: umidi_close,
1.23.2.2 yamt 140: umidi_rtmsg,
1.1 tshiozak 141: umidi_getinfo,
142: 0, /* ioctl */
143: };
144:
1.23.2.2 yamt 145: struct midi_hw_if_ext umidi_hw_if_ext = {
146: .channel = umidi_channelmsg,
147: .common = umidi_commonmsg,
148: .sysex = umidi_sysex,
149: };
150:
151: struct midi_hw_if_ext umidi_hw_if_mm = {
152: .channel = umidi_channelmsg,
153: .common = umidi_commonmsg,
154: .sysex = umidi_sysex,
155: .compress = 1,
156: };
157:
1.1 tshiozak 158: USB_DECLARE_DRIVER(umidi);
159:
160: USB_MATCH(umidi)
161: {
1.23.2.3 yamt 162: USB_IFMATCH_START(umidi, uaa);
1.1 tshiozak 163:
164: DPRINTFN(1,("umidi_match\n"));
165:
1.3 tshiozak 166: if (umidi_search_quirk(uaa->vendor, uaa->product, uaa->ifaceno))
167: return UMATCH_IFACECLASS_IFACESUBCLASS;
1.1 tshiozak 168:
1.23.2.3 yamt 169: if (uaa->class == UICLASS_AUDIO &&
170: uaa->subclass == UISUBCLASS_MIDISTREAM)
1.3 tshiozak 171: return UMATCH_IFACECLASS_IFACESUBCLASS;
1.1 tshiozak 172:
1.3 tshiozak 173: return UMATCH_NONE;
1.1 tshiozak 174: }
175:
176: USB_ATTACH(umidi)
177: {
178: usbd_status err;
1.23.2.3 yamt 179: USB_IFATTACH_START(umidi, sc, uaa);
1.23 augustss 180: char *devinfop;
1.1 tshiozak 181:
182: DPRINTFN(1,("umidi_attach\n"));
183:
1.23 augustss 184: devinfop = usbd_devinfo_alloc(uaa->device, 0);
185: printf("\n%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
186: usbd_devinfo_free(devinfop);
1.1 tshiozak 187:
188: sc->sc_iface = uaa->iface;
189: sc->sc_udev = uaa->device;
190:
191: sc->sc_quirk =
192: umidi_search_quirk(uaa->vendor, uaa->product, uaa->ifaceno);
193: printf("%s: ", USBDEVNAME(sc->sc_dev));
194: umidi_print_quirk(sc->sc_quirk);
195:
196:
197: err = alloc_all_endpoints(sc);
198: if (err!=USBD_NORMAL_COMPLETION) {
199: printf("%s: alloc_all_endpoints failed. (err=%d)\n",
200: USBDEVNAME(sc->sc_dev), err);
201: goto error;
202: }
203: err = alloc_all_jacks(sc);
204: if (err!=USBD_NORMAL_COMPLETION) {
205: free_all_endpoints(sc);
206: printf("%s: alloc_all_jacks failed. (err=%d)\n",
207: USBDEVNAME(sc->sc_dev), err);
208: goto error;
209: }
210: printf("%s: out=%d, in=%d\n",
211: USBDEVNAME(sc->sc_dev),
212: sc->sc_out_num_jacks, sc->sc_in_num_jacks);
213:
214: err = assign_all_jacks_automatically(sc);
215: if (err!=USBD_NORMAL_COMPLETION) {
216: unbind_all_jacks(sc);
217: free_all_jacks(sc);
218: free_all_endpoints(sc);
219: printf("%s: assign_all_jacks_automatically failed. (err=%d)\n",
220: USBDEVNAME(sc->sc_dev), err);
221: goto error;
222: }
223: err = attach_all_mididevs(sc);
224: if (err!=USBD_NORMAL_COMPLETION) {
225: free_all_jacks(sc);
226: free_all_endpoints(sc);
227: printf("%s: attach_all_mididevs failed. (err=%d)\n",
228: USBDEVNAME(sc->sc_dev), err);
229: }
230:
231: #ifdef UMIDI_DEBUG
232: dump_sc(sc);
233: #endif
234:
235: usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH,
236: sc->sc_udev, USBDEV(sc->sc_dev));
1.16 augustss 237:
1.1 tshiozak 238: USB_ATTACH_SUCCESS_RETURN;
239: error:
240: printf("%s: disabled.\n", USBDEVNAME(sc->sc_dev));
241: sc->sc_dying = 1;
242: USB_ATTACH_ERROR_RETURN;
243: }
244:
245: int
246: umidi_activate(device_ptr_t self, enum devact act)
247: {
1.3 tshiozak 248: struct umidi_softc *sc = (struct umidi_softc *)self;
1.1 tshiozak 249:
250: switch (act) {
251: case DVACT_ACTIVATE:
252: DPRINTFN(1,("umidi_activate (activate)\n"));
253:
254: return EOPNOTSUPP;
255: break;
256: case DVACT_DEACTIVATE:
257: DPRINTFN(1,("umidi_activate (deactivate)\n"));
258: sc->sc_dying = 1;
259: deactivate_all_mididevs(sc);
260: break;
261: }
262: return 0;
263: }
264:
265: USB_DETACH(umidi)
266: {
1.3 tshiozak 267: USB_DETACH_START(umidi, sc);
1.1 tshiozak 268:
269: DPRINTFN(1,("umidi_detach\n"));
270:
271: sc->sc_dying = 1;
272: detach_all_mididevs(sc, flags);
273: free_all_mididevs(sc);
274: free_all_jacks(sc);
275: free_all_endpoints(sc);
276:
1.3 tshiozak 277: usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
278: USBDEV(sc->sc_dev));
1.1 tshiozak 279:
280: return 0;
281: }
282:
283:
1.4 tshiozak 284: /*
285: * midi_if stuffs
286: */
1.1 tshiozak 287: int
288: umidi_open(void *addr,
289: int flags,
1.12 augustss 290: void (*iintr)(void *, int),
291: void (*ointr)(void *),
1.1 tshiozak 292: void *arg)
293: {
1.3 tshiozak 294: struct umidi_mididev *mididev = addr;
295: struct umidi_softc *sc = mididev->sc;
1.23.2.2 yamt 296: usbd_status err;
1.1 tshiozak 297:
298: DPRINTF(("umidi_open: sc=%p\n", sc));
299:
300: if (!sc)
301: return ENXIO;
302: if (mididev->opened)
303: return EBUSY;
1.4 tshiozak 304: if (sc->sc_dying)
1.1 tshiozak 305: return EIO;
306:
1.4 tshiozak 307: mididev->opened = 1;
1.1 tshiozak 308: mididev->flags = flags;
1.23.2.2 yamt 309: if ((mididev->flags & FWRITE) && mididev->out_jack) {
310: err = open_out_jack(mididev->out_jack, arg, ointr);
311: if ( err != USBD_NORMAL_COMPLETION )
312: goto bad;
313: }
1.3 tshiozak 314: if ((mididev->flags & FREAD) && mididev->in_jack) {
1.23.2.2 yamt 315: err = open_in_jack(mididev->in_jack, arg, iintr);
316: if ( err != USBD_NORMAL_COMPLETION
317: && err != USBD_IN_PROGRESS )
318: goto bad;
1.3 tshiozak 319: }
1.1 tshiozak 320:
321: return 0;
1.23.2.2 yamt 322: bad:
323: mididev->opened = 0;
324: DPRINTF(("umidi_open: usbd_status %d\n", err));
325: return USBD_IN_USE == err ? EBUSY : EIO;
1.1 tshiozak 326: }
327:
328: void
329: umidi_close(void *addr)
330: {
331: int s;
1.3 tshiozak 332: struct umidi_mididev *mididev = addr;
1.1 tshiozak 333:
334: s = splusb();
335: if ((mididev->flags & FWRITE) && mididev->out_jack)
1.4 tshiozak 336: close_out_jack(mididev->out_jack);
1.1 tshiozak 337: if ((mididev->flags & FREAD) && mididev->in_jack)
1.4 tshiozak 338: close_in_jack(mididev->in_jack);
1.1 tshiozak 339: mididev->opened = 0;
340: splx(s);
341: }
342:
343: int
1.23.2.2 yamt 344: umidi_channelmsg(void *addr, int status, int channel, u_char *msg,
345: int len)
346: {
347: struct umidi_mididev *mididev = addr;
348:
349: if (!mididev->out_jack || !mididev->opened)
350: return EIO;
351:
352: return out_jack_output(mididev->out_jack, msg, len, (status>>4)&0xf);
353: }
354:
355: int
356: umidi_commonmsg(void *addr, int status, u_char *msg, int len)
1.1 tshiozak 357: {
1.3 tshiozak 358: struct umidi_mididev *mididev = addr;
1.23.2.2 yamt 359: int cin;
1.1 tshiozak 360:
1.4 tshiozak 361: if (!mididev->out_jack || !mididev->opened)
1.1 tshiozak 362: return EIO;
363:
1.23.2.2 yamt 364: switch ( len ) {
365: case 1: cin = 5; break;
366: case 2: cin = 2; break;
367: case 3: cin = 3; break;
368: default: return EIO; /* or gcc warns of cin uninitialized */
369: }
370:
371: return out_jack_output(mididev->out_jack, msg, len, cin);
372: }
373:
374: int
375: umidi_sysex(void *addr, u_char *msg, int len)
376: {
377: struct umidi_mididev *mididev = addr;
378: int cin;
379:
380: if (!mididev->out_jack || !mididev->opened)
381: return EIO;
382:
383: switch ( len ) {
384: case 1: cin = 5; break;
385: case 2: cin = 6; break;
386: case 3: cin = (msg[2] == 0xf7) ? 7 : 4; break;
387: default: return EIO; /* or gcc warns of cin uninitialized */
388: }
389:
390: return out_jack_output(mididev->out_jack, msg, len, cin);
391: }
392:
393: int
394: umidi_rtmsg(void *addr, int d)
395: {
396: struct umidi_mididev *mididev = addr;
397: u_char msg = d;
398:
399: if (!mididev->out_jack || !mididev->opened)
400: return EIO;
401:
402: return out_jack_output(mididev->out_jack, &msg, 1, 0xf);
1.1 tshiozak 403: }
404:
405: void
406: umidi_getinfo(void *addr, struct midi_info *mi)
407: {
1.3 tshiozak 408: struct umidi_mididev *mididev = addr;
1.23.2.2 yamt 409: struct umidi_softc *sc = mididev->sc;
410: int mm = UMQ_ISTYPE(sc, UMQ_TYPE_MIDIMAN_GARBLE);
1.1 tshiozak 411:
1.23.2.2 yamt 412: mi->name = mididev->label;
1.3 tshiozak 413: mi->props = MIDI_PROP_OUT_INTR;
1.1 tshiozak 414: if (mididev->in_jack)
415: mi->props |= MIDI_PROP_CAN_INPUT;
1.23.2.2 yamt 416: midi_register_hw_if_ext(mm? &umidi_hw_if_mm : &umidi_hw_if_ext);
1.1 tshiozak 417: }
418:
419:
1.4 tshiozak 420: /*
421: * each endpoint stuffs
422: */
1.1 tshiozak 423:
1.3 tshiozak 424: /* alloc/free pipe */
1.1 tshiozak 425: static usbd_status
1.3 tshiozak 426: alloc_pipe(struct umidi_endpoint *ep)
1.1 tshiozak 427: {
428: struct umidi_softc *sc = ep->sc;
429: usbd_status err;
1.23.2.2 yamt 430: usb_endpoint_descriptor_t *epd;
431:
432: epd = usbd_get_endpoint_descriptor(sc->sc_iface, ep->addr);
433: /*
434: * For output, an improvement would be to have a buffer bigger than
435: * wMaxPacketSize by num_jacks-1 additional packet slots; that would
436: * allow out_solicit to fill the buffer to the full packet size in
437: * all cases. But to use usbd_alloc_buffer to get a slightly larger
438: * buffer would not be a good way to do that, because if the addition
439: * would make the buffer exceed USB_MEM_SMALL then a substantially
440: * larger block may be wastefully allocated. Some flavor of double
441: * buffering could serve the same purpose, but would increase the
442: * code complexity, so for now I will live with the current slight
443: * penalty of reducing max transfer size by (num_open-num_scheduled)
444: * packet slots.
445: */
446: ep->buffer_size = UGETW(epd->wMaxPacketSize);
447: ep->buffer_size -= ep->buffer_size % UMIDI_PACKET_SIZE;
448:
449: DPRINTF(("%s: alloc_pipe %p, buffer size %u\n",
450: USBDEVNAME(sc->sc_dev), ep, ep->buffer_size));
451: ep->num_scheduled = 0;
452: ep->this_schedule = 0;
453: ep->next_schedule = 0;
454: ep->soliciting = 0;
455: ep->armed = 0;
1.3 tshiozak 456: ep->xfer = usbd_alloc_xfer(sc->sc_udev);
1.11 augustss 457: if (ep->xfer == NULL) {
1.3 tshiozak 458: err = USBD_NOMEM;
459: goto quit;
460: }
1.23.2.2 yamt 461: ep->buffer = usbd_alloc_buffer(ep->xfer, ep->buffer_size);
1.11 augustss 462: if (ep->buffer == NULL) {
1.3 tshiozak 463: usbd_free_xfer(ep->xfer);
464: err = USBD_NOMEM;
465: goto quit;
466: }
1.23.2.2 yamt 467: ep->next_slot = ep->buffer;
1.3 tshiozak 468: err = usbd_open_pipe(sc->sc_iface, ep->addr, 0, &ep->pipe);
469: if (err)
470: usbd_free_xfer(ep->xfer);
1.23.2.4! yamt 471: ep->solicit_cookie = softint_establish(SOFTINT_CLOCK, out_solicit, ep);
1.1 tshiozak 472: quit:
473: return err;
474: }
475:
476: static void
1.3 tshiozak 477: free_pipe(struct umidi_endpoint *ep)
1.1 tshiozak 478: {
1.3 tshiozak 479: DPRINTF(("%s: free_pipe %p\n", USBDEVNAME(ep->sc->sc_dev), ep));
480: usbd_abort_pipe(ep->pipe);
481: usbd_close_pipe(ep->pipe);
482: usbd_free_xfer(ep->xfer);
1.23.2.4! yamt 483: softint_disestablish(ep->solicit_cookie);
1.1 tshiozak 484: }
485:
486:
487: /* alloc/free the array of endpoint structures */
488:
489: static usbd_status alloc_all_endpoints_fixed_ep(struct umidi_softc *);
490: static usbd_status alloc_all_endpoints_yamaha(struct umidi_softc *);
491: static usbd_status alloc_all_endpoints_genuine(struct umidi_softc *);
492:
493: static usbd_status
494: alloc_all_endpoints(struct umidi_softc *sc)
495: {
1.3 tshiozak 496: usbd_status err;
497: struct umidi_endpoint *ep;
498: int i;
1.9 tshiozak 499:
1.3 tshiozak 500: if (UMQ_ISTYPE(sc, UMQ_TYPE_FIXED_EP)) {
501: err = alloc_all_endpoints_fixed_ep(sc);
502: } else if (UMQ_ISTYPE(sc, UMQ_TYPE_YAMAHA)) {
503: err = alloc_all_endpoints_yamaha(sc);
1.1 tshiozak 504: } else {
1.3 tshiozak 505: err = alloc_all_endpoints_genuine(sc);
1.1 tshiozak 506: }
1.3 tshiozak 507: if (err!=USBD_NORMAL_COMPLETION)
508: return err;
509:
510: ep = sc->sc_endpoints;
1.8 tshiozak 511: for (i=sc->sc_out_num_endpoints+sc->sc_in_num_endpoints; i>0; i--) {
1.3 tshiozak 512: err = alloc_pipe(ep++);
513: if (err!=USBD_NORMAL_COMPLETION) {
514: for (; ep!=sc->sc_endpoints; ep--)
515: free_pipe(ep-1);
516: free(sc->sc_endpoints, M_USBDEV);
517: sc->sc_endpoints = sc->sc_out_ep = sc->sc_in_ep = NULL;
518: break;
519: }
520: }
521: return err;
1.1 tshiozak 522: }
523:
524: static void
525: free_all_endpoints(struct umidi_softc *sc)
526: {
1.3 tshiozak 527: int i;
1.8 tshiozak 528: for (i=0; i<sc->sc_in_num_endpoints+sc->sc_out_num_endpoints; i++)
1.3 tshiozak 529: free_pipe(&sc->sc_endpoints[i]);
1.14 kent 530: if (sc->sc_endpoints != NULL)
531: free(sc->sc_endpoints, M_USBDEV);
1.1 tshiozak 532: sc->sc_endpoints = sc->sc_out_ep = sc->sc_in_ep = NULL;
533: }
534:
535: static usbd_status
536: alloc_all_endpoints_fixed_ep(struct umidi_softc *sc)
537: {
1.3 tshiozak 538: usbd_status err;
1.1 tshiozak 539: struct umq_fixed_ep_desc *fp;
1.3 tshiozak 540: struct umidi_endpoint *ep;
1.1 tshiozak 541: usb_endpoint_descriptor_t *epd;
542: int i;
543:
544: fp = umidi_get_quirk_data_from_type(sc->sc_quirk,
545: UMQ_TYPE_FIXED_EP);
546: sc->sc_out_num_jacks = 0;
547: sc->sc_in_num_jacks = 0;
548: sc->sc_out_num_endpoints = fp->num_out_ep;
549: sc->sc_in_num_endpoints = fp->num_in_ep;
550: sc->sc_endpoints = malloc(sizeof(*sc->sc_out_ep)*
551: (sc->sc_out_num_endpoints+
552: sc->sc_in_num_endpoints),
553: M_USBDEV, M_WAITOK);
554: if (!sc->sc_endpoints) {
555: return USBD_NOMEM;
556: }
557: sc->sc_out_ep = sc->sc_out_num_endpoints ? sc->sc_endpoints : NULL;
558: sc->sc_in_ep =
559: sc->sc_in_num_endpoints ?
560: sc->sc_endpoints+sc->sc_out_num_endpoints : NULL;
1.3 tshiozak 561:
562: ep = &sc->sc_out_ep[0];
1.1 tshiozak 563: for (i=0; i<sc->sc_out_num_endpoints; i++) {
564: epd = usbd_interface2endpoint_descriptor(
565: sc->sc_iface,
566: fp->out_ep[i].ep);
567: if (!epd) {
568: printf("%s: cannot get endpoint descriptor(out:%d)\n",
569: USBDEVNAME(sc->sc_dev), fp->out_ep[i].ep);
1.3 tshiozak 570: err = USBD_INVAL;
1.1 tshiozak 571: goto error;
572: }
573: if (UE_GET_XFERTYPE(epd->bmAttributes)!=UE_BULK ||
574: UE_GET_DIR(epd->bEndpointAddress)!=UE_DIR_OUT) {
575: printf("%s: illegal endpoint(out:%d)\n",
576: USBDEVNAME(sc->sc_dev), fp->out_ep[i].ep);
1.3 tshiozak 577: err = USBD_INVAL;
1.1 tshiozak 578: goto error;
579: }
1.3 tshiozak 580: ep->sc = sc;
581: ep->addr = epd->bEndpointAddress;
582: ep->num_jacks = fp->out_ep[i].num_jacks;
1.1 tshiozak 583: sc->sc_out_num_jacks += fp->out_ep[i].num_jacks;
1.3 tshiozak 584: ep->num_open = 0;
585: memset(ep->jacks, 0, sizeof(ep->jacks));
586: ep++;
1.1 tshiozak 587: }
1.3 tshiozak 588: ep = &sc->sc_in_ep[0];
1.1 tshiozak 589: for (i=0; i<sc->sc_in_num_endpoints; i++) {
590: epd = usbd_interface2endpoint_descriptor(
591: sc->sc_iface,
592: fp->in_ep[i].ep);
593: if (!epd) {
594: printf("%s: cannot get endpoint descriptor(in:%d)\n",
595: USBDEVNAME(sc->sc_dev), fp->in_ep[i].ep);
1.3 tshiozak 596: err = USBD_INVAL;
1.1 tshiozak 597: goto error;
598: }
1.23.2.2 yamt 599: /*
600: * MIDISPORT_2X4 inputs on an interrupt rather than a bulk
601: * endpoint. The existing input logic in this driver seems
602: * to work successfully if we just stop treating an interrupt
603: * endpoint as illegal (or the in_progress status we get on
604: * the initial transfer). It does not seem necessary to
605: * actually use the interrupt flavor of alloc_pipe or make
606: * other serious rearrangements of logic. I like that.
607: */
608: switch ( UE_GET_XFERTYPE(epd->bmAttributes) ) {
609: case UE_BULK:
610: case UE_INTERRUPT:
611: if ( UE_DIR_IN == UE_GET_DIR(epd->bEndpointAddress) )
612: break;
613: /*FALLTHROUGH*/
614: default:
1.1 tshiozak 615: printf("%s: illegal endpoint(in:%d)\n",
616: USBDEVNAME(sc->sc_dev), fp->in_ep[i].ep);
1.3 tshiozak 617: err = USBD_INVAL;
1.1 tshiozak 618: goto error;
619: }
1.23.2.2 yamt 620:
1.3 tshiozak 621: ep->sc = sc;
622: ep->addr = epd->bEndpointAddress;
623: ep->num_jacks = fp->in_ep[i].num_jacks;
1.1 tshiozak 624: sc->sc_in_num_jacks += fp->in_ep[i].num_jacks;
1.3 tshiozak 625: ep->num_open = 0;
626: memset(ep->jacks, 0, sizeof(ep->jacks));
627: ep++;
1.1 tshiozak 628: }
629:
630: return USBD_NORMAL_COMPLETION;
631: error:
632: free(sc->sc_endpoints, M_USBDEV);
633: sc->sc_endpoints = NULL;
1.3 tshiozak 634: return err;
1.1 tshiozak 635: }
636:
637: static usbd_status
638: alloc_all_endpoints_yamaha(struct umidi_softc *sc)
639: {
640: /* This driver currently supports max 1in/1out bulk endpoints */
641: usb_descriptor_t *desc;
1.23.2.3 yamt 642: umidi_cs_descriptor_t *udesc;
1.8 tshiozak 643: usb_endpoint_descriptor_t *epd;
644: int out_addr, in_addr, i;
645: int dir;
1.1 tshiozak 646: size_t remain, descsize;
647:
1.8 tshiozak 648: sc->sc_out_num_jacks = sc->sc_in_num_jacks = 0;
649: out_addr = in_addr = 0;
650:
651: /* detect endpoints */
1.1 tshiozak 652: desc = TO_D(usbd_get_interface_descriptor(sc->sc_iface));
1.8 tshiozak 653: for (i=(int)TO_IFD(desc)->bNumEndpoints-1; i>=0; i--) {
654: epd = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
1.23.2.1 yamt 655: KASSERT(epd != NULL);
1.8 tshiozak 656: if (UE_GET_XFERTYPE(epd->bmAttributes) == UE_BULK) {
657: dir = UE_GET_DIR(epd->bEndpointAddress);
658: if (dir==UE_DIR_OUT && !out_addr)
659: out_addr = epd->bEndpointAddress;
660: else if (dir==UE_DIR_IN && !in_addr)
661: in_addr = epd->bEndpointAddress;
662: }
663: }
1.23.2.3 yamt 664: udesc = (umidi_cs_descriptor_t *)NEXT_D(desc);
1.1 tshiozak 665:
1.8 tshiozak 666: /* count jacks */
1.23.2.3 yamt 667: if (!(udesc->bDescriptorType==UDESC_CS_INTERFACE &&
668: udesc->bDescriptorSubtype==UMIDI_MS_HEADER))
1.8 tshiozak 669: return USBD_INVAL;
1.23.2.3 yamt 670: remain = (size_t)UGETW(TO_CSIFD(udesc)->wTotalLength) -
671: (size_t)udesc->bLength;
672: udesc = (umidi_cs_descriptor_t *)NEXT_D(udesc);
1.1 tshiozak 673:
674: while (remain>=sizeof(usb_descriptor_t)) {
1.23.2.3 yamt 675: descsize = udesc->bLength;
1.1 tshiozak 676: if (descsize>remain || descsize==0)
677: break;
1.23.2.3 yamt 678: if (udesc->bDescriptorType==UDESC_CS_INTERFACE &&
1.1 tshiozak 679: remain>=UMIDI_JACK_DESCRIPTOR_SIZE) {
1.23.2.3 yamt 680: if (udesc->bDescriptorSubtype==UMIDI_OUT_JACK)
1.1 tshiozak 681: sc->sc_out_num_jacks++;
1.23.2.3 yamt 682: else if (udesc->bDescriptorSubtype==UMIDI_IN_JACK)
1.1 tshiozak 683: sc->sc_in_num_jacks++;
684: }
1.23.2.3 yamt 685: udesc = (umidi_cs_descriptor_t *)NEXT_D(udesc);
1.1 tshiozak 686: remain-=descsize;
687: }
688:
1.8 tshiozak 689: /* validate some parameters */
1.1 tshiozak 690: if (sc->sc_out_num_jacks>UMIDI_MAX_EPJACKS)
691: sc->sc_out_num_jacks = UMIDI_MAX_EPJACKS;
692: if (sc->sc_in_num_jacks>UMIDI_MAX_EPJACKS)
693: sc->sc_in_num_jacks = UMIDI_MAX_EPJACKS;
1.8 tshiozak 694: if (sc->sc_out_num_jacks && out_addr) {
1.1 tshiozak 695: sc->sc_out_num_endpoints = 1;
696: } else {
697: sc->sc_out_num_endpoints = 0;
698: sc->sc_out_num_jacks = 0;
699: }
1.8 tshiozak 700: if (sc->sc_in_num_jacks && in_addr) {
1.1 tshiozak 701: sc->sc_in_num_endpoints = 1;
702: } else {
703: sc->sc_in_num_endpoints = 0;
704: sc->sc_in_num_jacks = 0;
705: }
706: sc->sc_endpoints = malloc(sizeof(struct umidi_endpoint)*
707: (sc->sc_out_num_endpoints+
708: sc->sc_in_num_endpoints),
709: M_USBDEV, M_WAITOK);
710: if (!sc->sc_endpoints)
711: return USBD_NOMEM;
712: if (sc->sc_out_num_endpoints) {
713: sc->sc_out_ep = sc->sc_endpoints;
714: sc->sc_out_ep->sc = sc;
715: sc->sc_out_ep->addr = out_addr;
716: sc->sc_out_ep->num_jacks = sc->sc_out_num_jacks;
717: sc->sc_out_ep->num_open = 0;
718: memset(sc->sc_out_ep->jacks, 0, sizeof(sc->sc_out_ep->jacks));
719: } else
720: sc->sc_out_ep = NULL;
721:
722: if (sc->sc_in_num_endpoints) {
723: sc->sc_in_ep = sc->sc_endpoints+sc->sc_out_num_endpoints;
724: sc->sc_in_ep->sc = sc;
725: sc->sc_in_ep->addr = in_addr;
726: sc->sc_in_ep->num_jacks = sc->sc_in_num_jacks;
727: sc->sc_in_ep->num_open = 0;
728: memset(sc->sc_in_ep->jacks, 0, sizeof(sc->sc_in_ep->jacks));
729: } else
730: sc->sc_in_ep = NULL;
731:
732: return USBD_NORMAL_COMPLETION;
733: }
734:
735: static usbd_status
736: alloc_all_endpoints_genuine(struct umidi_softc *sc)
737: {
1.20 gson 738: usb_interface_descriptor_t *interface_desc;
739: usb_config_descriptor_t *config_desc;
1.1 tshiozak 740: usb_descriptor_t *desc;
1.7 tshiozak 741: int num_ep;
1.1 tshiozak 742: size_t remain, descsize;
743: struct umidi_endpoint *p, *q, *lowest, *endep, tmpep;
744: int epaddr;
745:
1.20 gson 746: interface_desc = usbd_get_interface_descriptor(sc->sc_iface);
747: num_ep = interface_desc->bNumEndpoints;
748: sc->sc_endpoints = p = malloc(sizeof(struct umidi_endpoint) * num_ep,
1.1 tshiozak 749: M_USBDEV, M_WAITOK);
750: if (!p)
751: return USBD_NOMEM;
752:
753: sc->sc_out_num_jacks = sc->sc_in_num_jacks = 0;
754: sc->sc_out_num_endpoints = sc->sc_in_num_endpoints = 0;
755: epaddr = -1;
756:
757: /* get the list of endpoints for midi stream */
1.20 gson 758: config_desc = usbd_get_config_descriptor(sc->sc_udev);
759: desc = (usb_descriptor_t *) config_desc;
760: remain = (size_t)UGETW(config_desc->wTotalLength);
1.1 tshiozak 761: while (remain>=sizeof(usb_descriptor_t)) {
762: descsize = desc->bLength;
763: if (descsize>remain || descsize==0)
764: break;
765: if (desc->bDescriptorType==UDESC_ENDPOINT &&
766: remain>=USB_ENDPOINT_DESCRIPTOR_SIZE &&
767: UE_GET_XFERTYPE(TO_EPD(desc)->bmAttributes) == UE_BULK) {
768: epaddr = TO_EPD(desc)->bEndpointAddress;
769: } else if (desc->bDescriptorType==UDESC_CS_ENDPOINT &&
770: remain>=UMIDI_CS_ENDPOINT_DESCRIPTOR_SIZE &&
771: epaddr!=-1) {
772: if (num_ep>0) {
1.9 tshiozak 773: num_ep--;
1.1 tshiozak 774: p->sc = sc;
775: p->addr = epaddr;
776: p->num_jacks = TO_CSEPD(desc)->bNumEmbMIDIJack;
777: if (UE_GET_DIR(epaddr)==UE_DIR_OUT) {
778: sc->sc_out_num_endpoints++;
779: sc->sc_out_num_jacks += p->num_jacks;
780: } else {
781: sc->sc_in_num_endpoints++;
782: sc->sc_in_num_jacks += p->num_jacks;
783: }
784: p++;
785: }
786: } else
787: epaddr = -1;
788: desc = NEXT_D(desc);
789: remain-=descsize;
790: }
791:
792: /* sort endpoints */
793: num_ep = sc->sc_out_num_endpoints + sc->sc_in_num_endpoints;
794: p = sc->sc_endpoints;
795: endep = p + num_ep;
796: while (p<endep) {
797: lowest = p;
798: for (q=p+1; q<endep; q++) {
799: if ((UE_GET_DIR(lowest->addr)==UE_DIR_IN &&
800: UE_GET_DIR(q->addr)==UE_DIR_OUT) ||
801: ((UE_GET_DIR(lowest->addr)==
802: UE_GET_DIR(q->addr)) &&
803: (UE_GET_ADDR(lowest->addr)>
804: UE_GET_ADDR(q->addr))))
805: lowest = q;
806: }
807: if (lowest != p) {
808: memcpy((void *)&tmpep, (void *)p, sizeof(tmpep));
809: memcpy((void *)p, (void *)lowest, sizeof(tmpep));
810: memcpy((void *)lowest, (void *)&tmpep, sizeof(tmpep));
811: }
1.7 tshiozak 812: p->num_open = 0;
813: p++;
1.1 tshiozak 814: }
1.16 augustss 815:
1.1 tshiozak 816: sc->sc_out_ep = sc->sc_out_num_endpoints ? sc->sc_endpoints : NULL;
817: sc->sc_in_ep =
818: sc->sc_in_num_endpoints ?
819: sc->sc_endpoints+sc->sc_out_num_endpoints : NULL;
820:
821: return USBD_NORMAL_COMPLETION;
822: }
823:
824:
1.4 tshiozak 825: /*
826: * jack stuffs
827: */
828:
1.1 tshiozak 829: static usbd_status
830: alloc_all_jacks(struct umidi_softc *sc)
831: {
832: int i, j;
833: struct umidi_endpoint *ep;
1.23.2.2 yamt 834: struct umidi_jack *jack;
835: unsigned char *cn_spec;
836:
837: if (UMQ_ISTYPE(sc, UMQ_TYPE_CN_SEQ_PER_EP))
838: sc->cblnums_global = 0;
839: else if (UMQ_ISTYPE(sc, UMQ_TYPE_CN_SEQ_GLOBAL))
840: sc->cblnums_global = 1;
841: else {
842: /*
843: * I don't think this default is correct, but it preserves
844: * the prior behavior of the code. That's why I defined two
845: * complementary quirks. Any device for which the default
846: * behavior is wrong can be made to work by giving it an
847: * explicit quirk, and if a pattern ever develops (as I suspect
848: * it will) that a lot of otherwise standard USB MIDI devices
849: * need the CN_SEQ_PER_EP "quirk," then this default can be
850: * changed to 0, and the only devices that will break are those
851: * listing neither quirk, and they'll easily be fixed by giving
852: * them the CN_SEQ_GLOBAL quirk.
853: */
854: sc->cblnums_global = 1;
855: }
856:
857: if (UMQ_ISTYPE(sc, UMQ_TYPE_CN_FIXED))
858: cn_spec = umidi_get_quirk_data_from_type(sc->sc_quirk,
859: UMQ_TYPE_CN_FIXED);
860: else
861: cn_spec = NULL;
1.1 tshiozak 862:
863: /* allocate/initialize structures */
864: sc->sc_jacks =
865: malloc(sizeof(*sc->sc_out_jacks)*(sc->sc_in_num_jacks+
866: sc->sc_out_num_jacks),
867: M_USBDEV, M_WAITOK);
868: if (!sc->sc_jacks)
869: return USBD_NOMEM;
870: sc->sc_out_jacks =
871: sc->sc_out_num_jacks ? sc->sc_jacks : NULL;
872: sc->sc_in_jacks =
873: sc->sc_in_num_jacks ? sc->sc_jacks+sc->sc_out_num_jacks : NULL;
874:
875: jack = &sc->sc_out_jacks[0];
876: for (i=0; i<sc->sc_out_num_jacks; i++) {
1.4 tshiozak 877: jack->opened = 0;
878: jack->binded = 0;
879: jack->arg = NULL;
880: jack->u.out.intr = NULL;
1.23.2.2 yamt 881: jack->midiman_ppkt = NULL;
882: if ( sc->cblnums_global )
883: jack->cable_number = i;
1.1 tshiozak 884: jack++;
885: }
886: jack = &sc->sc_in_jacks[0];
887: for (i=0; i<sc->sc_in_num_jacks; i++) {
1.4 tshiozak 888: jack->opened = 0;
889: jack->binded = 0;
890: jack->arg = NULL;
891: jack->u.in.intr = NULL;
1.23.2.2 yamt 892: if ( sc->cblnums_global )
893: jack->cable_number = i;
1.1 tshiozak 894: jack++;
895: }
896:
897: /* assign each jacks to each endpoints */
898: jack = &sc->sc_out_jacks[0];
899: ep = &sc->sc_out_ep[0];
900: for (i=0; i<sc->sc_out_num_endpoints; i++) {
901: for (j=0; j<ep->num_jacks; j++) {
902: jack->endpoint = ep;
1.23.2.2 yamt 903: if ( cn_spec != NULL )
904: jack->cable_number = *cn_spec++;
905: else if ( !sc->cblnums_global )
906: jack->cable_number = j;
907: ep->jacks[jack->cable_number] = jack;
1.1 tshiozak 908: jack++;
909: }
910: ep++;
911: }
912: jack = &sc->sc_in_jacks[0];
913: ep = &sc->sc_in_ep[0];
914: for (i=0; i<sc->sc_in_num_endpoints; i++) {
915: for (j=0; j<ep->num_jacks; j++) {
916: jack->endpoint = ep;
1.23.2.2 yamt 917: if ( cn_spec != NULL )
918: jack->cable_number = *cn_spec++;
919: else if ( !sc->cblnums_global )
920: jack->cable_number = j;
921: ep->jacks[jack->cable_number] = jack;
1.1 tshiozak 922: jack++;
923: }
924: ep++;
925: }
926:
927: return USBD_NORMAL_COMPLETION;
928: }
929:
930: static void
931: free_all_jacks(struct umidi_softc *sc)
932: {
933: int s;
934:
935: s = splaudio();
936: if (sc->sc_out_jacks) {
937: free(sc->sc_jacks, M_USBDEV);
938: sc->sc_jacks = sc->sc_in_jacks = sc->sc_out_jacks = NULL;
939: }
940: splx(s);
941: }
942:
943: static usbd_status
944: bind_jacks_to_mididev(struct umidi_softc *sc,
945: struct umidi_jack *out_jack,
946: struct umidi_jack *in_jack,
947: struct umidi_mididev *mididev)
948: {
1.8 tshiozak 949: if ((out_jack && out_jack->binded) || (in_jack && in_jack->binded))
1.4 tshiozak 950: return USBD_IN_USE;
951: if (mididev->out_jack || mididev->in_jack)
1.1 tshiozak 952: return USBD_IN_USE;
953:
1.4 tshiozak 954: if (out_jack)
955: out_jack->binded = 1;
956: if (in_jack)
957: in_jack->binded = 1;
1.1 tshiozak 958: mididev->in_jack = in_jack;
959: mididev->out_jack = out_jack;
960:
961: return USBD_NORMAL_COMPLETION;
962: }
963:
1.4 tshiozak 964: static void
1.1 tshiozak 965: unbind_jacks_from_mididev(struct umidi_mididev *mididev)
966: {
1.15 tshiozak 967: if ((mididev->flags & FWRITE) && mididev->out_jack)
1.4 tshiozak 968: close_out_jack(mididev->out_jack);
1.15 tshiozak 969: if ((mididev->flags & FREAD) && mididev->in_jack)
1.4 tshiozak 970: close_in_jack(mididev->in_jack);
1.1 tshiozak 971:
1.4 tshiozak 972: if (mididev->out_jack)
973: mididev->out_jack->binded = 0;
974: if (mididev->in_jack)
975: mididev->in_jack->binded = 0;
976: mididev->out_jack = mididev->in_jack = NULL;
1.1 tshiozak 977: }
978:
1.4 tshiozak 979: static void
1.1 tshiozak 980: unbind_all_jacks(struct umidi_softc *sc)
981: {
982: int i;
983:
984: if (sc->sc_mididevs)
985: for (i=0; i<sc->sc_num_mididevs; i++) {
1.4 tshiozak 986: unbind_jacks_from_mididev(&sc->sc_mididevs[i]);
1.1 tshiozak 987: }
988: }
989:
990: static usbd_status
991: assign_all_jacks_automatically(struct umidi_softc *sc)
992: {
993: usbd_status err;
994: int i;
995: struct umidi_jack *out, *in;
1.23.2.2 yamt 996: signed char *asg_spec;
1.1 tshiozak 997:
998: err =
999: alloc_all_mididevs(sc,
1000: max(sc->sc_out_num_jacks, sc->sc_in_num_jacks));
1001: if (err!=USBD_NORMAL_COMPLETION)
1002: return err;
1003:
1.23.2.2 yamt 1004: if ( UMQ_ISTYPE(sc, UMQ_TYPE_MD_FIXED))
1005: asg_spec = umidi_get_quirk_data_from_type(sc->sc_quirk,
1006: UMQ_TYPE_MD_FIXED);
1007: else
1008: asg_spec = NULL;
1009:
1.1 tshiozak 1010: for (i=0; i<sc->sc_num_mididevs; i++) {
1.23.2.2 yamt 1011: if ( asg_spec != NULL ) {
1012: if ( *asg_spec == -1 )
1013: out = NULL;
1014: else
1015: out = &sc->sc_out_jacks[*asg_spec];
1016: ++ asg_spec;
1017: if ( *asg_spec == -1 )
1018: in = NULL;
1019: else
1020: in = &sc->sc_in_jacks[*asg_spec];
1021: ++ asg_spec;
1022: } else {
1023: out = (i<sc->sc_out_num_jacks) ? &sc->sc_out_jacks[i]
1024: : NULL;
1025: in = (i<sc->sc_in_num_jacks) ? &sc->sc_in_jacks[i]
1026: : NULL;
1027: }
1.1 tshiozak 1028: err = bind_jacks_to_mididev(sc, out, in, &sc->sc_mididevs[i]);
1029: if (err!=USBD_NORMAL_COMPLETION) {
1030: free_all_mididevs(sc);
1031: return err;
1032: }
1033: }
1034:
1035: return USBD_NORMAL_COMPLETION;
1036: }
1037:
1038: static usbd_status
1.4 tshiozak 1039: open_out_jack(struct umidi_jack *jack, void *arg, void (*intr)(void *))
1040: {
1041: struct umidi_endpoint *ep = jack->endpoint;
1.23.2.2 yamt 1042: umidi_packet_bufp end;
1043: int s;
1044: int err;
1.4 tshiozak 1045:
1046: if (jack->opened)
1047: return USBD_IN_USE;
1048:
1049: jack->arg = arg;
1050: jack->u.out.intr = intr;
1.23.2.2 yamt 1051: jack->midiman_ppkt = NULL;
1052: end = ep->buffer + ep->buffer_size / sizeof *ep->buffer;
1053: s = splusb();
1.4 tshiozak 1054: jack->opened = 1;
1055: ep->num_open++;
1.23.2.2 yamt 1056: /*
1057: * out_solicit maintains an invariant that there will always be
1058: * (num_open - num_scheduled) slots free in the buffer. as we have
1059: * just incremented num_open, the buffer may be too full to satisfy
1060: * the invariant until a transfer completes, for which we must wait.
1061: */
1062: while ( end - ep->next_slot < ep->num_open - ep->num_scheduled ) {
1063: err = tsleep(ep, PWAIT|PCATCH, "umi op", mstohz(10));
1064: if ( err ) {
1065: ep->num_open--;
1066: jack->opened = 0;
1067: splx(s);
1068: return USBD_IOERROR;
1069: }
1070: }
1071: splx(s);
1.4 tshiozak 1072:
1073: return USBD_NORMAL_COMPLETION;
1074: }
1075:
1076: static usbd_status
1077: open_in_jack(struct umidi_jack *jack, void *arg, void (*intr)(void *, int))
1.1 tshiozak 1078: {
1.3 tshiozak 1079: usbd_status err = USBD_NORMAL_COMPLETION;
1080: struct umidi_endpoint *ep = jack->endpoint;
1081:
1.4 tshiozak 1082: if (jack->opened)
1083: return USBD_IN_USE;
1084:
1085: jack->arg = arg;
1086: jack->u.in.intr = intr;
1087: jack->opened = 1;
1.3 tshiozak 1088: if (ep->num_open++==0 && UE_GET_DIR(ep->addr)==UE_DIR_IN) {
1089: err = start_input_transfer(ep);
1.18 gson 1090: if (err != USBD_NORMAL_COMPLETION &&
1091: err != USBD_IN_PROGRESS) {
1.3 tshiozak 1092: ep->num_open--;
1093: }
1094: }
1095:
1096: return err;
1.1 tshiozak 1097: }
1098:
1099: static void
1.4 tshiozak 1100: close_out_jack(struct umidi_jack *jack)
1.1 tshiozak 1101: {
1.23.2.2 yamt 1102: struct umidi_endpoint *ep;
1.3 tshiozak 1103: int s;
1.23.2.2 yamt 1104: u_int16_t mask;
1105: int err;
1.4 tshiozak 1106:
1107: if (jack->opened) {
1.23.2.2 yamt 1108: ep = jack->endpoint;
1109: mask = 1 << (jack->cable_number);
1.4 tshiozak 1110: s = splusb();
1.23.2.2 yamt 1111: while ( mask & (ep->this_schedule | ep->next_schedule) ) {
1112: err = tsleep(ep, PWAIT|PCATCH, "umi dr", mstohz(10));
1113: if ( err )
1.15 tshiozak 1114: break;
1.3 tshiozak 1115: }
1.4 tshiozak 1116: jack->opened = 0;
1117: jack->endpoint->num_open--;
1.23.2.2 yamt 1118: ep->this_schedule &= ~mask;
1119: ep->next_schedule &= ~mask;
1120: splx(s);
1.3 tshiozak 1121: }
1.1 tshiozak 1122: }
1123:
1.4 tshiozak 1124: static void
1125: close_in_jack(struct umidi_jack *jack)
1126: {
1127: if (jack->opened) {
1128: jack->opened = 0;
1.19 gson 1129: if (--jack->endpoint->num_open == 0) {
1130: usbd_abort_pipe(jack->endpoint->pipe);
1131: }
1.4 tshiozak 1132: }
1133: }
1.1 tshiozak 1134:
1135: static usbd_status
1136: attach_mididev(struct umidi_softc *sc, struct umidi_mididev *mididev)
1137: {
1138: if (mididev->sc)
1139: return USBD_IN_USE;
1140:
1141: mididev->sc = sc;
1.23.2.2 yamt 1142:
1143: mididev->label = describe_mididev(mididev);
1.1 tshiozak 1144:
1145: mididev->mdev = midi_attach_mi(&umidi_hw_if, mididev, &sc->sc_dev);
1146:
1147: return USBD_NORMAL_COMPLETION;
1148: }
1149:
1150: static usbd_status
1151: detach_mididev(struct umidi_mididev *mididev, int flags)
1152: {
1153: if (!mididev->sc)
1154: return USBD_NO_ADDR;
1155:
1156: if (mididev->opened) {
1157: umidi_close(mididev);
1158: }
1.4 tshiozak 1159: unbind_jacks_from_mididev(mididev);
1.1 tshiozak 1160:
1161: if (mididev->mdev)
1162: config_detach(mididev->mdev, flags);
1.23.2.2 yamt 1163:
1164: if (NULL != mididev->label) {
1165: free(mididev->label, M_USBDEV);
1166: mididev->label = NULL;
1167: }
1.1 tshiozak 1168:
1169: mididev->sc = NULL;
1170:
1171: return USBD_NORMAL_COMPLETION;
1172: }
1173:
1174: static usbd_status
1175: deactivate_mididev(struct umidi_mididev *mididev)
1176: {
1.4 tshiozak 1177: if (mididev->out_jack)
1178: mididev->out_jack->binded = 0;
1179: if (mididev->in_jack)
1180: mididev->in_jack->binded = 0;
1.1 tshiozak 1181: config_deactivate(mididev->mdev);
1182:
1183: return USBD_NORMAL_COMPLETION;
1184: }
1185:
1186: static usbd_status
1187: alloc_all_mididevs(struct umidi_softc *sc, int nmidi)
1188: {
1189: sc->sc_num_mididevs = nmidi;
1190: sc->sc_mididevs = malloc(sizeof(*sc->sc_mididevs)*nmidi,
1.13 tsutsui 1191: M_USBDEV, M_WAITOK|M_ZERO);
1.1 tshiozak 1192: if (!sc->sc_mididevs)
1193: return USBD_NOMEM;
1194:
1195: return USBD_NORMAL_COMPLETION;
1196: }
1197:
1198: static void
1199: free_all_mididevs(struct umidi_softc *sc)
1200: {
1201: sc->sc_num_mididevs = 0;
1202: if (sc->sc_mididevs)
1203: free(sc->sc_mididevs, M_USBDEV);
1204: }
1205:
1206: static usbd_status
1207: attach_all_mididevs(struct umidi_softc *sc)
1208: {
1209: usbd_status err;
1210: int i;
1211:
1212: if (sc->sc_mididevs)
1213: for (i=0; i<sc->sc_num_mididevs; i++) {
1214: err = attach_mididev(sc, &sc->sc_mididevs[i]);
1215: if (err!=USBD_NORMAL_COMPLETION)
1216: return err;
1217: }
1218:
1219: return USBD_NORMAL_COMPLETION;
1220: }
1221:
1222: static usbd_status
1223: detach_all_mididevs(struct umidi_softc *sc, int flags)
1224: {
1225: usbd_status err;
1226: int i;
1227:
1228: if (sc->sc_mididevs)
1229: for (i=0; i<sc->sc_num_mididevs; i++) {
1230: err = detach_mididev(&sc->sc_mididevs[i], flags);
1231: if (err!=USBD_NORMAL_COMPLETION)
1232: return err;
1233: }
1234:
1235: return USBD_NORMAL_COMPLETION;
1236: }
1237:
1238: static usbd_status
1239: deactivate_all_mididevs(struct umidi_softc *sc)
1240: {
1241: usbd_status err;
1242: int i;
1243:
1244: if (sc->sc_mididevs)
1245: for (i=0; i<sc->sc_num_mididevs; i++) {
1246: err = deactivate_mididev(&sc->sc_mididevs[i]);
1247: if (err!=USBD_NORMAL_COMPLETION)
1248: return err;
1249: }
1250:
1251: return USBD_NORMAL_COMPLETION;
1252: }
1253:
1.23.2.2 yamt 1254: /*
1255: * TODO: the 0-based cable numbers will often not match the labeling of the
1256: * equipment. Ideally:
1257: * For class-compliant devices: get the iJack string from the jack descriptor.
1258: * Otherwise:
1259: * - support a DISPLAY_BASE_CN quirk (add the value to each internal cable
1260: * number for display)
1261: * - support an array quirk explictly giving a char * for each jack.
1262: * For now, you get 0-based cable numbers. If there are multiple endpoints and
1263: * the CNs are not globally unique, each is shown with its associated endpoint
1264: * address in hex also. That should not be necessary when using iJack values
1265: * or a quirk array.
1266: */
1267: static char *
1268: describe_mididev(struct umidi_mididev *md)
1269: {
1270: char in_label[16];
1271: char out_label[16];
1272: char *unit_label;
1273: char *final_label;
1274: struct umidi_softc *sc;
1275: int show_ep_in;
1276: int show_ep_out;
1277: size_t len;
1278:
1279: sc = md->sc;
1280: show_ep_in = sc-> sc_in_num_endpoints > 1 && !sc->cblnums_global;
1281: show_ep_out = sc->sc_out_num_endpoints > 1 && !sc->cblnums_global;
1282:
1283: if ( NULL != md->in_jack )
1284: snprintf(in_label, sizeof in_label,
1285: show_ep_in ? "<%d(%x) " : "<%d ",
1286: md->in_jack->cable_number,
1287: md->in_jack->endpoint->addr);
1288: else
1289: in_label[0] = '\0';
1290:
1291: if ( NULL != md->out_jack )
1292: snprintf(out_label, sizeof out_label,
1293: show_ep_out ? ">%d(%x) " : ">%d ",
1294: md->out_jack->cable_number,
1295: md->out_jack->endpoint->addr);
1296: else
1297: in_label[0] = '\0';
1298:
1299: unit_label = USBDEVNAME(sc->sc_dev);
1300:
1301: len = strlen(in_label) + strlen(out_label) + strlen(unit_label) + 4;
1302:
1303: final_label = malloc(len, M_USBDEV, M_WAITOK);
1304:
1305: snprintf(final_label, len, "%s%son %s",
1306: in_label, out_label, unit_label);
1307:
1308: return final_label;
1309: }
1310:
1.1 tshiozak 1311: #ifdef UMIDI_DEBUG
1312: static void
1313: dump_sc(struct umidi_softc *sc)
1314: {
1315: int i;
1316:
1317: DPRINTFN(10, ("%s: dump_sc\n", USBDEVNAME(sc->sc_dev)));
1318: for (i=0; i<sc->sc_out_num_endpoints; i++) {
1319: DPRINTFN(10, ("\tout_ep(%p):\n", &sc->sc_out_ep[i]));
1320: dump_ep(&sc->sc_out_ep[i]);
1321: }
1322: for (i=0; i<sc->sc_in_num_endpoints; i++) {
1323: DPRINTFN(10, ("\tin_ep(%p):\n", &sc->sc_in_ep[i]));
1324: dump_ep(&sc->sc_in_ep[i]);
1325: }
1326: }
1327:
1328: static void
1329: dump_ep(struct umidi_endpoint *ep)
1330: {
1331: int i;
1.23.2.2 yamt 1332: for (i=0; i<UMIDI_MAX_EPJACKS; i++) {
1333: if (NULL==ep->jacks[i])
1334: continue;
1335: DPRINTFN(10, ("\t\tjack[%d]:%p:\n", i, ep->jacks[i]));
1.1 tshiozak 1336: dump_jack(ep->jacks[i]);
1337: }
1338: }
1339: static void
1340: dump_jack(struct umidi_jack *jack)
1341: {
1.15 tshiozak 1342: DPRINTFN(10, ("\t\t\tep=%p\n",
1343: jack->endpoint));
1.1 tshiozak 1344: }
1345:
1346: #endif /* UMIDI_DEBUG */
1347:
1348:
1349:
1350: /*
1351: * MUX MIDI PACKET
1352: */
1353:
1.5 jdolecek 1354: static const int packet_length[16] = {
1.1 tshiozak 1355: /*0*/ -1,
1356: /*1*/ -1,
1357: /*2*/ 2,
1358: /*3*/ 3,
1359: /*4*/ 3,
1360: /*5*/ 1,
1361: /*6*/ 2,
1362: /*7*/ 3,
1363: /*8*/ 3,
1364: /*9*/ 3,
1365: /*A*/ 3,
1366: /*B*/ 3,
1367: /*C*/ 2,
1368: /*D*/ 2,
1369: /*E*/ 3,
1370: /*F*/ 1,
1371: };
1372:
1373: #define GET_CN(p) (((unsigned char)(p)>>4)&0x0F)
1374: #define GET_CIN(p) ((unsigned char)(p)&0x0F)
1375: #define MIX_CN_CIN(cn, cin) \
1376: ((unsigned char)((((unsigned char)(cn)&0x0F)<<4)| \
1.3 tshiozak 1377: ((unsigned char)(cin)&0x0F)))
1.1 tshiozak 1378:
1379: static usbd_status
1380: start_input_transfer(struct umidi_endpoint *ep)
1381: {
1382: usbd_setup_xfer(ep->xfer, ep->pipe,
1383: (usbd_private_handle)ep,
1.23.2.2 yamt 1384: ep->buffer, ep->buffer_size,
1385: USBD_SHORT_XFER_OK | USBD_NO_COPY,
1386: USBD_NO_TIMEOUT, in_intr);
1.1 tshiozak 1387: return usbd_transfer(ep->xfer);
1388: }
1389:
1390: static usbd_status
1391: start_output_transfer(struct umidi_endpoint *ep)
1392: {
1.23.2.2 yamt 1393: usbd_status rv;
1394: u_int32_t length;
1395: int i;
1396:
1397: length = (ep->next_slot - ep->buffer) * sizeof *ep->buffer;
1398: DPRINTFN(200,("umidi out transfer: start %p end %p length %u\n",
1399: ep->buffer, ep->next_slot, length));
1.1 tshiozak 1400: usbd_setup_xfer(ep->xfer, ep->pipe,
1401: (usbd_private_handle)ep,
1.23.2.2 yamt 1402: ep->buffer, length,
1.11 augustss 1403: USBD_NO_COPY, USBD_NO_TIMEOUT, out_intr);
1.23.2.2 yamt 1404: rv = usbd_transfer(ep->xfer);
1405:
1406: /*
1407: * Once the transfer is scheduled, no more adding to partial
1408: * packets within it.
1409: */
1410: if (UMQ_ISTYPE(ep->sc, UMQ_TYPE_MIDIMAN_GARBLE)) {
1411: for (i=0; i<UMIDI_MAX_EPJACKS; ++i)
1412: if (NULL != ep->jacks[i])
1413: ep->jacks[i]->midiman_ppkt = NULL;
1414: }
1415:
1416: return rv;
1.1 tshiozak 1417: }
1418:
1.4 tshiozak 1419: #ifdef UMIDI_DEBUG
1420: #define DPR_PACKET(dir, sc, p) \
1.23.2.2 yamt 1421: if ((unsigned char)(p)[1]!=0xFE) \
1.4 tshiozak 1422: DPRINTFN(500, \
1423: ("%s: umidi packet(" #dir "): %02X %02X %02X %02X\n", \
1424: USBDEVNAME(sc->sc_dev), \
1.23.2.2 yamt 1425: (unsigned char)(p)[0], \
1426: (unsigned char)(p)[1], \
1427: (unsigned char)(p)[2], \
1428: (unsigned char)(p)[3]));
1.4 tshiozak 1429: #else
1430: #define DPR_PACKET(dir, sc, p)
1431: #endif
1432:
1.23.2.2 yamt 1433: /*
1434: * A 4-byte Midiman packet superficially resembles a 4-byte USB MIDI packet
1435: * with the cable number and length in the last byte instead of the first,
1436: * but there the resemblance ends. Where a USB MIDI packet is a semantic
1437: * unit, a Midiman packet is just a wrapper for 1 to 3 bytes of raw MIDI
1438: * with a cable nybble and a length nybble (which, unlike the CIN of a
1439: * real USB MIDI packet, has no semantics at all besides the length).
1440: * A packet received from a Midiman may contain part of a MIDI message,
1441: * more than one MIDI message, or parts of more than one MIDI message. A
1442: * three-byte MIDI message may arrive in three packets of data length 1, and
1443: * running status may be used. Happily, the midi(4) driver above us will put
1444: * it all back together, so the only cost is in USB bandwidth. The device
1445: * has an easier time with what it receives from us: we'll pack messages in
1446: * and across packets, but filling the packets whenever possible and,
1447: * as midi(4) hands us a complete message at a time, we'll never send one
1448: * in a dribble of short packets.
1449: */
1450:
1.4 tshiozak 1451: static int
1.23.2.2 yamt 1452: out_jack_output(struct umidi_jack *out_jack, u_char *src, int len, int cin)
1.4 tshiozak 1453: {
1454: struct umidi_endpoint *ep = out_jack->endpoint;
1455: struct umidi_softc *sc = ep->sc;
1.23.2.2 yamt 1456: unsigned char *packet;
1.4 tshiozak 1457: int s;
1.23.2.2 yamt 1458: int plen;
1459: int poff;
1.4 tshiozak 1460:
1461: if (sc->sc_dying)
1462: return EIO;
1463:
1.23.2.2 yamt 1464: if (!out_jack->opened)
1465: return ENODEV; /* XXX as it was, is this the right errno? */
1.4 tshiozak 1466:
1.23.2.2 yamt 1467: #ifdef UMIDI_DEBUG
1468: if ( umididebug >= 100 )
1469: microtime(&umidi_tv);
1470: #endif
1471: DPRINTFN(100, ("umidi out: %lu.%06lus ep=%p cn=%d len=%d cin=%#x\n",
1472: umidi_tv.tv_sec%100, umidi_tv.tv_usec,
1473: ep, out_jack->cable_number, len, cin));
1474:
1475: s = splusb();
1476: packet = *ep->next_slot++;
1477: KASSERT(ep->buffer_size >=
1478: (ep->next_slot - ep->buffer) * sizeof *ep->buffer);
1479: memset(packet, 0, UMIDI_PACKET_SIZE);
1480: if (UMQ_ISTYPE(sc, UMQ_TYPE_MIDIMAN_GARBLE)) {
1481: if (NULL != out_jack->midiman_ppkt) { /* fill out a prev pkt */
1482: poff = 0x0f & (out_jack->midiman_ppkt[3]);
1483: plen = 3 - poff;
1484: if (plen > len)
1485: plen = len;
1486: memcpy(out_jack->midiman_ppkt+poff, src, plen);
1487: src += plen;
1488: len -= plen;
1489: plen += poff;
1490: out_jack->midiman_ppkt[3] =
1491: MIX_CN_CIN(out_jack->cable_number, plen);
1492: DPR_PACKET(out+, sc, out_jack->midiman_ppkt);
1493: if (3 == plen)
1494: out_jack->midiman_ppkt = NULL; /* no more */
1495: }
1496: if (0 == len)
1497: ep->next_slot--; /* won't be needed, nevermind */
1498: else {
1499: memcpy(packet, src, len);
1500: packet[3] = MIX_CN_CIN(out_jack->cable_number, len);
1501: DPR_PACKET(out, sc, packet);
1502: if (len < 3)
1503: out_jack->midiman_ppkt = packet;
1504: }
1505: } else { /* the nice simple USB class-compliant case */
1506: packet[0] = MIX_CN_CIN(out_jack->cable_number, cin);
1507: memcpy(packet+1, src, len);
1508: DPR_PACKET(out, sc, packet);
1509: }
1510: ep->next_schedule |= 1<<(out_jack->cable_number);
1511: ++ ep->num_scheduled;
1512: if ( !ep->armed && !ep->soliciting ) {
1513: /*
1514: * It would be bad to call out_solicit directly here (the
1515: * caller need not be reentrant) but a soft interrupt allows
1516: * solicit to run immediately the caller exits its critical
1517: * section, and if the caller has more to write we can get it
1518: * before starting the USB transfer, and send a longer one.
1519: */
1520: ep->soliciting = 1;
1.23.2.4! yamt 1521: softint_schedule(ep->solicit_cookie);
1.23.2.2 yamt 1522: }
1523: splx(s);
1524:
1525: return 0;
1.4 tshiozak 1526: }
1527:
1.1 tshiozak 1528: static void
1.23.2.2 yamt 1529: in_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
1530: usbd_status status)
1.1 tshiozak 1531: {
1.4 tshiozak 1532: int cn, len, i;
1.1 tshiozak 1533: struct umidi_endpoint *ep = (struct umidi_endpoint *)priv;
1.4 tshiozak 1534: struct umidi_jack *jack;
1.23.2.2 yamt 1535: unsigned char *packet;
1536: umidi_packet_bufp slot;
1537: umidi_packet_bufp end;
1538: unsigned char *data;
1539: u_int32_t count;
1.1 tshiozak 1540:
1.3 tshiozak 1541: if (ep->sc->sc_dying || !ep->num_open)
1.1 tshiozak 1542: return;
1543:
1.23.2.2 yamt 1544: usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
1545: if ( 0 == count % UMIDI_PACKET_SIZE ) {
1546: DPRINTFN(200,("%s: input endpoint %p transfer length %u\n",
1547: USBDEVNAME(ep->sc->sc_dev), ep, count));
1548: } else {
1549: DPRINTF(("%s: input endpoint %p odd transfer length %u\n",
1550: USBDEVNAME(ep->sc->sc_dev), ep, count));
1551: }
1552:
1553: slot = ep->buffer;
1554: end = slot + count / sizeof *slot;
1555:
1556: for ( packet = *slot; slot < end; packet = *++slot ) {
1557:
1558: if ( UMQ_ISTYPE(ep->sc, UMQ_TYPE_MIDIMAN_GARBLE) ) {
1559: cn = (0xf0&(packet[3]))>>4;
1560: len = 0x0f&(packet[3]);
1561: data = packet;
1562: } else {
1563: cn = GET_CN(packet[0]);
1564: len = packet_length[GET_CIN(packet[0])];
1565: data = packet + 1;
1566: }
1567: /* 0 <= cn <= 15 by inspection of above code */
1568: if (!(jack = ep->jacks[cn]) || cn != jack->cable_number) {
1569: DPRINTF(("%s: stray input endpoint %p cable %d len %d: "
1570: "%02X %02X %02X (try CN_SEQ quirk?)\n",
1571: USBDEVNAME(ep->sc->sc_dev), ep, cn, len,
1572: (unsigned)data[0],
1573: (unsigned)data[1],
1574: (unsigned)data[2]));
1575: return;
1576: }
1577:
1578: if (!jack->binded || !jack->opened)
1579: continue;
1580:
1581: DPRINTFN(500,("%s: input endpoint %p cable %d len %d: "
1582: "%02X %02X %02X\n",
1583: USBDEVNAME(ep->sc->sc_dev), ep, cn, len,
1584: (unsigned)data[0],
1585: (unsigned)data[1],
1586: (unsigned)data[2]));
1587:
1588: if (jack->u.in.intr) {
1589: for (i=0; i<len; i++) {
1590: (*jack->u.in.intr)(jack->arg, data[i]);
1591: }
1.4 tshiozak 1592: }
1.23.2.2 yamt 1593:
1.4 tshiozak 1594: }
1.1 tshiozak 1595:
1596: (void)start_input_transfer(ep);
1597: }
1598:
1599: static void
1.23.2.2 yamt 1600: out_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
1601: usbd_status status)
1.1 tshiozak 1602: {
1603: struct umidi_endpoint *ep = (struct umidi_endpoint *)priv;
1604: struct umidi_softc *sc = ep->sc;
1.23.2.2 yamt 1605: u_int32_t count;
1.1 tshiozak 1606:
1.23.2.2 yamt 1607: if (sc->sc_dying)
1.1 tshiozak 1608: return;
1609:
1.23.2.2 yamt 1610: #ifdef UMIDI_DEBUG
1611: if ( umididebug >= 200 )
1612: microtime(&umidi_tv);
1613: #endif
1614: usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
1615: if ( 0 == count % UMIDI_PACKET_SIZE ) {
1616: DPRINTFN(200,("%s: %lu.%06lus out ep %p xfer length %u\n",
1617: USBDEVNAME(ep->sc->sc_dev),
1618: umidi_tv.tv_sec%100, umidi_tv.tv_usec, ep, count));
1619: } else {
1620: DPRINTF(("%s: output endpoint %p odd transfer length %u\n",
1621: USBDEVNAME(ep->sc->sc_dev), ep, count));
1622: }
1623: count /= UMIDI_PACKET_SIZE;
1624:
1625: /*
1626: * If while the transfer was pending we buffered any new messages,
1627: * move them to the start of the buffer.
1628: */
1629: ep->next_slot -= count;
1630: if ( ep->buffer < ep->next_slot ) {
1631: memcpy(ep->buffer, ep->buffer + count,
1632: (char *)ep->next_slot - (char *)ep->buffer);
1633: }
1634: wakeup(ep);
1635: /*
1636: * Do not want anyone else to see armed <- 0 before soliciting <- 1.
1637: * Running at splusb so the following should happen to be safe.
1638: */
1639: ep->armed = 0;
1640: if ( !ep->soliciting ) {
1641: ep->soliciting = 1;
1642: out_solicit(ep);
1.1 tshiozak 1643: }
1644: }
1645:
1.23.2.2 yamt 1646: /*
1647: * A jack on which we have received a packet must be called back on its
1648: * out.intr handler before it will send us another; it is considered
1649: * 'scheduled'. It is nice and predictable - as long as it is scheduled,
1650: * we need no extra buffer space for it.
1651: *
1652: * In contrast, a jack that is open but not scheduled may supply us a packet
1653: * at any time, driven by the top half, and we must be able to accept it, no
1654: * excuses. So we must ensure that at any point in time there are at least
1655: * (num_open - num_scheduled) slots free.
1656: *
1657: * As long as there are more slots free than that minimum, we can loop calling
1658: * scheduled jacks back on their "interrupt" handlers, soliciting more
1659: * packets, starting the USB transfer only when the buffer space is down to
1660: * the minimum or no jack has any more to send.
1661: */
1.1 tshiozak 1662: static void
1.23.2.2 yamt 1663: out_solicit(void *arg)
1.1 tshiozak 1664: {
1.23.2.2 yamt 1665: struct umidi_endpoint *ep = arg;
1666: int s;
1667: umidi_packet_bufp end;
1668: u_int16_t which;
1669: struct umidi_jack *jack;
1670:
1671: end = ep->buffer + ep->buffer_size / sizeof *ep->buffer;
1672:
1673: for ( ;; ) {
1674: s = splusb();
1675: if ( end - ep->next_slot <= ep->num_open - ep->num_scheduled )
1676: break; /* at splusb */
1677: if ( ep->this_schedule == 0 ) {
1678: if ( ep->next_schedule == 0 )
1679: break; /* at splusb */
1680: ep->this_schedule = ep->next_schedule;
1681: ep->next_schedule = 0;
1682: }
1683: /*
1684: * At least one jack is scheduled. Find and mask off the least
1685: * set bit in this_schedule and decrement num_scheduled.
1686: * Convert mask to bit index to find the corresponding jack,
1687: * and call its intr handler. If it has a message, it will call
1688: * back one of the output methods, which will set its bit in
1689: * next_schedule (not copied into this_schedule until the
1690: * latter is empty). In this way we round-robin the jacks that
1691: * have messages to send, until the buffer is as full as we
1692: * dare, and then start a transfer.
1693: */
1694: which = ep->this_schedule;
1695: which &= (~which)+1; /* now mask of least set bit */
1696: ep->this_schedule &= ~which;
1697: -- ep->num_scheduled;
1698: splx(s);
1.1 tshiozak 1699:
1.23.2.2 yamt 1700: -- which; /* now 1s below mask - count 1s to get index */
1701: which -= ((which >> 1) & 0x5555);/* SWAR credit aggregate.org */
1702: which = (((which >> 2) & 0x3333) + (which & 0x3333));
1703: which = (((which >> 4) + which) & 0x0f0f);
1704: which += (which >> 8);
1705: which &= 0x1f; /* the bit index a/k/a jack number */
1706:
1707: jack = ep->jacks[which];
1708: if (jack->u.out.intr)
1709: (*jack->u.out.intr)(jack->arg);
1.1 tshiozak 1710: }
1.23.2.2 yamt 1711: /* splusb at loop exit */
1712: if ( !ep->armed && ep->next_slot > ep->buffer )
1713: ep->armed = (USBD_IN_PROGRESS == start_output_transfer(ep));
1714: ep->soliciting = 0;
1715: splx(s);
1.1 tshiozak 1716: }
CVSweb <webmaster@jp.NetBSD.org>