Includes {{{ #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> }}} {{{ usb_error_t usb_pipe_open(struct usb_device *udev, const uint8_t *ifaces, struct usb_pipe **, const struct usb_config *, int count, void *priv); void usb_pipe_halt(struct usb_pipe *); void usb_pipe_ready(struct usb_pipe *); void usb_pipe_drain(struct usb_pipe *); void usb_pipe_close(struct usb_pipe **, int count); void usb_clear_stall(struct usb_pipe *); struct usb_urb *usb_get_urb(struct usb_pipe *, int flags); int usb_set_frame_data(struct usb_urb *, int index, void *, int len); int usb_copyin_urb(struct usb_urb *, void *, int offset, int len); int usb_copyout_urb(struct usb_urb *, void *, int offset, int len); void usb_cancel_urb(struct usb_urb *); void usb_free_urb(struct usb_urb *); void usb_submit_urb(struct usb_pipe *, struct usb_urb *, int flags); }}} == Structs == === struct usb_pipe === A queue for processing urb pointers. Has one dma map and will reload the dma if the current urb is different to the one previously loaded. The pipe has a private mutex for queueing and dequeueing, it does not need to share the mutex with the driver. === struct usb_urb === A data buffer. The memory can be preallocated (fixed size) or passed in from an external buffer. The urb has a single reference and does not need to be locked. Notes: * usb_pipe_open() will preallocate the requested number of urb structures and keep on a free list * usb_get_urb() will get a urb from the free list. * usb_free_urb() does not actually free the memory, it puts the urb on the pipe free list * usb_pipe_halt() cancels all pending urbs and sets the pipe to blocking * usb_pipe_ready() will unblock the pipe and start processing anything in the urb queue * usb_submit_urb() will put the urb on the processing list = Examples (overly simplified to show basic working) = === example 1, external buffer === {{{ ep_config { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = 0, /* external */ .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = write_callback, .timeout = 5000, /* ms */ .min_urb = 8, .max_urb = 8, } { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 0, /* external */ .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = read_callback, .min_urb = 8, .max_urb = 8, } attach() error = usb_pipe_open(uaa->device, &iface_index, sc->sc_xfer, ep_config, XXX_N_TRANSFER, sc); detach() usb_pipe_close(sc->sc_xfer, XXX_N_TRANSFER); init() /* will return up to 'max_urb' times */ while((urb = usb_get_urb(sc->sc_readxfer) != NULL) { m = m_getcl(M_WAIT, MT_DATA, M_PKTHDR); usb_set_frame_data(urb, 0, m->data, m->len); usb_set_priv(urb, m); usb_submit_urb(urb); } usb_pipe_ready(sc->sc_readxfer); usb_pipe_ready(sc->sc_writexfer); stop() usb_pipe_halt(sc->sc_readxfer); usb_pipe_halt(sc->sc_writexfer); start(m) urb = usb_get_urb(sc->sc_writexfer); if (urb == NULL) return; usb_set_frame_data(urb, 0, m->data, m->len); usb_set_priv(urb, m); usb_submit_urb(urb); write_callback(urb, error) pipe = urb->pipe; if (error) usb_clear_stall(pipe); m = usb_get_priv(urb); usb_free_urb(urb); m_freem(m); start() /* try to send more */ read_callback(urb, error) pipe = urb->pipe; if (error) usb_clear_stall(pipe); m = usb_get_priv(urb); mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (!mnew) { ifp->if_ierrors++; goto skip; } m_input(m); usb_set_frame_data(urb, 0, mnew->data, mnew->len); usb_set_priv(urb, mnew); skip: usb_submit_urb(urb); }}} === example 2, internal buffer === {{{ ep_config { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = 512, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = write_callback, .timeout = 5000, /* ms */ .min_urb = 4, .max_urb = 4, } { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 512, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = read_callback, .max_urb = 4, } attach() error = usb_pipe_open(uaa->device, &iface_index, sc->sc_xfer, ep_config, XXX_N_TRANSFER, sc); detach() usb_pipe_close(sc->sc_xfer, XXX_N_TRANSFER); ttyopen() /* will return up to 'max_urb' times */ while((urb = usb_get_urb(sc->sc_readxfer) != NULL) { usb_submit_urb(urb); } usb_pipe_ready(sc->sc_readxfer); usb_pipe_ready(sc->sc_writexfer); ttyclose() usb_pipe_halt(sc->sc_readxfer); usb_pipe_halt(sc->sc_writexfer); ttywakeup() urb = usb_get_urb(sc->sc_writexfer); if (urb == NULL) return; /* all buffers in flight */ ttydisc_getc(urb->data, urb->len); usb_submit_urb(urb); write_callback(urb, error) pipe = urb->pipe; if (error) usb_clear_stall(pipe); /* refill buffer */ if (ttydisc_getc(urb->data, urb->len)) usb_submit_urb(urb); else usb_free_urb(urb); read_callback(urb, error) pipe = urb->pipe; if (error) usb_clear_stall(pipe); tty_rint(urb->data, urb->len); usb_submit_urb(urb); }}}