int usb_uiomove(struct usb_page_cache *pc, struct uio *uio, usb_frlength_t pc_offset, usb_frlength_t len) { struct usb_page_search res; int error = 0; while (len != 0) { usbd_get_page(pc, pc_offset, &res); if (res.length > len) { res.length = len; } /* * "uiomove()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things */ error = uiomove(res.buffer, res.length, uio); if (error) { break; } pc_offset += res.length; len -= res.length; } return (error); }
void ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, uint32_t offset, uint32_t len) { struct usb_page_search res; struct tty *tp = sc->sc_tty; char *buf; uint32_t cnt; mtx_assert(sc->sc_mtx, MA_OWNED); if (tty_gone(tp)) return; /* multiport device polling */ if (len == 0) return; /* no data */ /* set a flag to prevent recursation ? */ while (len > 0) { usbd_get_page(pc, offset, &res); if (res.length > len) { res.length = len; } len -= res.length; offset += res.length; /* pass characters to tty layer */ buf = res.buffer; cnt = res.length; /* first check if we can pass the buffer directly */ if (ttydisc_can_bypass(tp)) { if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { DPRINTF("tp=%p, data lost\n", tp); } continue; } /* need to loop */ for (cnt = 0; cnt != res.length; cnt++) { if (ttydisc_rint(tp, buf[cnt], 0) == -1) { /* XXX what should we do? */ DPRINTF("tp=%p, lost %d " "chars\n", tp, res.length - cnt); break; } } } ttydisc_rint_done(tp); }
static void uhso_mux_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhso_softc *sc = usbd_xfer_softc(xfer); struct uhso_tty *ht; struct usb_page_cache *pc; struct usb_device_request req; int actlen; struct usb_page_search res; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); ht = usbd_xfer_get_priv(xfer); UHSO_DPRINTF(3, "status=%d, using mux port %d\n", USB_GET_STATE(xfer), ht->ht_muxport); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: UHSO_DPRINTF(3, "wrote %zd data bytes to muxport %d\n", actlen - sizeof(struct usb_device_request) , ht->ht_muxport); /* FALLTHROUGH */ case USB_ST_SETUP: pc = usbd_xfer_get_frame(xfer, 1); if (ucom_get_data(&sc->sc_ucom[ht->ht_muxport], pc, 0, 32, &actlen)) { usbd_get_page(pc, 0, &res); memset(&req, 0, sizeof(struct usb_device_request)); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; USETW(req.wValue, 0); USETW(req.wIndex, ht->ht_muxport); USETW(req.wLength, actlen); pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &req, sizeof(req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, actlen); usbd_xfer_set_frames(xfer, 2); UHSO_DPRINTF(3, "Prepared %d bytes for transmit " "on muxport %d\n", actlen, ht->ht_muxport); usbd_transfer_submit(xfer); } break; default: UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error)); if (error == USB_ERR_CANCELLED) break; break; } }
/* * Interrupt callback for the multiplexed serial port. Indicates * which serial port has data waiting. */ static void uhso_mux_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_page_cache *pc; struct usb_page_search res; struct uhso_softc *sc = usbd_xfer_softc(xfer); unsigned int i, mux; UHSO_DPRINTF(3, "status %d\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* * The multiplexed port number can be found at the first byte. * It contains a bit mask, we transform this in to an integer. */ pc = usbd_xfer_get_frame(xfer, 0); usbd_get_page(pc, 0, &res); i = *((unsigned char *)res.buffer); mux = 0; while (i >>= 1) { mux++; } UHSO_DPRINTF(3, "mux port %d (%d)\n", mux, i); if (mux > UHSO_MPORT_TYPE_NOMAX) break; /* Issue a read for this serial port */ usbd_xfer_set_priv( sc->sc_tty[mux].ht_xfer[UHSO_CTRL_READ], &sc->sc_tty[mux]); usbd_transfer_start(sc->sc_tty[mux].ht_xfer[UHSO_CTRL_READ]); break; case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error)); if (error == USB_ERR_CANCELLED) break; usbd_xfer_set_stall(xfer); goto tr_setup; } }
/*------------------------------------------------------------------------* * ucom_get_data * * Return values: * 0: No data is available. * Else: Data is available. *------------------------------------------------------------------------*/ uint8_t ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, uint32_t offset, uint32_t len, uint32_t *actlen) { struct usb_page_search res; struct tty *tp = sc->sc_tty; uint32_t cnt; uint32_t offset_orig; mtx_assert(sc->sc_mtx, MA_OWNED); if (tty_gone(tp) || !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { actlen[0] = 0; return (0); /* multiport device polling */ } offset_orig = offset; while (len != 0) { usbd_get_page(pc, offset, &res); if (res.length > len) { res.length = len; } /* copy data directly into USB buffer */ cnt = ttydisc_getc(tp, res.buffer, res.length); offset += cnt; len -= cnt; if (cnt < res.length) { /* end of buffer */ break; } } actlen[0] = offset - offset_orig; DPRINTF("cnt=%d\n", actlen[0]); if (actlen[0] == 0) { return (0); } return (1); }
/*------------------------------------------------------------------------* * usbd_frame_zero - zero DMA-able memory *------------------------------------------------------------------------*/ void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len) { struct usb_page_search res; while (len != 0) { usbd_get_page(cache, offset, &res); if (res.length > len) { res.length = len; } memset(res.buffer, 0, res.length); offset += res.length; len -= res.length; } }
/*------------------------------------------------------------------------* * usbd_copy_in - copy directly to DMA-able memory *------------------------------------------------------------------------*/ void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len) { struct usb_page_search buf_res; while (len != 0) { usbd_get_page(cache, offset, &buf_res); if (buf_res.length > len) { buf_res.length = len; } memcpy(buf_res.buffer, ptr, buf_res.length); offset += buf_res.length; len -= buf_res.length; ptr = USB_ADD_BYTES(ptr, buf_res.length); } }
/*------------------------------------------------------------------------* * usbd_copy_out - copy directly from DMA-able memory *------------------------------------------------------------------------*/ void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len) { struct usb_page_search res; while (len != 0) { usbd_get_page(cache, offset, &res); if (res.length > len) { res.length = len; } memcpy(ptr, res.buffer, res.length); offset += res.length; len -= res.length; ptr = USB_ADD_BYTES(ptr, res.length); } }
/*------------------------------------------------------------------------* * usb_pc_buffer_is_aligned - verify alignment * * This function is used to check if a page cache buffer is properly * aligned to reduce the use of bounce buffers in PIO mode. *------------------------------------------------------------------------*/ uint8_t usb_pc_buffer_is_aligned(struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t mask) { struct usb_page_search buf_res; while (len != 0) { usbd_get_page(pc, offset, &buf_res); if (buf_res.length > len) buf_res.length = len; if (USB_P2U(buf_res.buffer) & mask) return (0); if (buf_res.length & mask) return (0); offset += buf_res.length; len -= buf_res.length; } return (1); }
int usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len) { struct usb_page_search res; int error; while (len != 0) { usbd_get_page(cache, offset, &res); if (res.length > len) { res.length = len; } error = copyout(res.buffer, ptr, res.length); if (error) return (error); offset += res.length; len -= res.length; ptr = USB_ADD_BYTES(ptr, res.length); } return (0); /* success */ }
int usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len) { struct usb_page_search buf_res; int error; while (len != 0) { usbd_get_page(cache, offset, &buf_res); if (buf_res.length > len) { buf_res.length = len; } error = copyin(ptr, buf_res.buffer, buf_res.length); if (error) return (error); offset += buf_res.length; len -= buf_res.length; ptr = USB_ADD_BYTES(ptr, buf_res.length); } return (0); /* success */ }
void ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, uint32_t offset, uint32_t len) { struct usb_page_search res; struct tty *tp = sc->sc_tty; char *buf; uint32_t cnt; UCOM_MTX_ASSERT(sc, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_CONSOLE) { unsigned int temp; /* get maximum RX length */ temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low; temp %= UCOM_CONS_BUFSIZE; /* limit RX length */ if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high)) temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high); if (temp > len) temp = len; /* copy out data */ usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp); /* update counters */ ucom_cons_rx_high += temp; ucom_cons_rx_high %= UCOM_CONS_BUFSIZE; return; } if (tty_gone(tp)) return; /* multiport device polling */ if (len == 0) return; /* no data */ /* set a flag to prevent recursation ? */ while (len > 0) { usbd_get_page(pc, offset, &res); if (res.length > len) { res.length = len; } len -= res.length; offset += res.length; /* pass characters to tty layer */ buf = res.buffer; cnt = res.length; /* first check if we can pass the buffer directly */ if (ttydisc_can_bypass(tp)) { /* clear any jitter buffer */ sc->sc_jitterbuf_in = 0; sc->sc_jitterbuf_out = 0; if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { DPRINTF("tp=%p, data lost\n", tp); } continue; } /* need to loop */ for (cnt = 0; cnt != res.length; cnt++) { if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out || ttydisc_rint(tp, buf[cnt], 0) == -1) { uint16_t end; uint16_t pos; pos = sc->sc_jitterbuf_in; end = sc->sc_jitterbuf_out + UCOM_JITTERBUF_SIZE - 1; if (end >= UCOM_JITTERBUF_SIZE) end -= UCOM_JITTERBUF_SIZE; for (; cnt != res.length; cnt++) { if (pos == end) break; sc->sc_jitterbuf[pos] = buf[cnt]; pos++; if (pos >= UCOM_JITTERBUF_SIZE) pos -= UCOM_JITTERBUF_SIZE; } sc->sc_jitterbuf_in = pos; /* set RTS in async fashion */ if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) ucom_rts(sc, 1); DPRINTF("tp=%p, lost %d " "chars\n", tp, res.length - cnt); break; } } } ttydisc_rint_done(tp); }
/*------------------------------------------------------------------------* * ucom_get_data * * Return values: * 0: No data is available. * Else: Data is available. *------------------------------------------------------------------------*/ uint8_t ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, uint32_t offset, uint32_t len, uint32_t *actlen) { struct usb_page_search res; struct tty *tp = sc->sc_tty; uint32_t cnt; uint32_t offset_orig; UCOM_MTX_ASSERT(sc, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_CONSOLE) { unsigned int temp; /* get total TX length */ temp = ucom_cons_tx_high - ucom_cons_tx_low; temp %= UCOM_CONS_BUFSIZE; /* limit TX length */ if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low)) temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low); if (temp > len) temp = len; /* copy in data */ usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp); /* update counters */ ucom_cons_tx_low += temp; ucom_cons_tx_low %= UCOM_CONS_BUFSIZE; /* store actual length */ *actlen = temp; return (temp ? 1 : 0); } if (tty_gone(tp) || !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { actlen[0] = 0; return (0); /* multiport device polling */ } offset_orig = offset; while (len != 0) { usbd_get_page(pc, offset, &res); if (res.length > len) { res.length = len; } /* copy data directly into USB buffer */ cnt = ttydisc_getc(tp, res.buffer, res.length); offset += cnt; len -= cnt; if (cnt < res.length) { /* end of buffer */ break; } } actlen[0] = offset - offset_orig; DPRINTF("cnt=%d\n", actlen[0]); if (actlen[0] == 0) { return (0); } return (1); }