int ugenopen(dev_t dev, int flag, int mode, struct proc *p) { struct ugen_softc *sc; int unit = UGENUNIT(dev); int endpt = UGENENDPOINT(dev); usb_endpoint_descriptor_t *edesc; struct ugen_endpoint *sce; int dir, isize; usbd_status err; struct usbd_xfer *xfer; void *buf; int i, j; if (unit >= ugen_cd.cd_ndevs) return (ENXIO); sc = ugen_cd.cd_devs[unit]; if (sc == NULL) return (ENXIO); DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", flag, mode, unit, endpt)); if (sc == NULL || usbd_is_dying(sc->sc_udev)) return (ENXIO); if (sc->sc_is_open[endpt]) return (EBUSY); if (endpt == USB_CONTROL_ENDPOINT) { sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1; return (0); } /* Make sure there are pipes for all directions. */ for (dir = OUT; dir <= IN; dir++) { if (flag & (dir == OUT ? FWRITE : FREAD)) { sce = &sc->sc_endpoints[endpt][dir]; if (sce == 0 || sce->edesc == 0) return (ENXIO); } } /* Actually open the pipes. */ /* XXX Should back out properly if it fails. */ for (dir = OUT; dir <= IN; dir++) { if (!(flag & (dir == OUT ? FWRITE : FREAD))) continue; sce = &sc->sc_endpoints[endpt][dir]; sce->state = 0; sce->timeout = USBD_NO_TIMEOUT; DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", sc, endpt, dir, sce)); edesc = sce->edesc; switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: if (dir == OUT) { err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); break; } isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK); DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", endpt, isize)); clalloc(&sce->q, UGEN_IBSIZE, 0); err = usbd_open_pipe_intr(sce->iface, edesc->bEndpointAddress, USBD_SHORT_XFER_OK, &sce->pipeh, sce, sce->ibuf, isize, ugenintr, USBD_DEFAULT_INTERVAL); if (err) { free(sce->ibuf, M_USBDEV, 0); clfree(&sce->q); return (EIO); } DPRINTFN(5, ("ugenopen: interrupt open done\n")); break; case UE_BULK: err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); break; case UE_ISOCHRONOUS: if (dir == OUT) return (EINVAL); isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); sce->ibuf = mallocarray(isize, UGEN_NISOFRAMES, M_USBDEV, M_WAITOK); sce->cur = sce->fill = sce->ibuf; sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES; DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n", endpt, isize)); err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) { free(sce->ibuf, M_USBDEV, 0); return (EIO); } for(i = 0; i < UGEN_NISOREQS; ++i) { sce->isoreqs[i].sce = sce; xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) goto bad; sce->isoreqs[i].xfer = xfer; buf = usbd_alloc_buffer (xfer, isize * UGEN_NISORFRMS); if (buf == 0) { i++; goto bad; } sce->isoreqs[i].dmabuf = buf; for(j = 0; j < UGEN_NISORFRMS; ++j) sce->isoreqs[i].sizes[j] = isize; usbd_setup_isoc_xfer(xfer, sce->pipeh, &sce->isoreqs[i], sce->isoreqs[i].sizes, UGEN_NISORFRMS, USBD_NO_COPY | USBD_SHORT_XFER_OK, ugen_isoc_rintr); (void)usbd_transfer(xfer); } DPRINTFN(5, ("ugenopen: isoc open done\n")); break; bad: while (--i >= 0) /* implicit buffer free */ usbd_free_xfer(sce->isoreqs[i].xfer); return (ENOMEM); case UE_CONTROL: sce->timeout = USBD_DEFAULT_TIMEOUT; return (EINVAL); } } sc->sc_is_open[endpt] = 1; return (0); }
void ubt_xmit_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc) { struct mbuf *m; uint8_t *buf; int num, len, size, space; if (sc->sc_dying) return; space = sc->sc_scowr_size * UBT_NFRAMES; buf = isoc->buf; len = 0; /* * Fill the request buffer with data from the queue, * keeping any leftover packet on our private hook. * * Complete packets are passed back up to the stack * for disposal, since we can't rely on the controller * to tell us when it has finished with them. */ m = sc->sc_scowr_mbuf; while (space > 0) { if (m == NULL) { crit_enter(); IF_DEQUEUE(&sc->sc_scowr_queue, m); crit_exit(); if (m == NULL) break; m_adj(m, 1); /* packet type */ } if (m->m_pkthdr.len > 0) { size = MIN(m->m_pkthdr.len, space); m_copydata(m, 0, size, buf); m_adj(m, size); buf += size; len += size; space -= size; } if (m->m_pkthdr.len == 0) { sc->sc_stats.sco_tx++; if (!hci_complete_sco(sc->sc_unit, m)) sc->sc_stats.err_tx++; m = NULL; } } sc->sc_scowr_mbuf = m; DPRINTFN(15, "isoc=%p, len=%d, space=%d\n", isoc, len, space); if (len == 0) /* nothing to send */ return; sc->sc_refcnt++; sc->sc_scowr_busy = 1; sc->sc_stats.byte_tx += len; isoc->busy = 1; /* * calculate number of isoc frames and sizes */ for (num = 0 ; len > 0 ; num++) { size = MIN(sc->sc_scowr_size, len); isoc->size[num] = size; len -= size; } usbd_setup_isoc_xfer(isoc->xfer, sc->sc_scowr_pipe, isoc, isoc->size, num, USBD_NO_COPY | USBD_FORCE_SHORT_XFER, ubt_xmit_sco_complete); usbd_transfer(isoc->xfer); }