Index: head/sys/dev/usb/controller/at91dci.c =================================================================== --- head/sys/dev/usb/controller/at91dci.c (revision 298931) +++ head/sys/dev/usb/controller/at91dci.c (revision 298932) @@ -1,2380 +1,2380 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file contains the driver for the AT91 series USB Device * Controller */ /* * Thanks to "David Brownell" for helping out regarding the hardware * endpoint profiles. */ /* * NOTE: The "fifo_bank" is not reset in hardware when the endpoint is * reset. * * NOTE: When the chip detects BUS-reset it will also reset the * endpoints, Function-address and more. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR at91dcidebug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #define AT9100_DCI_BUS2SC(bus) \ ((struct at91dci_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct at91dci_softc *)0)->sc_bus)))) #define AT9100_DCI_PC2SC(pc) \ AT9100_DCI_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus) #define AT9100_DCI_THREAD_IRQ \ (AT91_UDP_INT_BUS | AT91_UDP_INT_END_BR | AT91_UDP_INT_RXRSM | AT91_UDP_INT_RXSUSP) #ifdef USB_DEBUG static int at91dcidebug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, at91dci, CTLFLAG_RW, 0, "USB at91dci"); SYSCTL_INT(_hw_usb_at91dci, OID_AUTO, debug, CTLFLAG_RWTUN, &at91dcidebug, 0, "at91dci debug level"); #endif #define AT9100_DCI_INTR_ENDPT 1 /* prototypes */ static const struct usb_bus_methods at91dci_bus_methods; static const struct usb_pipe_methods at91dci_device_bulk_methods; static const struct usb_pipe_methods at91dci_device_ctrl_methods; static const struct usb_pipe_methods at91dci_device_intr_methods; static const struct usb_pipe_methods at91dci_device_isoc_fs_methods; static at91dci_cmd_t at91dci_setup_rx; static at91dci_cmd_t at91dci_data_rx; static at91dci_cmd_t at91dci_data_tx; static at91dci_cmd_t at91dci_data_tx_sync; static void at91dci_device_done(struct usb_xfer *, usb_error_t); static void at91dci_do_poll(struct usb_bus *); static void at91dci_standard_done(struct usb_xfer *); static void at91dci_root_intr(struct at91dci_softc *sc); /* * NOTE: Some of the bits in the CSR register have inverse meaning so * we need a helper macro when acknowledging events: */ #define AT91_CSR_ACK(csr, what) do { \ (csr) &= ~((AT91_UDP_CSR_FORCESTALL| \ AT91_UDP_CSR_TXPKTRDY| \ AT91_UDP_CSR_RXBYTECNT) ^ (what));\ (csr) |= ((AT91_UDP_CSR_RX_DATA_BK0| \ AT91_UDP_CSR_RX_DATA_BK1| \ AT91_UDP_CSR_TXCOMP| \ AT91_UDP_CSR_RXSETUP| \ AT91_UDP_CSR_STALLSENT) ^ (what)); \ } while (0) /* * Here is a list of what the chip supports. * Probably it supports more than listed here! */ static const struct usb_hw_ep_profile at91dci_ep_profile[AT91_UDP_EP_MAX] = { [0] = { .max_in_frame_size = 8, .max_out_frame_size = 8, .is_simplex = 1, .support_control = 1, }, [1] = { .max_in_frame_size = 64, .max_out_frame_size = 64, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, [2] = { .max_in_frame_size = 64, .max_out_frame_size = 64, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, [3] = { /* can also do BULK */ .max_in_frame_size = 8, .max_out_frame_size = 8, .is_simplex = 1, .support_interrupt = 1, .support_in = 1, .support_out = 1, }, [4] = { .max_in_frame_size = 256, .max_out_frame_size = 256, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, [5] = { .max_in_frame_size = 256, .max_out_frame_size = 256, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, }; static void at91dci_get_hw_ep_profile(struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr) { if (ep_addr < AT91_UDP_EP_MAX) { *ppf = (at91dci_ep_profile + ep_addr); } else { *ppf = NULL; } } static void at91dci_clocks_on(struct at91dci_softc *sc) { if (sc->sc_flags.clocks_off && sc->sc_flags.port_powered) { DPRINTFN(5, "\n"); if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } sc->sc_flags.clocks_off = 0; /* enable Transceiver */ AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, 0); } } static void at91dci_clocks_off(struct at91dci_softc *sc) { if (!sc->sc_flags.clocks_off) { DPRINTFN(5, "\n"); /* disable Transceiver */ AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); if (sc->sc_clocks_off) { (sc->sc_clocks_off) (sc->sc_clocks_arg); } sc->sc_flags.clocks_off = 1; } } static void at91dci_pull_up(struct at91dci_softc *sc) { /* pullup D+, if possible */ if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) { sc->sc_flags.d_pulled_up = 1; (sc->sc_pull_up) (sc->sc_pull_arg); } } static void at91dci_pull_down(struct at91dci_softc *sc) { /* pulldown D+, if possible */ if (sc->sc_flags.d_pulled_up) { sc->sc_flags.d_pulled_up = 0; (sc->sc_pull_down) (sc->sc_pull_arg); } } static void at91dci_wakeup_peer(struct at91dci_softc *sc) { if (!(sc->sc_flags.status_suspend)) { return; } AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, AT91_UDP_GSTATE_ESR); /* wait 8 milliseconds */ /* Wait for reset to complete. */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, 0); } static void at91dci_set_address(struct at91dci_softc *sc, uint8_t addr) { DPRINTFN(5, "addr=%d\n", addr); AT91_UDP_WRITE_4(sc, AT91_UDP_FADDR, addr | AT91_UDP_FADDR_EN); } static uint8_t at91dci_setup_rx(struct at91dci_softc *sc, struct at91dci_td *td) { struct usb_device_request req; uint32_t csr; uint32_t temp; uint16_t count; /* read out FIFO status */ csr = AT91_UDP_READ_4(sc, td->status_reg); DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); temp = csr; temp &= (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1 | AT91_UDP_CSR_STALLSENT | AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_TXCOMP); if (!(csr & AT91_UDP_CSR_RXSETUP)) { goto not_complete; } /* clear did stall */ td->did_stall = 0; /* get the packet byte count */ count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16; /* verify data length */ if (count != td->remainder) { DPRINTFN(0, "Invalid SETUP packet " "length, %d bytes\n", count); goto not_complete; } if (count != sizeof(req)) { DPRINTFN(0, "Unsupported SETUP packet " "length, %d bytes\n", count); goto not_complete; } /* receive data */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, td->fifo_reg, (void *)&req, sizeof(req)); /* copy data into real buffer */ usbd_copy_in(td->pc, 0, &req, sizeof(req)); td->offset = sizeof(req); td->remainder = 0; /* sneak peek the set address */ if ((req.bmRequestType == UT_WRITE_DEVICE) && (req.bRequest == UR_SET_ADDRESS)) { sc->sc_dv_addr = req.wValue[0] & 0x7F; } else { sc->sc_dv_addr = 0xFF; } /* sneak peek the endpoint direction */ if (req.bmRequestType & UE_DIR_IN) { csr |= AT91_UDP_CSR_DIR; } else { csr &= ~AT91_UDP_CSR_DIR; } /* write the direction of the control transfer */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); return (0); /* complete */ not_complete: /* abort any ongoing transfer */ if (!td->did_stall) { DPRINTFN(5, "stalling\n"); temp |= AT91_UDP_CSR_FORCESTALL; td->did_stall = 1; } /* clear interrupts, if any */ if (temp) { DPRINTFN(5, "clearing 0x%08x\n", temp); AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); } return (1); /* not complete */ } static uint8_t at91dci_data_rx(struct at91dci_softc *sc, struct at91dci_td *td) { struct usb_page_search buf_res; uint32_t csr; uint32_t temp; uint16_t count; uint8_t to; uint8_t got_short; to = 2; /* don't loop forever! */ got_short = 0; /* check if any of the FIFO banks have data */ repeat: /* read out FIFO status */ csr = AT91_UDP_READ_4(sc, td->status_reg); DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); if (csr & AT91_UDP_CSR_RXSETUP) { if (td->remainder == 0) { /* * We are actually complete and have * received the next SETUP */ DPRINTFN(5, "faking complete\n"); return (0); /* complete */ } /* * USB Host Aborted the transfer. */ td->error = 1; return (0); /* complete */ } /* Make sure that "STALLSENT" gets cleared */ temp = csr; temp &= AT91_UDP_CSR_STALLSENT; /* check status */ if (!(csr & (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1))) { if (temp) { /* write command */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); } return (1); /* not complete */ } /* get the packet byte count */ count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16; /* verify the packet byte count */ if (count != td->max_packet_size) { if (count < td->max_packet_size) { /* we have a short packet */ td->short_pkt = 1; got_short = 1; } else { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } while (count > 0) { usbd_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* receive data */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, td->fifo_reg, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* clear status bits */ if (td->support_multi_buffer) { if (td->fifo_bank) { td->fifo_bank = 0; temp |= AT91_UDP_CSR_RX_DATA_BK1; } else { td->fifo_bank = 1; temp |= AT91_UDP_CSR_RX_DATA_BK0; } } else { temp |= (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1); } /* write command */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); /* * NOTE: We may have to delay a little bit before * proceeding after clearing the DATA_BK bits. */ /* check if we are complete */ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ return (0); } /* else need to receive a zero length packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t at91dci_data_tx(struct at91dci_softc *sc, struct at91dci_td *td) { struct usb_page_search buf_res; uint32_t csr; uint32_t temp; uint16_t count; uint8_t to; to = 2; /* don't loop forever! */ repeat: /* read out FIFO status */ csr = AT91_UDP_READ_4(sc, td->status_reg); DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); if (csr & AT91_UDP_CSR_RXSETUP) { /* * The current transfer was aborted * by the USB Host */ td->error = 1; return (0); /* complete */ } /* Make sure that "STALLSENT" gets cleared */ temp = csr; temp &= AT91_UDP_CSR_STALLSENT; if (csr & AT91_UDP_CSR_TXPKTRDY) { if (temp) { /* write command */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); } return (1); /* not complete */ } else { /* clear TXCOMP and set TXPKTRDY */ temp |= (AT91_UDP_CSR_TXCOMP | AT91_UDP_CSR_TXPKTRDY); } count = td->max_packet_size; if (td->remainder < count) { /* we have a short packet */ td->short_pkt = 1; count = td->remainder; } while (count > 0) { usbd_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* transmit data */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, td->fifo_reg, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* write command */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) { return (0); /* complete */ } /* else we need to transmit a short packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t at91dci_data_tx_sync(struct at91dci_softc *sc, struct at91dci_td *td) { uint32_t csr; uint32_t temp; /* read out FIFO status */ csr = AT91_UDP_READ_4(sc, td->status_reg); DPRINTFN(5, "csr=0x%08x\n", csr); if (csr & AT91_UDP_CSR_RXSETUP) { DPRINTFN(5, "faking complete\n"); /* Race condition */ return (0); /* complete */ } temp = csr; temp &= (AT91_UDP_CSR_STALLSENT | AT91_UDP_CSR_TXCOMP); /* check status */ if (csr & AT91_UDP_CSR_TXPKTRDY) { goto not_complete; } if (!(csr & AT91_UDP_CSR_TXCOMP)) { goto not_complete; } if (td->status_reg == AT91_UDP_CSR(0) && sc->sc_dv_addr != 0xFF) { /* * The AT91 has a special requirement with regard to * setting the address and that is to write the new * address before clearing TXCOMP: */ at91dci_set_address(sc, sc->sc_dv_addr); } /* write command */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); return (0); /* complete */ not_complete: if (temp) { /* write command */ AT91_CSR_ACK(csr, temp); AT91_UDP_WRITE_4(sc, td->status_reg, csr); } return (1); /* not complete */ } static void at91dci_xfer_do_fifo(struct usb_xfer *xfer) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); struct at91dci_td *td; uint8_t temp; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; if (td == NULL) return; while (1) { if ((td->func) (sc, td)) { /* operation in progress */ break; } if (((void *)td) == xfer->td_transfer_last) { goto done; } if (td->error) { goto done; } else if (td->remainder > 0) { /* * We had a short transfer. If there is no alternate * next, stop processing ! */ if (!td->alt_next) { goto done; } } /* * Fetch the next transfer descriptor and transfer * some flags to the next transfer descriptor */ temp = 0; if (td->fifo_bank) temp |= 1; td = td->obj_next; xfer->td_transfer_cache = td; if (temp & 1) td->fifo_bank = 1; } return; done: temp = (xfer->endpointno & UE_ADDR); /* update FIFO bank flag and multi buffer */ if (td->fifo_bank) { sc->sc_ep_flags[temp].fifo_bank = 1; } else { sc->sc_ep_flags[temp].fifo_bank = 0; } /* compute all actual lengths */ xfer->td_transfer_cache = NULL; sc->sc_xfer_complete = 1; } static uint8_t at91dci_xfer_do_complete(struct usb_xfer *xfer) { struct at91dci_td *td; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; if (td == NULL) { /* compute all actual lengths */ at91dci_standard_done(xfer); return(1); } return (0); } static void at91dci_interrupt_poll_locked(struct at91dci_softc *sc) { struct usb_xfer *xfer; TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) at91dci_xfer_do_fifo(xfer); } static void at91dci_interrupt_complete_locked(struct at91dci_softc *sc) { struct usb_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (at91dci_xfer_do_complete(xfer)) goto repeat; } } void at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on) { DPRINTFN(5, "vbus = %u\n", is_on); if (is_on) { if (!sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 1; /* complete root HUB interrupt endpoint */ at91dci_root_intr(sc); } } else { if (sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* complete root HUB interrupt endpoint */ at91dci_root_intr(sc); } } } int at91dci_filter_interrupt(void *arg) { struct at91dci_softc *sc = arg; int retval = FILTER_HANDLED; uint32_t status; USB_BUS_SPIN_LOCK(&sc->sc_bus); status = AT91_UDP_READ_4(sc, AT91_UDP_ISR); status &= AT91_UDP_INT_DEFAULT; if (status & AT9100_DCI_THREAD_IRQ) retval = FILTER_SCHEDULE_THREAD; /* acknowledge interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, status & ~AT9100_DCI_THREAD_IRQ); /* poll FIFOs, if any */ at91dci_interrupt_poll_locked(sc); if (sc->sc_xfer_complete != 0) retval = FILTER_SCHEDULE_THREAD; USB_BUS_SPIN_UNLOCK(&sc->sc_bus); return (retval); } void at91dci_interrupt(void *arg) { struct at91dci_softc *sc = arg; uint32_t status; USB_BUS_LOCK(&sc->sc_bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); status = AT91_UDP_READ_4(sc, AT91_UDP_ISR); status &= AT9100_DCI_THREAD_IRQ; /* acknowledge interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, status); /* check for any bus state change interrupts */ if (status & AT91_UDP_INT_BUS) { DPRINTFN(5, "real bus interrupt 0x%08x\n", status); if (status & AT91_UDP_INT_END_BR) { /* set correct state */ sc->sc_flags.status_bus_reset = 1; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* disable resume interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_RXRSM); /* enable suspend interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_RXSUSP); } /* * If RXRSM and RXSUSP is set at the same time we interpret * that like RESUME. Resume is set when there is at least 3 * milliseconds of inactivity on the USB BUS. */ if (status & AT91_UDP_INT_RXRSM) { if (sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 1; /* disable resume interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_RXRSM); /* enable suspend interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_RXSUSP); } } else if (status & AT91_UDP_INT_RXSUSP) { if (!sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 1; sc->sc_flags.change_suspend = 1; /* disable suspend interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_RXSUSP); /* enable resume interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_RXRSM); } } /* complete root HUB interrupt endpoint */ at91dci_root_intr(sc); } if (sc->sc_xfer_complete != 0) { sc->sc_xfer_complete = 0; at91dci_interrupt_complete_locked(sc); } USB_BUS_SPIN_UNLOCK(&sc->sc_bus); USB_BUS_UNLOCK(&sc->sc_bus); } static void at91dci_setup_standard_chain_sub(struct at91dci_std_temp *temp) { struct at91dci_td *td; /* get current Transfer Descriptor */ td = temp->td_next; temp->td = td; /* prepare for next TD */ temp->td_next = td->obj_next; /* fill out the Transfer Descriptor */ td->func = temp->func; td->pc = temp->pc; td->offset = temp->offset; td->remainder = temp->len; td->fifo_bank = 0; td->error = 0; td->did_stall = temp->did_stall; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; } static void at91dci_setup_standard_chain(struct usb_xfer *xfer) { struct at91dci_std_temp temp; struct at91dci_softc *sc; struct at91dci_td *td; uint32_t x; uint8_t ep_no; uint8_t need_sync; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); temp.max_frame_size = xfer->max_frame_size; td = xfer->td_start[0]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; /* setup temp */ temp.pc = NULL; temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.offset = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr; temp.did_stall = !xfer->flags_int.control_stall; sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); ep_no = (xfer->endpointno & UE_ADDR); /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.func = &at91dci_setup_rx; temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) temp.setup_alt_next = 0; } at91dci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } if (x != xfer->nframes) { if (xfer->endpointno & UE_DIR_IN) { temp.func = &at91dci_data_tx; need_sync = 1; } else { temp.func = &at91dci_data_rx; need_sync = 0; } /* setup "pc" pointer */ temp.pc = xfer->frbuffers + x; } else { need_sync = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_act) { temp.setup_alt_next = 0; } } else { temp.setup_alt_next = 0; } } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.short_pkt = 0; } else { /* regular data transfer */ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; } at91dci_setup_standard_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += temp.len; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* always setup a valid "pc" pointer for status and sync */ temp.pc = xfer->frbuffers + 0; temp.len = 0; temp.short_pkt = 0; temp.setup_alt_next = 0; /* check if we need to sync */ if (need_sync) { /* we need a SYNC point after TX */ temp.func = &at91dci_data_tx_sync; at91dci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (!xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current * endpoint direction. */ if (xfer->endpointno & UE_DIR_IN) { temp.func = &at91dci_data_rx; need_sync = 0; } else { temp.func = &at91dci_data_tx; need_sync = 1; } at91dci_setup_standard_chain_sub(&temp); if (need_sync) { /* we need a SYNC point after TX */ temp.func = &at91dci_data_tx_sync; at91dci_setup_standard_chain_sub(&temp); } } } /* must have at least one frame! */ td = temp.td; xfer->td_transfer_last = td; /* setup the correct fifo bank */ if (sc->sc_ep_flags[ep_no].fifo_bank) { td = xfer->td_transfer_first; td->fifo_bank = 1; } } static void at91dci_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ at91dci_device_done(xfer, USB_ERR_TIMEOUT); } static void at91dci_start_standard_chain(struct usb_xfer *xfer) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); DPRINTFN(9, "\n"); USB_BUS_SPIN_LOCK(&sc->sc_bus); /* poll one time */ at91dci_xfer_do_fifo(xfer); if (at91dci_xfer_do_complete(xfer) == 0) { uint8_t ep_no = xfer->endpointno & UE_ADDR; /* * Only enable the endpoint interrupt when we are actually * waiting for data, hence we are dealing with level * triggered interrupts ! */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_EP(ep_no)); DPRINTFN(15, "enable interrupts on endpoint %d\n", ep_no); /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usbd_transfer_timeout_ms(xfer, &at91dci_timeout, xfer->timeout); } } USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void at91dci_root_intr(struct at91dci_softc *sc) { DPRINTFN(9, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* set port bit */ sc->sc_hub_idata[0] = 0x02; /* we only have one port */ uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } static usb_error_t at91dci_standard_done_sub(struct usb_xfer *xfer) { struct at91dci_td *td; uint32_t len; uint8_t error; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; do { len = td->remainder; if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract * the remainder from "frlengths[]": */ if (len > xfer->frlengths[xfer->aframes]) { td->error = 1; } else { xfer->frlengths[xfer->aframes] -= len; } } /* Check for transfer error */ if (td->error) { /* the transfer is finished */ error = 1; td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr) { /* follow alt next */ if (td->alt_next) { td = td->obj_next; } else { td = NULL; } } else { /* the transfer is finished */ td = NULL; } error = 0; break; } td = td->obj_next; /* this USB frame is complete */ error = 0; break; } while (0); /* update transfer cache */ xfer->td_transfer_cache = td; return (error ? USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); } static void at91dci_standard_done(struct usb_xfer *xfer) { usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = at91dci_standard_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = at91dci_standard_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = at91dci_standard_done_sub(xfer); } done: at91dci_device_done(xfer, err); } /*------------------------------------------------------------------------* * at91dci_device_done * * NOTE: this function can be called more than one time on the * same USB transfer! *------------------------------------------------------------------------*/ static void at91dci_device_done(struct usb_xfer *xfer, usb_error_t error) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); uint8_t ep_no; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); USB_BUS_SPIN_LOCK(&sc->sc_bus); if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { ep_no = (xfer->endpointno & UE_ADDR); /* disable endpoint interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_EP(ep_no)); DPRINTFN(15, "disable interrupts on endpoint %d\n", ep_no); } /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void at91dci_xfer_stall(struct usb_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_STALLED); } static void at91dci_set_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t *did_stall) { struct at91dci_softc *sc; uint32_t csr_val; uint8_t csr_reg; USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); DPRINTFN(5, "endpoint=%p\n", ep); /* set FORCESTALL */ sc = AT9100_DCI_BUS2SC(udev->bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); csr_reg = (ep->edesc->bEndpointAddress & UE_ADDR); csr_reg = AT91_UDP_CSR(csr_reg); csr_val = AT91_UDP_READ_4(sc, csr_reg); AT91_CSR_ACK(csr_val, AT91_UDP_CSR_FORCESTALL); AT91_UDP_WRITE_4(sc, csr_reg, csr_val); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void at91dci_clear_stall_sub(struct at91dci_softc *sc, uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) { const struct usb_hw_ep_profile *pf; uint32_t csr_val; uint32_t temp; uint8_t csr_reg; uint8_t to; if (ep_type == UE_CONTROL) { /* clearing stall is not needed */ return; } USB_BUS_SPIN_LOCK(&sc->sc_bus); /* compute CSR register offset */ csr_reg = AT91_UDP_CSR(ep_no); /* compute default CSR value */ csr_val = 0; AT91_CSR_ACK(csr_val, 0); /* disable endpoint */ AT91_UDP_WRITE_4(sc, csr_reg, csr_val); /* get endpoint profile */ at91dci_get_hw_ep_profile(NULL, &pf, ep_no); /* reset FIFO */ AT91_UDP_WRITE_4(sc, AT91_UDP_RST, AT91_UDP_RST_EP(ep_no)); AT91_UDP_WRITE_4(sc, AT91_UDP_RST, 0); /* * NOTE: One would assume that a FIFO reset would release the - * FIFO banks aswell, but it doesn't! We have to do this + * FIFO banks as well, but it doesn't! We have to do this * manually! */ /* release FIFO banks, if any */ for (to = 0; to != 2; to++) { /* get csr value */ csr_val = AT91_UDP_READ_4(sc, csr_reg); if (csr_val & (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1)) { /* clear status bits */ if (pf->support_multi_buffer) { if (sc->sc_ep_flags[ep_no].fifo_bank) { sc->sc_ep_flags[ep_no].fifo_bank = 0; temp = AT91_UDP_CSR_RX_DATA_BK1; } else { sc->sc_ep_flags[ep_no].fifo_bank = 1; temp = AT91_UDP_CSR_RX_DATA_BK0; } } else { temp = (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1); } } else { temp = 0; } /* clear FORCESTALL */ temp |= AT91_UDP_CSR_STALLSENT; AT91_CSR_ACK(csr_val, temp); AT91_UDP_WRITE_4(sc, csr_reg, csr_val); } /* compute default CSR value */ csr_val = 0; AT91_CSR_ACK(csr_val, 0); /* enable endpoint */ csr_val &= ~AT91_UDP_CSR_ET_MASK; csr_val |= AT91_UDP_CSR_EPEDS; if (ep_type == UE_CONTROL) { csr_val |= AT91_UDP_CSR_ET_CTRL; } else { if (ep_type == UE_BULK) { csr_val |= AT91_UDP_CSR_ET_BULK; } else if (ep_type == UE_INTERRUPT) { csr_val |= AT91_UDP_CSR_ET_INT; } else { csr_val |= AT91_UDP_CSR_ET_ISO; } if (ep_dir & UE_DIR_IN) { csr_val |= AT91_UDP_CSR_ET_DIR_IN; } } /* enable endpoint */ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(ep_no), csr_val); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void at91dci_clear_stall(struct usb_device *udev, struct usb_endpoint *ep) { struct at91dci_softc *sc; struct usb_endpoint_descriptor *ed; DPRINTFN(5, "endpoint=%p\n", ep); USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check mode */ if (udev->flags.usb_mode != USB_MODE_DEVICE) { /* not supported */ return; } /* get softc */ sc = AT9100_DCI_BUS2SC(udev->bus); /* get endpoint descriptor */ ed = ep->edesc; /* reset endpoint */ at91dci_clear_stall_sub(sc, (ed->bEndpointAddress & UE_ADDR), (ed->bmAttributes & UE_XFERTYPE), (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); } usb_error_t at91dci_init(struct at91dci_softc *sc) { uint32_t csr_val; uint8_t n; DPRINTF("start\n"); /* set up the bus structure */ sc->sc_bus.usbrev = USB_REV_1_1; sc->sc_bus.methods = &at91dci_bus_methods; USB_BUS_LOCK(&sc->sc_bus); /* turn on clocks */ if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } /* wait a little for things to stabilise */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF); /* compute default CSR value */ csr_val = 0; AT91_CSR_ACK(csr_val, 0); /* disable all endpoints */ for (n = 0; n != AT91_UDP_EP_MAX; n++) { /* disable endpoint */ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(n), csr_val); } /* enable the control endpoint */ AT91_CSR_ACK(csr_val, AT91_UDP_CSR_ET_CTRL | AT91_UDP_CSR_EPEDS); /* write to FIFO control register */ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(0), csr_val); /* enable the interrupts we want */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_BUS); /* turn off clocks */ at91dci_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ at91dci_do_poll(&sc->sc_bus); return (0); /* success */ } void at91dci_uninit(struct at91dci_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF); sc->sc_flags.port_powered = 0; sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; at91dci_pull_down(sc); at91dci_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void at91dci_suspend(struct at91dci_softc *sc) { /* TODO */ } static void at91dci_resume(struct at91dci_softc *sc) { /* TODO */ } static void at91dci_do_poll(struct usb_bus *bus) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); at91dci_interrupt_poll_locked(sc); at91dci_interrupt_complete_locked(sc); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * at91dci bulk support *------------------------------------------------------------------------*/ static void at91dci_device_bulk_open(struct usb_xfer *xfer) { return; } static void at91dci_device_bulk_close(struct usb_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_bulk_enter(struct usb_xfer *xfer) { return; } static void at91dci_device_bulk_start(struct usb_xfer *xfer) { /* setup TDs */ at91dci_setup_standard_chain(xfer); at91dci_start_standard_chain(xfer); } static const struct usb_pipe_methods at91dci_device_bulk_methods = { .open = at91dci_device_bulk_open, .close = at91dci_device_bulk_close, .enter = at91dci_device_bulk_enter, .start = at91dci_device_bulk_start, }; /*------------------------------------------------------------------------* * at91dci control support *------------------------------------------------------------------------*/ static void at91dci_device_ctrl_open(struct usb_xfer *xfer) { return; } static void at91dci_device_ctrl_close(struct usb_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_ctrl_enter(struct usb_xfer *xfer) { return; } static void at91dci_device_ctrl_start(struct usb_xfer *xfer) { /* setup TDs */ at91dci_setup_standard_chain(xfer); at91dci_start_standard_chain(xfer); } static const struct usb_pipe_methods at91dci_device_ctrl_methods = { .open = at91dci_device_ctrl_open, .close = at91dci_device_ctrl_close, .enter = at91dci_device_ctrl_enter, .start = at91dci_device_ctrl_start, }; /*------------------------------------------------------------------------* * at91dci interrupt support *------------------------------------------------------------------------*/ static void at91dci_device_intr_open(struct usb_xfer *xfer) { return; } static void at91dci_device_intr_close(struct usb_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_intr_enter(struct usb_xfer *xfer) { return; } static void at91dci_device_intr_start(struct usb_xfer *xfer) { /* setup TDs */ at91dci_setup_standard_chain(xfer); at91dci_start_standard_chain(xfer); } static const struct usb_pipe_methods at91dci_device_intr_methods = { .open = at91dci_device_intr_open, .close = at91dci_device_intr_close, .enter = at91dci_device_intr_enter, .start = at91dci_device_intr_start, }; /*------------------------------------------------------------------------* * at91dci full speed isochronous support *------------------------------------------------------------------------*/ static void at91dci_device_isoc_fs_open(struct usb_xfer *xfer) { return; } static void at91dci_device_isoc_fs_close(struct usb_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_isoc_fs_enter(struct usb_xfer *xfer) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); uint32_t temp; uint32_t nframes; DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->endpoint->isoc_next, xfer->nframes); /* get the current frame index */ nframes = AT91_UDP_READ_4(sc, AT91_UDP_FRM); /* * check if the frame index is within the window where the frames * will be inserted */ temp = (nframes - xfer->endpoint->isoc_next) & AT91_UDP_FRM_MASK; if ((xfer->endpoint->is_synced == 0) || (temp < xfer->nframes)) { /* * If there is data underflow or the endpoint queue is * empty we schedule the transfer a few frames ahead * of the current frame position. Else two isochronous * transfers might overlap. */ xfer->endpoint->isoc_next = (nframes + 3) & AT91_UDP_FRM_MASK; xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->endpoint->isoc_next - nframes) & AT91_UDP_FRM_MASK; /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb_isoc_time_expand(&sc->sc_bus, nframes) + temp + xfer->nframes; /* compute frame number for next insertion */ xfer->endpoint->isoc_next += xfer->nframes; /* setup TDs */ at91dci_setup_standard_chain(xfer); } static void at91dci_device_isoc_fs_start(struct usb_xfer *xfer) { /* start TD chain */ at91dci_start_standard_chain(xfer); } static const struct usb_pipe_methods at91dci_device_isoc_fs_methods = { .open = at91dci_device_isoc_fs_open, .close = at91dci_device_isoc_fs_close, .enter = at91dci_device_isoc_fs_enter, .start = at91dci_device_isoc_fs_start, }; /*------------------------------------------------------------------------* * at91dci root control support *------------------------------------------------------------------------* * Simulate a hardware HUB by handling all the necessary requests. *------------------------------------------------------------------------*/ static const struct usb_device_descriptor at91dci_devd = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = UDESC_DEVICE, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_FSHUB, .bMaxPacketSize = 64, .bcdDevice = {0x00, 0x01}, .iManufacturer = 1, .iProduct = 2, .bNumConfigurations = 1, }; static const struct at91dci_config_desc at91dci_confd = { .confd = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(at91dci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, }, .ifcd = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = 0, }, .endpd = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = (UE_DIR_IN | AT9100_DCI_INTR_ENDPT), .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, .bInterval = 255, }, }; #define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) } static const struct usb_hub_descriptor_min at91dci_hubd = { .bDescLength = sizeof(at91dci_hubd), .bDescriptorType = UDESC_HUB, .bNbrPorts = 1, HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)), .bPwrOn2PwrGood = 50, .bHubContrCurrent = 0, .DeviceRemovable = {0}, /* port is removable */ }; #define STRING_VENDOR \ "A\0T\0M\0E\0L" #define STRING_PRODUCT \ "D\0C\0I\0 \0R\0o\0o\0t\0 \0H\0U\0B" USB_MAKE_STRING_DESC(STRING_VENDOR, at91dci_vendor); USB_MAKE_STRING_DESC(STRING_PRODUCT, at91dci_product); static usb_error_t at91dci_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(udev->bus); const void *ptr; uint16_t len; uint16_t value; uint16_t index; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_temp; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); /* demultiplex the control request */ switch (req->bmRequestType) { case UT_READ_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (req->bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_DESCRIPTOR: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (req->bRequest) { case UR_CLEAR_FEATURE: switch (UGETW(req->wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (UGETW(req->wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; case UR_SYNCH_FRAME: goto tr_valid; /* nop */ default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (req->bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; case UT_WRITE_INTERFACE: switch (req->bRequest) { case UR_SET_INTERFACE: goto tr_handle_set_interface; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (req->bRequest) { case UR_GET_INTERFACE: goto tr_handle_get_interface; case UR_GET_STATUS: goto tr_handle_get_iface_status; default: goto tr_stalled; } break; case UT_WRITE_CLASS_INTERFACE: case UT_WRITE_VENDOR_INTERFACE: /* XXX forward */ break; case UT_READ_CLASS_INTERFACE: case UT_READ_VENDOR_INTERFACE: /* XXX forward */ break; case UT_WRITE_CLASS_DEVICE: switch (req->bRequest) { case UR_CLEAR_FEATURE: goto tr_valid; case UR_SET_DESCRIPTOR: case UR_SET_FEATURE: break; default: goto tr_stalled; } break; case UT_WRITE_CLASS_OTHER: switch (req->bRequest) { case UR_CLEAR_FEATURE: goto tr_handle_clear_port_feature; case UR_SET_FEATURE: goto tr_handle_set_port_feature; case UR_CLEAR_TT_BUFFER: case UR_RESET_TT: case UR_STOP_TT: goto tr_valid; default: goto tr_stalled; } break; case UT_READ_CLASS_OTHER: switch (req->bRequest) { case UR_GET_TT_STATE: goto tr_handle_get_tt_state; case UR_GET_STATUS: goto tr_handle_get_port_status; default: goto tr_stalled; } break; case UT_READ_CLASS_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; case UR_GET_STATUS: goto tr_handle_get_class_status; default: goto tr_stalled; } break; default: goto tr_stalled; } goto tr_valid; tr_handle_get_descriptor: switch (value >> 8) { case UDESC_DEVICE: if (value & 0xff) { goto tr_stalled; } len = sizeof(at91dci_devd); ptr = (const void *)&at91dci_devd; goto tr_valid; case UDESC_CONFIG: if (value & 0xff) { goto tr_stalled; } len = sizeof(at91dci_confd); ptr = (const void *)&at91dci_confd; goto tr_valid; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ len = sizeof(usb_string_lang_en); ptr = (const void *)&usb_string_lang_en; goto tr_valid; case 1: /* Vendor */ len = sizeof(at91dci_vendor); ptr = (const void *)&at91dci_vendor; goto tr_valid; case 2: /* Product */ len = sizeof(at91dci_product); ptr = (const void *)&at91dci_product; goto tr_valid; default: break; } break; default: goto tr_stalled; } goto tr_stalled; tr_handle_get_config: len = 1; sc->sc_hub_temp.wValue[0] = sc->sc_conf; goto tr_valid; tr_handle_get_status: len = 2; USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); goto tr_valid; tr_handle_set_address: if (value & 0xFF00) { goto tr_stalled; } sc->sc_rt_addr = value; goto tr_valid; tr_handle_set_config: if (value >= 2) { goto tr_stalled; } sc->sc_conf = value; goto tr_valid; tr_handle_get_interface: len = 1; sc->sc_hub_temp.wValue[0] = 0; goto tr_valid; tr_handle_get_tt_state: tr_handle_get_class_status: tr_handle_get_iface_status: tr_handle_get_ep_status: len = 2; USETW(sc->sc_hub_temp.wValue, 0); goto tr_valid; tr_handle_set_halt: tr_handle_set_interface: tr_handle_set_wakeup: tr_handle_clear_wakeup: tr_handle_clear_halt: goto tr_valid; tr_handle_clear_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_SUSPEND: at91dci_wakeup_peer(sc); break; case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 0; break; case UHF_PORT_TEST: case UHF_PORT_INDICATOR: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 0; at91dci_pull_down(sc); at91dci_clocks_off(sc); break; case UHF_C_PORT_CONNECTION: sc->sc_flags.change_connect = 0; break; case UHF_C_PORT_SUSPEND: sc->sc_flags.change_suspend = 0; break; default: err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_set_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); switch (value) { case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 1; break; case UHF_PORT_SUSPEND: case UHF_PORT_RESET: case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 1; break; default: err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_get_port_status: DPRINTFN(9, "UR_GET_PORT_STATUS\n"); if (index != 1) { goto tr_stalled; } if (sc->sc_flags.status_vbus) { at91dci_clocks_on(sc); at91dci_pull_up(sc); } else { at91dci_pull_down(sc); at91dci_clocks_off(sc); } /* Select FULL-speed and Device Side Mode */ value = UPS_PORT_MODE_DEVICE; if (sc->sc_flags.port_powered) { value |= UPS_PORT_POWER; } if (sc->sc_flags.port_enabled) { value |= UPS_PORT_ENABLED; } if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { value |= UPS_CURRENT_CONNECT_STATUS; } if (sc->sc_flags.status_suspend) { value |= UPS_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortStatus, value); value = 0; if (sc->sc_flags.change_connect) { value |= UPS_C_CONNECT_STATUS; if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { /* reset endpoint flags */ memset(sc->sc_ep_flags, 0, sizeof(sc->sc_ep_flags)); } } if (sc->sc_flags.change_suspend) { value |= UPS_C_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortChange, value); len = sizeof(sc->sc_hub_temp.ps); goto tr_valid; tr_handle_get_class_descriptor: if (value & 0xFF) { goto tr_stalled; } ptr = (const void *)&at91dci_hubd; len = sizeof(at91dci_hubd); goto tr_valid; tr_stalled: err = USB_ERR_STALLED; tr_valid: done: *plength = len; *pptr = ptr; return (err); } static void at91dci_xfer_setup(struct usb_setup_params *parm) { const struct usb_hw_ep_profile *pf; struct at91dci_softc *sc; struct usb_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; uint8_t ep_no; sc = AT9100_DCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * NOTE: This driver does not use any of the parameters that * are computed from the following values. Just set some * reasonable dummies: */ parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x500; usbd_transfer_setup_sub(parm); /* * compute maximum number of TDs */ if (parm->methods == &at91dci_device_ctrl_methods) { ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ + 1 /* SYNC 2 */ ; } else if (parm->methods == &at91dci_device_bulk_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &at91dci_device_intr_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &at91dci_device_isoc_fs_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else { ntd = 0; } /* * check if "usbd_transfer_setup_sub" set an error */ if (parm->err) { return; } /* * allocate transfer descriptors */ last_obj = NULL; /* * get profile stuff */ if (ntd) { ep_no = xfer->endpointno & UE_ADDR; at91dci_get_hw_ep_profile(parm->udev, &pf, ep_no); if (pf == NULL) { /* should not happen */ parm->err = USB_ERR_INVAL; return; } } else { ep_no = 0; pf = NULL; } /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); for (n = 0; n != ntd; n++) { struct at91dci_td *td; if (parm->buf) { td = USB_ADD_BYTES(parm->buf, parm->size[0]); /* init TD */ td->max_packet_size = xfer->max_packet_size; td->status_reg = AT91_UDP_CSR(ep_no); td->fifo_reg = AT91_UDP_FDR(ep_no); if (pf->support_multi_buffer) { td->support_multi_buffer = 1; } td->obj_next = last_obj; last_obj = td; } parm->size[0] += sizeof(*td); } xfer->td_start[0] = last_obj; } static void at91dci_xfer_unsetup(struct usb_xfer *xfer) { return; } static void at91dci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(udev->bus); DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode, sc->sc_rt_addr); if (udev->device_index != sc->sc_rt_addr) { if (udev->speed != USB_SPEED_FULL) { /* not supported */ return; } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ep->methods = &at91dci_device_ctrl_methods; break; case UE_INTERRUPT: ep->methods = &at91dci_device_intr_methods; break; case UE_ISOCHRONOUS: ep->methods = &at91dci_device_isoc_fs_methods; break; case UE_BULK: ep->methods = &at91dci_device_bulk_methods; break; default: /* do nothing */ break; } } } static void at91dci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: at91dci_suspend(sc); break; case USB_HW_POWER_SHUTDOWN: at91dci_uninit(sc); break; case USB_HW_POWER_RESUME: at91dci_resume(sc); break; default: break; } } static const struct usb_bus_methods at91dci_bus_methods = { .endpoint_init = &at91dci_ep_init, .xfer_setup = &at91dci_xfer_setup, .xfer_unsetup = &at91dci_xfer_unsetup, .get_hw_ep_profile = &at91dci_get_hw_ep_profile, .set_stall = &at91dci_set_stall, .xfer_stall = &at91dci_xfer_stall, .clear_stall = &at91dci_clear_stall, .roothub_exec = &at91dci_roothub_exec, .xfer_poll = &at91dci_do_poll, .set_hw_power_sleep = &at91dci_set_hw_power_sleep, }; Index: head/sys/dev/usb/controller/dwc_otg.c =================================================================== --- head/sys/dev/usb/controller/dwc_otg.c (revision 298931) +++ head/sys/dev/usb/controller/dwc_otg.c (revision 298932) @@ -1,4982 +1,4982 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2015 Daisuke Aoyama. All rights reserved. * Copyright (c) 2012-2015 Hans Petter Selasky. All rights reserved. * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file contains the driver for the DesignWare series USB 2.0 OTG * Controller. */ /* * LIMITATION: Drivers must be bound to all OUT endpoints in the * active configuration for this driver to work properly. Blocking any * OUT endpoint will block all OUT endpoints including the control * endpoint. Usually this is not a problem. */ /* * NOTE: Writing to non-existing registers appears to cause an * internal reset. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR dwc_otg_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include #define DWC_OTG_BUS2SC(bus) \ ((struct dwc_otg_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct dwc_otg_softc *)0)->sc_bus)))) #define DWC_OTG_PC2UDEV(pc) \ (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev) #define DWC_OTG_MSK_GINT_ENABLED \ (GINTMSK_ENUMDONEMSK | \ GINTMSK_USBRSTMSK | \ GINTMSK_USBSUSPMSK | \ GINTMSK_IEPINTMSK | \ GINTMSK_SESSREQINTMSK | \ GINTMSK_RXFLVLMSK | \ GINTMSK_HCHINTMSK | \ GINTMSK_OTGINTMSK | \ GINTMSK_PRTINTMSK) #define DWC_OTG_MSK_GINT_THREAD_IRQ \ (GINTSTS_USBRST | GINTSTS_ENUMDONE | GINTSTS_PRTINT | \ GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTMSK_OTGINTMSK | \ GINTSTS_SESSREQINT) #define DWC_OTG_PHY_ULPI 0 #define DWC_OTG_PHY_HSIC 1 #define DWC_OTG_PHY_INTERNAL 2 #ifndef DWC_OTG_PHY_DEFAULT #define DWC_OTG_PHY_DEFAULT DWC_OTG_PHY_ULPI #endif static int dwc_otg_phy_type = DWC_OTG_PHY_DEFAULT; static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW, 0, "USB DWC OTG"); SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, phy_type, CTLFLAG_RDTUN, &dwc_otg_phy_type, 0, "DWC OTG PHY TYPE - 0/1/2 - ULPI/HSIC/INTERNAL"); #ifdef USB_DEBUG static int dwc_otg_debug; SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RWTUN, &dwc_otg_debug, 0, "DWC OTG debug level"); #endif #define DWC_OTG_INTR_ENDPT 1 /* prototypes */ static const struct usb_bus_methods dwc_otg_bus_methods; static const struct usb_pipe_methods dwc_otg_device_non_isoc_methods; static const struct usb_pipe_methods dwc_otg_device_isoc_methods; static dwc_otg_cmd_t dwc_otg_setup_rx; static dwc_otg_cmd_t dwc_otg_data_rx; static dwc_otg_cmd_t dwc_otg_data_tx; static dwc_otg_cmd_t dwc_otg_data_tx_sync; static dwc_otg_cmd_t dwc_otg_host_setup_tx; static dwc_otg_cmd_t dwc_otg_host_data_tx; static dwc_otg_cmd_t dwc_otg_host_data_rx; static void dwc_otg_device_done(struct usb_xfer *, usb_error_t); static void dwc_otg_do_poll(struct usb_bus *); static void dwc_otg_standard_done(struct usb_xfer *); static void dwc_otg_root_intr(struct dwc_otg_softc *); static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *); /* * Here is a configuration that the chip supports. */ static const struct usb_hw_ep_profile dwc_otg_ep_profile[1] = { [0] = { .max_in_frame_size = 64,/* fixed */ .max_out_frame_size = 64, /* fixed */ .is_simplex = 1, .support_control = 1, } }; static void dwc_otg_get_hw_ep_profile(struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr) { struct dwc_otg_softc *sc; sc = DWC_OTG_BUS2SC(udev->bus); if (ep_addr < sc->sc_dev_ep_max) *ppf = &sc->sc_hw_ep_profile[ep_addr].usb; else *ppf = NULL; } static void dwc_otg_write_fifo(struct dwc_otg_softc *sc, struct usb_page_cache *pc, uint32_t offset, uint32_t fifo, uint32_t count) { uint32_t temp; /* round down length to nearest 4-bytes */ temp = count & ~3; /* check if we can write the data directly */ if (temp != 0 && usb_pc_buffer_is_aligned(pc, offset, temp, 3)) { struct usb_page_search buf_res; /* pre-subtract length */ count -= temp; /* iterate buffer list */ do { /* get current buffer pointer */ usbd_get_page(pc, offset, &buf_res); if (buf_res.length > temp) buf_res.length = temp; /* transfer data into FIFO */ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, fifo, buf_res.buffer, buf_res.length / 4); offset += buf_res.length; fifo += buf_res.length; temp -= buf_res.length; } while (temp != 0); } /* check for remainder */ if (count != 0) { /* clear topmost word before copy */ sc->sc_bounce_buffer[(count - 1) / 4] = 0; /* copy out data */ usbd_copy_out(pc, offset, sc->sc_bounce_buffer, count); /* transfer data into FIFO */ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, fifo, sc->sc_bounce_buffer, (count + 3) / 4); } } static void dwc_otg_read_fifo(struct dwc_otg_softc *sc, struct usb_page_cache *pc, uint32_t offset, uint32_t count) { uint32_t temp; /* round down length to nearest 4-bytes */ temp = count & ~3; /* check if we can read the data directly */ if (temp != 0 && usb_pc_buffer_is_aligned(pc, offset, temp, 3)) { struct usb_page_search buf_res; /* pre-subtract length */ count -= temp; /* iterate buffer list */ do { /* get current buffer pointer */ usbd_get_page(pc, offset, &buf_res); if (buf_res.length > temp) buf_res.length = temp; /* transfer data from FIFO */ bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, sc->sc_current_rx_fifo, buf_res.buffer, buf_res.length / 4); offset += buf_res.length; sc->sc_current_rx_fifo += buf_res.length; sc->sc_current_rx_bytes -= buf_res.length; temp -= buf_res.length; } while (temp != 0); } /* check for remainder */ if (count != 0) { /* read data into bounce buffer */ bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, sc->sc_current_rx_fifo, sc->sc_bounce_buffer, (count + 3) / 4); /* store data into proper buffer */ usbd_copy_in(pc, offset, sc->sc_bounce_buffer, count); /* round length up to nearest 4 bytes */ count = (count + 3) & ~3; /* update counters */ sc->sc_current_rx_bytes -= count; sc->sc_current_rx_fifo += count; } } static void dwc_otg_tx_fifo_reset(struct dwc_otg_softc *sc, uint32_t value) { uint32_t temp; /* reset FIFO */ DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, value); /* wait for reset to complete */ for (temp = 0; temp != 16; temp++) { value = DWC_OTG_READ_4(sc, DOTG_GRSTCTL); if (!(value & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH))) break; } } static int dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode) { struct dwc_otg_profile *pf; uint32_t fifo_size; uint32_t fifo_regs; uint32_t tx_start; uint8_t x; fifo_size = sc->sc_fifo_size; /* * NOTE: Reserved fixed size area at end of RAM, which must * not be allocated to the FIFOs: */ fifo_regs = 4 * 16; if (fifo_size < fifo_regs) { DPRINTF("Too little FIFO\n"); return (EINVAL); } /* subtract FIFO regs from total once */ fifo_size -= fifo_regs; /* split equally for IN and OUT */ fifo_size /= 2; /* Align to 4 bytes boundary (refer to PGM) */ fifo_size &= ~3; /* set global receive FIFO size */ DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4); tx_start = fifo_size; if (fifo_size < 64) { DPRINTFN(-1, "Not enough data space for EP0 FIFO.\n"); return (EINVAL); } if (mode == DWC_MODE_HOST) { /* reset active endpoints */ sc->sc_active_rx_ep = 0; /* split equally for periodic and non-periodic */ fifo_size /= 2; DPRINTF("PTX/NPTX FIFO=%u\n", fifo_size); /* align to 4 bytes boundary */ fifo_size &= ~3; DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, ((fifo_size / 4) << 16) | (tx_start / 4)); tx_start += fifo_size; for (x = 0; x != sc->sc_host_ch_max; x++) { /* enable all host interrupts */ DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_DEFAULT_MASK); } DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ, ((fifo_size / 4) << 16) | (tx_start / 4)); /* reset host channel state */ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state)); /* enable all host channel interrupts */ DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, (1U << sc->sc_host_ch_max) - 1U); } if (mode == DWC_MODE_DEVICE) { DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, (0x10 << 16) | (tx_start / 4)); fifo_size -= 0x40; tx_start += 0x40; /* setup control endpoint profile */ sc->sc_hw_ep_profile[0].usb = dwc_otg_ep_profile[0]; /* reset active endpoints */ sc->sc_active_rx_ep = 1; for (x = 1; x != sc->sc_dev_ep_max; x++) { pf = sc->sc_hw_ep_profile + x; pf->usb.max_out_frame_size = 1024 * 3; pf->usb.is_simplex = 0; /* assume duplex */ pf->usb.support_bulk = 1; pf->usb.support_interrupt = 1; pf->usb.support_isochronous = 1; pf->usb.support_out = 1; if (x < sc->sc_dev_in_ep_max) { uint32_t limit; limit = (x == 1) ? MIN(DWC_OTG_TX_MAX_FIFO_SIZE, DWC_OTG_MAX_TXN) : MIN(DWC_OTG_MAX_TXN / 2, DWC_OTG_TX_MAX_FIFO_SIZE); /* see if there is enough FIFO space */ if (limit <= fifo_size) { pf->max_buffer = limit; pf->usb.support_in = 1; } else { limit = MIN(DWC_OTG_TX_MAX_FIFO_SIZE, 0x40); if (limit <= fifo_size) { pf->usb.support_in = 1; } else { pf->usb.is_simplex = 1; limit = 0; } } /* set FIFO size */ DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x), ((limit / 4) << 16) | (tx_start / 4)); tx_start += limit; fifo_size -= limit; pf->usb.max_in_frame_size = limit; } else { pf->usb.is_simplex = 1; } DPRINTF("FIFO%d = IN:%d / OUT:%d\n", x, pf->usb.max_in_frame_size, pf->usb.max_out_frame_size); } } /* reset RX FIFO */ dwc_otg_tx_fifo_reset(sc, GRSTCTL_RXFFLSH); if (mode != DWC_MODE_OTG) { /* reset all TX FIFOs */ dwc_otg_tx_fifo_reset(sc, GRSTCTL_TXFIFO(0x10) | GRSTCTL_TXFFLSH); } else { /* reset active endpoints */ sc->sc_active_rx_ep = 0; /* reset host channel state */ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state)); } return (0); } static uint8_t dwc_otg_uses_split(struct usb_device *udev) { /* * When a LOW or FULL speed device is connected directly to * the USB port we don't use split transactions: */ return (udev->speed != USB_SPEED_HIGH && udev->parent_hs_hub != NULL && udev->parent_hs_hub->parent_hub != NULL); } static void dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc) { /* * Disabled until further. Assuming that the register is already * programmed correctly by the boot loader. */ #if 0 uint32_t temp; /* setup HOST frame interval register, based on existing value */ temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK; if (temp >= 10000) temp /= 1000; else temp /= 125; /* figure out nearest X-tal value */ if (temp >= 54) temp = 60; /* MHz */ else if (temp >= 39) temp = 48; /* MHz */ else temp = 30; /* MHz */ if (sc->sc_flags.status_high_speed) temp *= 125; else temp *= 1000; DPRINTF("HFIR=0x%08x\n", temp); DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp); #endif } static void dwc_otg_clocks_on(struct dwc_otg_softc *sc) { if (sc->sc_flags.clocks_off && sc->sc_flags.port_powered) { DPRINTFN(5, "\n"); /* TODO - platform specific */ sc->sc_flags.clocks_off = 0; } } static void dwc_otg_clocks_off(struct dwc_otg_softc *sc) { if (!sc->sc_flags.clocks_off) { DPRINTFN(5, "\n"); /* TODO - platform specific */ sc->sc_flags.clocks_off = 1; } } static void dwc_otg_pull_up(struct dwc_otg_softc *sc) { uint32_t temp; /* pullup D+, if possible */ if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) { sc->sc_flags.d_pulled_up = 1; temp = DWC_OTG_READ_4(sc, DOTG_DCTL); temp &= ~DCTL_SFTDISCON; DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); } } static void dwc_otg_pull_down(struct dwc_otg_softc *sc) { uint32_t temp; /* pulldown D+, if possible */ if (sc->sc_flags.d_pulled_up) { sc->sc_flags.d_pulled_up = 0; temp = DWC_OTG_READ_4(sc, DOTG_DCTL); temp |= DCTL_SFTDISCON; DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); } } static void dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc) { /* In device mode we don't use the SOF interrupt */ if (sc->sc_flags.status_device_mode != 0) return; /* Ensure the SOF interrupt is not disabled */ sc->sc_needsof = 1; /* Check if the SOF interrupt is already enabled */ if ((sc->sc_irq_mask & GINTMSK_SOFMSK) != 0) return; sc->sc_irq_mask |= GINTMSK_SOFMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } static void dwc_otg_resume_irq(struct dwc_otg_softc *sc) { if (sc->sc_flags.status_suspend) { /* update status bits */ sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 1; if (sc->sc_flags.status_device_mode) { /* * Disable resume interrupt and enable suspend * interrupt: */ sc->sc_irq_mask &= ~GINTMSK_WKUPINTMSK; sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); } } static void dwc_otg_suspend_irq(struct dwc_otg_softc *sc) { if (!sc->sc_flags.status_suspend) { /* update status bits */ sc->sc_flags.status_suspend = 1; sc->sc_flags.change_suspend = 1; if (sc->sc_flags.status_device_mode) { /* * Disable suspend interrupt and enable resume * interrupt: */ sc->sc_irq_mask &= ~GINTMSK_USBSUSPMSK; sc->sc_irq_mask |= GINTMSK_WKUPINTMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); } } static void dwc_otg_wakeup_peer(struct dwc_otg_softc *sc) { if (!sc->sc_flags.status_suspend) return; DPRINTFN(5, "Remote wakeup\n"); if (sc->sc_flags.status_device_mode) { uint32_t temp; /* enable remote wakeup signalling */ temp = DWC_OTG_READ_4(sc, DOTG_DCTL); temp |= DCTL_RMTWKUPSIG; DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); /* Wait 8ms for remote wakeup to complete. */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); temp &= ~DCTL_RMTWKUPSIG; DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); } else { /* enable USB port */ DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0); /* wait 10ms */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); /* resume port */ sc->sc_hprt_val |= HPRT_PRTRES; DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); /* Wait 100ms for resume signalling to complete. */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10); /* clear suspend and resume */ sc->sc_hprt_val &= ~(HPRT_PRTSUSP | HPRT_PRTRES); DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); /* Wait 4ms */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); } /* need to fake resume IRQ */ dwc_otg_resume_irq(sc); } static void dwc_otg_set_address(struct dwc_otg_softc *sc, uint8_t addr) { uint32_t temp; DPRINTFN(5, "addr=%d\n", addr); temp = DWC_OTG_READ_4(sc, DOTG_DCFG); temp &= ~DCFG_DEVADDR_SET(0x7F); temp |= DCFG_DEVADDR_SET(addr); DWC_OTG_WRITE_4(sc, DOTG_DCFG, temp); } static void dwc_otg_common_rx_ack(struct dwc_otg_softc *sc) { DPRINTFN(5, "RX status clear\n"); /* enable RX FIFO level interrupt */ sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); if (sc->sc_current_rx_bytes != 0) { /* need to dump remaining data */ bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, sc->sc_current_rx_fifo, sc->sc_bounce_buffer, sc->sc_current_rx_bytes / 4); /* clear number of active bytes to receive */ sc->sc_current_rx_bytes = 0; } /* clear cached status */ sc->sc_last_rx_status = 0; } static void dwc_otg_clear_hcint(struct dwc_otg_softc *sc, uint8_t x) { uint32_t hcint; /* clear all pending interrupts */ hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint); /* clear buffered interrupts */ sc->sc_chan_state[x].hcint = 0; } static uint8_t dwc_otg_host_check_tx_fifo_empty(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint32_t temp; temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); if (td->ep_type == UE_ISOCHRONOUS) { /* * NOTE: USB INTERRUPT transactions are executed like * USB CONTROL transactions! See the setup standard * chain function for more information. */ if (!(temp & GINTSTS_PTXFEMP)) { DPRINTF("Periodic TX FIFO is not empty\n"); if (!(sc->sc_irq_mask & GINTMSK_PTXFEMPMSK)) { sc->sc_irq_mask |= GINTMSK_PTXFEMPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } return (1); /* busy */ } } else { if (!(temp & GINTSTS_NPTXFEMP)) { DPRINTF("Non-periodic TX FIFO is not empty\n"); if (!(sc->sc_irq_mask & GINTMSK_NPTXFEMPMSK)) { sc->sc_irq_mask |= GINTMSK_NPTXFEMPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } return (1); /* busy */ } } return (0); /* ready for transmit */ } static uint8_t dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t is_out) { uint8_t x; uint8_t y; uint8_t z; if (td->channel[0] < DWC_OTG_MAX_CHANNELS) return (0); /* already allocated */ /* check if device is suspended */ if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0) return (1); /* busy - cannot transfer data */ /* compute needed TX FIFO size */ if (is_out != 0) { if (dwc_otg_host_check_tx_fifo_empty(sc, td) != 0) return (1); /* busy - cannot transfer data */ } z = td->max_packet_count; for (x = y = 0; x != sc->sc_host_ch_max; x++) { /* check if channel is allocated */ if (sc->sc_chan_state[x].allocated != 0) continue; /* check if channel is still enabled */ if (sc->sc_chan_state[x].wait_halted != 0) continue; /* store channel number */ td->channel[y++] = x; /* check if we got all channels */ if (y == z) break; } if (y != z) { /* reset channel variable */ td->channel[0] = DWC_OTG_MAX_CHANNELS; td->channel[1] = DWC_OTG_MAX_CHANNELS; td->channel[2] = DWC_OTG_MAX_CHANNELS; /* wait a bit */ dwc_otg_enable_sof_irq(sc); return (1); /* busy - not enough channels */ } for (y = 0; y != z; y++) { x = td->channel[y]; /* set allocated */ sc->sc_chan_state[x].allocated = 1; /* set wait halted */ sc->sc_chan_state[x].wait_halted = 1; /* clear interrupts */ dwc_otg_clear_hcint(sc, x); DPRINTF("CH=%d HCCHAR=0x%08x " "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt); /* set active channel */ sc->sc_active_rx_ep |= (1 << x); } return (0); /* allocated */ } static void dwc_otg_host_channel_free_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t index) { uint32_t hcchar; uint8_t x; if (td->channel[index] >= DWC_OTG_MAX_CHANNELS) return; /* already freed */ /* free channel */ x = td->channel[index]; td->channel[index] = DWC_OTG_MAX_CHANNELS; DPRINTF("CH=%d\n", x); /* * We need to let programmed host channels run till complete * else the host channel will stop functioning. */ sc->sc_chan_state[x].allocated = 0; /* ack any pending messages */ if (sc->sc_last_rx_status != 0 && GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) { dwc_otg_common_rx_ack(sc); } /* clear active channel */ sc->sc_active_rx_ep &= ~(1 << x); /* check if already halted */ if (sc->sc_chan_state[x].wait_halted == 0) return; /* disable host channel */ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); if (hcchar & HCCHAR_CHENA) { DPRINTF("Halting channel %d\n", x); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), hcchar | HCCHAR_CHDIS); /* don't write HCCHAR until the channel is halted */ } else { sc->sc_chan_state[x].wait_halted = 0; } } static void dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint8_t x; for (x = 0; x != td->max_packet_count; x++) dwc_otg_host_channel_free_sub(sc, td, x); } static void dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint8_t x; /* dump any pending messages */ if (sc->sc_last_rx_status == 0) return; for (x = 0; x != td->max_packet_count; x++) { if (td->channel[x] >= DWC_OTG_MAX_CHANNELS || td->channel[x] != GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) continue; dwc_otg_common_rx_ack(sc); break; } } static uint8_t dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { struct usb_device_request req __aligned(4); uint32_t hcint; uint32_t hcchar; uint8_t delta; dwc_otg_host_dump_rx(sc, td); if (td->channel[0] < DWC_OTG_MAX_CHANNELS) { hcint = sc->sc_chan_state[td->channel[0]].hcint; DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", td->channel[0], td->state, hcint, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0]))); } else { hcint = 0; goto check_state; } if (hcint & (HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { /* give success bits priority over failure bits */ } else if (hcint & HCINT_STALL) { DPRINTF("CH=%d STALL\n", td->channel[0]); td->error_stall = 1; td->error_any = 1; goto complete; } else if (hcint & HCINT_ERRORS) { DPRINTF("CH=%d ERROR\n", td->channel[0]); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { td->error_any = 1; goto complete; } } if (hcint & (HCINT_ERRORS | HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { if (!(hcint & HCINT_ERRORS)) td->errcnt = 0; } check_state: switch (td->state) { case DWC_CHAN_ST_START: goto send_pkt; case DWC_CHAN_ST_WAIT_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; } else if (hcint & (HCINT_ACK | HCINT_NYET)) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; td->tt_scheduled = 0; goto complete; } break; case DWC_CHAN_ST_WAIT_S_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; } else if (hcint & (HCINT_ACK | HCINT_NYET)) { goto send_cpkt; } break; case DWC_CHAN_ST_WAIT_C_ANE: if (hcint & HCINT_NYET) { goto send_cpkt; } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; } else if (hcint & HCINT_ACK) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; goto complete; } break; case DWC_CHAN_ST_WAIT_C_PKT: goto send_cpkt; default: break; } goto busy; send_pkt: /* free existing channel, if any */ dwc_otg_host_channel_free(sc, td); if (sizeof(req) != td->remainder) { td->error_any = 1; goto complete; } if (td->hcsplt != 0) { delta = td->tt_start_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_START; goto busy; } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > 5) { /* missed it */ td->tt_scheduled = 0; td->state = DWC_CHAN_ST_START; goto busy; } } /* allocate a new channel */ if (dwc_otg_host_channel_alloc(sc, td, 1)) { td->state = DWC_CHAN_ST_START; goto busy; } if (td->hcsplt != 0) { td->hcsplt &= ~HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_S_ANE; } else { td->state = DWC_CHAN_ST_WAIT_ANE; } /* copy out control request */ usbd_copy_out(td->pc, 0, &req, sizeof(req)); DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); hcchar = td->hcchar; hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK); hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; /* must enable channel before writing data to FIFO */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar); /* transfer data into FIFO */ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4); /* wait until next slot before trying complete split */ td->tt_complete_slot = sc->sc_last_frame_num + 1; /* store number of bytes transmitted */ td->tx_bytes = sizeof(req); goto busy; send_cpkt: /* free existing channel, if any */ dwc_otg_host_channel_free(sc, td); delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > DWC_OTG_TT_SLOT_MAX) { /* we missed the service interval */ if (td->ep_type != UE_ISOCHRONOUS) td->error_any = 1; goto complete; } /* allocate a new channel */ if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } /* wait until next slot before trying complete split */ td->tt_complete_slot = sc->sc_last_frame_num + 1; td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); hcchar = td->hcchar; hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK); hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; /* must enable channel before writing data to FIFO */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar); busy: return (1); /* busy */ complete: dwc_otg_host_channel_free(sc, td); return (0); /* complete */ } static uint8_t dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { struct usb_device_request req __aligned(4); uint32_t temp; uint16_t count; /* check endpoint status */ if (sc->sc_last_rx_status == 0) goto not_complete; if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != 0) goto not_complete; if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) != GRXSTSRD_STP_DATA) { if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) != GRXSTSRD_STP_COMPLETE || td->remainder != 0) { /* release FIFO */ dwc_otg_common_rx_ack(sc); goto not_complete; } /* release FIFO */ dwc_otg_common_rx_ack(sc); return (0); /* complete */ } if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != GRXSTSRD_DPID_DATA0) { /* release FIFO */ dwc_otg_common_rx_ack(sc); goto not_complete; } DPRINTFN(5, "GRXSTSR=0x%08x\n", sc->sc_last_rx_status); /* clear did stall */ td->did_stall = 0; /* get the packet byte count */ count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); if (count != sizeof(req)) { DPRINTFN(0, "Unsupported SETUP packet " "length, %d bytes\n", count); /* release FIFO */ dwc_otg_common_rx_ack(sc); goto not_complete; } /* read FIFO */ dwc_otg_read_fifo(sc, td->pc, 0, sizeof(req)); /* copy out control request */ usbd_copy_out(td->pc, 0, &req, sizeof(req)); td->offset = sizeof(req); td->remainder = 0; /* sneak peek the set address */ if ((req.bmRequestType == UT_WRITE_DEVICE) && (req.bRequest == UR_SET_ADDRESS)) { /* must write address before ZLP */ dwc_otg_set_address(sc, req.wValue[0] & 0x7F); } /* don't send any data by default */ DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0), DIEPCTL_EPDIS); DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DOEPCTL_EPDIS); /* reset IN endpoint buffer */ dwc_otg_tx_fifo_reset(sc, GRSTCTL_TXFIFO(0) | GRSTCTL_TXFFLSH); /* acknowledge RX status */ dwc_otg_common_rx_ack(sc); td->did_stall = 1; not_complete: /* abort any ongoing transfer, before enabling again */ if (!td->did_stall) { td->did_stall = 1; DPRINTFN(5, "stalling IN and OUT direction\n"); temp = sc->sc_out_ctl[0]; /* set stall after enabling endpoint */ DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0), temp | DOEPCTL_STALL); temp = sc->sc_in_ctl[0]; /* set stall assuming endpoint is enabled */ DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0), temp | DIEPCTL_STALL); } return (1); /* not complete */ } static uint8_t dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint8_t delta; delta = sc->sc_tmr_val - td->tmr_val; if (delta >= 128) return (1); /* busy */ td->tmr_val = sc->sc_tmr_val + td->tmr_res; /* set toggle, if any */ if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } return (0); } static uint8_t dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint8_t frame_num = (uint8_t)sc->sc_last_frame_num; if (td->ep_type == UE_ISOCHRONOUS) { /* non TT isochronous traffic */ if (frame_num & (td->tmr_res - 1)) goto busy; if ((frame_num ^ td->tmr_val) & td->tmr_res) goto busy; td->tmr_val = td->tmr_res + sc->sc_last_frame_num; td->toggle = 0; return (0); } else if (td->ep_type == UE_INTERRUPT) { if (!td->tt_scheduled) goto busy; td->tt_scheduled = 0; return (0); } else if (td->did_nak != 0) { /* check if we should pause sending queries for 125us */ if (td->tmr_res == frame_num) { /* wait a bit */ dwc_otg_enable_sof_irq(sc); goto busy; } } else if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } /* query for data one more time */ td->tmr_res = frame_num; td->did_nak = 0; return (0); busy: return (1); } static uint8_t dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t channel) { uint32_t count; /* check endpoint status */ if (sc->sc_last_rx_status == 0) goto busy; if (channel >= DWC_OTG_MAX_CHANNELS) goto busy; if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel) goto busy; switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) { case GRXSTSRH_IN_DATA: DPRINTF("DATA ST=%d STATUS=0x%08x\n", (int)td->state, (int)sc->sc_last_rx_status); if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) { /* * When using SPLIT transactions on interrupt * endpoints, sometimes data occurs twice. */ DPRINTF("Data already received\n"); break; } /* get the packet byte count */ count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); /* check for ISOCHRONOUS endpoint */ if (td->ep_type == UE_ISOCHRONOUS) { if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != GRXSTSRD_DPID_DATA0) { /* more data to be received */ td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE; } else { /* all data received */ td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; /* verify the packet byte count */ if (count != td->remainder) { /* we have a short packet */ td->short_pkt = 1; td->got_short = 1; } } } else { /* verify the packet byte count */ if (count != td->max_packet_size) { if (count < td->max_packet_size) { /* we have a short packet */ td->short_pkt = 1; td->got_short = 1; } else { /* invalid USB packet */ td->error_any = 1; /* release FIFO */ dwc_otg_common_rx_ack(sc); goto complete; } } td->toggle ^= 1; td->tt_scheduled = 0; } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error_any = 1; /* release FIFO */ dwc_otg_common_rx_ack(sc); goto complete; } /* read data from FIFO */ dwc_otg_read_fifo(sc, td->pc, td->offset, count); td->remainder -= count; td->offset += count; sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY; break; default: break; } /* release FIFO */ dwc_otg_common_rx_ack(sc); busy: return (0); complete: return (1); } static uint8_t dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint32_t hcint = 0; uint32_t hcchar; uint8_t delta; uint8_t channel; uint8_t x; for (x = 0; x != td->max_packet_count; x++) { channel = td->channel[x]; if (channel >= DWC_OTG_MAX_CHANNELS) continue; hcint |= sc->sc_chan_state[channel].hcint; DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", channel, td->state, hcint, DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel))); /* check interrupt bits */ if (hcint & (HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { /* give success bits priority over failure bits */ } else if (hcint & HCINT_STALL) { DPRINTF("CH=%d STALL\n", channel); td->error_stall = 1; td->error_any = 1; goto complete; } else if (hcint & HCINT_ERRORS) { DPRINTF("CH=%d ERROR\n", channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { if (td->ep_type != UE_ISOCHRONOUS) { td->error_any = 1; goto complete; } } } /* check channels for data, if any */ if (dwc_otg_host_data_rx_sub(sc, td, channel)) goto complete; /* refresh interrupt status */ hcint |= sc->sc_chan_state[channel].hcint; if (hcint & (HCINT_ERRORS | HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { if (!(hcint & HCINT_ERRORS)) td->errcnt = 0; } } switch (td->state) { case DWC_CHAN_ST_START: if (td->hcsplt != 0) goto receive_spkt; else goto receive_pkt; case DWC_CHAN_ST_WAIT_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { if (td->ep_type == UE_INTERRUPT) { /* * The USB specification does not * mandate a particular data toggle * value for USB INTERRUPT * transfers. Switch the data toggle * value to receive the packet * correctly: */ if (hcint & HCINT_DATATGLERR) { DPRINTF("Retrying packet due to " "data toggle error\n"); td->toggle ^= 1; goto receive_pkt; } } else if (td->ep_type == UE_ISOCHRONOUS) { goto complete; } td->did_nak = 1; td->tt_scheduled = 0; if (td->hcsplt != 0) goto receive_spkt; else goto receive_pkt; } else if (hcint & HCINT_NYET) { if (td->hcsplt != 0) { /* try again */ goto receive_pkt; } else { /* not a valid token for IN endpoints */ td->error_any = 1; goto complete; } } else if (hcint & HCINT_ACK) { /* wait for data - ACK arrived first */ if (!(hcint & HCINT_SOFTWARE_ONLY)) goto busy; if (td->ep_type == UE_ISOCHRONOUS) { /* check if we are complete */ if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) { goto complete; } else { /* get more packets */ goto busy; } } else { /* check if we are complete */ if ((td->remainder == 0) || (td->got_short != 0)) { if (td->short_pkt) goto complete; /* * Else need to receive a zero length * packet. */ } td->tt_scheduled = 0; td->did_nak = 0; if (td->hcsplt != 0) goto receive_spkt; else goto receive_pkt; } } break; case DWC_CHAN_ST_WAIT_S_ANE: /* * NOTE: The DWC OTG hardware provides a fake ACK in * case of interrupt and isochronous transfers: */ if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto receive_spkt; } else if (hcint & HCINT_NYET) { td->tt_scheduled = 0; goto receive_spkt; } else if (hcint & HCINT_ACK) { td->did_nak = 0; goto receive_pkt; } break; case DWC_CHAN_ST_WAIT_C_PKT: goto receive_pkt; default: break; } goto busy; receive_pkt: /* free existing channel, if any */ dwc_otg_host_channel_free(sc, td); if (td->hcsplt != 0) { delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > DWC_OTG_TT_SLOT_MAX) { if (td->ep_type != UE_ISOCHRONOUS) { /* we missed the service interval */ td->error_any = 1; } goto complete; } /* complete split */ td->hcsplt |= HCSPLT_COMPSPLT; } else if (dwc_otg_host_rate_check(sc, td)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } /* allocate a new channel */ if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } /* set toggle, if any */ if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } td->state = DWC_CHAN_ST_WAIT_ANE; for (x = 0; x != td->max_packet_count; x++) { channel = td->channel[x]; /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; /* receive complete split ASAP */ if ((sc->sc_last_frame_num & 1) != 0 && td->ep_type == UE_ISOCHRONOUS) hcchar |= HCCHAR_ODDFRM; else hcchar &= ~HCCHAR_ODDFRM; /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); } /* wait until next slot before trying complete split */ td->tt_complete_slot = sc->sc_last_frame_num + 1; goto busy; receive_spkt: /* free existing channel(s), if any */ dwc_otg_host_channel_free(sc, td); delta = td->tt_start_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_START; goto busy; } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > 5) { /* missed it */ td->tt_scheduled = 0; td->state = DWC_CHAN_ST_START; goto busy; } /* allocate a new channel */ if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_START; goto busy; } channel = td->channel[0]; td->hcsplt &= ~HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_S_ANE; /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); /* send after next SOF event */ if ((sc->sc_last_frame_num & 1) == 0 && td->ep_type == UE_ISOCHRONOUS) td->hcchar |= HCCHAR_ODDFRM; else td->hcchar &= ~HCCHAR_ODDFRM; hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; /* wait until next slot before trying complete split */ td->tt_complete_slot = sc->sc_last_frame_num + 1; /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); busy: return (1); /* busy */ complete: dwc_otg_host_channel_free(sc, td); return (0); /* complete */ } static uint8_t dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint32_t temp; uint16_t count; uint8_t got_short; got_short = 0; /* check endpoint status */ if (sc->sc_last_rx_status == 0) goto not_complete; if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->ep_no) goto not_complete; /* check for SETUP packet */ if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) == GRXSTSRD_STP_DATA || (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) == GRXSTSRD_STP_COMPLETE) { if (td->remainder == 0) { /* * We are actually complete and have * received the next SETUP */ DPRINTFN(5, "faking complete\n"); return (0); /* complete */ } /* * USB Host Aborted the transfer. */ td->error_any = 1; return (0); /* complete */ } if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) != GRXSTSRD_OUT_DATA) { /* release FIFO */ dwc_otg_common_rx_ack(sc); goto not_complete; } /* get the packet byte count */ count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); /* verify the packet byte count */ if (count != td->max_packet_size) { if (count < td->max_packet_size) { /* we have a short packet */ td->short_pkt = 1; got_short = 1; } else { /* invalid USB packet */ td->error_any = 1; /* release FIFO */ dwc_otg_common_rx_ack(sc); return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error_any = 1; /* release FIFO */ dwc_otg_common_rx_ack(sc); return (0); /* we are complete */ } /* read data from FIFO */ dwc_otg_read_fifo(sc, td->pc, td->offset, count); td->remainder -= count; td->offset += count; /* release FIFO */ dwc_otg_common_rx_ack(sc); temp = sc->sc_out_ctl[td->ep_no]; /* check for isochronous mode */ if ((temp & DIEPCTL_EPTYPE_MASK) == (DIEPCTL_EPTYPE_ISOC << DIEPCTL_EPTYPE_SHIFT)) { /* toggle odd or even frame bit */ if (temp & DIEPCTL_SETD1PID) { temp &= ~DIEPCTL_SETD1PID; temp |= DIEPCTL_SETD0PID; } else { temp &= ~DIEPCTL_SETD0PID; temp |= DIEPCTL_SETD1PID; } sc->sc_out_ctl[td->ep_no] = temp; } /* check if we are complete */ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ return (0); } /* else need to receive a zero length packet */ } not_complete: /* enable SETUP and transfer complete interrupt */ if (td->ep_no == 0) { DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DXEPTSIZ_SET_MULTI(3) | DXEPTSIZ_SET_NPKT(1) | DXEPTSIZ_SET_NBYTES(td->max_packet_size)); } else { /* allow reception of multiple packets */ DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(td->ep_no), DXEPTSIZ_SET_MULTI(1) | DXEPTSIZ_SET_NPKT(4) | DXEPTSIZ_SET_NBYTES(4 * ((td->max_packet_size + 3) & ~3))); } temp = sc->sc_out_ctl[td->ep_no]; DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp | DOEPCTL_EPENA | DOEPCTL_CNAK); return (1); /* not complete */ } static uint8_t dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint32_t count; uint32_t hcint; uint32_t hcchar; uint8_t delta; uint8_t channel; uint8_t x; dwc_otg_host_dump_rx(sc, td); /* check that last channel is complete */ channel = td->channel[td->npkt]; if (channel < DWC_OTG_MAX_CHANNELS) { hcint = sc->sc_chan_state[channel].hcint; DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", channel, td->state, hcint, DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel))); if (hcint & (HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { /* give success bits priority over failure bits */ } else if (hcint & HCINT_STALL) { DPRINTF("CH=%d STALL\n", channel); td->error_stall = 1; td->error_any = 1; goto complete; } else if (hcint & HCINT_ERRORS) { DPRINTF("CH=%d ERROR\n", channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { td->error_any = 1; goto complete; } } if (hcint & (HCINT_ERRORS | HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { if (!(hcint & HCINT_ERRORS)) td->errcnt = 0; } } else { hcint = 0; } switch (td->state) { case DWC_CHAN_ST_START: goto send_pkt; case DWC_CHAN_ST_WAIT_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; } else if (hcint & (HCINT_ACK | HCINT_NYET)) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle ^= 1; /* check if next response will be a NAK */ if (hcint & HCINT_NYET) td->did_nak = 1; else td->did_nak = 0; td->tt_scheduled = 0; /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) goto complete; /* * Else we need to transmit a short * packet: */ } goto send_pkt; } break; case DWC_CHAN_ST_WAIT_S_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; } else if (hcint & (HCINT_ACK | HCINT_NYET)) { td->did_nak = 0; goto send_cpkt; } break; case DWC_CHAN_ST_WAIT_C_ANE: if (hcint & HCINT_NYET) { goto send_cpkt; } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; } else if (hcint & HCINT_ACK) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle ^= 1; td->did_nak = 0; td->tt_scheduled = 0; /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) goto complete; /* else we need to transmit a short packet */ } goto send_pkt; } break; case DWC_CHAN_ST_WAIT_C_PKT: goto send_cpkt; case DWC_CHAN_ST_TX_WAIT_ISOC: /* Check if ISOCHRONOUS OUT traffic is complete */ if ((hcint & HCINT_HCH_DONE_MASK) == 0) break; td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; goto complete; default: break; } goto busy; send_pkt: /* free existing channel(s), if any */ dwc_otg_host_channel_free(sc, td); if (td->hcsplt != 0) { delta = td->tt_start_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_START; goto busy; } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > 5) { /* missed it */ td->tt_scheduled = 0; td->state = DWC_CHAN_ST_START; goto busy; } } else if (dwc_otg_host_rate_check(sc, td)) { td->state = DWC_CHAN_ST_START; goto busy; } /* allocate a new channel */ if (dwc_otg_host_channel_alloc(sc, td, 1)) { td->state = DWC_CHAN_ST_START; goto busy; } /* set toggle, if any */ if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } if (td->ep_type == UE_ISOCHRONOUS) { /* ISOCHRONOUS OUT transfers don't have any ACKs */ td->state = DWC_CHAN_ST_TX_WAIT_ISOC; td->hcsplt &= ~HCSPLT_COMPSPLT; if (td->hcsplt != 0) { /* get maximum transfer length */ count = td->remainder; if (count > HCSPLT_XACTLEN_BURST) { DPRINTF("TT overflow\n"); td->error_any = 1; goto complete; } /* Update transaction position */ td->hcsplt &= ~HCSPLT_XACTPOS_MASK; td->hcsplt |= (HCSPLT_XACTPOS_ALL << HCSPLT_XACTPOS_SHIFT); } } else if (td->hcsplt != 0) { td->hcsplt &= ~HCSPLT_COMPSPLT; /* Wait for ACK/NAK/ERR from TT */ td->state = DWC_CHAN_ST_WAIT_S_ANE; } else { /* Wait for ACK/NAK/STALL from device */ td->state = DWC_CHAN_ST_WAIT_ANE; } td->tx_bytes = 0; for (x = 0; x != td->max_packet_count; x++) { uint32_t rem_bytes; channel = td->channel[x]; /* send one packet at a time */ count = td->max_packet_size; rem_bytes = td->remainder - td->tx_bytes; if (rem_bytes < count) { /* we have a short packet */ td->short_pkt = 1; count = rem_bytes; } if (count == rem_bytes) { /* last packet */ switch (x) { case 0: DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (count << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); break; case 1: DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (count << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT)); break; default: DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (count << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT)); break; } } else if (td->ep_type == UE_ISOCHRONOUS && td->max_packet_count > 1) { /* ISOCHRONOUS multi packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (count << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT)); } else { /* TODO: HCTSIZ_DOPNG */ /* standard BULK/INTERRUPT/CONTROL packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (count << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); } DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); hcchar = td->hcchar; hcchar &= ~HCCHAR_EPDIR_IN; /* send after next SOF event */ if ((sc->sc_last_frame_num & 1) == 0 && td->ep_type == UE_ISOCHRONOUS) hcchar |= HCCHAR_ODDFRM; else hcchar &= ~HCCHAR_ODDFRM; /* must enable before writing data to FIFO */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); if (count != 0) { /* write data into FIFO */ dwc_otg_write_fifo(sc, td->pc, td->offset + td->tx_bytes, DOTG_DFIFO(channel), count); } /* store number of bytes transmitted */ td->tx_bytes += count; /* store last packet index */ td->npkt = x; /* check for last packet */ if (count == rem_bytes) break; } goto busy; send_cpkt: /* free existing channel, if any */ dwc_otg_host_channel_free(sc, td); delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > DWC_OTG_TT_SLOT_MAX) { /* we missed the service interval */ if (td->ep_type != UE_ISOCHRONOUS) td->error_any = 1; goto complete; } /* allocate a new channel */ if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } channel = td->channel[0]; td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); hcchar = td->hcchar; hcchar &= ~HCCHAR_EPDIR_IN; /* receive complete split ASAP */ if ((sc->sc_last_frame_num & 1) != 0 && td->ep_type == UE_ISOCHRONOUS) hcchar |= HCCHAR_ODDFRM; else hcchar &= ~HCCHAR_ODDFRM; /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); /* wait until next slot before trying complete split */ td->tt_complete_slot = sc->sc_last_frame_num + 1; busy: return (1); /* busy */ complete: dwc_otg_host_channel_free(sc, td); return (0); /* complete */ } static uint8_t dwc_otg_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint32_t max_buffer; uint32_t count; uint32_t fifo_left; uint32_t mpkt; uint32_t temp; uint8_t to; to = 3; /* don't loop forever! */ max_buffer = sc->sc_hw_ep_profile[td->ep_no].max_buffer; repeat: /* check for for endpoint 0 data */ temp = sc->sc_last_rx_status; if ((td->ep_no == 0) && (temp != 0) && (GRXSTSRD_CHNUM_GET(temp) == 0)) { if ((temp & GRXSTSRD_PKTSTS_MASK) != GRXSTSRD_STP_DATA && (temp & GRXSTSRD_PKTSTS_MASK) != GRXSTSRD_STP_COMPLETE) { /* dump data - wrong direction */ dwc_otg_common_rx_ack(sc); } else { /* * The current transfer was cancelled * by the USB Host: */ td->error_any = 1; return (0); /* complete */ } } /* fill in more TX data, if possible */ if (td->tx_bytes != 0) { uint16_t cpkt; /* check if packets have been transferred */ temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no)); /* get current packet number */ cpkt = DXEPTSIZ_GET_NPKT(temp); if (cpkt >= td->npkt) { fifo_left = 0; } else { if (max_buffer != 0) { fifo_left = (td->npkt - cpkt) * td->max_packet_size; if (fifo_left > max_buffer) fifo_left = max_buffer; } else { fifo_left = td->max_packet_size; } } count = td->tx_bytes; if (count > fifo_left) count = fifo_left; if (count != 0) { /* write data into FIFO */ dwc_otg_write_fifo(sc, td->pc, td->offset, DOTG_DFIFO(td->ep_no), count); td->tx_bytes -= count; td->remainder -= count; td->offset += count; td->npkt = cpkt; } if (td->tx_bytes != 0) goto not_complete; /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) return (0); /* complete */ /* else we need to transmit a short packet */ } } if (!to--) goto not_complete; /* check if not all packets have been transferred */ temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no)); if (DXEPTSIZ_GET_NPKT(temp) != 0) { DPRINTFN(5, "busy ep=%d npkt=%d DIEPTSIZ=0x%08x " "DIEPCTL=0x%08x\n", td->ep_no, DXEPTSIZ_GET_NPKT(temp), temp, DWC_OTG_READ_4(sc, DOTG_DIEPCTL(td->ep_no))); goto not_complete; } DPRINTFN(5, "rem=%u ep=%d\n", td->remainder, td->ep_no); /* try to optimise by sending more data */ if ((max_buffer != 0) && ((td->max_packet_size & 3) == 0)) { /* send multiple packets at the same time */ mpkt = max_buffer / td->max_packet_size; if (mpkt > 0x3FE) mpkt = 0x3FE; count = td->remainder; if (count > 0x7FFFFF) count = 0x7FFFFF - (0x7FFFFF % td->max_packet_size); td->npkt = count / td->max_packet_size; /* * NOTE: We could use 0x3FE instead of "mpkt" in the * check below to get more throughput, but then we * have a dependency towards non-generic chip features * to disable the TX-FIFO-EMPTY interrupts on a per * endpoint basis. Increase the maximum buffer size of * the IN endpoint to increase the performance. */ if (td->npkt > mpkt) { td->npkt = mpkt; count = td->max_packet_size * mpkt; } else if ((count == 0) || (count % td->max_packet_size)) { /* we are transmitting a short packet */ td->npkt++; td->short_pkt = 1; } } else { /* send one packet at a time */ mpkt = 1; count = td->max_packet_size; if (td->remainder < count) { /* we have a short packet */ td->short_pkt = 1; count = td->remainder; } td->npkt = 1; } DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(td->ep_no), DXEPTSIZ_SET_MULTI(1) | DXEPTSIZ_SET_NPKT(td->npkt) | DXEPTSIZ_SET_NBYTES(count)); /* make room for buffering */ td->npkt += mpkt; temp = sc->sc_in_ctl[td->ep_no]; /* check for isochronous mode */ if ((temp & DIEPCTL_EPTYPE_MASK) == (DIEPCTL_EPTYPE_ISOC << DIEPCTL_EPTYPE_SHIFT)) { /* toggle odd or even frame bit */ if (temp & DIEPCTL_SETD1PID) { temp &= ~DIEPCTL_SETD1PID; temp |= DIEPCTL_SETD0PID; } else { temp &= ~DIEPCTL_SETD0PID; temp |= DIEPCTL_SETD1PID; } sc->sc_in_ctl[td->ep_no] = temp; } /* must enable before writing data to FIFO */ DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(td->ep_no), temp | DIEPCTL_EPENA | DIEPCTL_CNAK); td->tx_bytes = count; /* check remainder */ if (td->tx_bytes == 0 && td->remainder == 0) { if (td->short_pkt) return (0); /* complete */ /* else we need to transmit a short packet */ } goto repeat; not_complete: return (1); /* not complete */ } static uint8_t dwc_otg_data_tx_sync(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint32_t temp; /* * If all packets are transferred we are complete: */ temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no)); /* check that all packets have been transferred */ if (DXEPTSIZ_GET_NPKT(temp) != 0) { DPRINTFN(5, "busy ep=%d\n", td->ep_no); goto not_complete; } return (0); not_complete: /* we only want to know if there is a SETUP packet or free IN packet */ temp = sc->sc_last_rx_status; if ((td->ep_no == 0) && (temp != 0) && (GRXSTSRD_CHNUM_GET(temp) == 0)) { if ((temp & GRXSTSRD_PKTSTS_MASK) == GRXSTSRD_STP_DATA || (temp & GRXSTSRD_PKTSTS_MASK) == GRXSTSRD_STP_COMPLETE) { DPRINTFN(5, "faking complete\n"); /* * Race condition: We are complete! */ return (0); } else { /* dump data - wrong direction */ dwc_otg_common_rx_ack(sc); } } return (1); /* not complete */ } static void dwc_otg_xfer_do_fifo(struct dwc_otg_softc *sc, struct usb_xfer *xfer) { struct dwc_otg_td *td; uint8_t toggle; uint8_t tmr_val; uint8_t tmr_res; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; if (td == NULL) return; while (1) { if ((td->func) (sc, td)) { /* operation in progress */ break; } if (((void *)td) == xfer->td_transfer_last) { goto done; } if (td->error_any) { goto done; } else if (td->remainder > 0) { /* * We had a short transfer. If there is no alternate * next, stop processing ! */ if (!td->alt_next) goto done; } /* * Fetch the next transfer descriptor and transfer * some flags to the next transfer descriptor */ tmr_res = td->tmr_res; tmr_val = td->tmr_val; toggle = td->toggle; td = td->obj_next; xfer->td_transfer_cache = td; td->toggle = toggle; /* transfer toggle */ td->tmr_res = tmr_res; td->tmr_val = tmr_val; } return; done: xfer->td_transfer_cache = NULL; sc->sc_xfer_complete = 1; } static uint8_t dwc_otg_xfer_do_complete_locked(struct dwc_otg_softc *sc, struct usb_xfer *xfer) { struct dwc_otg_td *td; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; if (td == NULL) { /* compute all actual lengths */ dwc_otg_standard_done(xfer); return (1); } return (0); } static void dwc_otg_timer(void *_sc) { struct dwc_otg_softc *sc = _sc; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTF("\n"); USB_BUS_SPIN_LOCK(&sc->sc_bus); /* increment timer value */ sc->sc_tmr_val++; /* enable SOF interrupt, which will poll jobs */ dwc_otg_enable_sof_irq(sc); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); if (sc->sc_timer_active) { /* restart timer */ usb_callout_reset(&sc->sc_timer, hz / (1000 / DWC_OTG_HOST_TIMER_RATE), &dwc_otg_timer, sc); } } static void dwc_otg_timer_start(struct dwc_otg_softc *sc) { if (sc->sc_timer_active != 0) return; sc->sc_timer_active = 1; /* restart timer */ usb_callout_reset(&sc->sc_timer, hz / (1000 / DWC_OTG_HOST_TIMER_RATE), &dwc_otg_timer, sc); } static void dwc_otg_timer_stop(struct dwc_otg_softc *sc) { if (sc->sc_timer_active == 0) return; sc->sc_timer_active = 0; /* stop timer */ usb_callout_stop(&sc->sc_timer); } static uint16_t dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo) { if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX) pinfo->slot_index++; return (pinfo->slot_index); } static uint8_t dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc) { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *xfer; struct usb_xfer *xfer_next; struct dwc_otg_td *td; uint16_t temp; uint16_t slot; temp = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK; if (sc->sc_last_frame_num == temp) return (0); sc->sc_last_frame_num = temp; TAILQ_INIT(&head); if ((temp & 7) == 0) { /* reset the schedule */ memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->ep_type != UE_ISOCHRONOUS) continue; /* check for IN direction */ if ((td->hcchar & HCCHAR_EPDIR_IN) != 0) continue; sc->sc_needsof = 1; if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; /* compute slot */ slot = dwc_otg_compute_isoc_rx_tt_slot( sc->sc_tt_info + td->tt_index); if (slot > 3) { /* * Not enough time to get complete * split executed. */ continue; } /* Delayed start */ td->tt_start_slot = temp + slot; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->ep_type != UE_ISOCHRONOUS) continue; /* check for OUT direction */ if ((td->hcchar & HCCHAR_EPDIR_IN) == 0) continue; sc->sc_needsof = 1; if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; /* Start ASAP */ td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->ep_type != UE_INTERRUPT) continue; if (td->tt_scheduled != 0) { sc->sc_needsof = 1; continue; } if (dwc_otg_host_rate_check_interrupt(sc, td)) continue; if (td->hcsplt == 0) { sc->sc_needsof = 1; td->tt_scheduled = 1; continue; } /* start ASAP */ td->tt_start_slot = temp; sc->sc_needsof = 1; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->ep_type != UE_CONTROL) { continue; } sc->sc_needsof = 1; if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; /* start ASAP */ td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } } if ((temp & 7) < 6) { TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->ep_type != UE_BULK) { continue; } sc->sc_needsof = 1; if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; /* start ASAP */ td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } } /* Put TT transfers in execution order at the end */ TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); /* move all TT transfers in front, keeping the current order */ TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->hcsplt == 0) continue; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } TAILQ_CONCAT(&head, &sc->sc_bus.intr_q.head, wait_entry); TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); /* put non-TT non-ISOCHRONOUS transfers last */ TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->hcsplt != 0 || td->ep_type == UE_ISOCHRONOUS) continue; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); if ((temp & 7) == 0) { DPRINTFN(12, "SOF interrupt #%d, needsof=%d\n", (int)temp, (int)sc->sc_needsof); /* update SOF IRQ mask */ if (sc->sc_irq_mask & GINTMSK_SOFMSK) { if (sc->sc_needsof == 0) { sc->sc_irq_mask &= ~GINTMSK_SOFMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } } else { if (sc->sc_needsof != 0) { sc->sc_irq_mask |= GINTMSK_SOFMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } } /* clear need SOF flag */ sc->sc_needsof = 0; } return (1); } static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc) { struct usb_xfer *xfer; uint32_t count; uint32_t temp; uint32_t haint; uint8_t got_rx_status; uint8_t x; if (sc->sc_flags.status_device_mode == 0) { /* * Update host transfer schedule, so that new * transfers can be issued: */ dwc_otg_update_host_transfer_schedule_locked(sc); } count = 0; repeat: if (++count == 16) { /* give other interrupts a chance */ DPRINTF("Yield\n"); return; } /* get all host channel interrupts */ haint = DWC_OTG_READ_4(sc, DOTG_HAINT); while (1) { x = ffs(haint) - 1; if (x >= sc->sc_host_ch_max) break; temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); temp &= ~HCINT_SOFTWARE_ONLY; sc->sc_chan_state[x].hcint |= temp; haint &= ~(1U << x); } if (sc->sc_last_rx_status == 0) { temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); if (temp & GINTSTS_RXFLVL) { /* pop current status */ sc->sc_last_rx_status = DWC_OTG_READ_4(sc, DOTG_GRXSTSPD); } if (sc->sc_last_rx_status != 0) { uint8_t ep_no; temp = sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK; /* non-data messages we simply skip */ if (temp != GRXSTSRD_STP_DATA && temp != GRXSTSRD_STP_COMPLETE && temp != GRXSTSRD_OUT_DATA) { /* check for halted channel */ if (temp == GRXSTSRH_HALTED) { ep_no = GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status); sc->sc_chan_state[ep_no].wait_halted = 0; DPRINTFN(5, "channel halt complete ch=%u\n", ep_no); } /* store bytes and FIFO offset */ sc->sc_current_rx_bytes = 0; sc->sc_current_rx_fifo = 0; /* acknowledge status */ dwc_otg_common_rx_ack(sc); goto repeat; } temp = GRXSTSRD_BCNT_GET( sc->sc_last_rx_status); ep_no = GRXSTSRD_CHNUM_GET( sc->sc_last_rx_status); /* store bytes and FIFO offset */ sc->sc_current_rx_bytes = (temp + 3) & ~3; sc->sc_current_rx_fifo = DOTG_DFIFO(ep_no); DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no); /* check if we should dump the data */ if (!(sc->sc_active_rx_ep & (1U << ep_no))) { dwc_otg_common_rx_ack(sc); goto repeat; } got_rx_status = 1; DPRINTFN(5, "RX status = 0x%08x: ch=%d pid=%d bytes=%d sts=%d\n", sc->sc_last_rx_status, ep_no, (sc->sc_last_rx_status >> 15) & 3, GRXSTSRD_BCNT_GET(sc->sc_last_rx_status), (sc->sc_last_rx_status >> 17) & 15); } else { got_rx_status = 0; } } else { uint8_t ep_no; ep_no = GRXSTSRD_CHNUM_GET( sc->sc_last_rx_status); /* check if we should dump the data */ if (!(sc->sc_active_rx_ep & (1U << ep_no))) { dwc_otg_common_rx_ack(sc); goto repeat; } got_rx_status = 1; } /* execute FIFOs */ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) dwc_otg_xfer_do_fifo(sc, xfer); if (got_rx_status) { /* check if data was consumed */ if (sc->sc_last_rx_status == 0) goto repeat; /* disable RX FIFO level interrupt */ sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } } static void dwc_otg_interrupt_complete_locked(struct dwc_otg_softc *sc) { struct usb_xfer *xfer; repeat: /* scan for completion events */ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (dwc_otg_xfer_do_complete_locked(sc, xfer)) goto repeat; } } static void dwc_otg_vbus_interrupt(struct dwc_otg_softc *sc, uint8_t is_on) { DPRINTFN(5, "vbus = %u\n", is_on); /* * If the USB host mode is forced, then assume VBUS is always * present else rely on the input to this function: */ if ((is_on != 0) || (sc->sc_mode == DWC_MODE_HOST)) { if (!sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 1; /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); } } else { if (sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); } } } int dwc_otg_filter_interrupt(void *arg) { struct dwc_otg_softc *sc = arg; int retval = FILTER_HANDLED; uint32_t status; USB_BUS_SPIN_LOCK(&sc->sc_bus); /* read and clear interrupt status */ status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); /* clear interrupts we are handling here */ DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & ~DWC_OTG_MSK_GINT_THREAD_IRQ); /* check for USB state change interrupts */ if ((status & DWC_OTG_MSK_GINT_THREAD_IRQ) != 0) retval = FILTER_SCHEDULE_THREAD; /* clear FIFO empty interrupts */ if (status & sc->sc_irq_mask & (GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP)) { sc->sc_irq_mask &= ~(GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP); DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } /* clear all IN endpoint interrupts */ if (status & GINTSTS_IEPINT) { uint32_t temp; uint8_t x; for (x = 0; x != sc->sc_dev_in_ep_max; x++) { temp = DWC_OTG_READ_4(sc, DOTG_DIEPINT(x)); if (temp & DIEPMSK_XFERCOMPLMSK) { DWC_OTG_WRITE_4(sc, DOTG_DIEPINT(x), DIEPMSK_XFERCOMPLMSK); } } } /* poll FIFOs, if any */ dwc_otg_interrupt_poll_locked(sc); if (sc->sc_xfer_complete != 0) retval = FILTER_SCHEDULE_THREAD; USB_BUS_SPIN_UNLOCK(&sc->sc_bus); return (retval); } void dwc_otg_interrupt(void *arg) { struct dwc_otg_softc *sc = arg; uint32_t status; USB_BUS_LOCK(&sc->sc_bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); /* read and clear interrupt status */ status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); /* clear interrupts we are handling here */ DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & DWC_OTG_MSK_GINT_THREAD_IRQ); DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n", status, DWC_OTG_READ_4(sc, DOTG_HAINT), DWC_OTG_READ_4(sc, DOTG_HFNUM)); if (status & GINTSTS_USBRST) { /* set correct state */ sc->sc_flags.status_device_mode = 1; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* Disable SOF interrupt */ sc->sc_irq_mask &= ~GINTMSK_SOFMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); } /* check for any bus state change interrupts */ if (status & GINTSTS_ENUMDONE) { uint32_t temp; DPRINTFN(5, "end of reset\n"); /* set correct state */ sc->sc_flags.status_device_mode = 1; sc->sc_flags.status_bus_reset = 1; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; sc->sc_flags.status_low_speed = 0; sc->sc_flags.port_enabled = 1; /* reset FIFOs */ (void) dwc_otg_init_fifo(sc, DWC_MODE_DEVICE); /* reset function address */ dwc_otg_set_address(sc, 0); /* figure out enumeration speed */ temp = DWC_OTG_READ_4(sc, DOTG_DSTS); if (DSTS_ENUMSPD_GET(temp) == DSTS_ENUMSPD_HI) sc->sc_flags.status_high_speed = 1; else sc->sc_flags.status_high_speed = 0; /* * Disable resume and SOF interrupt, and enable * suspend and RX frame interrupt: */ sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK); sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); } if (status & GINTSTS_PRTINT) { uint32_t hprt; hprt = DWC_OTG_READ_4(sc, DOTG_HPRT); /* clear change bits */ DWC_OTG_WRITE_4(sc, DOTG_HPRT, (hprt & ( HPRT_PRTPWR | HPRT_PRTENCHNG | HPRT_PRTCONNDET | HPRT_PRTOVRCURRCHNG)) | sc->sc_hprt_val); DPRINTFN(12, "GINTSTS=0x%08x, HPRT=0x%08x\n", status, hprt); sc->sc_flags.status_device_mode = 0; if (hprt & HPRT_PRTCONNSTS) sc->sc_flags.status_bus_reset = 1; else sc->sc_flags.status_bus_reset = 0; if (hprt & HPRT_PRTENCHNG) sc->sc_flags.change_enabled = 1; if (hprt & HPRT_PRTENA) sc->sc_flags.port_enabled = 1; else sc->sc_flags.port_enabled = 0; if (hprt & HPRT_PRTOVRCURRCHNG) sc->sc_flags.change_over_current = 1; if (hprt & HPRT_PRTOVRCURRACT) sc->sc_flags.port_over_current = 1; else sc->sc_flags.port_over_current = 0; if (hprt & HPRT_PRTPWR) sc->sc_flags.port_powered = 1; else sc->sc_flags.port_powered = 0; if (((hprt & HPRT_PRTSPD_MASK) >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_LOW) sc->sc_flags.status_low_speed = 1; else sc->sc_flags.status_low_speed = 0; if (((hprt & HPRT_PRTSPD_MASK) >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_HIGH) sc->sc_flags.status_high_speed = 1; else sc->sc_flags.status_high_speed = 0; if (hprt & HPRT_PRTCONNDET) sc->sc_flags.change_connect = 1; if (hprt & HPRT_PRTSUSP) dwc_otg_suspend_irq(sc); else dwc_otg_resume_irq(sc); /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); /* update host frame interval */ dwc_otg_update_host_frame_interval(sc); } /* * If resume and suspend is set at the same time we interpret * that like RESUME. Resume is set when there is at least 3 * milliseconds of inactivity on the USB BUS. */ if (status & GINTSTS_WKUPINT) { DPRINTFN(5, "resume interrupt\n"); dwc_otg_resume_irq(sc); } else if (status & GINTSTS_USBSUSP) { DPRINTFN(5, "suspend interrupt\n"); dwc_otg_suspend_irq(sc); } /* check VBUS */ if (status & (GINTSTS_USBSUSP | GINTSTS_USBRST | GINTMSK_OTGINTMSK | GINTSTS_SESSREQINT)) { uint32_t temp; temp = DWC_OTG_READ_4(sc, DOTG_GOTGCTL); DPRINTFN(5, "GOTGCTL=0x%08x\n", temp); dwc_otg_vbus_interrupt(sc, (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0); } if (sc->sc_xfer_complete != 0) { sc->sc_xfer_complete = 0; /* complete FIFOs, if any */ dwc_otg_interrupt_complete_locked(sc); } USB_BUS_SPIN_UNLOCK(&sc->sc_bus); USB_BUS_UNLOCK(&sc->sc_bus); } static void dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp) { struct dwc_otg_td *td; /* get current Transfer Descriptor */ td = temp->td_next; temp->td = td; /* prepare for next TD */ temp->td_next = td->obj_next; /* fill out the Transfer Descriptor */ td->func = temp->func; td->pc = temp->pc; td->offset = temp->offset; td->remainder = temp->len; td->tx_bytes = 0; td->error_any = 0; td->error_stall = 0; td->npkt = 0; td->did_stall = temp->did_stall; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; td->set_toggle = 0; td->got_short = 0; td->did_nak = 0; td->channel[0] = DWC_OTG_MAX_CHANNELS; td->channel[1] = DWC_OTG_MAX_CHANNELS; td->channel[2] = DWC_OTG_MAX_CHANNELS; td->state = 0; td->errcnt = 0; td->tt_scheduled = 0; td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; } static void dwc_otg_setup_standard_chain(struct usb_xfer *xfer) { struct dwc_otg_std_temp temp; struct dwc_otg_td *td; uint32_t x; uint8_t need_sync; uint8_t is_host; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); temp.max_frame_size = xfer->max_frame_size; td = xfer->td_start[0]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; /* setup temp */ temp.pc = NULL; temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.offset = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr; temp.did_stall = !xfer->flags_int.control_stall; is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST); /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { if (is_host) temp.func = &dwc_otg_host_setup_tx; else temp.func = &dwc_otg_setup_rx; temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) temp.setup_alt_next = 0; } dwc_otg_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } if (x != xfer->nframes) { if (xfer->endpointno & UE_DIR_IN) { if (is_host) { temp.func = &dwc_otg_host_data_rx; need_sync = 0; } else { temp.func = &dwc_otg_data_tx; need_sync = 1; } } else { if (is_host) { temp.func = &dwc_otg_host_data_tx; need_sync = 0; } else { temp.func = &dwc_otg_data_rx; need_sync = 0; } } /* setup "pc" pointer */ temp.pc = xfer->frbuffers + x; } else { need_sync = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_act) { temp.setup_alt_next = 0; } } else { temp.setup_alt_next = 0; } } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.short_pkt = 0; } else { /* regular data transfer */ temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1); } dwc_otg_setup_standard_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += temp.len; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } if (xfer->flags_int.control_xfr) { /* always setup a valid "pc" pointer for status and sync */ temp.pc = xfer->frbuffers + 0; temp.len = 0; temp.short_pkt = 0; temp.setup_alt_next = 0; /* check if we need to sync */ if (need_sync) { /* we need a SYNC point after TX */ temp.func = &dwc_otg_data_tx_sync; dwc_otg_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (!xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current * endpoint direction. */ if (xfer->endpointno & UE_DIR_IN) { if (is_host) { temp.func = &dwc_otg_host_data_tx; need_sync = 0; } else { temp.func = &dwc_otg_data_rx; need_sync = 0; } } else { if (is_host) { temp.func = &dwc_otg_host_data_rx; need_sync = 0; } else { temp.func = &dwc_otg_data_tx; need_sync = 1; } } dwc_otg_setup_standard_chain_sub(&temp); /* data toggle should be DATA1 */ td = temp.td; td->set_toggle = 1; if (need_sync) { /* we need a SYNC point after TX */ temp.func = &dwc_otg_data_tx_sync; dwc_otg_setup_standard_chain_sub(&temp); } } } else { /* check if we need to sync */ if (need_sync) { temp.pc = xfer->frbuffers + 0; temp.len = 0; temp.short_pkt = 0; temp.setup_alt_next = 0; /* we need a SYNC point after TX */ temp.func = &dwc_otg_data_tx_sync; dwc_otg_setup_standard_chain_sub(&temp); } } /* must have at least one frame! */ td = temp.td; xfer->td_transfer_last = td; if (is_host) { struct dwc_otg_softc *sc; uint32_t hcchar; uint32_t hcsplt; sc = DWC_OTG_BUS2SC(xfer->xroot->bus); /* get first again */ td = xfer->td_transfer_first; td->toggle = (xfer->endpoint->toggle_next ? 1 : 0); hcchar = (xfer->address << HCCHAR_DEVADDR_SHIFT) | ((xfer->endpointno & UE_ADDR) << HCCHAR_EPNUM_SHIFT) | (xfer->max_packet_size << HCCHAR_MPS_SHIFT) | HCCHAR_CHENA; /* * We are not always able to meet the timing * requirements of the USB interrupt endpoint's * complete split token, when doing transfers going * via a transaction translator. Use the CONTROL * transfer type instead of the INTERRUPT transfer * type in general, as a means to workaround * that. This trick should work for both FULL and LOW * speed USB traffic going through a TT. For non-TT - * traffic it works aswell. The reason for using + * traffic it works as well. The reason for using * CONTROL type instead of BULK is that some TTs might * reject LOW speed BULK traffic. */ if (td->ep_type == UE_INTERRUPT) hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT); else hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT); if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) hcchar |= HCCHAR_EPDIR_IN; switch (xfer->xroot->udev->speed) { case USB_SPEED_LOW: hcchar |= HCCHAR_LSPDDEV; /* FALLTHROUGH */ case USB_SPEED_FULL: /* check if root HUB port is running High Speed */ if (dwc_otg_uses_split(xfer->xroot->udev)) { hcsplt = HCSPLT_SPLTENA | (xfer->xroot->udev->hs_port_no << HCSPLT_PRTADDR_SHIFT) | (xfer->xroot->udev->hs_hub_addr << HCSPLT_HUBADDR_SHIFT); } else { hcsplt = 0; } if (td->ep_type == UE_INTERRUPT) { uint32_t ival; ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE; if (ival == 0) ival = 1; else if (ival > 127) ival = 127; td->tmr_val = sc->sc_tmr_val + ival; td->tmr_res = ival; } else if (td->ep_type == UE_ISOCHRONOUS) { td->tmr_res = 1; td->tmr_val = sc->sc_last_frame_num; if (td->hcchar & HCCHAR_EPDIR_IN) td->tmr_val++; } else { td->tmr_val = 0; td->tmr_res = (uint8_t)sc->sc_last_frame_num; } break; case USB_SPEED_HIGH: hcsplt = 0; if (td->ep_type == UE_INTERRUPT) { uint32_t ival; hcchar |= ((xfer->max_packet_count & 3) << HCCHAR_MC_SHIFT); ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE; if (ival == 0) ival = 1; else if (ival > 127) ival = 127; td->tmr_val = sc->sc_tmr_val + ival; td->tmr_res = ival; } else if (td->ep_type == UE_ISOCHRONOUS) { hcchar |= ((xfer->max_packet_count & 3) << HCCHAR_MC_SHIFT); td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer); td->tmr_val = sc->sc_last_frame_num; if (td->hcchar & HCCHAR_EPDIR_IN) td->tmr_val += td->tmr_res; } else { td->tmr_val = 0; td->tmr_res = (uint8_t)sc->sc_last_frame_num; } break; default: hcsplt = 0; td->tmr_val = 0; td->tmr_res = 0; break; } /* store configuration in all TD's */ while (1) { td->hcchar = hcchar; td->hcsplt = hcsplt; if (((void *)td) == xfer->td_transfer_last) break; td = td->obj_next; } } } static void dwc_otg_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ dwc_otg_device_done(xfer, USB_ERR_TIMEOUT); } static void dwc_otg_start_standard_chain(struct usb_xfer *xfer) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); DPRINTFN(9, "\n"); /* * Poll one time in device mode, which will turn on the * endpoint interrupts. Else wait for SOF interrupt in host * mode. */ USB_BUS_SPIN_LOCK(&sc->sc_bus); if (sc->sc_flags.status_device_mode != 0) { dwc_otg_xfer_do_fifo(sc, xfer); if (dwc_otg_xfer_do_complete_locked(sc, xfer)) goto done; } else { struct dwc_otg_td *td = xfer->td_transfer_cache; if (td->ep_type == UE_ISOCHRONOUS && (td->hcchar & HCCHAR_EPDIR_IN) == 0) { /* * Need to start ISOCHRONOUS OUT transfer ASAP * because execution is delayed by one 125us * microframe: */ dwc_otg_xfer_do_fifo(sc, xfer); if (dwc_otg_xfer_do_complete_locked(sc, xfer)) goto done; } } /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usbd_transfer_timeout_ms(xfer, &dwc_otg_timeout, xfer->timeout); } if (sc->sc_flags.status_device_mode != 0) goto done; /* enable SOF interrupt, if any */ dwc_otg_enable_sof_irq(sc); done: USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void dwc_otg_root_intr(struct dwc_otg_softc *sc) { DPRINTFN(9, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* set port bit */ sc->sc_hub_idata[0] = 0x02; /* we only have one port */ uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } static usb_error_t dwc_otg_standard_done_sub(struct usb_xfer *xfer) { struct dwc_otg_td *td; uint32_t len; usb_error_t error; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; do { len = td->remainder; /* store last data toggle */ xfer->endpoint->toggle_next = td->toggle; if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract * the remainder from "frlengths[]": */ if (len > xfer->frlengths[xfer->aframes]) { td->error_any = 1; } else { xfer->frlengths[xfer->aframes] -= len; } } /* Check for transfer error */ if (td->error_any) { /* the transfer is finished */ error = (td->error_stall ? USB_ERR_STALLED : USB_ERR_IOERROR); td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr) { /* follow alt next */ if (td->alt_next) { td = td->obj_next; } else { td = NULL; } } else { /* the transfer is finished */ td = NULL; } error = 0; break; } td = td->obj_next; /* this USB frame is complete */ error = 0; break; } while (0); /* update transfer cache */ xfer->td_transfer_cache = td; return (error); } static void dwc_otg_standard_done(struct usb_xfer *xfer) { usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = dwc_otg_standard_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = dwc_otg_standard_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = dwc_otg_standard_done_sub(xfer); } done: dwc_otg_device_done(xfer, err); } /*------------------------------------------------------------------------* * dwc_otg_device_done * * NOTE: this function can be called more than one time on the * same USB transfer! *------------------------------------------------------------------------*/ static void dwc_otg_device_done(struct usb_xfer *xfer, usb_error_t error) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); DPRINTFN(9, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); USB_BUS_SPIN_LOCK(&sc->sc_bus); if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* Interrupts are cleared by the interrupt handler */ } else { struct dwc_otg_td *td; td = xfer->td_transfer_cache; if (td != NULL) dwc_otg_host_channel_free(sc, td); } /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void dwc_otg_xfer_stall(struct usb_xfer *xfer) { dwc_otg_device_done(xfer, USB_ERR_STALLED); } static void dwc_otg_set_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t *did_stall) { struct dwc_otg_softc *sc; uint32_t temp; uint32_t reg; uint8_t ep_no; USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check mode */ if (udev->flags.usb_mode != USB_MODE_DEVICE) { /* not supported */ return; } sc = DWC_OTG_BUS2SC(udev->bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); /* get endpoint address */ ep_no = ep->edesc->bEndpointAddress; DPRINTFN(5, "endpoint=0x%x\n", ep_no); if (ep_no & UE_DIR_IN) { reg = DOTG_DIEPCTL(ep_no & UE_ADDR); temp = sc->sc_in_ctl[ep_no & UE_ADDR]; } else { reg = DOTG_DOEPCTL(ep_no & UE_ADDR); temp = sc->sc_out_ctl[ep_no & UE_ADDR]; } /* disable and stall endpoint */ DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_EPDIS); DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_STALL); /* clear active OUT ep */ if (!(ep_no & UE_DIR_IN)) { sc->sc_active_rx_ep &= ~(1U << (ep_no & UE_ADDR)); if (sc->sc_last_rx_status != 0 && (ep_no & UE_ADDR) == GRXSTSRD_CHNUM_GET( sc->sc_last_rx_status)) { /* dump data */ dwc_otg_common_rx_ack(sc); /* poll interrupt */ dwc_otg_interrupt_poll_locked(sc); dwc_otg_interrupt_complete_locked(sc); } } USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void dwc_otg_clear_stall_sub_locked(struct dwc_otg_softc *sc, uint32_t mps, uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) { uint32_t reg; uint32_t temp; if (ep_type == UE_CONTROL) { /* clearing stall is not needed */ return; } if (ep_dir) { reg = DOTG_DIEPCTL(ep_no); } else { reg = DOTG_DOEPCTL(ep_no); sc->sc_active_rx_ep |= (1U << ep_no); } /* round up and mask away the multiplier count */ mps = (mps + 3) & 0x7FC; if (ep_type == UE_BULK) { temp = DIEPCTL_EPTYPE_SET( DIEPCTL_EPTYPE_BULK) | DIEPCTL_USBACTEP; } else if (ep_type == UE_INTERRUPT) { temp = DIEPCTL_EPTYPE_SET( DIEPCTL_EPTYPE_INTERRUPT) | DIEPCTL_USBACTEP; } else { temp = DIEPCTL_EPTYPE_SET( DIEPCTL_EPTYPE_ISOC) | DIEPCTL_USBACTEP; } temp |= DIEPCTL_MPS_SET(mps); temp |= DIEPCTL_TXFNUM_SET(ep_no); if (ep_dir) sc->sc_in_ctl[ep_no] = temp; else sc->sc_out_ctl[ep_no] = temp; DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_EPDIS); DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_SETD0PID); DWC_OTG_WRITE_4(sc, reg, temp | DIEPCTL_SNAK); /* we only reset the transmit FIFO */ if (ep_dir) { dwc_otg_tx_fifo_reset(sc, GRSTCTL_TXFIFO(ep_no) | GRSTCTL_TXFFLSH); DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(ep_no), 0); } /* poll interrupt */ dwc_otg_interrupt_poll_locked(sc); dwc_otg_interrupt_complete_locked(sc); } static void dwc_otg_clear_stall(struct usb_device *udev, struct usb_endpoint *ep) { struct dwc_otg_softc *sc; struct usb_endpoint_descriptor *ed; DPRINTFN(5, "endpoint=%p\n", ep); USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check mode */ if (udev->flags.usb_mode != USB_MODE_DEVICE) { /* not supported */ return; } /* get softc */ sc = DWC_OTG_BUS2SC(udev->bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); /* get endpoint descriptor */ ed = ep->edesc; /* reset endpoint */ dwc_otg_clear_stall_sub_locked(sc, UGETW(ed->wMaxPacketSize), (ed->bEndpointAddress & UE_ADDR), (ed->bmAttributes & UE_XFERTYPE), (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } static void dwc_otg_device_state_change(struct usb_device *udev) { struct dwc_otg_softc *sc; uint8_t x; /* check mode */ if (udev->flags.usb_mode != USB_MODE_DEVICE) { /* not supported */ return; } /* get softc */ sc = DWC_OTG_BUS2SC(udev->bus); /* deactivate all other endpoint but the control endpoint */ if (udev->state == USB_STATE_CONFIGURED || udev->state == USB_STATE_ADDRESSED) { USB_BUS_LOCK(&sc->sc_bus); for (x = 1; x != sc->sc_dev_ep_max; x++) { if (x < sc->sc_dev_in_ep_max) { DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(x), DIEPCTL_EPDIS); DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(x), 0); } DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(x), DOEPCTL_EPDIS); DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(x), 0); } USB_BUS_UNLOCK(&sc->sc_bus); } } int dwc_otg_init(struct dwc_otg_softc *sc) { uint32_t temp; DPRINTF("start\n"); /* set up the bus structure */ sc->sc_bus.usbrev = USB_REV_2_0; sc->sc_bus.methods = &dwc_otg_bus_methods; usb_callout_init_mtx(&sc->sc_timer, &sc->sc_bus.bus_mtx, 0); USB_BUS_LOCK(&sc->sc_bus); /* turn on clocks */ dwc_otg_clocks_on(sc); temp = DWC_OTG_READ_4(sc, DOTG_GSNPSID); DPRINTF("Version = 0x%08x\n", temp); /* disconnect */ DWC_OTG_WRITE_4(sc, DOTG_DCTL, DCTL_SFTDISCON); /* wait for host to detect disconnect */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 32); DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, GRSTCTL_CSFTRST); /* wait a little bit for block to reset */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 128); switch (sc->sc_mode) { case DWC_MODE_DEVICE: temp = GUSBCFG_FORCEDEVMODE; break; case DWC_MODE_HOST: temp = GUSBCFG_FORCEHOSTMODE; break; default: temp = 0; break; } /* select HSIC, ULPI or internal PHY mode */ switch (dwc_otg_phy_type) { case DWC_OTG_PHY_HSIC: DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, GUSBCFG_PHYIF | GUSBCFG_TRD_TIM_SET(5) | temp); DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0x000000EC); temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, temp & ~GLPMCFG_HSIC_CONN); DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, temp | GLPMCFG_HSIC_CONN); break; case DWC_OTG_PHY_ULPI: DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_TRD_TIM_SET(5) | temp); DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0); temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, temp & ~GLPMCFG_HSIC_CONN); break; case DWC_OTG_PHY_INTERNAL: DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, GUSBCFG_PHYSEL | GUSBCFG_TRD_TIM_SET(5) | temp); DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0); temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, temp & ~GLPMCFG_HSIC_CONN); temp = DWC_OTG_READ_4(sc, DOTG_GGPIO); temp &= ~(DOTG_GGPIO_NOVBUSSENS | DOTG_GGPIO_I2CPADEN); temp |= (DOTG_GGPIO_VBUSASEN | DOTG_GGPIO_VBUSBSEN | DOTG_GGPIO_PWRDWN); DWC_OTG_WRITE_4(sc, DOTG_GGPIO, temp); break; default: break; } /* clear global nak */ DWC_OTG_WRITE_4(sc, DOTG_DCTL, DCTL_CGOUTNAK | DCTL_CGNPINNAK); /* disable USB port */ DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0xFFFFFFFF); /* wait 10ms */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); /* enable USB port */ DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0); /* wait 10ms */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG3); sc->sc_fifo_size = 4 * GHWCFG3_DFIFODEPTH_GET(temp); temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2); sc->sc_dev_ep_max = GHWCFG2_NUMDEVEPS_GET(temp); if (sc->sc_dev_ep_max > DWC_OTG_MAX_ENDPOINTS) sc->sc_dev_ep_max = DWC_OTG_MAX_ENDPOINTS; sc->sc_host_ch_max = GHWCFG2_NUMHSTCHNL_GET(temp); if (sc->sc_host_ch_max > DWC_OTG_MAX_CHANNELS) sc->sc_host_ch_max = DWC_OTG_MAX_CHANNELS; temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG4); sc->sc_dev_in_ep_max = GHWCFG4_NUM_IN_EP_GET(temp); DPRINTF("Total FIFO size = %d bytes, Device EPs = %d/%d Host CHs = %d\n", sc->sc_fifo_size, sc->sc_dev_ep_max, sc->sc_dev_in_ep_max, sc->sc_host_ch_max); /* setup FIFO */ if (dwc_otg_init_fifo(sc, sc->sc_mode)) { USB_BUS_UNLOCK(&sc->sc_bus); return (EINVAL); } /* enable interrupts */ sc->sc_irq_mask = DWC_OTG_MSK_GINT_ENABLED; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_DEVICE) { /* enable all endpoint interrupts */ temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2); if (temp & GHWCFG2_MPI) { uint8_t x; DPRINTF("Disable Multi Process Interrupts\n"); for (x = 0; x != sc->sc_dev_in_ep_max; x++) { DWC_OTG_WRITE_4(sc, DOTG_DIEPEACHINTMSK(x), 0); DWC_OTG_WRITE_4(sc, DOTG_DOEPEACHINTMSK(x), 0); } DWC_OTG_WRITE_4(sc, DOTG_DEACHINTMSK, 0); } DWC_OTG_WRITE_4(sc, DOTG_DIEPMSK, DIEPMSK_XFERCOMPLMSK); DWC_OTG_WRITE_4(sc, DOTG_DOEPMSK, 0); DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF); } if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_HOST) { /* setup clocks */ temp = DWC_OTG_READ_4(sc, DOTG_HCFG); temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK); temp |= (1 << HCFG_FSLSPCLKSEL_SHIFT); DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp); } /* only enable global IRQ */ DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, GAHBCFG_GLBLINTRMSK); /* turn off clocks */ dwc_otg_clocks_off(sc); /* read initial VBUS state */ temp = DWC_OTG_READ_4(sc, DOTG_GOTGCTL); DPRINTFN(5, "GOTCTL=0x%08x\n", temp); dwc_otg_vbus_interrupt(sc, (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0); USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ dwc_otg_do_poll(&sc->sc_bus); return (0); /* success */ } void dwc_otg_uninit(struct dwc_otg_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); /* stop host timer */ dwc_otg_timer_stop(sc); /* set disconnect */ DWC_OTG_WRITE_4(sc, DOTG_DCTL, DCTL_SFTDISCON); /* turn off global IRQ */ DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, 0); sc->sc_flags.port_enabled = 0; sc->sc_flags.port_powered = 0; sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; dwc_otg_pull_down(sc); dwc_otg_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); usb_callout_drain(&sc->sc_timer); } static void dwc_otg_suspend(struct dwc_otg_softc *sc) { return; } static void dwc_otg_resume(struct dwc_otg_softc *sc) { return; } static void dwc_otg_do_poll(struct usb_bus *bus) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); dwc_otg_interrupt_poll_locked(sc); dwc_otg_interrupt_complete_locked(sc); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * DWC OTG bulk support * DWC OTG control support * DWC OTG interrupt support *------------------------------------------------------------------------*/ static void dwc_otg_device_non_isoc_open(struct usb_xfer *xfer) { } static void dwc_otg_device_non_isoc_close(struct usb_xfer *xfer) { dwc_otg_device_done(xfer, USB_ERR_CANCELLED); } static void dwc_otg_device_non_isoc_enter(struct usb_xfer *xfer) { } static void dwc_otg_device_non_isoc_start(struct usb_xfer *xfer) { /* setup TDs */ dwc_otg_setup_standard_chain(xfer); dwc_otg_start_standard_chain(xfer); } static const struct usb_pipe_methods dwc_otg_device_non_isoc_methods = { .open = dwc_otg_device_non_isoc_open, .close = dwc_otg_device_non_isoc_close, .enter = dwc_otg_device_non_isoc_enter, .start = dwc_otg_device_non_isoc_start, }; /*------------------------------------------------------------------------* * DWC OTG full speed isochronous support *------------------------------------------------------------------------*/ static void dwc_otg_device_isoc_open(struct usb_xfer *xfer) { } static void dwc_otg_device_isoc_close(struct usb_xfer *xfer) { dwc_otg_device_done(xfer, USB_ERR_CANCELLED); } static void dwc_otg_device_isoc_enter(struct usb_xfer *xfer) { } static void dwc_otg_device_isoc_start(struct usb_xfer *xfer) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); uint32_t temp; uint32_t msframes; uint32_t framenum; uint8_t shift = usbd_xfer_get_fps_shift(xfer); DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->endpoint->isoc_next, xfer->nframes); if (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST) { temp = DWC_OTG_READ_4(sc, DOTG_HFNUM); /* get the current frame index */ framenum = (temp & HFNUM_FRNUM_MASK); } else { temp = DWC_OTG_READ_4(sc, DOTG_DSTS); /* get the current frame index */ framenum = DSTS_SOFFN_GET(temp); } /* * Check if port is doing 8000 or 1000 frames per second: */ if (sc->sc_flags.status_high_speed) framenum /= 8; framenum &= DWC_OTG_FRAME_MASK; /* * Compute number of milliseconds worth of data traffic for * this USB transfer: */ if (xfer->xroot->udev->speed == USB_SPEED_HIGH) msframes = ((xfer->nframes << shift) + 7) / 8; else msframes = xfer->nframes; /* * check if the frame index is within the window where the frames * will be inserted */ temp = (framenum - xfer->endpoint->isoc_next) & DWC_OTG_FRAME_MASK; if ((xfer->endpoint->is_synced == 0) || (temp < msframes)) { /* * If there is data underflow or the pipe queue is * empty we schedule the transfer a few frames ahead * of the current frame position. Else two isochronous * transfers might overlap. */ xfer->endpoint->isoc_next = (framenum + 3) & DWC_OTG_FRAME_MASK; xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->endpoint->isoc_next - framenum) & DWC_OTG_FRAME_MASK; /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb_isoc_time_expand(&sc->sc_bus, framenum) + temp + msframes; /* setup TDs */ dwc_otg_setup_standard_chain(xfer); /* compute frame number for next insertion */ xfer->endpoint->isoc_next += msframes; /* start TD chain */ dwc_otg_start_standard_chain(xfer); } static const struct usb_pipe_methods dwc_otg_device_isoc_methods = { .open = dwc_otg_device_isoc_open, .close = dwc_otg_device_isoc_close, .enter = dwc_otg_device_isoc_enter, .start = dwc_otg_device_isoc_start, }; /*------------------------------------------------------------------------* * DWC OTG root control support *------------------------------------------------------------------------* * Simulate a hardware HUB by handling all the necessary requests. *------------------------------------------------------------------------*/ static const struct usb_device_descriptor dwc_otg_devd = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = UDESC_DEVICE, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_HSHUBSTT, .bMaxPacketSize = 64, .bcdDevice = {0x00, 0x01}, .iManufacturer = 1, .iProduct = 2, .bNumConfigurations = 1, }; static const struct dwc_otg_config_desc dwc_otg_confd = { .confd = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(dwc_otg_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, }, .ifcd = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = 0, }, .endpd = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = (UE_DIR_IN | DWC_OTG_INTR_ENDPT), .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, .bInterval = 255, }, }; #define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) } static const struct usb_hub_descriptor_min dwc_otg_hubd = { .bDescLength = sizeof(dwc_otg_hubd), .bDescriptorType = UDESC_HUB, .bNbrPorts = 1, HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)), .bPwrOn2PwrGood = 50, .bHubContrCurrent = 0, .DeviceRemovable = {0}, /* port is removable */ }; #define STRING_VENDOR \ "D\0W\0C\0O\0T\0G" #define STRING_PRODUCT \ "O\0T\0G\0 \0R\0o\0o\0t\0 \0H\0U\0B" USB_MAKE_STRING_DESC(STRING_VENDOR, dwc_otg_vendor); USB_MAKE_STRING_DESC(STRING_PRODUCT, dwc_otg_product); static usb_error_t dwc_otg_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus); const void *ptr; uint16_t len; uint16_t value; uint16_t index; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_temp; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); /* demultiplex the control request */ switch (req->bmRequestType) { case UT_READ_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (req->bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_DESCRIPTOR: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (req->bRequest) { case UR_CLEAR_FEATURE: switch (UGETW(req->wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (UGETW(req->wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; case UR_SYNCH_FRAME: goto tr_valid; /* nop */ default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (req->bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; case UT_WRITE_INTERFACE: switch (req->bRequest) { case UR_SET_INTERFACE: goto tr_handle_set_interface; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (req->bRequest) { case UR_GET_INTERFACE: goto tr_handle_get_interface; case UR_GET_STATUS: goto tr_handle_get_iface_status; default: goto tr_stalled; } break; case UT_WRITE_CLASS_INTERFACE: case UT_WRITE_VENDOR_INTERFACE: /* XXX forward */ break; case UT_READ_CLASS_INTERFACE: case UT_READ_VENDOR_INTERFACE: /* XXX forward */ break; case UT_WRITE_CLASS_DEVICE: switch (req->bRequest) { case UR_CLEAR_FEATURE: goto tr_valid; case UR_SET_DESCRIPTOR: case UR_SET_FEATURE: break; default: goto tr_stalled; } break; case UT_WRITE_CLASS_OTHER: switch (req->bRequest) { case UR_CLEAR_FEATURE: goto tr_handle_clear_port_feature; case UR_SET_FEATURE: goto tr_handle_set_port_feature; case UR_CLEAR_TT_BUFFER: case UR_RESET_TT: case UR_STOP_TT: goto tr_valid; default: goto tr_stalled; } break; case UT_READ_CLASS_OTHER: switch (req->bRequest) { case UR_GET_TT_STATE: goto tr_handle_get_tt_state; case UR_GET_STATUS: goto tr_handle_get_port_status; default: goto tr_stalled; } break; case UT_READ_CLASS_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; case UR_GET_STATUS: goto tr_handle_get_class_status; default: goto tr_stalled; } break; default: goto tr_stalled; } goto tr_valid; tr_handle_get_descriptor: switch (value >> 8) { case UDESC_DEVICE: if (value & 0xff) { goto tr_stalled; } len = sizeof(dwc_otg_devd); ptr = (const void *)&dwc_otg_devd; goto tr_valid; case UDESC_CONFIG: if (value & 0xff) { goto tr_stalled; } len = sizeof(dwc_otg_confd); ptr = (const void *)&dwc_otg_confd; goto tr_valid; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ len = sizeof(usb_string_lang_en); ptr = (const void *)&usb_string_lang_en; goto tr_valid; case 1: /* Vendor */ len = sizeof(dwc_otg_vendor); ptr = (const void *)&dwc_otg_vendor; goto tr_valid; case 2: /* Product */ len = sizeof(dwc_otg_product); ptr = (const void *)&dwc_otg_product; goto tr_valid; default: break; } break; default: goto tr_stalled; } goto tr_stalled; tr_handle_get_config: len = 1; sc->sc_hub_temp.wValue[0] = sc->sc_conf; goto tr_valid; tr_handle_get_status: len = 2; USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); goto tr_valid; tr_handle_set_address: if (value & 0xFF00) { goto tr_stalled; } sc->sc_rt_addr = value; goto tr_valid; tr_handle_set_config: if (value >= 2) { goto tr_stalled; } sc->sc_conf = value; goto tr_valid; tr_handle_get_interface: len = 1; sc->sc_hub_temp.wValue[0] = 0; goto tr_valid; tr_handle_get_tt_state: tr_handle_get_class_status: tr_handle_get_iface_status: tr_handle_get_ep_status: len = 2; USETW(sc->sc_hub_temp.wValue, 0); goto tr_valid; tr_handle_set_halt: tr_handle_set_interface: tr_handle_set_wakeup: tr_handle_clear_wakeup: tr_handle_clear_halt: goto tr_valid; tr_handle_clear_port_feature: if (index != 1) goto tr_stalled; DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_SUSPEND: dwc_otg_wakeup_peer(sc); break; case UHF_PORT_ENABLE: if (sc->sc_flags.status_device_mode == 0) { DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val | HPRT_PRTENA); } sc->sc_flags.port_enabled = 0; break; case UHF_C_PORT_RESET: sc->sc_flags.change_reset = 0; break; case UHF_C_PORT_ENABLE: sc->sc_flags.change_enabled = 0; break; case UHF_C_PORT_OVER_CURRENT: sc->sc_flags.change_over_current = 0; break; case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 0; if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) { sc->sc_hprt_val = 0; DWC_OTG_WRITE_4(sc, DOTG_HPRT, HPRT_PRTENA); } dwc_otg_pull_down(sc); dwc_otg_clocks_off(sc); break; case UHF_C_PORT_CONNECTION: /* clear connect change flag */ sc->sc_flags.change_connect = 0; break; case UHF_C_PORT_SUSPEND: sc->sc_flags.change_suspend = 0; break; default: err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_set_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); switch (value) { case UHF_PORT_ENABLE: break; case UHF_PORT_SUSPEND: if (sc->sc_flags.status_device_mode == 0) { /* set suspend BIT */ sc->sc_hprt_val |= HPRT_PRTSUSP; DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); /* generate HUB suspend event */ dwc_otg_suspend_irq(sc); } break; case UHF_PORT_RESET: if (sc->sc_flags.status_device_mode == 0) { DPRINTF("PORT RESET\n"); /* enable PORT reset */ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val | HPRT_PRTRST); /* Wait 62.5ms for reset to complete */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16); DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); /* Wait 62.5ms for reset to complete */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16); /* reset FIFOs */ (void) dwc_otg_init_fifo(sc, DWC_MODE_HOST); sc->sc_flags.change_reset = 1; } else { err = USB_ERR_IOERROR; } break; case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 1; if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) { sc->sc_hprt_val |= HPRT_PRTPWR; DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); } if (sc->sc_mode == DWC_MODE_DEVICE || sc->sc_mode == DWC_MODE_OTG) { /* pull up D+, if any */ dwc_otg_pull_up(sc); } break; default: err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_get_port_status: DPRINTFN(9, "UR_GET_PORT_STATUS\n"); if (index != 1) goto tr_stalled; if (sc->sc_flags.status_vbus) dwc_otg_clocks_on(sc); else dwc_otg_clocks_off(sc); /* Select Device Side Mode */ if (sc->sc_flags.status_device_mode) { value = UPS_PORT_MODE_DEVICE; dwc_otg_timer_stop(sc); } else { value = 0; dwc_otg_timer_start(sc); } if (sc->sc_flags.status_high_speed) value |= UPS_HIGH_SPEED; else if (sc->sc_flags.status_low_speed) value |= UPS_LOW_SPEED; if (sc->sc_flags.port_powered) value |= UPS_PORT_POWER; if (sc->sc_flags.port_enabled) value |= UPS_PORT_ENABLED; if (sc->sc_flags.port_over_current) value |= UPS_OVERCURRENT_INDICATOR; if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) value |= UPS_CURRENT_CONNECT_STATUS; if (sc->sc_flags.status_suspend) value |= UPS_SUSPEND; USETW(sc->sc_hub_temp.ps.wPortStatus, value); value = 0; if (sc->sc_flags.change_connect) value |= UPS_C_CONNECT_STATUS; if (sc->sc_flags.change_suspend) value |= UPS_C_SUSPEND; if (sc->sc_flags.change_reset) value |= UPS_C_PORT_RESET; if (sc->sc_flags.change_over_current) value |= UPS_C_OVERCURRENT_INDICATOR; USETW(sc->sc_hub_temp.ps.wPortChange, value); len = sizeof(sc->sc_hub_temp.ps); goto tr_valid; tr_handle_get_class_descriptor: if (value & 0xFF) { goto tr_stalled; } ptr = (const void *)&dwc_otg_hubd; len = sizeof(dwc_otg_hubd); goto tr_valid; tr_stalled: err = USB_ERR_STALLED; tr_valid: done: *plength = len; *pptr = ptr; return (err); } static void dwc_otg_xfer_setup(struct usb_setup_params *parm) { struct usb_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; uint8_t ep_no; uint8_t ep_type; xfer = parm->curr_xfer; /* * NOTE: This driver does not use any of the parameters that * are computed from the following values. Just set some * reasonable dummies: */ parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 3; parm->hc_max_frame_size = 3 * 0x500; usbd_transfer_setup_sub(parm); /* * compute maximum number of TDs */ ep_type = (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE); if (ep_type == UE_CONTROL) { ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ + 1 /* SYNC 2 */ + 1 /* SYNC 3 */; } else { ntd = xfer->nframes + 1 /* SYNC */ ; } /* * check if "usbd_transfer_setup_sub" set an error */ if (parm->err) return; /* * allocate transfer descriptors */ last_obj = NULL; ep_no = xfer->endpointno & UE_ADDR; /* * Check for a valid endpoint profile in USB device mode: */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { const struct usb_hw_ep_profile *pf; dwc_otg_get_hw_ep_profile(parm->udev, &pf, ep_no); if (pf == NULL) { /* should not happen */ parm->err = USB_ERR_INVAL; return; } } /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); for (n = 0; n != ntd; n++) { struct dwc_otg_td *td; if (parm->buf) { td = USB_ADD_BYTES(parm->buf, parm->size[0]); /* compute shared bandwidth resource index for TT */ if (dwc_otg_uses_split(parm->udev)) { if (parm->udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) td->tt_index = parm->udev->device_index; else td->tt_index = parm->udev->parent_hs_hub->device_index; } else { td->tt_index = parm->udev->device_index; } /* init TD */ td->max_packet_size = xfer->max_packet_size; td->max_packet_count = xfer->max_packet_count; /* range check */ if (td->max_packet_count == 0 || td->max_packet_count > 3) td->max_packet_count = 1; td->ep_no = ep_no; td->ep_type = ep_type; td->obj_next = last_obj; last_obj = td; } parm->size[0] += sizeof(*td); } xfer->td_start[0] = last_obj; } static void dwc_otg_xfer_unsetup(struct usb_xfer *xfer) { return; } static void dwc_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus); DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d,%d)\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode, sc->sc_rt_addr, udev->device_index); if (udev->device_index != sc->sc_rt_addr) { if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (udev->speed != USB_SPEED_FULL && udev->speed != USB_SPEED_HIGH) { /* not supported */ return; } } else { if (udev->speed == USB_SPEED_HIGH && (edesc->wMaxPacketSize[1] & 0x18) != 0 && (edesc->bmAttributes & UE_XFERTYPE) != UE_ISOCHRONOUS) { /* not supported */ DPRINTFN(-1, "Non-isochronous high bandwidth " "endpoint not supported\n"); return; } } if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS) ep->methods = &dwc_otg_device_isoc_methods; else ep->methods = &dwc_otg_device_non_isoc_methods; } } static void dwc_otg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: dwc_otg_suspend(sc); break; case USB_HW_POWER_SHUTDOWN: dwc_otg_uninit(sc); break; case USB_HW_POWER_RESUME: dwc_otg_resume(sc); break; default: break; } } static void dwc_otg_get_dma_delay(struct usb_device *udev, uint32_t *pus) { /* DMA delay - wait until any use of memory is finished */ *pus = (2125); /* microseconds */ } static void dwc_otg_device_resume(struct usb_device *udev) { DPRINTF("\n"); /* poll all transfers again to restart resumed ones */ dwc_otg_do_poll(udev->bus); } static void dwc_otg_device_suspend(struct usb_device *udev) { DPRINTF("\n"); } static const struct usb_bus_methods dwc_otg_bus_methods = { .endpoint_init = &dwc_otg_ep_init, .xfer_setup = &dwc_otg_xfer_setup, .xfer_unsetup = &dwc_otg_xfer_unsetup, .get_hw_ep_profile = &dwc_otg_get_hw_ep_profile, .xfer_stall = &dwc_otg_xfer_stall, .set_stall = &dwc_otg_set_stall, .clear_stall = &dwc_otg_clear_stall, .roothub_exec = &dwc_otg_roothub_exec, .xfer_poll = &dwc_otg_do_poll, .device_state_change = &dwc_otg_device_state_change, .set_hw_power_sleep = &dwc_otg_set_hw_power_sleep, .get_dma_delay = &dwc_otg_get_dma_delay, .device_resume = &dwc_otg_device_resume, .device_suspend = &dwc_otg_device_suspend, }; Index: head/sys/dev/usb/controller/ehci.c =================================================================== --- head/sys/dev/usb/controller/ehci.c (revision 298931) +++ head/sys/dev/usb/controller/ehci.c (revision 298932) @@ -1,3973 +1,3973 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 2004 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 2004 Lennart Augustsson. All rights reserved. * Copyright (c) 2004 Charles M. Hannum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * * The EHCI 0.96 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r096.pdf * The EHCI 1.0 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r10.pdf * and the USB 2.0 spec at * http://www.usb.org/developers/docs/usb_20.zip * */ /* * TODO: * 1) command failures are not recovered correctly */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR ehcidebug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include #define EHCI_BUS2SC(bus) \ ((ehci_softc_t *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((ehci_softc_t *)0)->sc_bus)))) #ifdef USB_DEBUG static int ehcidebug = 0; static int ehcinohighspeed = 0; static int ehciiaadbug = 0; static int ehcilostintrbug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RWTUN, &ehcidebug, 0, "Debug level"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, no_hs, CTLFLAG_RWTUN, &ehcinohighspeed, 0, "Disable High Speed USB"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, iaadbug, CTLFLAG_RWTUN, &ehciiaadbug, 0, "Enable doorbell bug workaround"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, lostintrbug, CTLFLAG_RWTUN, &ehcilostintrbug, 0, "Enable lost interrupt bug workaround"); static void ehci_dump_regs(ehci_softc_t *sc); static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh); #endif #define EHCI_INTR_ENDPT 1 static const struct usb_bus_methods ehci_bus_methods; static const struct usb_pipe_methods ehci_device_bulk_methods; static const struct usb_pipe_methods ehci_device_ctrl_methods; static const struct usb_pipe_methods ehci_device_intr_methods; static const struct usb_pipe_methods ehci_device_isoc_fs_methods; static const struct usb_pipe_methods ehci_device_isoc_hs_methods; static void ehci_do_poll(struct usb_bus *); static void ehci_device_done(struct usb_xfer *, usb_error_t); static uint8_t ehci_check_transfer(struct usb_xfer *); static void ehci_timeout(void *); static void ehci_poll_timeout(void *); static void ehci_root_intr(ehci_softc_t *sc); struct ehci_std_temp { ehci_softc_t *sc; struct usb_page_cache *pc; ehci_qtd_t *td; ehci_qtd_t *td_next; uint32_t average; uint32_t qtd_status; uint32_t len; uint16_t max_frame_size; uint8_t shortpkt; uint8_t auto_data_toggle; uint8_t setup_alt_next; uint8_t last_frame; }; void ehci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) { ehci_softc_t *sc = EHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, sizeof(uint32_t) * EHCI_FRAMELIST_COUNT, EHCI_FRAMELIST_ALIGN); cb(bus, &sc->sc_hw.terminate_pc, &sc->sc_hw.terminate_pg, sizeof(struct ehci_qh_sub), EHCI_QH_ALIGN); cb(bus, &sc->sc_hw.async_start_pc, &sc->sc_hw.async_start_pg, sizeof(ehci_qh_t), EHCI_QH_ALIGN); for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, sizeof(ehci_qh_t), EHCI_QH_ALIGN); } for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.isoc_hs_start_pc + i, sc->sc_hw.isoc_hs_start_pg + i, sizeof(ehci_itd_t), EHCI_ITD_ALIGN); } for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.isoc_fs_start_pc + i, sc->sc_hw.isoc_fs_start_pg + i, sizeof(ehci_sitd_t), EHCI_SITD_ALIGN); } } usb_error_t ehci_reset(ehci_softc_t *sc) { uint32_t hcr; int i; EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); for (i = 0; i < 100; i++) { usb_pause_mtx(NULL, hz / 128); hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; if (!hcr) { if (sc->sc_vendor_post_reset != NULL) sc->sc_vendor_post_reset(sc); return (0); } } device_printf(sc->sc_bus.bdev, "reset timeout\n"); return (USB_ERR_IOERROR); } static usb_error_t ehci_hcreset(ehci_softc_t *sc) { uint32_t hcr; int i; EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ for (i = 0; i < 100; i++) { usb_pause_mtx(NULL, hz / 128); hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (hcr) break; } if (!hcr) /* * Fall through and try reset anyway even though * Table 2-9 in the EHCI spec says this will result * in undefined behavior. */ device_printf(sc->sc_bus.bdev, "stop timeout\n"); return (ehci_reset(sc)); } static int ehci_init_sub(struct ehci_softc *sc) { struct usb_page_search buf_res; uint32_t cparams; uint32_t hcr; uint8_t i; cparams = EREAD4(sc, EHCI_HCCPARAMS); DPRINTF("cparams=0x%x\n", cparams); if (EHCI_HCC_64BIT(cparams)) { DPRINTF("HCC uses 64-bit structures\n"); /* MUST clear segment register if 64 bit capable */ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); } usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); /* enable interrupts */ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* turn on controller */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_ITC_1 | /* 1 microframes interrupt delay */ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | EHCI_CMD_ASE | EHCI_CMD_PSE | EHCI_CMD_RS); /* Take over port ownership */ EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); for (i = 0; i < 100; i++) { usb_pause_mtx(NULL, hz / 128); hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "run timeout\n"); return (USB_ERR_IOERROR); } return (USB_ERR_NORMAL_COMPLETION); } usb_error_t ehci_init(ehci_softc_t *sc) { struct usb_page_search buf_res; uint32_t version; uint32_t sparams; uint16_t i; uint16_t x; uint16_t y; uint16_t bit; usb_error_t err = 0; DPRINTF("start\n"); usb_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0); usb_callout_init_mtx(&sc->sc_tmo_poll, &sc->sc_bus.bus_mtx, 0); sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); #ifdef USB_DEBUG if (ehciiaadbug) sc->sc_flags |= EHCI_SCFLG_IAADBUG; if (ehcilostintrbug) sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; if (ehcidebug > 2) { ehci_dump_regs(sc); } #endif version = EHCI_HCIVERSION(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n", version >> 8, version & 0xff); sparams = EREAD4(sc, EHCI_HCSPARAMS); DPRINTF("sparams=0x%x\n", sparams); sc->sc_noport = EHCI_HCS_N_PORTS(sparams); sc->sc_bus.usbrev = USB_REV_2_0; if (!(sc->sc_flags & EHCI_SCFLG_DONTRESET)) { /* Reset the controller */ DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)); err = ehci_hcreset(sc); if (err) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); return (err); } } /* * use current frame-list-size selection 0: 1024*4 bytes 1: 512*4 * bytes 2: 256*4 bytes 3: unknown */ if (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD)) == 3) { device_printf(sc->sc_bus.bdev, "invalid frame-list-size\n"); return (USB_ERR_IOERROR); } /* set up the bus struct */ sc->sc_bus.methods = &ehci_bus_methods; sc->sc_eintrs = EHCI_NORMAL_INTRS; if (1) { struct ehci_qh_sub *qh; usbd_get_page(&sc->sc_hw.terminate_pc, 0, &buf_res); qh = buf_res.buffer; sc->sc_terminate_self = htohc32(sc, buf_res.physaddr); /* init terminate TD */ qh->qtd_next = htohc32(sc, EHCI_LINK_TERMINATE); qh->qtd_altnext = htohc32(sc, EHCI_LINK_TERMINATE); qh->qtd_status = htohc32(sc, EHCI_QTD_HALTED); } for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { ehci_qh_t *qh; usbd_get_page(sc->sc_hw.intr_start_pc + i, 0, &buf_res); qh = buf_res.buffer; /* initialize page cache pointer */ qh->page_cache = sc->sc_hw.intr_start_pc + i; /* store a pointer to queue head */ sc->sc_intr_p_last[i] = qh; qh->qh_self = htohc32(sc, buf_res.physaddr) | htohc32(sc, EHCI_LINK_QH); qh->qh_endp = htohc32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); qh->qh_endphub = htohc32(sc, EHCI_QH_SET_MULT(1)); qh->qh_curqtd = 0; qh->qh_qtd.qtd_next = htohc32(sc, EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_altnext = htohc32(sc, EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_status = htohc32(sc, EHCI_QTD_HALTED); } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2; while (bit) { x = bit; while (x & bit) { ehci_qh_t *qh_x; ehci_qh_t *qh_y; y = (x ^ bit) | (bit / 2); qh_x = sc->sc_intr_p_last[x]; qh_y = sc->sc_intr_p_last[y]; /* * the next QH has half the poll interval */ qh_x->qh_link = qh_y->qh_self; x++; } bit >>= 1; } if (1) { ehci_qh_t *qh; qh = sc->sc_intr_p_last[0]; /* the last (1ms) QH terminates */ qh->qh_link = htohc32(sc, EHCI_LINK_TERMINATE); } for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { ehci_sitd_t *sitd; ehci_itd_t *itd; usbd_get_page(sc->sc_hw.isoc_fs_start_pc + i, 0, &buf_res); sitd = buf_res.buffer; /* initialize page cache pointer */ sitd->page_cache = sc->sc_hw.isoc_fs_start_pc + i; /* store a pointer to the transfer descriptor */ sc->sc_isoc_fs_p_last[i] = sitd; /* initialize full speed isochronous */ sitd->sitd_self = htohc32(sc, buf_res.physaddr) | htohc32(sc, EHCI_LINK_SITD); sitd->sitd_back = htohc32(sc, EHCI_LINK_TERMINATE); sitd->sitd_next = sc->sc_intr_p_last[i | (EHCI_VIRTUAL_FRAMELIST_COUNT / 2)]->qh_self; usbd_get_page(sc->sc_hw.isoc_hs_start_pc + i, 0, &buf_res); itd = buf_res.buffer; /* initialize page cache pointer */ itd->page_cache = sc->sc_hw.isoc_hs_start_pc + i; /* store a pointer to the transfer descriptor */ sc->sc_isoc_hs_p_last[i] = itd; /* initialize high speed isochronous */ itd->itd_self = htohc32(sc, buf_res.physaddr) | htohc32(sc, EHCI_LINK_ITD); itd->itd_next = sitd->sitd_self; } usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); if (1) { uint32_t *pframes; pframes = buf_res.buffer; /* * execution order: * pframes -> high speed isochronous -> * full speed isochronous -> interrupt QH's */ for (i = 0; i < EHCI_FRAMELIST_COUNT; i++) { pframes[i] = sc->sc_isoc_hs_p_last [i & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1)]->itd_self; } } usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); if (1) { ehci_qh_t *qh; qh = buf_res.buffer; /* initialize page cache pointer */ qh->page_cache = &sc->sc_hw.async_start_pc; /* store a pointer to the queue head */ sc->sc_async_p_last = qh; /* init dummy QH that starts the async list */ qh->qh_self = htohc32(sc, buf_res.physaddr) | htohc32(sc, EHCI_LINK_QH); /* fill the QH */ qh->qh_endp = htohc32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); qh->qh_endphub = htohc32(sc, EHCI_QH_SET_MULT(1)); qh->qh_link = qh->qh_self; qh->qh_curqtd = 0; /* fill the overlay qTD */ qh->qh_qtd.qtd_next = htohc32(sc, EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_altnext = htohc32(sc, EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_status = htohc32(sc, EHCI_QTD_HALTED); } /* flush all cache into memory */ usb_bus_mem_flush_all(&sc->sc_bus, &ehci_iterate_hw_softc); #ifdef USB_DEBUG if (ehcidebug) { ehci_dump_sqh(sc, sc->sc_async_p_last); } #endif /* finial setup */ err = ehci_init_sub(sc); if (!err) { /* catch any lost interrupts */ ehci_do_poll(&sc->sc_bus); } return (err); } /* * shut down the controller when the system is going down */ void ehci_detach(ehci_softc_t *sc) { USB_BUS_LOCK(&sc->sc_bus); usb_callout_stop(&sc->sc_tmo_pcd); usb_callout_stop(&sc->sc_tmo_poll); EOWRITE4(sc, EHCI_USBINTR, 0); USB_BUS_UNLOCK(&sc->sc_bus); if (ehci_hcreset(sc)) { DPRINTF("reset failed!\n"); } /* XXX let stray task complete */ usb_pause_mtx(NULL, hz / 20); usb_callout_drain(&sc->sc_tmo_pcd); usb_callout_drain(&sc->sc_tmo_poll); } static void ehci_suspend(ehci_softc_t *sc) { DPRINTF("stopping the HC\n"); /* reset HC */ ehci_hcreset(sc); } static void ehci_resume(ehci_softc_t *sc) { /* reset HC */ ehci_hcreset(sc); /* setup HC */ ehci_init_sub(sc); /* catch any lost interrupts */ ehci_do_poll(&sc->sc_bus); } #ifdef USB_DEBUG static void ehci_dump_regs(ehci_softc_t *sc) { uint32_t i; i = EOREAD4(sc, EHCI_USBCMD); printf("cmd=0x%08x\n", i); if (i & EHCI_CMD_ITC_1) printf(" EHCI_CMD_ITC_1\n"); if (i & EHCI_CMD_ITC_2) printf(" EHCI_CMD_ITC_2\n"); if (i & EHCI_CMD_ITC_4) printf(" EHCI_CMD_ITC_4\n"); if (i & EHCI_CMD_ITC_8) printf(" EHCI_CMD_ITC_8\n"); if (i & EHCI_CMD_ITC_16) printf(" EHCI_CMD_ITC_16\n"); if (i & EHCI_CMD_ITC_32) printf(" EHCI_CMD_ITC_32\n"); if (i & EHCI_CMD_ITC_64) printf(" EHCI_CMD_ITC_64\n"); if (i & EHCI_CMD_ASPME) printf(" EHCI_CMD_ASPME\n"); if (i & EHCI_CMD_ASPMC) printf(" EHCI_CMD_ASPMC\n"); if (i & EHCI_CMD_LHCR) printf(" EHCI_CMD_LHCR\n"); if (i & EHCI_CMD_IAAD) printf(" EHCI_CMD_IAAD\n"); if (i & EHCI_CMD_ASE) printf(" EHCI_CMD_ASE\n"); if (i & EHCI_CMD_PSE) printf(" EHCI_CMD_PSE\n"); if (i & EHCI_CMD_FLS_M) printf(" EHCI_CMD_FLS_M\n"); if (i & EHCI_CMD_HCRESET) printf(" EHCI_CMD_HCRESET\n"); if (i & EHCI_CMD_RS) printf(" EHCI_CMD_RS\n"); i = EOREAD4(sc, EHCI_USBSTS); printf("sts=0x%08x\n", i); if (i & EHCI_STS_ASS) printf(" EHCI_STS_ASS\n"); if (i & EHCI_STS_PSS) printf(" EHCI_STS_PSS\n"); if (i & EHCI_STS_REC) printf(" EHCI_STS_REC\n"); if (i & EHCI_STS_HCH) printf(" EHCI_STS_HCH\n"); if (i & EHCI_STS_IAA) printf(" EHCI_STS_IAA\n"); if (i & EHCI_STS_HSE) printf(" EHCI_STS_HSE\n"); if (i & EHCI_STS_FLR) printf(" EHCI_STS_FLR\n"); if (i & EHCI_STS_PCD) printf(" EHCI_STS_PCD\n"); if (i & EHCI_STS_ERRINT) printf(" EHCI_STS_ERRINT\n"); if (i & EHCI_STS_INT) printf(" EHCI_STS_INT\n"); printf("ien=0x%08x\n", EOREAD4(sc, EHCI_USBINTR)); printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), EOREAD4(sc, EHCI_PERIODICLISTBASE), EOREAD4(sc, EHCI_ASYNCLISTADDR)); for (i = 1; i <= sc->sc_noport; i++) { printf("port %d status=0x%08x\n", i, EOREAD4(sc, EHCI_PORTSC(i))); } } static void ehci_dump_link(ehci_softc_t *sc, uint32_t link, int type) { link = hc32toh(sc, link); printf("0x%08x", link); if (link & EHCI_LINK_TERMINATE) printf(""); else { printf("<"); if (type) { switch (EHCI_LINK_TYPE(link)) { case EHCI_LINK_ITD: printf("ITD"); break; case EHCI_LINK_QH: printf("QH"); break; case EHCI_LINK_SITD: printf("SITD"); break; case EHCI_LINK_FSTN: printf("FSTN"); break; } } printf(">"); } } static void ehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd) { uint32_t s; printf(" next="); ehci_dump_link(sc, qtd->qtd_next, 0); printf(" altnext="); ehci_dump_link(sc, qtd->qtd_altnext, 0); printf("\n"); s = hc32toh(sc, qtd->qtd_status); printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); printf(" cerr=%d pid=%d stat=%s%s%s%s%s%s%s%s\n", EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), (s & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE", (s & EHCI_QTD_HALTED) ? "-HALTED" : "", (s & EHCI_QTD_BUFERR) ? "-BUFERR" : "", (s & EHCI_QTD_BABBLE) ? "-BABBLE" : "", (s & EHCI_QTD_XACTERR) ? "-XACTERR" : "", (s & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "", (s & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "", (s & EHCI_QTD_PINGSTATE) ? "-PING" : ""); for (s = 0; s < 5; s++) { printf(" buffer[%d]=0x%08x\n", s, hc32toh(sc, qtd->qtd_buffer[s])); } for (s = 0; s < 5; s++) { printf(" buffer_hi[%d]=0x%08x\n", s, hc32toh(sc, qtd->qtd_buffer_hi[s])); } } static uint8_t ehci_dump_sqtd(ehci_softc_t *sc, ehci_qtd_t *sqtd) { uint8_t temp; usb_pc_cpu_invalidate(sqtd->page_cache); printf("QTD(%p) at 0x%08x:\n", sqtd, hc32toh(sc, sqtd->qtd_self)); ehci_dump_qtd(sc, sqtd); temp = (sqtd->qtd_next & htohc32(sc, EHCI_LINK_TERMINATE)) ? 1 : 0; return (temp); } static void ehci_dump_sqtds(ehci_softc_t *sc, ehci_qtd_t *sqtd) { uint16_t i; uint8_t stop; stop = 0; for (i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) { stop = ehci_dump_sqtd(sc, sqtd); } if (sqtd) { printf("dump aborted, too many TDs\n"); } } static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *qh) { uint32_t endp; uint32_t endphub; usb_pc_cpu_invalidate(qh->page_cache); printf("QH(%p) at 0x%08x:\n", qh, hc32toh(sc, qh->qh_self) & ~0x1F); printf(" link="); ehci_dump_link(sc, qh->qh_link, 1); printf("\n"); endp = hc32toh(sc, qh->qh_endp); printf(" endp=0x%08x\n", endp); printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); printf(" mpl=0x%x ctl=%d nrl=%d\n", EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), EHCI_QH_GET_NRL(endp)); endphub = hc32toh(sc, qh->qh_endphub); printf(" endphub=0x%08x\n", endphub); printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), EHCI_QH_GET_MULT(endphub)); printf(" curqtd="); ehci_dump_link(sc, qh->qh_curqtd, 0); printf("\n"); printf("Overlay qTD:\n"); ehci_dump_qtd(sc, (void *)&qh->qh_qtd); } static void ehci_dump_sitd(ehci_softc_t *sc, ehci_sitd_t *sitd) { usb_pc_cpu_invalidate(sitd->page_cache); printf("SITD(%p) at 0x%08x\n", sitd, hc32toh(sc, sitd->sitd_self) & ~0x1F); printf(" next=0x%08x\n", hc32toh(sc, sitd->sitd_next)); printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", hc32toh(sc, sitd->sitd_portaddr), (sitd->sitd_portaddr & htohc32(sc, EHCI_SITD_SET_DIR_IN)) ? "in" : "out", EHCI_SITD_GET_ADDR(hc32toh(sc, sitd->sitd_portaddr)), EHCI_SITD_GET_ENDPT(hc32toh(sc, sitd->sitd_portaddr)), EHCI_SITD_GET_PORT(hc32toh(sc, sitd->sitd_portaddr)), EHCI_SITD_GET_HUBA(hc32toh(sc, sitd->sitd_portaddr))); printf(" mask=0x%08x\n", hc32toh(sc, sitd->sitd_mask)); printf(" status=0x%08x <%s> len=0x%x\n", hc32toh(sc, sitd->sitd_status), (sitd->sitd_status & htohc32(sc, EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", EHCI_SITD_GET_LEN(hc32toh(sc, sitd->sitd_status))); printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", hc32toh(sc, sitd->sitd_back), hc32toh(sc, sitd->sitd_bp[0]), hc32toh(sc, sitd->sitd_bp[1]), hc32toh(sc, sitd->sitd_bp_hi[0]), hc32toh(sc, sitd->sitd_bp_hi[1])); } static void ehci_dump_itd(ehci_softc_t *sc, ehci_itd_t *itd) { usb_pc_cpu_invalidate(itd->page_cache); printf("ITD(%p) at 0x%08x\n", itd, hc32toh(sc, itd->itd_self) & ~0x1F); printf(" next=0x%08x\n", hc32toh(sc, itd->itd_next)); printf(" status[0]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[0]), (itd->itd_status[0] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[1]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[1]), (itd->itd_status[1] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[2]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[2]), (itd->itd_status[2] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[3]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[3]), (itd->itd_status[3] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[4]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[4]), (itd->itd_status[4] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[5]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[5]), (itd->itd_status[5] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[6]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[6]), (itd->itd_status[6] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[7]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[7]), (itd->itd_status[7] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" bp[0]=0x%08x\n", hc32toh(sc, itd->itd_bp[0])); printf(" addr=0x%02x; endpt=0x%01x\n", EHCI_ITD_GET_ADDR(hc32toh(sc, itd->itd_bp[0])), EHCI_ITD_GET_ENDPT(hc32toh(sc, itd->itd_bp[0]))); printf(" bp[1]=0x%08x\n", hc32toh(sc, itd->itd_bp[1])); printf(" dir=%s; mpl=0x%02x\n", (hc32toh(sc, itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", EHCI_ITD_GET_MPL(hc32toh(sc, itd->itd_bp[1]))); printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", hc32toh(sc, itd->itd_bp[2]), hc32toh(sc, itd->itd_bp[3]), hc32toh(sc, itd->itd_bp[4]), hc32toh(sc, itd->itd_bp[5]), hc32toh(sc, itd->itd_bp[6])); printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" " 0x%08x,0x%08x,0x%08x\n", hc32toh(sc, itd->itd_bp_hi[0]), hc32toh(sc, itd->itd_bp_hi[1]), hc32toh(sc, itd->itd_bp_hi[2]), hc32toh(sc, itd->itd_bp_hi[3]), hc32toh(sc, itd->itd_bp_hi[4]), hc32toh(sc, itd->itd_bp_hi[5]), hc32toh(sc, itd->itd_bp_hi[6])); } static void ehci_dump_isoc(ehci_softc_t *sc) { ehci_itd_t *itd; ehci_sitd_t *sitd; uint16_t max = 1000; uint16_t pos; pos = (EOREAD4(sc, EHCI_FRINDEX) / 8) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); printf("%s: isochronous dump from frame 0x%03x:\n", __FUNCTION__, pos); itd = sc->sc_isoc_hs_p_last[pos]; sitd = sc->sc_isoc_fs_p_last[pos]; while (itd && max && max--) { ehci_dump_itd(sc, itd); itd = itd->prev; } while (sitd && max && max--) { ehci_dump_sitd(sc, sitd); sitd = sitd->prev; } } #endif static void ehci_transfer_intr_enqueue(struct usb_xfer *xfer) { /* check for early completion */ if (ehci_check_transfer(xfer)) { return; } /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usbd_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout); } } #define EHCI_APPEND_FS_TD(std,last) (last) = _ehci_append_fs_td(std,last) static ehci_sitd_t * _ehci_append_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) { DPRINTFN(11, "%p to %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->sitd_next = last->sitd_next; std->prev = last; usb_pc_cpu_flush(std->page_cache); /* * the last->next->prev is never followed: std->next->prev = std; */ last->next = std; last->sitd_next = std->sitd_self; usb_pc_cpu_flush(last->page_cache); return (std); } #define EHCI_APPEND_HS_TD(std,last) (last) = _ehci_append_hs_td(std,last) static ehci_itd_t * _ehci_append_hs_td(ehci_itd_t *std, ehci_itd_t *last) { DPRINTFN(11, "%p to %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->itd_next = last->itd_next; std->prev = last; usb_pc_cpu_flush(std->page_cache); /* * the last->next->prev is never followed: std->next->prev = std; */ last->next = std; last->itd_next = std->itd_self; usb_pc_cpu_flush(last->page_cache); return (std); } #define EHCI_APPEND_QH(sqh,last) (last) = _ehci_append_qh(sqh,last) static ehci_qh_t * _ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(11, "%p to %p\n", sqh, last); if (sqh->prev != NULL) { /* should not happen */ DPRINTFN(0, "QH already linked!\n"); return (last); } /* (sc->sc_bus.mtx) must be locked */ sqh->next = last->next; sqh->qh_link = last->qh_link; sqh->prev = last; usb_pc_cpu_flush(sqh->page_cache); /* * the last->next->prev is never followed: sqh->next->prev = sqh; */ last->next = sqh; last->qh_link = sqh->qh_self; usb_pc_cpu_flush(last->page_cache); return (sqh); } #define EHCI_REMOVE_FS_TD(std,last) (last) = _ehci_remove_fs_td(std,last) static ehci_sitd_t * _ehci_remove_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) { DPRINTFN(11, "%p from %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->sitd_next = std->sitd_next; usb_pc_cpu_flush(std->prev->page_cache); if (std->next) { std->next->prev = std->prev; usb_pc_cpu_flush(std->next->page_cache); } return ((last == std) ? std->prev : last); } #define EHCI_REMOVE_HS_TD(std,last) (last) = _ehci_remove_hs_td(std,last) static ehci_itd_t * _ehci_remove_hs_td(ehci_itd_t *std, ehci_itd_t *last) { DPRINTFN(11, "%p from %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->itd_next = std->itd_next; usb_pc_cpu_flush(std->prev->page_cache); if (std->next) { std->next->prev = std->prev; usb_pc_cpu_flush(std->next->page_cache); } return ((last == std) ? std->prev : last); } #define EHCI_REMOVE_QH(sqh,last) (last) = _ehci_remove_qh(sqh,last) static ehci_qh_t * _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(11, "%p from %p\n", sqh, last); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if (sqh->prev) { sqh->prev->next = sqh->next; sqh->prev->qh_link = sqh->qh_link; usb_pc_cpu_flush(sqh->prev->page_cache); if (sqh->next) { sqh->next->prev = sqh->prev; usb_pc_cpu_flush(sqh->next->page_cache); } last = ((last == sqh) ? sqh->prev : last); sqh->prev = 0; usb_pc_cpu_flush(sqh->page_cache); } return (last); } static void ehci_data_toggle_update(struct usb_xfer *xfer, uint16_t actlen, uint16_t xlen) { uint16_t rem; uint8_t dt; /* count number of full packets */ dt = (actlen / xfer->max_packet_size) & 1; /* compute remainder */ rem = actlen % xfer->max_packet_size; if (rem > 0) dt ^= 1; /* short packet at the end */ else if (actlen != xlen) dt ^= 1; /* zero length packet at the end */ else if (xlen == 0) dt ^= 1; /* zero length transfer */ xfer->endpoint->toggle_next ^= dt; } static usb_error_t ehci_non_isoc_done_sub(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_qtd_t *td; ehci_qtd_t *td_alt_next; uint32_t status; uint16_t len; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; if (xfer->aframes != xfer->nframes) { usbd_xfer_set_frame_len(xfer, xfer->aframes, 0); } while (1) { usb_pc_cpu_invalidate(td->page_cache); status = hc32toh(sc, td->qtd_status); len = EHCI_QTD_GET_BYTES(status); /* * Verify the status length and * add the length to "frlengths[]": */ if (len > td->len) { /* should not happen */ DPRINTF("Invalid status length, " "0x%04x/0x%04x bytes\n", len, td->len); status |= EHCI_QTD_HALTED; } else if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] += td->len - len; /* manually update data toggle */ ehci_data_toggle_update(xfer, td->len - len, td->len); } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } /* Check for transfer error */ if (status & EHCI_QTD_HALTED) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; #ifdef USB_DEBUG if (status & EHCI_QTD_STATERRS) { DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x" "status=%s%s%s%s%s%s%s%s\n", xfer->address, xfer->endpointno, xfer->aframes, (status & EHCI_QTD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]", (status & EHCI_QTD_HALTED) ? "[HALTED]" : "", (status & EHCI_QTD_BUFERR) ? "[BUFERR]" : "", (status & EHCI_QTD_BABBLE) ? "[BABBLE]" : "", (status & EHCI_QTD_XACTERR) ? "[XACTERR]" : "", (status & EHCI_QTD_MISSEDMICRO) ? "[MISSED]" : "", (status & EHCI_QTD_SPLITXSTATE) ? "[SPLIT]" : "", (status & EHCI_QTD_PINGSTATE) ? "[PING]" : ""); } #endif if (status & EHCI_QTD_HALTED) { if ((xfer->xroot->udev->parent_hs_hub != NULL) || (xfer->xroot->udev->address != 0)) { /* try to separate I/O errors from STALL */ if (EHCI_QTD_GET_CERR(status) == 0) return (USB_ERR_IOERROR); } return (USB_ERR_STALLED); } return (USB_ERR_NORMAL_COMPLETION); } static void ehci_non_isoc_done(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_qh_t *qh; uint32_t status; usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); #ifdef USB_DEBUG if (ehcidebug > 10) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_dump_sqtds(sc, xfer->td_transfer_first); } #endif /* extract data toggle directly from the QH's overlay area */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; usb_pc_cpu_invalidate(qh->page_cache); status = hc32toh(sc, qh->qh_qtd.qtd_status); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = ehci_non_isoc_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = ehci_non_isoc_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = ehci_non_isoc_done_sub(xfer); } done: ehci_device_done(xfer, err); } /*------------------------------------------------------------------------* * ehci_check_transfer * * Return values: * 0: USB transfer is not finished * Else: USB transfer is finished *------------------------------------------------------------------------*/ static uint8_t ehci_check_transfer(struct usb_xfer *xfer) { const struct usb_pipe_methods *methods = xfer->endpoint->methods; ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); uint32_t status; DPRINTFN(13, "xfer=%p checking transfer\n", xfer); if (methods == &ehci_device_isoc_fs_methods) { ehci_sitd_t *td; /* isochronous full speed transfer */ td = xfer->td_transfer_last; usb_pc_cpu_invalidate(td->page_cache); status = hc32toh(sc, td->sitd_status); /* also check if first is complete */ td = xfer->td_transfer_first; usb_pc_cpu_invalidate(td->page_cache); status |= hc32toh(sc, td->sitd_status); if (!(status & EHCI_SITD_ACTIVE)) { ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } } else if (methods == &ehci_device_isoc_hs_methods) { ehci_itd_t *td; /* isochronous high speed transfer */ /* check last transfer */ td = xfer->td_transfer_last; usb_pc_cpu_invalidate(td->page_cache); status = td->itd_status[0]; status |= td->itd_status[1]; status |= td->itd_status[2]; status |= td->itd_status[3]; status |= td->itd_status[4]; status |= td->itd_status[5]; status |= td->itd_status[6]; status |= td->itd_status[7]; /* also check first transfer */ td = xfer->td_transfer_first; usb_pc_cpu_invalidate(td->page_cache); status |= td->itd_status[0]; status |= td->itd_status[1]; status |= td->itd_status[2]; status |= td->itd_status[3]; status |= td->itd_status[4]; status |= td->itd_status[5]; status |= td->itd_status[6]; status |= td->itd_status[7]; /* if no transactions are active we continue */ if (!(status & htohc32(sc, EHCI_ITD_ACTIVE))) { ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } } else { ehci_qtd_t *td; ehci_qh_t *qh; /* non-isochronous transfer */ /* * check whether there is an error somewhere in the middle, * or whether there was a short packet (SPD and not ACTIVE) */ td = xfer->td_transfer_cache; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; usb_pc_cpu_invalidate(qh->page_cache); status = hc32toh(sc, qh->qh_qtd.qtd_status); if (status & EHCI_QTD_ACTIVE) { /* transfer is pending */ goto done; } while (1) { usb_pc_cpu_invalidate(td->page_cache); status = hc32toh(sc, td->qtd_status); /* * Check if there is an active TD which * indicates that the transfer isn't done. */ if (status & EHCI_QTD_ACTIVE) { /* update cache */ xfer->td_transfer_cache = td; goto done; } /* * last transfer descriptor makes the transfer done */ if (((void *)td) == xfer->td_transfer_last) { break; } /* * any kind of error makes the transfer done */ if (status & EHCI_QTD_HALTED) { break; } /* * if there is no alternate next transfer, a short * packet also makes the transfer done */ if (EHCI_QTD_GET_BYTES(status)) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { td = td->alt_next; continue; } } /* transfer is done */ break; } td = td->obj_next; } ehci_non_isoc_done(xfer); goto transferred; } done: DPRINTFN(13, "xfer=%p is still active\n", xfer); return (0); transferred: return (1); } static void ehci_pcd_enable(ehci_softc_t *sc) { USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); sc->sc_eintrs |= EHCI_STS_PCD; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* acknowledge any PCD interrupt */ EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD); ehci_root_intr(sc); } static void ehci_interrupt_poll(ehci_softc_t *sc) { struct usb_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* * check if transfer is transferred */ if (ehci_check_transfer(xfer)) { /* queue has been modified */ goto repeat; } } } /* * Some EHCI chips from VIA / ATI seem to trigger interrupts before * writing back the qTD status, or miss signalling occasionally under * heavy load. If the host machine is too fast, we can miss * transaction completion - when we scan the active list the * transaction still seems to be active. This generally exhibits * itself as a umass stall that never recovers. * * We work around this behaviour by setting up this callback after any * softintr that completes with transactions still pending, giving us * another chance to check for completion after the writeback has * taken place. */ static void ehci_poll_timeout(void *arg) { ehci_softc_t *sc = arg; DPRINTFN(3, "\n"); ehci_interrupt_poll(sc); } /*------------------------------------------------------------------------* * ehci_interrupt - EHCI interrupt handler * * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, * hence the interrupt handler will be setup before "sc->sc_bus.bdev" * is present ! *------------------------------------------------------------------------*/ void ehci_interrupt(ehci_softc_t *sc) { uint32_t status; USB_BUS_LOCK(&sc->sc_bus); DPRINTFN(16, "real interrupt\n"); #ifdef USB_DEBUG if (ehcidebug > 15) { ehci_dump_regs(sc); } #endif status = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); if (status == 0) { /* the interrupt was not for us */ goto done; } if (!(status & sc->sc_eintrs)) { goto done; } EOWRITE4(sc, EHCI_USBSTS, status); /* acknowledge */ status &= sc->sc_eintrs; if (status & EHCI_STS_HSE) { printf("%s: unrecoverable error, " "controller halted\n", __FUNCTION__); #ifdef USB_DEBUG ehci_dump_regs(sc); ehci_dump_isoc(sc); #endif } if (status & EHCI_STS_PCD) { /* * Disable PCD interrupt for now, because it will be * on until the port has been reset. */ sc->sc_eintrs &= ~EHCI_STS_PCD; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); ehci_root_intr(sc); /* do not allow RHSC interrupts > 1 per second */ usb_callout_reset(&sc->sc_tmo_pcd, hz, (void *)&ehci_pcd_enable, sc); } status &= ~(EHCI_STS_INT | EHCI_STS_ERRINT | EHCI_STS_PCD | EHCI_STS_IAA); if (status != 0) { /* block unprocessed interrupts */ sc->sc_eintrs &= ~status; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); printf("%s: blocking interrupts 0x%x\n", __FUNCTION__, status); } /* poll all the USB transfers */ ehci_interrupt_poll(sc); if (sc->sc_flags & EHCI_SCFLG_LOSTINTRBUG) { usb_callout_reset(&sc->sc_tmo_poll, hz / 128, (void *)&ehci_poll_timeout, sc); } done: USB_BUS_UNLOCK(&sc->sc_bus); } /* * called when a request does not complete */ static void ehci_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ ehci_device_done(xfer, USB_ERR_TIMEOUT); } static void ehci_do_poll(struct usb_bus *bus) { ehci_softc_t *sc = EHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); ehci_interrupt_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void ehci_setup_standard_chain_sub(struct ehci_std_temp *temp) { struct usb_page_search buf_res; ehci_qtd_t *td; ehci_qtd_t *td_next; ehci_qtd_t *td_alt_next; uint32_t buf_offset; uint32_t average; uint32_t len_old; uint32_t terminate; uint32_t qtd_altnext; uint8_t shortpkt_old; uint8_t precompute; terminate = temp->sc->sc_terminate_self; qtd_altnext = temp->sc->sc_terminate_self; td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; len_old = temp->len; precompute = 1; restart: td = temp->td; td_next = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) { break; } /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; average = 0; } else { average = temp->average; if (temp->len < average) { if (temp->len % temp->max_frame_size) { temp->shortpkt = 1; } average = temp->len; } } if (td_next == NULL) { panic("%s: out of EHCI transfer descriptors!", __FUNCTION__); } /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->qtd_status = temp->qtd_status | htohc32(temp->sc, EHCI_QTD_IOC | EHCI_QTD_SET_BYTES(average)); if (average == 0) { if (temp->auto_data_toggle == 0) { /* update data toggle, ZLP case */ temp->qtd_status ^= htohc32(temp->sc, EHCI_QTD_TOGGLE_MASK); } td->len = 0; /* properly reset reserved fields */ td->qtd_buffer[0] = 0; td->qtd_buffer[1] = 0; td->qtd_buffer[2] = 0; td->qtd_buffer[3] = 0; td->qtd_buffer[4] = 0; td->qtd_buffer_hi[0] = 0; td->qtd_buffer_hi[1] = 0; td->qtd_buffer_hi[2] = 0; td->qtd_buffer_hi[3] = 0; td->qtd_buffer_hi[4] = 0; } else { uint8_t x; if (temp->auto_data_toggle == 0) { /* update data toggle */ if (howmany(average, temp->max_frame_size) & 1) { temp->qtd_status ^= htohc32(temp->sc, EHCI_QTD_TOGGLE_MASK); } } td->len = average; /* update remaining length */ temp->len -= average; /* fill out buffer pointers */ usbd_get_page(temp->pc, buf_offset, &buf_res); td->qtd_buffer[0] = htohc32(temp->sc, buf_res.physaddr); td->qtd_buffer_hi[0] = 0; x = 1; while (average > EHCI_PAGE_SIZE) { average -= EHCI_PAGE_SIZE; buf_offset += EHCI_PAGE_SIZE; usbd_get_page(temp->pc, buf_offset, &buf_res); td->qtd_buffer[x] = htohc32(temp->sc, buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[x] = 0; x++; } /* * NOTE: The "average" variable is never zero after * exiting the loop above ! * * NOTE: We have to subtract one from the offset to * ensure that we are computing the physical address * of a valid page ! */ buf_offset += average; usbd_get_page(temp->pc, buf_offset - 1, &buf_res); td->qtd_buffer[x] = htohc32(temp->sc, buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[x] = 0; /* properly reset reserved fields */ while (++x < EHCI_QTD_NBUFFERS) { td->qtd_buffer[x] = 0; td->qtd_buffer_hi[x] = 0; } } if (td_next) { /* link the current TD with the next one */ td->qtd_next = td_next->qtd_self; } td->qtd_altnext = qtd_altnext; td->alt_next = td_alt_next; usb_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* setup alt next pointer, if any */ if (temp->last_frame) { td_alt_next = NULL; qtd_altnext = terminate; } else { /* we use this field internally */ td_alt_next = td_next; if (temp->setup_alt_next) { qtd_altnext = td_next->qtd_self; } else { qtd_altnext = terminate; } } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } temp->td = td; temp->td_next = td_next; } static void ehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last) { struct ehci_std_temp temp; const struct usb_pipe_methods *methods; ehci_qh_t *qh; ehci_qtd_t *td; uint32_t qh_endp; uint32_t qh_endphub; uint32_t x; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); temp.average = xfer->max_hc_frame_size; temp.max_frame_size = xfer->max_frame_size; temp.sc = EHCI_BUS2SC(xfer->xroot->bus); /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; temp.td = NULL; temp.td_next = td; temp.qtd_status = 0; temp.last_frame = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok; if (xfer->flags_int.control_xfr) { if (xfer->endpoint->toggle_next) { /* DATA1 is next */ temp.qtd_status |= htohc32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); } temp.auto_data_toggle = 0; } else { temp.auto_data_toggle = 1; } if ((xfer->xroot->udev->parent_hs_hub != NULL) || (xfer->xroot->udev->address != 0)) { /* max 3 retries */ temp.qtd_status |= htohc32(temp.sc, EHCI_QTD_SET_CERR(3)); } /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { xfer->endpoint->toggle_next = 0; temp.qtd_status &= htohc32(temp.sc, EHCI_QTD_SET_CERR(3)); temp.qtd_status |= htohc32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | EHCI_QTD_SET_TOGGLE(0)); temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) { temp.last_frame = 1; temp.setup_alt_next = 0; } } ehci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.pc = xfer->frbuffers + x; x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { /* no STATUS stage yet, DATA is last */ if (xfer->flags_int.control_act) { temp.last_frame = 1; temp.setup_alt_next = 0; } } else { temp.last_frame = 1; temp.setup_alt_next = 0; } } /* keep previous data toggle and error count */ temp.qtd_status &= htohc32(temp.sc, EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(1)); if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; } else { /* regular data transfer */ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; } /* set endpoint direction */ temp.qtd_status |= (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) ? htohc32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : htohc32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT)); ehci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current endpoint * direction. */ temp.qtd_status &= htohc32(temp.sc, EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(1)); temp.qtd_status |= (UE_GET_DIR(xfer->endpointno) == UE_DIR_OUT) ? htohc32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | EHCI_QTD_SET_TOGGLE(1)) : htohc32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) | EHCI_QTD_SET_TOGGLE(1)); temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; temp.last_frame = 1; temp.setup_alt_next = 0; ehci_setup_standard_chain_sub(&temp); } td = temp.td; /* the last TD terminates the transfer: */ td->qtd_next = htohc32(temp.sc, EHCI_LINK_TERMINATE); td->qtd_altnext = htohc32(temp.sc, EHCI_LINK_TERMINATE); usb_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ xfer->td_transfer_last = td; #ifdef USB_DEBUG if (ehcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->endpoint->toggle_next); ehci_dump_sqtds(temp.sc, xfer->td_transfer_first); } #endif methods = xfer->endpoint->methods; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* the "qh_link" field is filled when the QH is added */ qh_endp = (EHCI_QH_SET_ADDR(xfer->address) | EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpointno)) | EHCI_QH_SET_MPL(xfer->max_packet_size)); if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) { qh_endp |= EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH); if (methods != &ehci_device_intr_methods) qh_endp |= EHCI_QH_SET_NRL(8); } else { if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_FULL) { qh_endp |= EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL); } else { qh_endp |= EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW); } if (methods == &ehci_device_ctrl_methods) { qh_endp |= EHCI_QH_CTL; } if (methods != &ehci_device_intr_methods) { /* Only try one time per microframe! */ qh_endp |= EHCI_QH_SET_NRL(1); } } if (temp.auto_data_toggle == 0) { /* software computes the data toggle */ qh_endp |= EHCI_QH_DTC; } qh->qh_endp = htohc32(temp.sc, qh_endp); qh_endphub = (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) | EHCI_QH_SET_CMASK(xfer->endpoint->usb_cmask) | EHCI_QH_SET_SMASK(xfer->endpoint->usb_smask) | EHCI_QH_SET_HUBA(xfer->xroot->udev->hs_hub_addr) | EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no)); qh->qh_endphub = htohc32(temp.sc, qh_endphub); qh->qh_curqtd = 0; /* fill the overlay qTD */ if (temp.auto_data_toggle && xfer->endpoint->toggle_next) { /* DATA1 is next */ qh->qh_qtd.qtd_status = htohc32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); } else { qh->qh_qtd.qtd_status = 0; } td = xfer->td_transfer_first; qh->qh_qtd.qtd_next = td->qtd_self; qh->qh_qtd.qtd_altnext = htohc32(temp.sc, EHCI_LINK_TERMINATE); /* properly reset reserved fields */ qh->qh_qtd.qtd_buffer[0] = 0; qh->qh_qtd.qtd_buffer[1] = 0; qh->qh_qtd.qtd_buffer[2] = 0; qh->qh_qtd.qtd_buffer[3] = 0; qh->qh_qtd.qtd_buffer[4] = 0; qh->qh_qtd.qtd_buffer_hi[0] = 0; qh->qh_qtd.qtd_buffer_hi[1] = 0; qh->qh_qtd.qtd_buffer_hi[2] = 0; qh->qh_qtd.qtd_buffer_hi[3] = 0; qh->qh_qtd.qtd_buffer_hi[4] = 0; usb_pc_cpu_flush(qh->page_cache); if (xfer->xroot->udev->flags.self_suspended == 0) { EHCI_APPEND_QH(qh, *qh_last); } } static void ehci_root_intr(ehci_softc_t *sc) { uint16_t i; uint16_t m; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* clear any old interrupt data */ memset(sc->sc_hub_idata, 0, sizeof(sc->sc_hub_idata)); /* set bits */ m = (sc->sc_noport + 1); if (m > (8 * sizeof(sc->sc_hub_idata))) { m = (8 * sizeof(sc->sc_hub_idata)); } for (i = 1; i < m; i++) { /* pick out CHANGE bits from the status register */ if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) { sc->sc_hub_idata[i / 8] |= 1 << (i % 8); DPRINTF("port %d changed\n", i); } } uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } static void ehci_isoc_fs_done(ehci_softc_t *sc, struct usb_xfer *xfer) { uint32_t nframes = xfer->nframes; uint32_t status; uint32_t *plen = xfer->frlengths; uint16_t len = 0; ehci_sitd_t *td = xfer->td_transfer_first; ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos]; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_fs_p_last[0]; } #ifdef USB_DEBUG if (ehcidebug > 15) { DPRINTF("isoc FS-TD\n"); ehci_dump_sitd(sc, td); } #endif usb_pc_cpu_invalidate(td->page_cache); status = hc32toh(sc, td->sitd_status); len = EHCI_SITD_GET_LEN(status); DPRINTFN(2, "status=0x%08x, rem=%u\n", status, len); if (*plen >= len) { len = *plen - len; } else { len = 0; } *plen = len; /* remove FS-TD from schedule */ EHCI_REMOVE_FS_TD(td, *pp_last); pp_last++; plen++; td = td->obj_next; } xfer->aframes = xfer->nframes; } static void ehci_isoc_hs_done(ehci_softc_t *sc, struct usb_xfer *xfer) { uint32_t nframes = xfer->nframes; uint32_t status; uint32_t *plen = xfer->frlengths; uint16_t len = 0; uint8_t td_no = 0; ehci_itd_t *td = xfer->td_transfer_first; ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos]; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); while (nframes) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_hs_p_last[0]; } #ifdef USB_DEBUG if (ehcidebug > 15) { DPRINTF("isoc HS-TD\n"); ehci_dump_itd(sc, td); } #endif usb_pc_cpu_invalidate(td->page_cache); status = hc32toh(sc, td->itd_status[td_no]); len = EHCI_ITD_GET_LEN(status); DPRINTFN(2, "status=0x%08x, len=%u\n", status, len); if (xfer->endpoint->usb_smask & (1 << td_no)) { if (*plen >= len) { /* * The length is valid. NOTE: The * complete length is written back * into the status field, and not the * remainder like with other transfer * descriptor types. */ } else { /* Invalid length - truncate */ len = 0; } *plen = len; plen++; nframes--; } td_no++; if ((td_no == 8) || (nframes == 0)) { /* remove HS-TD from schedule */ EHCI_REMOVE_HS_TD(td, *pp_last); pp_last++; td_no = 0; td = td->obj_next; } } xfer->aframes = xfer->nframes; } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void ehci_device_done(struct usb_xfer *xfer, usb_error_t error) { const struct usb_pipe_methods *methods = xfer->endpoint->methods; ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); if ((methods == &ehci_device_bulk_methods) || (methods == &ehci_device_ctrl_methods)) { #ifdef USB_DEBUG if (ehcidebug > 8) { DPRINTF("nexttog=%d; data after transfer:\n", xfer->endpoint->toggle_next); ehci_dump_sqtds(sc, xfer->td_transfer_first); } #endif EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_async_p_last); } if (methods == &ehci_device_intr_methods) { EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_intr_p_last[xfer->qh_pos]); } /* * Only finish isochronous transfers once which will update * "xfer->frlengths". */ if (xfer->td_transfer_first && xfer->td_transfer_last) { if (methods == &ehci_device_isoc_fs_methods) { ehci_isoc_fs_done(sc, xfer); } if (methods == &ehci_device_isoc_hs_methods) { ehci_isoc_hs_done(sc, xfer); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * ehci bulk support *------------------------------------------------------------------------*/ static void ehci_device_bulk_open(struct usb_xfer *xfer) { return; } static void ehci_device_bulk_close(struct usb_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_bulk_enter(struct usb_xfer *xfer) { return; } static void ehci_doorbell_async(struct ehci_softc *sc) { uint32_t temp; /* * XXX Performance quirk: Some Host Controllers have a too low * interrupt rate. Issue an IAAD to stimulate the Host * Controller after queueing the BULK transfer. * * XXX Force the host controller to refresh any QH caches. */ temp = EOREAD4(sc, EHCI_USBCMD); if (!(temp & EHCI_CMD_IAAD)) EOWRITE4(sc, EHCI_USBCMD, temp | EHCI_CMD_IAAD); } static void ehci_device_bulk_start(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); /* * XXX Certain nVidia chipsets choke when using the IAAD * feature too frequently. */ if (sc->sc_flags & EHCI_SCFLG_IAADBUG) return; ehci_doorbell_async(sc); } static const struct usb_pipe_methods ehci_device_bulk_methods = { .open = ehci_device_bulk_open, .close = ehci_device_bulk_close, .enter = ehci_device_bulk_enter, .start = ehci_device_bulk_start, }; /*------------------------------------------------------------------------* * ehci control support *------------------------------------------------------------------------*/ static void ehci_device_ctrl_open(struct usb_xfer *xfer) { return; } static void ehci_device_ctrl_close(struct usb_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_ctrl_enter(struct usb_xfer *xfer) { return; } static void ehci_device_ctrl_start(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ehci_device_ctrl_methods = { .open = ehci_device_ctrl_open, .close = ehci_device_ctrl_close, .enter = ehci_device_ctrl_enter, .start = ehci_device_ctrl_start, }; /*------------------------------------------------------------------------* * ehci interrupt support *------------------------------------------------------------------------*/ static void ehci_device_intr_open(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); uint16_t best; uint16_t bit; uint16_t x; usb_hs_bandwidth_alloc(xfer); /* * Find the best QH position corresponding to the given interval: */ best = 0; bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2; while (bit) { if (xfer->interval >= bit) { x = bit; best = bit; while (x & bit) { if (sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(3, "best=%d interval=%d\n", best, xfer->interval); } static void ehci_device_intr_close(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); sc->sc_intr_stat[xfer->qh_pos]--; ehci_device_done(xfer, USB_ERR_CANCELLED); /* bandwidth must be freed after device done */ usb_hs_bandwidth_free(xfer); } static void ehci_device_intr_enter(struct usb_xfer *xfer) { return; } static void ehci_device_intr_start(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ehci_device_intr_methods = { .open = ehci_device_intr_open, .close = ehci_device_intr_close, .enter = ehci_device_intr_enter, .start = ehci_device_intr_start, }; /*------------------------------------------------------------------------* * ehci full speed isochronous support *------------------------------------------------------------------------*/ static void ehci_device_isoc_fs_open(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_sitd_t *td; uint32_t sitd_portaddr; uint8_t ds; sitd_portaddr = EHCI_SITD_SET_ADDR(xfer->address) | EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpointno)) | EHCI_SITD_SET_HUBA(xfer->xroot->udev->hs_hub_addr) | EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no); if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) sitd_portaddr |= EHCI_SITD_SET_DIR_IN; sitd_portaddr = htohc32(sc, sitd_portaddr); /* initialize all TD's */ for (ds = 0; ds != 2; ds++) { for (td = xfer->td_start[ds]; td; td = td->obj_next) { td->sitd_portaddr = sitd_portaddr; /* * TODO: make some kind of automatic * SMASK/CMASK selection based on micro-frame * usage * * micro-frame usage (8 microframes per 1ms) */ td->sitd_back = htohc32(sc, EHCI_LINK_TERMINATE); usb_pc_cpu_flush(td->page_cache); } } } static void ehci_device_isoc_fs_close(struct usb_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_isoc_fs_enter(struct usb_xfer *xfer) { struct usb_page_search buf_res; ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_sitd_t *td; ehci_sitd_t *td_last = NULL; ehci_sitd_t **pp_last; uint32_t *plen; uint32_t buf_offset; uint32_t nframes; uint32_t temp; uint32_t sitd_mask; uint16_t tlen; uint8_t sa; uint8_t sb; #ifdef USB_DEBUG uint8_t once = 1; #endif DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->endpoint->isoc_next, xfer->nframes); /* get the current frame index */ nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; /* * check if the frame index is within the window where the frames * will be inserted */ buf_offset = (nframes - xfer->endpoint->isoc_next) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); if ((xfer->endpoint->is_synced == 0) || (buf_offset < xfer->nframes)) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->endpoint->isoc_next = (nframes + 3) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ buf_offset = (xfer->endpoint->isoc_next - nframes) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + xfer->nframes; /* get the real number of frames */ nframes = xfer->nframes; buf_offset = 0; plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_fs_p_last[xfer->endpoint->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->endpoint->isoc_next; while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) pp_last = &sc->sc_isoc_fs_p_last[0]; /* reuse sitd_portaddr and sitd_back from last transfer */ if (*plen > xfer->max_frame_size) { #ifdef USB_DEBUG if (once) { once = 0; printf("%s: frame length(%d) exceeds %d " "bytes (frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } /* allocate a slot */ sa = usbd_fs_isoc_schedule_alloc_slot(xfer, xfer->isoc_time_complete - nframes - 1); if (sa == 255) { /* * Schedule is FULL, set length to zero: */ *plen = 0; sa = USB_FS_ISOC_UFRAME_MAX - 1; } if (*plen) { /* * only call "usbd_get_page()" when we have a * non-zero length */ usbd_get_page(xfer->frbuffers, buf_offset, &buf_res); td->sitd_bp[0] = htohc32(sc, buf_res.physaddr); buf_offset += *plen; /* * NOTE: We need to subtract one from the offset so * that we are on a valid page! */ usbd_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); temp = buf_res.physaddr & ~0xFFF; } else { td->sitd_bp[0] = 0; temp = 0; } if (UE_GET_DIR(xfer->endpointno) == UE_DIR_OUT) { tlen = *plen; if (tlen <= 188) { temp |= 1; /* T-count = 1, TP = ALL */ tlen = 1; } else { tlen += 187; tlen /= 188; temp |= tlen; /* T-count = [1..6] */ temp |= 8; /* TP = Begin */ } tlen += sa; if (tlen >= 8) { sb = 0; } else { sb = (1 << tlen); } sa = (1 << sa); sa = (sb - sa) & 0x3F; sb = 0; } else { sb = (-(4 << sa)) & 0xFE; sa = (1 << sa) & 0x3F; } sitd_mask = (EHCI_SITD_SET_SMASK(sa) | EHCI_SITD_SET_CMASK(sb)); td->sitd_bp[1] = htohc32(sc, temp); td->sitd_mask = htohc32(sc, sitd_mask); if (nframes == 0) { td->sitd_status = htohc32(sc, EHCI_SITD_IOC | EHCI_SITD_ACTIVE | EHCI_SITD_SET_LEN(*plen)); } else { td->sitd_status = htohc32(sc, EHCI_SITD_ACTIVE | EHCI_SITD_SET_LEN(*plen)); } usb_pc_cpu_flush(td->page_cache); #ifdef USB_DEBUG if (ehcidebug > 15) { DPRINTF("FS-TD %d\n", nframes); ehci_dump_sitd(sc, td); } #endif /* insert TD into schedule */ EHCI_APPEND_FS_TD(td, *pp_last); pp_last++; plen++; td_last = td; td = td->obj_next; } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); /* * We don't allow cancelling of the SPLIT transaction USB FULL * speed transfer, because it disturbs the bandwidth * computation algorithm. */ xfer->flags_int.can_cancel_immed = 0; } static void ehci_device_isoc_fs_start(struct usb_xfer *xfer) { /* * We don't allow cancelling of the SPLIT transaction USB FULL * speed transfer, because it disturbs the bandwidth * computation algorithm. */ xfer->flags_int.can_cancel_immed = 0; /* set a default timeout */ if (xfer->timeout == 0) xfer->timeout = 500; /* ms */ /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ehci_device_isoc_fs_methods = { .open = ehci_device_isoc_fs_open, .close = ehci_device_isoc_fs_close, .enter = ehci_device_isoc_fs_enter, .start = ehci_device_isoc_fs_start, }; /*------------------------------------------------------------------------* * ehci high speed isochronous support *------------------------------------------------------------------------*/ static void ehci_device_isoc_hs_open(struct usb_xfer *xfer) { ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_itd_t *td; uint32_t temp; uint8_t ds; usb_hs_bandwidth_alloc(xfer); /* initialize all TD's */ for (ds = 0; ds != 2; ds++) { for (td = xfer->td_start[ds]; td; td = td->obj_next) { /* set TD inactive */ td->itd_status[0] = 0; td->itd_status[1] = 0; td->itd_status[2] = 0; td->itd_status[3] = 0; td->itd_status[4] = 0; td->itd_status[5] = 0; td->itd_status[6] = 0; td->itd_status[7] = 0; /* set endpoint and address */ td->itd_bp[0] = htohc32(sc, EHCI_ITD_SET_ADDR(xfer->address) | EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpointno))); temp = EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF); /* set direction */ if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) { temp |= EHCI_ITD_SET_DIR_IN; } /* set maximum packet size */ td->itd_bp[1] = htohc32(sc, temp); /* set transfer multiplier */ td->itd_bp[2] = htohc32(sc, xfer->max_packet_count & 3); usb_pc_cpu_flush(td->page_cache); } } } static void ehci_device_isoc_hs_close(struct usb_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); /* bandwidth must be freed after device done */ usb_hs_bandwidth_free(xfer); } static void ehci_device_isoc_hs_enter(struct usb_xfer *xfer) { struct usb_page_search buf_res; ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); ehci_itd_t *td; ehci_itd_t *td_last = NULL; ehci_itd_t **pp_last; bus_size_t page_addr; uint32_t *plen; uint32_t status; uint32_t buf_offset; uint32_t nframes; uint32_t itd_offset[8 + 1]; uint8_t x; uint8_t td_no; uint8_t page_no; uint8_t shift = usbd_xfer_get_fps_shift(xfer); #ifdef USB_DEBUG uint8_t once = 1; #endif DPRINTFN(6, "xfer=%p next=%d nframes=%d shift=%d\n", xfer, xfer->endpoint->isoc_next, xfer->nframes, (int)shift); /* get the current frame index */ nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; /* * check if the frame index is within the window where the frames * will be inserted */ buf_offset = (nframes - xfer->endpoint->isoc_next) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); if ((xfer->endpoint->is_synced == 0) || (buf_offset < (((xfer->nframes << shift) + 7) / 8))) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->endpoint->isoc_next = (nframes + 3) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ buf_offset = (xfer->endpoint->isoc_next - nframes) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + (((xfer->nframes << shift) + 7) / 8); /* get the real number of frames */ nframes = xfer->nframes; buf_offset = 0; td_no = 0; plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_hs_p_last[xfer->endpoint->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->endpoint->isoc_next; while (nframes) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_hs_p_last[0]; } /* range check */ if (*plen > xfer->max_frame_size) { #ifdef USB_DEBUG if (once) { once = 0; printf("%s: frame length(%d) exceeds %d bytes " "(frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } if (xfer->endpoint->usb_smask & (1 << td_no)) { status = (EHCI_ITD_SET_LEN(*plen) | EHCI_ITD_ACTIVE | EHCI_ITD_SET_PG(0)); td->itd_status[td_no] = htohc32(sc, status); itd_offset[td_no] = buf_offset; buf_offset += *plen; plen++; nframes --; } else { td->itd_status[td_no] = 0; /* not active */ itd_offset[td_no] = buf_offset; } td_no++; if ((td_no == 8) || (nframes == 0)) { /* the rest of the transfers are not active, if any */ for (x = td_no; x != 8; x++) { td->itd_status[x] = 0; /* not active */ } /* check if there is any data to be transferred */ if (itd_offset[0] != buf_offset) { page_no = 0; itd_offset[td_no] = buf_offset; /* get first page offset */ usbd_get_page(xfer->frbuffers, itd_offset[0], &buf_res); /* get page address */ page_addr = buf_res.physaddr & ~0xFFF; /* update page address */ td->itd_bp[0] &= htohc32(sc, 0xFFF); td->itd_bp[0] |= htohc32(sc, page_addr); for (x = 0; x != td_no; x++) { /* set page number and page offset */ status = (EHCI_ITD_SET_PG(page_no) | (buf_res.physaddr & 0xFFF)); td->itd_status[x] |= htohc32(sc, status); /* get next page offset */ if (itd_offset[x + 1] == buf_offset) { /* * We subtract one so that * we don't go off the last * page! */ usbd_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); } else { usbd_get_page(xfer->frbuffers, itd_offset[x + 1], &buf_res); } /* check if we need a new page */ if ((buf_res.physaddr ^ page_addr) & ~0xFFF) { /* new page needed */ page_addr = buf_res.physaddr & ~0xFFF; if (page_no == 6) { panic("%s: too many pages\n", __FUNCTION__); } page_no++; /* update page address */ td->itd_bp[page_no] &= htohc32(sc, 0xFFF); td->itd_bp[page_no] |= htohc32(sc, page_addr); } } } /* set IOC bit if we are complete */ if (nframes == 0) { td->itd_status[td_no - 1] |= htohc32(sc, EHCI_ITD_IOC); } usb_pc_cpu_flush(td->page_cache); #ifdef USB_DEBUG if (ehcidebug > 15) { DPRINTF("HS-TD %d\n", nframes); ehci_dump_itd(sc, td); } #endif /* insert TD into schedule */ EHCI_APPEND_HS_TD(td, *pp_last); pp_last++; td_no = 0; td_last = td; td = td->obj_next; } } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); } static void ehci_device_isoc_hs_start(struct usb_xfer *xfer) { /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ehci_device_isoc_hs_methods = { .open = ehci_device_isoc_hs_open, .close = ehci_device_isoc_hs_close, .enter = ehci_device_isoc_hs_enter, .start = ehci_device_isoc_hs_start, }; /*------------------------------------------------------------------------* * ehci root control support *------------------------------------------------------------------------* * Simulate a hardware hub by handling all the necessary requests. *------------------------------------------------------------------------*/ static const struct usb_device_descriptor ehci_devd = { sizeof(struct usb_device_descriptor), UDESC_DEVICE, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_HSHUBSTT, /* protocol */ 64, /* max packet */ {0}, {0}, {0x00, 0x01}, /* device id */ - 1, 2, 0, /* string indicies */ + 1, 2, 0, /* string indexes */ 1 /* # of configurations */ }; static const struct usb_device_qualifier ehci_odevd = { sizeof(struct usb_device_qualifier), UDESC_DEVICE_QUALIFIER, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 0, /* max packet */ 0, /* # of configurations */ 0 }; static const struct ehci_config_desc ehci_confd = { .confd = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(ehci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0 /* max power */ }, .ifcd = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = 0, }, .endpd = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | EHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, /* max packet (63 ports) */ .bInterval = 255, }, }; static const struct usb_hub_descriptor ehci_hubd = { .bDescLength = 0, /* dynamic length */ .bDescriptorType = UDESC_HUB, }; uint16_t ehci_get_port_speed_portsc(struct ehci_softc *sc, uint16_t index) { uint32_t v; v = EOREAD4(sc, EHCI_PORTSC(index)); v = (v >> EHCI_PORTSC_PSPD_SHIFT) & EHCI_PORTSC_PSPD_MASK; if (v == EHCI_PORT_SPEED_HIGH) return (UPS_HIGH_SPEED); if (v == EHCI_PORT_SPEED_LOW) return (UPS_LOW_SPEED); return (0); } uint16_t ehci_get_port_speed_hostc(struct ehci_softc *sc, uint16_t index) { uint32_t v; v = EOREAD4(sc, EHCI_HOSTC(index)); v = (v >> EHCI_HOSTC_PSPD_SHIFT) & EHCI_HOSTC_PSPD_MASK; if (v == EHCI_PORT_SPEED_HIGH) return (UPS_HIGH_SPEED); if (v == EHCI_PORT_SPEED_LOW) return (UPS_LOW_SPEED); return (0); } static void ehci_disown(ehci_softc_t *sc, uint16_t index, uint8_t lowspeed) { uint32_t port; uint32_t v; DPRINTF("index=%d lowspeed=%d\n", index, lowspeed); port = EHCI_PORTSC(index); v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; EOWRITE4(sc, port, v | EHCI_PS_PO); } static usb_error_t ehci_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); const char *str_ptr; const void *ptr; uint32_t port; uint32_t v; uint16_t len; uint16_t i; uint16_t value; uint16_t index; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_desc; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", req->bmRequestType, req->bRequest, UGETW(req->wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(ehci_devd); ptr = (const void *)&ehci_devd; break; /* * We can't really operate at another speed, * but the specification says we need this * descriptor: */ case UDESC_DEVICE_QUALIFIER: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(ehci_odevd); ptr = (const void *)&ehci_odevd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(ehci_confd); ptr = (const void *)&ehci_confd; break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ str_ptr = "\001"; break; case 1: /* Vendor */ str_ptr = sc->sc_vendor; break; case 2: /* Product */ str_ptr = "EHCI root HUB"; break; default: str_ptr = ""; break; } len = usb_make_str_desc( sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), str_ptr); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= EHCI_MAX_DEVICES) { err = USB_ERR_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if ((value != 0) && (value != 1)) { err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n"); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; switch (value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v & ~EHCI_PS_PE); break; case UHF_PORT_SUSPEND: if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) { /* * waking up a High Speed device is rather * complicated if */ EOWRITE4(sc, port, v | EHCI_PS_FPR); } /* wait 20ms for resume sequence to complete */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP | EHCI_PS_FPR | (3 << 10) /* High Speed */ )); /* 4ms settle time */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); break; case UHF_PORT_POWER: EOWRITE4(sc, port, v & ~EHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(3, "clear port test " "%d\n", index); break; case UHF_PORT_INDICATOR: DPRINTFN(3, "clear port ind " "%d\n", index); EOWRITE4(sc, port, v & ~EHCI_PS_PIC); break; case UHF_C_PORT_CONNECTION: EOWRITE4(sc, port, v | EHCI_PS_CSC); break; case UHF_C_PORT_ENABLE: EOWRITE4(sc, port, v | EHCI_PS_PEC); break; case UHF_C_PORT_SUSPEND: EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_C_PORT_OVER_CURRENT: EOWRITE4(sc, port, v | EHCI_PS_OCC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } v = EREAD4(sc, EHCI_HCSPARAMS); sc->sc_hub_desc.hubd = ehci_hubd; sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; if (EHCI_HCS_PPC(v)) i = UHD_PWR_INDIVIDUAL; else i = UHD_PWR_NO_SWITCH; if (EHCI_HCS_P_INDICATOR(v)) i |= UHD_PORT_IND; USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, i); /* XXX can't find out? */ sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; /* XXX don't know if ports are removable or not */ sc->sc_hub_desc.hubd.bDescLength = 8 + ((sc->sc_noport + 7) / 8); len = sc->sc_hub_desc.hubd.bDescLength; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "get port status i=%d\n", index); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } v = EOREAD4(sc, EHCI_PORTSC(index)); DPRINTFN(9, "port status=0x%04x\n", v); if (sc->sc_flags & EHCI_SCFLG_TT) { if (sc->sc_vendor_get_port_speed != NULL) { i = sc->sc_vendor_get_port_speed(sc, index); } else { device_printf(sc->sc_bus.bdev, "EHCI_SCFLG_TT quirk is set but " "sc_vendor_get_hub_speed() is NULL\n"); i = UPS_HIGH_SPEED; } } else { i = UPS_HIGH_SPEED; } if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; if ((v & EHCI_PS_SUSP) && !(v & EHCI_PS_FPR)) i |= UPS_SUSPEND; if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; if (v & EHCI_PS_PR) i |= UPS_RESET; if (v & EHCI_PS_PP) i |= UPS_PORT_POWER; USETW(sc->sc_hub_desc.ps.wPortStatus, i); i = 0; if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (v & EHCI_PS_FPR) i |= UPS_C_SUSPEND; if (sc->sc_isreset) i |= UPS_C_PORT_RESET; USETW(sc->sc_hub_desc.ps.wPortChange, i); len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; switch (value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v | EHCI_PS_PE); break; case UHF_PORT_SUSPEND: EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_PORT_RESET: DPRINTFN(6, "reset port %d\n", index); #ifdef USB_DEBUG if (ehcinohighspeed) { /* * Connect USB device to companion * controller. */ ehci_disown(sc, index, 1); break; } #endif if (EHCI_PS_IS_LOWSPEED(v) && (sc->sc_flags & EHCI_SCFLG_TT) == 0) { /* Low speed device, give up ownership. */ ehci_disown(sc, index, 1); break; } /* Start reset sequence. */ v &= ~(EHCI_PS_PE | EHCI_PS_PR); EOWRITE4(sc, port, v | EHCI_PS_PR); /* Wait for reset to complete. */ usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(usb_port_root_reset_delay)); /* Terminate reset sequence. */ if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM)) EOWRITE4(sc, port, v); /* Wait for HC to complete reset. */ usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(EHCI_PORT_RESET_COMPLETE)); v = EOREAD4(sc, port); DPRINTF("ehci after reset, status=0x%08x\n", v); if (v & EHCI_PS_PR) { device_printf(sc->sc_bus.bdev, "port reset timeout\n"); err = USB_ERR_TIMEOUT; goto done; } if (!(v & EHCI_PS_PE) && (sc->sc_flags & EHCI_SCFLG_TT) == 0) { /* Not a high speed device, give up ownership.*/ ehci_disown(sc, index, 0); break; } sc->sc_isreset = 1; DPRINTF("ehci port %d reset, status = 0x%08x\n", index, v); break; case UHF_PORT_POWER: DPRINTFN(3, "set port power %d\n", index); EOWRITE4(sc, port, v | EHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(3, "set port test %d\n", index); break; case UHF_PORT_INDICATOR: DPRINTFN(3, "set port ind %d\n", index); EOWRITE4(sc, port, v | EHCI_PS_PIC); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: err = USB_ERR_IOERROR; goto done; } done: *plength = len; *pptr = ptr; return (err); } static void ehci_xfer_setup(struct usb_setup_params *parm) { struct usb_page_search page_info; struct usb_page_cache *pc; ehci_softc_t *sc; struct usb_xfer *xfer; void *last_obj; uint32_t nqtd; uint32_t nqh; uint32_t nsitd; uint32_t nitd; uint32_t n; sc = EHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; nqtd = 0; nqh = 0; nsitd = 0; nitd = 0; /* * compute maximum number of some structures */ if (parm->methods == &ehci_device_ctrl_methods) { /* * The proof for the "nqtd" formula is illustrated like * this: * * +------------------------------------+ * | | * | |remainder -> | * | +-----+---+ | * | | xxx | x | frm 0 | * | +-----+---++ | * | | xxx | xx | frm 1 | * | +-----+----+ | * | ... | * +------------------------------------+ * * "xxx" means a completely full USB transfer descriptor * * "x" and "xx" means a short USB packet * * For the remainder of an USB transfer modulo * "max_data_length" we need two USB transfer descriptors. * One to transfer the remaining data and one to finalise * with a zero length packet in case the "force_short_xfer" * flag is set. We only need two USB transfer descriptors in * the case where the transfer length of the first one is a * factor of "max_frame_size". The rest of the needed USB * transfer descriptors is given by the buffer size divided * by the maximum data payload. */ parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nqh = 1; nqtd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_hc_frame_size)); } else if (parm->methods == &ehci_device_bulk_methods) { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nqh = 1; nqtd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); } else if (parm->methods == &ehci_device_intr_methods) { if (parm->speed == USB_SPEED_HIGH) { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 3; } else if (parm->speed == USB_SPEED_FULL) { parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME; parm->hc_max_packet_count = 1; } else { parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME / 8; parm->hc_max_packet_count = 1; } parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nqh = 1; nqtd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); } else if (parm->methods == &ehci_device_isoc_fs_methods) { parm->hc_max_packet_size = 0x3FF; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x3FF; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nsitd = xfer->nframes; } else if (parm->methods == &ehci_device_isoc_hs_methods) { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 3; parm->hc_max_frame_size = 0xC00; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nitd = ((xfer->nframes + 7) / 8) << usbd_xfer_get_fps_shift(xfer); } else { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x400; usbd_transfer_setup_sub(parm); } alloc_dma_set: if (parm->err) { return; } /* * Allocate queue heads and transfer descriptors */ last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_itd_t), EHCI_ITD_ALIGN, nitd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nitd; n++) { ehci_itd_t *td; usbd_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->itd_self = htohc32(sc, page_info.physaddr | EHCI_LINK_ITD); td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb_pc_cpu_flush(pc + n); } } if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_sitd_t), EHCI_SITD_ALIGN, nsitd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nsitd; n++) { ehci_sitd_t *td; usbd_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->sitd_self = htohc32(sc, page_info.physaddr | EHCI_LINK_SITD); td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb_pc_cpu_flush(pc + n); } } if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_qtd_t), EHCI_QTD_ALIGN, nqtd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqtd; n++) { ehci_qtd_t *qtd; usbd_get_page(pc + n, 0, &page_info); qtd = page_info.buffer; /* init TD */ qtd->qtd_self = htohc32(sc, page_info.physaddr); qtd->obj_next = last_obj; qtd->page_cache = pc + n; last_obj = qtd; usb_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_qh_t), EHCI_QH_ALIGN, nqh)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqh; n++) { ehci_qh_t *qh; usbd_get_page(pc + n, 0, &page_info); qh = page_info.buffer; /* init QH */ qh->qh_self = htohc32(sc, page_info.physaddr | EHCI_LINK_QH); qh->obj_next = last_obj; qh->page_cache = pc + n; last_obj = qh; usb_pc_cpu_flush(pc + n); } } xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static void ehci_xfer_unsetup(struct usb_xfer *xfer) { return; } static void ehci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode, sc->sc_addr); if (udev->device_index != sc->sc_addr) { if ((udev->speed != USB_SPEED_HIGH) && ((udev->hs_hub_addr == 0) || (udev->hs_port_no == 0) || (udev->parent_hs_hub == NULL) || (udev->parent_hs_hub->hub == NULL))) { /* We need a transaction translator */ goto done; } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ep->methods = &ehci_device_ctrl_methods; break; case UE_INTERRUPT: ep->methods = &ehci_device_intr_methods; break; case UE_ISOCHRONOUS: if (udev->speed == USB_SPEED_HIGH) { ep->methods = &ehci_device_isoc_hs_methods; } else if (udev->speed == USB_SPEED_FULL) { ep->methods = &ehci_device_isoc_fs_methods; } break; case UE_BULK: ep->methods = &ehci_device_bulk_methods; break; default: /* do nothing */ break; } } done: return; } static void ehci_get_dma_delay(struct usb_device *udev, uint32_t *pus) { /* * Wait until the hardware has finished any possible use of * the transfer descriptor(s) and QH */ *pus = (1125); /* microseconds */ } static void ehci_device_resume(struct usb_device *udev) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); struct usb_xfer *xfer; const struct usb_pipe_methods *methods; DPRINTF("\n"); USB_BUS_LOCK(udev->bus); TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (xfer->xroot->udev == udev) { methods = xfer->endpoint->methods; if ((methods == &ehci_device_bulk_methods) || (methods == &ehci_device_ctrl_methods)) { EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_async_p_last); } if (methods == &ehci_device_intr_methods) { EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_intr_p_last[xfer->qh_pos]); } } } USB_BUS_UNLOCK(udev->bus); return; } static void ehci_device_suspend(struct usb_device *udev) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); struct usb_xfer *xfer; const struct usb_pipe_methods *methods; DPRINTF("\n"); USB_BUS_LOCK(udev->bus); TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (xfer->xroot->udev == udev) { methods = xfer->endpoint->methods; if ((methods == &ehci_device_bulk_methods) || (methods == &ehci_device_ctrl_methods)) { EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_async_p_last); } if (methods == &ehci_device_intr_methods) { EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_intr_p_last[xfer->qh_pos]); } } } USB_BUS_UNLOCK(udev->bus); } static void ehci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct ehci_softc *sc = EHCI_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: case USB_HW_POWER_SHUTDOWN: ehci_suspend(sc); break; case USB_HW_POWER_RESUME: ehci_resume(sc); break; default: break; } } static void ehci_set_hw_power(struct usb_bus *bus) { ehci_softc_t *sc = EHCI_BUS2SC(bus); uint32_t temp; uint32_t flags; DPRINTF("\n"); USB_BUS_LOCK(bus); flags = bus->hw_power_state; temp = EOREAD4(sc, EHCI_USBCMD); temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE); if (flags & (USB_HW_POWER_CONTROL | USB_HW_POWER_BULK)) { DPRINTF("Async is active\n"); temp |= EHCI_CMD_ASE; } if (flags & (USB_HW_POWER_INTERRUPT | USB_HW_POWER_ISOC)) { DPRINTF("Periodic is active\n"); temp |= EHCI_CMD_PSE; } EOWRITE4(sc, EHCI_USBCMD, temp); USB_BUS_UNLOCK(bus); return; } static void ehci_start_dma_delay_second(struct usb_xfer *xfer) { struct ehci_softc *sc = EHCI_BUS2SC(xfer->xroot->bus); DPRINTF("\n"); /* trigger doorbell */ ehci_doorbell_async(sc); /* give the doorbell 4ms */ usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, 4); } /* * Ring the doorbell twice before freeing any DMA descriptors. Some host * controllers apparently cache the QH descriptors and need a message * that the cache needs to be discarded. */ static void ehci_start_dma_delay(struct usb_xfer *xfer) { struct ehci_softc *sc = EHCI_BUS2SC(xfer->xroot->bus); DPRINTF("\n"); /* trigger doorbell */ ehci_doorbell_async(sc); /* give the doorbell 4ms */ usbd_transfer_timeout_ms(xfer, (void (*)(void *))&ehci_start_dma_delay_second, 4); } static const struct usb_bus_methods ehci_bus_methods = { .endpoint_init = ehci_ep_init, .xfer_setup = ehci_xfer_setup, .xfer_unsetup = ehci_xfer_unsetup, .get_dma_delay = ehci_get_dma_delay, .device_resume = ehci_device_resume, .device_suspend = ehci_device_suspend, .set_hw_power = ehci_set_hw_power, .set_hw_power_sleep = ehci_set_hw_power_sleep, .roothub_exec = ehci_roothub_exec, .xfer_poll = ehci_do_poll, .start_dma_delay = ehci_start_dma_delay, }; Index: head/sys/dev/usb/controller/ohci.c =================================================================== --- head/sys/dev/usb/controller/ohci.c (revision 298931) +++ head/sys/dev/usb/controller/ohci.c (revision 298932) @@ -1,2734 +1,2734 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR ohcidebug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include #define OHCI_BUS2SC(bus) \ ((ohci_softc_t *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((ohci_softc_t *)0)->sc_bus)))) #ifdef USB_DEBUG static int ohcidebug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci"); SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RWTUN, &ohcidebug, 0, "ohci debug level"); static void ohci_dumpregs(ohci_softc_t *); static void ohci_dump_tds(ohci_td_t *); static uint8_t ohci_dump_td(ohci_td_t *); static void ohci_dump_ed(ohci_ed_t *); static uint8_t ohci_dump_itd(ohci_itd_t *); static void ohci_dump_itds(ohci_itd_t *); #endif #define OBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define OWRITE1(sc, r, x) \ do { OBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) #define OWRITE2(sc, r, x) \ do { OBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) #define OWRITE4(sc, r, x) \ do { OBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) #define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define OHCI_INTR_ENDPT 1 static const struct usb_bus_methods ohci_bus_methods; static const struct usb_pipe_methods ohci_device_bulk_methods; static const struct usb_pipe_methods ohci_device_ctrl_methods; static const struct usb_pipe_methods ohci_device_intr_methods; static const struct usb_pipe_methods ohci_device_isoc_methods; static void ohci_do_poll(struct usb_bus *bus); static void ohci_device_done(struct usb_xfer *xfer, usb_error_t error); static void ohci_timeout(void *arg); static uint8_t ohci_check_transfer(struct usb_xfer *xfer); static void ohci_root_intr(ohci_softc_t *sc); struct ohci_std_temp { struct usb_page_cache *pc; ohci_td_t *td; ohci_td_t *td_next; uint32_t average; uint32_t td_flags; uint32_t len; uint16_t max_frame_size; uint8_t shortpkt; uint8_t setup_alt_next; uint8_t last_frame; }; static struct ohci_hcca * ohci_get_hcca(ohci_softc_t *sc) { usb_pc_cpu_invalidate(&sc->sc_hw.hcca_pc); return (sc->sc_hcca_p); } void ohci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) { struct ohci_softc *sc = OHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.hcca_pc, &sc->sc_hw.hcca_pg, sizeof(ohci_hcca_t), OHCI_HCCA_ALIGN); cb(bus, &sc->sc_hw.ctrl_start_pc, &sc->sc_hw.ctrl_start_pg, sizeof(ohci_ed_t), OHCI_ED_ALIGN); cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg, sizeof(ohci_ed_t), OHCI_ED_ALIGN); cb(bus, &sc->sc_hw.isoc_start_pc, &sc->sc_hw.isoc_start_pg, sizeof(ohci_ed_t), OHCI_ED_ALIGN); for (i = 0; i != OHCI_NO_EDS; i++) { cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, sizeof(ohci_ed_t), OHCI_ED_ALIGN); } } static usb_error_t ohci_controller_init(ohci_softc_t *sc, int do_suspend) { struct usb_page_search buf_res; uint32_t i; uint32_t ctl; uint32_t ival; uint32_t hcr; uint32_t fm; uint32_t per; uint32_t desca; /* Determine in what context we are running. */ ctl = OREAD4(sc, OHCI_CONTROL); if (ctl & OHCI_IR) { /* SMM active, request change */ DPRINTF("SMM active, request owner change\n"); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_OCR); for (i = 0; (i < 100) && (ctl & OHCI_IR); i++) { usb_pause_mtx(NULL, hz / 1000); ctl = OREAD4(sc, OHCI_CONTROL); } if (ctl & OHCI_IR) { device_printf(sc->sc_bus.bdev, "SMM does not respond, resetting\n"); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); goto reset; } } else { DPRINTF("cold started\n"); reset: /* controller was cold started */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_BUS_RESET_DELAY)); } /* * This reset should not be necessary according to the OHCI spec, but * without it some controllers do not start. */ DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_BUS_RESET_DELAY)); /* we now own the host controller and the bus has been reset */ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL)); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */ /* nominal time for a reset is 10 us */ for (i = 0; i < 10; i++) { DELAY(10); hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR; if (!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); return (USB_ERR_IOERROR); } #ifdef USB_DEBUG if (ohcidebug > 15) { ohci_dumpregs(sc); } #endif if (do_suspend) { OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_SUSPEND); return (USB_ERR_NORMAL_COMPLETION); } /* The controller is now in SUSPEND state, we have 2ms to finish. */ /* set up HC registers */ usbd_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res); OWRITE4(sc, OHCI_HCCA, buf_res.physaddr); usbd_get_page(&sc->sc_hw.ctrl_start_pc, 0, &buf_res); OWRITE4(sc, OHCI_CONTROL_HEAD_ED, buf_res.physaddr); usbd_get_page(&sc->sc_hw.bulk_start_pc, 0, &buf_res); OWRITE4(sc, OHCI_BULK_HEAD_ED, buf_res.physaddr); /* disable all interrupts and then switch on all desired interrupts */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE); /* switch on desired functional features */ ctl = OREAD4(sc, OHCI_CONTROL); ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR); ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE | OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL; /* And finally start it! */ OWRITE4(sc, OHCI_CONTROL, ctl); /* * The controller is now OPERATIONAL. Set a some final * registers that should be set earlier, but that the * controller ignores when in the SUSPEND state. */ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT; fm |= OHCI_FSMPS(ival) | ival; OWRITE4(sc, OHCI_FM_INTERVAL, fm); per = OHCI_PERIODIC(ival); /* 90% periodic */ OWRITE4(sc, OHCI_PERIODIC_START, per); /* Fiddle the No OverCurrent Protection bit to avoid chip bug. */ desca = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca | OHCI_NOCP); OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(OHCI_ENABLE_POWER_DELAY)); OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca); /* * The AMD756 requires a delay before re-reading the register, * otherwise it will occasionally report 0 ports. */ sc->sc_noport = 0; for (i = 0; (i < 10) && (sc->sc_noport == 0); i++) { usb_pause_mtx(NULL, USB_MS_TO_TICKS(OHCI_READ_DESC_DELAY)); sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A)); } #ifdef USB_DEBUG if (ohcidebug > 5) { ohci_dumpregs(sc); } #endif return (USB_ERR_NORMAL_COMPLETION); } static struct ohci_ed * ohci_init_ed(struct usb_page_cache *pc) { struct usb_page_search buf_res; struct ohci_ed *ed; usbd_get_page(pc, 0, &buf_res); ed = buf_res.buffer; ed->ed_self = htole32(buf_res.physaddr); ed->ed_flags = htole32(OHCI_ED_SKIP); ed->page_cache = pc; return (ed); } usb_error_t ohci_init(ohci_softc_t *sc) { struct usb_page_search buf_res; uint16_t i; uint16_t bit; uint16_t x; uint16_t y; DPRINTF("start\n"); sc->sc_eintrs = OHCI_NORMAL_INTRS; /* * Setup all ED's */ sc->sc_ctrl_p_last = ohci_init_ed(&sc->sc_hw.ctrl_start_pc); sc->sc_bulk_p_last = ohci_init_ed(&sc->sc_hw.bulk_start_pc); sc->sc_isoc_p_last = ohci_init_ed(&sc->sc_hw.isoc_start_pc); for (i = 0; i != OHCI_NO_EDS; i++) { sc->sc_intr_p_last[i] = ohci_init_ed(sc->sc_hw.intr_start_pc + i); } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = OHCI_NO_EDS / 2; while (bit) { x = bit; while (x & bit) { ohci_ed_t *ed_x; ohci_ed_t *ed_y; y = (x ^ bit) | (bit / 2); /* * the next QH has half the poll interval */ ed_x = sc->sc_intr_p_last[x]; ed_y = sc->sc_intr_p_last[y]; ed_x->next = NULL; ed_x->ed_next = ed_y->ed_self; x++; } bit >>= 1; } if (1) { ohci_ed_t *ed_int; ohci_ed_t *ed_isc; ed_int = sc->sc_intr_p_last[0]; ed_isc = sc->sc_isoc_p_last; /* the last (1ms) QH */ ed_int->next = ed_isc; ed_int->ed_next = ed_isc->ed_self; } usbd_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res); sc->sc_hcca_p = buf_res.buffer; /* * Fill HCCA interrupt table. The bit reversal is to get * the tree set up properly to spread the interrupts. */ for (i = 0; i != OHCI_NO_INTRS; i++) { sc->sc_hcca_p->hcca_interrupt_table[i] = sc->sc_intr_p_last[i | (OHCI_NO_EDS / 2)]->ed_self; } /* flush all cache into memory */ usb_bus_mem_flush_all(&sc->sc_bus, &ohci_iterate_hw_softc); /* set up the bus struct */ sc->sc_bus.methods = &ohci_bus_methods; usb_callout_init_mtx(&sc->sc_tmo_rhsc, &sc->sc_bus.bus_mtx, 0); #ifdef USB_DEBUG if (ohcidebug > 15) { for (i = 0; i != OHCI_NO_EDS; i++) { printf("ed#%d ", i); ohci_dump_ed(sc->sc_intr_p_last[i]); } printf("iso "); ohci_dump_ed(sc->sc_isoc_p_last); } #endif sc->sc_bus.usbrev = USB_REV_1_0; if (ohci_controller_init(sc, 0) != 0) return (USB_ERR_INVAL); /* catch any lost interrupts */ ohci_do_poll(&sc->sc_bus); return (USB_ERR_NORMAL_COMPLETION); } /* * shut down the controller when the system is going down */ void ohci_detach(struct ohci_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); usb_callout_stop(&sc->sc_tmo_rhsc); OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); USB_BUS_UNLOCK(&sc->sc_bus); /* XXX let stray task complete */ usb_pause_mtx(NULL, hz / 20); usb_callout_drain(&sc->sc_tmo_rhsc); } static void ohci_suspend(ohci_softc_t *sc) { DPRINTF("\n"); #ifdef USB_DEBUG if (ohcidebug > 2) ohci_dumpregs(sc); #endif /* reset HC and leave it suspended */ ohci_controller_init(sc, 1); } static void ohci_resume(ohci_softc_t *sc) { DPRINTF("\n"); #ifdef USB_DEBUG if (ohcidebug > 2) ohci_dumpregs(sc); #endif /* some broken BIOSes never initialize the Controller chip */ ohci_controller_init(sc, 0); /* catch any lost interrupts */ ohci_do_poll(&sc->sc_bus); } #ifdef USB_DEBUG static void ohci_dumpregs(ohci_softc_t *sc) { struct ohci_hcca *hcca; DPRINTF("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n", OREAD4(sc, OHCI_REVISION), OREAD4(sc, OHCI_CONTROL), OREAD4(sc, OHCI_COMMAND_STATUS)); DPRINTF(" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n", OREAD4(sc, OHCI_INTERRUPT_STATUS), OREAD4(sc, OHCI_INTERRUPT_ENABLE), OREAD4(sc, OHCI_INTERRUPT_DISABLE)); DPRINTF(" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n", OREAD4(sc, OHCI_HCCA), OREAD4(sc, OHCI_PERIOD_CURRENT_ED), OREAD4(sc, OHCI_CONTROL_HEAD_ED)); DPRINTF(" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n", OREAD4(sc, OHCI_CONTROL_CURRENT_ED), OREAD4(sc, OHCI_BULK_HEAD_ED), OREAD4(sc, OHCI_BULK_CURRENT_ED)); DPRINTF(" done=0x%08x fmival=0x%08x fmrem=0x%08x\n", OREAD4(sc, OHCI_DONE_HEAD), OREAD4(sc, OHCI_FM_INTERVAL), OREAD4(sc, OHCI_FM_REMAINING)); DPRINTF(" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n", OREAD4(sc, OHCI_FM_NUMBER), OREAD4(sc, OHCI_PERIODIC_START), OREAD4(sc, OHCI_LS_THRESHOLD)); DPRINTF(" desca=0x%08x descb=0x%08x stat=0x%08x\n", OREAD4(sc, OHCI_RH_DESCRIPTOR_A), OREAD4(sc, OHCI_RH_DESCRIPTOR_B), OREAD4(sc, OHCI_RH_STATUS)); DPRINTF(" port1=0x%08x port2=0x%08x\n", OREAD4(sc, OHCI_RH_PORT_STATUS(1)), OREAD4(sc, OHCI_RH_PORT_STATUS(2))); hcca = ohci_get_hcca(sc); DPRINTF(" HCCA: frame_number=0x%04x done_head=0x%08x\n", le32toh(hcca->hcca_frame_number), le32toh(hcca->hcca_done_head)); } static void ohci_dump_tds(ohci_td_t *std) { for (; std; std = std->obj_next) { if (ohci_dump_td(std)) { break; } } } static uint8_t ohci_dump_td(ohci_td_t *std) { uint32_t td_flags; uint8_t temp; usb_pc_cpu_invalidate(std->page_cache); td_flags = le32toh(std->td_flags); temp = (std->td_next == 0); printf("TD(%p) at 0x%08x: %s%s%s%s%s delay=%d ec=%d " "cc=%d\ncbp=0x%08x next=0x%08x be=0x%08x\n", std, le32toh(std->td_self), (td_flags & OHCI_TD_R) ? "-R" : "", (td_flags & OHCI_TD_OUT) ? "-OUT" : "", (td_flags & OHCI_TD_IN) ? "-IN" : "", ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_1) ? "-TOG1" : "", ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_0) ? "-TOG0" : "", OHCI_TD_GET_DI(td_flags), OHCI_TD_GET_EC(td_flags), OHCI_TD_GET_CC(td_flags), le32toh(std->td_cbp), le32toh(std->td_next), le32toh(std->td_be)); return (temp); } static uint8_t ohci_dump_itd(ohci_itd_t *sitd) { uint32_t itd_flags; uint16_t i; uint8_t temp; usb_pc_cpu_invalidate(sitd->page_cache); itd_flags = le32toh(sitd->itd_flags); temp = (sitd->itd_next == 0); printf("ITD(%p) at 0x%08x: sf=%d di=%d fc=%d cc=%d\n" "bp0=0x%08x next=0x%08x be=0x%08x\n", sitd, le32toh(sitd->itd_self), OHCI_ITD_GET_SF(itd_flags), OHCI_ITD_GET_DI(itd_flags), OHCI_ITD_GET_FC(itd_flags), OHCI_ITD_GET_CC(itd_flags), le32toh(sitd->itd_bp0), le32toh(sitd->itd_next), le32toh(sitd->itd_be)); for (i = 0; i < OHCI_ITD_NOFFSET; i++) { printf("offs[%d]=0x%04x ", i, (uint32_t)le16toh(sitd->itd_offset[i])); } printf("\n"); return (temp); } static void ohci_dump_itds(ohci_itd_t *sitd) { for (; sitd; sitd = sitd->obj_next) { if (ohci_dump_itd(sitd)) { break; } } } static void ohci_dump_ed(ohci_ed_t *sed) { uint32_t ed_flags; uint32_t ed_headp; usb_pc_cpu_invalidate(sed->page_cache); ed_flags = le32toh(sed->ed_flags); ed_headp = le32toh(sed->ed_headp); printf("ED(%p) at 0x%08x: addr=%d endpt=%d maxp=%d flags=%s%s%s%s%s\n" "tailp=0x%08x headflags=%s%s headp=0x%08x nexted=0x%08x\n", sed, le32toh(sed->ed_self), OHCI_ED_GET_FA(ed_flags), OHCI_ED_GET_EN(ed_flags), OHCI_ED_GET_MAXP(ed_flags), (ed_flags & OHCI_ED_DIR_OUT) ? "-OUT" : "", (ed_flags & OHCI_ED_DIR_IN) ? "-IN" : "", (ed_flags & OHCI_ED_SPEED) ? "-LOWSPEED" : "", (ed_flags & OHCI_ED_SKIP) ? "-SKIP" : "", (ed_flags & OHCI_ED_FORMAT_ISO) ? "-ISO" : "", le32toh(sed->ed_tailp), (ed_headp & OHCI_HALTED) ? "-HALTED" : "", (ed_headp & OHCI_TOGGLECARRY) ? "-CARRY" : "", le32toh(sed->ed_headp), le32toh(sed->ed_next)); } #endif static void ohci_transfer_intr_enqueue(struct usb_xfer *xfer) { /* check for early completion */ if (ohci_check_transfer(xfer)) { return; } /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usbd_transfer_timeout_ms(xfer, &ohci_timeout, xfer->timeout); } } #define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last) static ohci_ed_t * _ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last) { DPRINTFN(11, "%p to %p\n", sed, last); if (sed->prev != NULL) { /* should not happen */ DPRINTFN(0, "ED already linked!\n"); return (last); } /* (sc->sc_bus.bus_mtx) must be locked */ sed->next = last->next; sed->ed_next = last->ed_next; sed->ed_tailp = 0; sed->prev = last; usb_pc_cpu_flush(sed->page_cache); /* * the last->next->prev is never followed: sed->next->prev = sed; */ last->next = sed; last->ed_next = sed->ed_self; usb_pc_cpu_flush(last->page_cache); return (sed); } #define OHCI_REMOVE_QH(sed,last) (last) = _ohci_remove_qh(sed,last) static ohci_ed_t * _ohci_remove_qh(ohci_ed_t *sed, ohci_ed_t *last) { DPRINTFN(11, "%p from %p\n", sed, last); /* (sc->sc_bus.bus_mtx) must be locked */ /* only remove if not removed from a queue */ if (sed->prev) { sed->prev->next = sed->next; sed->prev->ed_next = sed->ed_next; usb_pc_cpu_flush(sed->prev->page_cache); if (sed->next) { sed->next->prev = sed->prev; usb_pc_cpu_flush(sed->next->page_cache); } last = ((last == sed) ? sed->prev : last); sed->prev = 0; usb_pc_cpu_flush(sed->page_cache); } return (last); } static void ohci_isoc_done(struct usb_xfer *xfer) { uint8_t nframes; uint32_t *plen = xfer->frlengths; volatile uint16_t *olen; uint16_t len = 0; ohci_itd_t *td = xfer->td_transfer_first; while (1) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } #ifdef USB_DEBUG if (ohcidebug > 5) { DPRINTF("isoc TD\n"); ohci_dump_itd(td); } #endif usb_pc_cpu_invalidate(td->page_cache); nframes = td->frames; olen = &td->itd_offset[0]; if (nframes > 8) { nframes = 8; } while (nframes--) { len = le16toh(*olen); if ((len >> 12) == OHCI_CC_NOT_ACCESSED) { len = 0; } else { len &= ((1 << 12) - 1); } if (len > *plen) { len = 0;/* invalid length */ } *plen = len; plen++; olen++; } if (((void *)td) == xfer->td_transfer_last) { break; } td = td->obj_next; } xfer->aframes = xfer->nframes; ohci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); } #ifdef USB_DEBUG static const char *const ohci_cc_strs[] = { "NO_ERROR", "CRC", "BIT_STUFFING", "DATA_TOGGLE_MISMATCH", "STALL", "DEVICE_NOT_RESPONDING", "PID_CHECK_FAILURE", "UNEXPECTED_PID", "DATA_OVERRUN", "DATA_UNDERRUN", "BUFFER_OVERRUN", "BUFFER_UNDERRUN", "reserved", "reserved", "NOT_ACCESSED", "NOT_ACCESSED" }; #endif static usb_error_t ohci_non_isoc_done_sub(struct usb_xfer *xfer) { ohci_td_t *td; ohci_td_t *td_alt_next; uint32_t temp; uint32_t phy_start; uint32_t phy_end; uint32_t td_flags; uint16_t cc; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; td_flags = 0; if (xfer->aframes != xfer->nframes) { usbd_xfer_set_frame_len(xfer, xfer->aframes, 0); } while (1) { usb_pc_cpu_invalidate(td->page_cache); phy_start = le32toh(td->td_cbp); td_flags = le32toh(td->td_flags); cc = OHCI_TD_GET_CC(td_flags); if (phy_start) { /* * short transfer - compute the number of remaining * bytes in the hardware buffer: */ phy_end = le32toh(td->td_be); temp = (OHCI_PAGE(phy_start ^ phy_end) ? (OHCI_PAGE_SIZE + 1) : 0x0001); temp += OHCI_PAGE_OFFSET(phy_end); temp -= OHCI_PAGE_OFFSET(phy_start); if (temp > td->len) { /* guard against corruption */ cc = OHCI_CC_STALL; } else if (xfer->aframes != xfer->nframes) { /* * Sum up total transfer length * in "frlengths[]": */ xfer->frlengths[xfer->aframes] += td->len - temp; } } else { if (xfer->aframes != xfer->nframes) { /* transfer was complete */ xfer->frlengths[xfer->aframes] += td->len; } } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } /* Check transfer status */ if (cc) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (phy_start) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; DPRINTFN(16, "error cc=%d (%s)\n", cc, ohci_cc_strs[cc]); return ((cc == 0) ? USB_ERR_NORMAL_COMPLETION : (cc == OHCI_CC_STALL) ? USB_ERR_STALLED : USB_ERR_IOERROR); } static void ohci_non_isoc_done(struct usb_xfer *xfer) { usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); #ifdef USB_DEBUG if (ohcidebug > 10) { ohci_dump_tds(xfer->td_transfer_first); } #endif /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = ohci_non_isoc_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = ohci_non_isoc_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = ohci_non_isoc_done_sub(xfer); } done: ohci_device_done(xfer, err); } /*------------------------------------------------------------------------* * ohci_check_transfer_sub *------------------------------------------------------------------------*/ static void ohci_check_transfer_sub(struct usb_xfer *xfer) { ohci_td_t *td; ohci_ed_t *ed; uint32_t phy_start; uint32_t td_flags; uint32_t td_next; uint16_t cc; td = xfer->td_transfer_cache; while (1) { usb_pc_cpu_invalidate(td->page_cache); phy_start = le32toh(td->td_cbp); td_flags = le32toh(td->td_flags); td_next = le32toh(td->td_next); /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { /* the transfer is finished */ td = NULL; break; } /* Check transfer status */ cc = OHCI_TD_GET_CC(td_flags); if (cc) { /* the transfer is finished */ td = NULL; break; } /* * Check if we reached the last packet * or if there is a short packet: */ if (((td_next & (~0xF)) == OHCI_TD_NEXT_END) || phy_start) { /* follow alt next */ td = td->alt_next; break; } td = td->obj_next; } /* update transfer cache */ xfer->td_transfer_cache = td; if (td) { ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; ed->ed_headp = td->td_self; usb_pc_cpu_flush(ed->page_cache); DPRINTFN(13, "xfer=%p following alt next\n", xfer); /* * Make sure that the OHCI re-scans the schedule by * writing the BLF and CLF bits: */ if (xfer->xroot->udev->flags.self_suspended) { /* nothing to do */ } else if (xfer->endpoint->methods == &ohci_device_bulk_methods) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); } else if (xfer->endpoint->methods == &ohci_device_ctrl_methods) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); } } } /*------------------------------------------------------------------------* * ohci_check_transfer * * Return values: * 0: USB transfer is not finished * Else: USB transfer is finished *------------------------------------------------------------------------*/ static uint8_t ohci_check_transfer(struct usb_xfer *xfer) { ohci_ed_t *ed; uint32_t ed_headp; uint32_t ed_tailp; DPRINTFN(13, "xfer=%p checking transfer\n", xfer); ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; usb_pc_cpu_invalidate(ed->page_cache); ed_headp = le32toh(ed->ed_headp); ed_tailp = le32toh(ed->ed_tailp); if ((ed_headp & OHCI_HALTED) || (((ed_headp ^ ed_tailp) & (~0xF)) == 0)) { if (xfer->endpoint->methods == &ohci_device_isoc_methods) { /* isochronous transfer */ ohci_isoc_done(xfer); } else { if (xfer->flags_int.short_frames_ok) { ohci_check_transfer_sub(xfer); if (xfer->td_transfer_cache) { /* not finished yet */ return (0); } } /* store data-toggle */ if (ed_headp & OHCI_TOGGLECARRY) { xfer->endpoint->toggle_next = 1; } else { xfer->endpoint->toggle_next = 0; } /* non-isochronous transfer */ ohci_non_isoc_done(xfer); } return (1); } DPRINTFN(13, "xfer=%p is still active\n", xfer); return (0); } static void ohci_rhsc_enable(ohci_softc_t *sc) { DPRINTFN(5, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); sc->sc_eintrs |= OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); /* acknowledge any RHSC interrupt */ OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_RHSC); ohci_root_intr(sc); } static void ohci_interrupt_poll(ohci_softc_t *sc) { struct usb_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* * check if transfer is transferred */ if (ohci_check_transfer(xfer)) { /* queue has been modified */ goto repeat; } } } /*------------------------------------------------------------------------* * ohci_interrupt - OHCI interrupt handler * * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, * hence the interrupt handler will be setup before "sc->sc_bus.bdev" * is present ! *------------------------------------------------------------------------*/ void ohci_interrupt(ohci_softc_t *sc) { struct ohci_hcca *hcca; uint32_t status; uint32_t done; USB_BUS_LOCK(&sc->sc_bus); hcca = ohci_get_hcca(sc); DPRINTFN(16, "real interrupt\n"); #ifdef USB_DEBUG if (ohcidebug > 15) { ohci_dumpregs(sc); } #endif done = le32toh(hcca->hcca_done_head); /* * The LSb of done is used to inform the HC Driver that an interrupt * condition exists for both the Done list and for another event * recorded in HcInterruptStatus. On an interrupt from the HC, the * HC Driver checks the HccaDoneHead Value. If this value is 0, then * the interrupt was caused by other than the HccaDoneHead update * and the HcInterruptStatus register needs to be accessed to * determine that exact interrupt cause. If HccaDoneHead is nonzero, * then a Done list update interrupt is indicated and if the LSb of * done is nonzero, then an additional interrupt event is indicated * and HcInterruptStatus should be checked to determine its cause. */ if (done != 0) { status = 0; if (done & ~OHCI_DONE_INTRS) { status |= OHCI_WDH; } if (done & OHCI_DONE_INTRS) { status |= OREAD4(sc, OHCI_INTERRUPT_STATUS); } hcca->hcca_done_head = 0; usb_pc_cpu_flush(&sc->sc_hw.hcca_pc); } else { status = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH; } status &= ~OHCI_MIE; if (status == 0) { /* * nothing to be done (PCI shared * interrupt) */ goto done; } OWRITE4(sc, OHCI_INTERRUPT_STATUS, status); /* Acknowledge */ status &= sc->sc_eintrs; if (status == 0) { goto done; } if (status & (OHCI_SO | OHCI_RD | OHCI_UE | OHCI_RHSC)) { #if 0 if (status & OHCI_SO) { /* XXX do what */ } #endif if (status & OHCI_RD) { printf("%s: resume detect\n", __FUNCTION__); /* XXX process resume detect */ } if (status & OHCI_UE) { printf("%s: unrecoverable error, " "controller halted\n", __FUNCTION__); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); /* XXX what else */ } if (status & OHCI_RHSC) { /* * Disable RHSC interrupt for now, because it will be * on until the port has been reset. */ sc->sc_eintrs &= ~OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC); ohci_root_intr(sc); /* do not allow RHSC interrupts > 1 per second */ usb_callout_reset(&sc->sc_tmo_rhsc, hz, (void *)&ohci_rhsc_enable, sc); } } status &= ~(OHCI_RHSC | OHCI_WDH | OHCI_SO); if (status != 0) { /* Block unprocessed interrupts. XXX */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, status); sc->sc_eintrs &= ~status; printf("%s: blocking intrs 0x%x\n", __FUNCTION__, status); } /* poll all the USB transfers */ ohci_interrupt_poll(sc); done: USB_BUS_UNLOCK(&sc->sc_bus); } /* * called when a request does not complete */ static void ohci_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ ohci_device_done(xfer, USB_ERR_TIMEOUT); } static void ohci_do_poll(struct usb_bus *bus) { struct ohci_softc *sc = OHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); ohci_interrupt_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void ohci_setup_standard_chain_sub(struct ohci_std_temp *temp) { struct usb_page_search buf_res; ohci_td_t *td; ohci_td_t *td_next; ohci_td_t *td_alt_next; uint32_t buf_offset; uint32_t average; uint32_t len_old; uint8_t shortpkt_old; uint8_t precompute; td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; len_old = temp->len; precompute = 1; /* software is used to detect short incoming transfers */ if ((temp->td_flags & htole32(OHCI_TD_DP_MASK)) == htole32(OHCI_TD_IN)) { temp->td_flags |= htole32(OHCI_TD_R); } else { temp->td_flags &= ~htole32(OHCI_TD_R); } restart: td = temp->td; td_next = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) { break; } /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; average = 0; } else { average = temp->average; if (temp->len < average) { if (temp->len % temp->max_frame_size) { temp->shortpkt = 1; } average = temp->len; } } if (td_next == NULL) { panic("%s: out of OHCI transfer descriptors!", __FUNCTION__); } /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->td_flags = temp->td_flags; /* the next TD uses TOGGLE_CARRY */ temp->td_flags &= ~htole32(OHCI_TD_TOGGLE_MASK); if (average == 0) { /* * The buffer start and end phys addresses should be * 0x0 for a zero length packet. */ td->td_cbp = 0; td->td_be = 0; td->len = 0; } else { usbd_get_page(temp->pc, buf_offset, &buf_res); td->td_cbp = htole32(buf_res.physaddr); buf_offset += (average - 1); usbd_get_page(temp->pc, buf_offset, &buf_res); td->td_be = htole32(buf_res.physaddr); buf_offset++; td->len = average; /* update remaining length */ temp->len -= average; } if ((td_next == td_alt_next) && temp->setup_alt_next) { /* we need to receive these frames one by one ! */ td->td_flags &= htole32(~OHCI_TD_INTR_MASK); td->td_flags |= htole32(OHCI_TD_SET_DI(1)); td->td_next = htole32(OHCI_TD_NEXT_END); } else { if (td_next) { /* link the current TD with the next one */ td->td_next = td_next->td_self; } } td->alt_next = td_alt_next; usb_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* setup alt next pointer, if any */ if (temp->last_frame) { /* no alternate next */ td_alt_next = NULL; } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } temp->td = td; temp->td_next = td_next; } static void ohci_setup_standard_chain(struct usb_xfer *xfer, ohci_ed_t **ed_last) { struct ohci_std_temp temp; const struct usb_pipe_methods *methods; ohci_ed_t *ed; ohci_td_t *td; uint32_t ed_flags; uint32_t x; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); temp.average = xfer->max_hc_frame_size; temp.max_frame_size = xfer->max_frame_size; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; temp.td = NULL; temp.td_next = td; temp.last_frame = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok; methods = xfer->endpoint->methods; /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC | OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) { temp.last_frame = 1; temp.setup_alt_next = 0; } } ohci_setup_standard_chain_sub(&temp); /* * XXX assume that the setup message is * contained within one USB packet: */ xfer->endpoint->toggle_next = 1; } x = 1; } else { x = 0; } temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR); /* set data toggle */ if (xfer->endpoint->toggle_next) { temp.td_flags |= htole32(OHCI_TD_TOGGLE_1); } else { temp.td_flags |= htole32(OHCI_TD_TOGGLE_0); } /* set endpoint direction */ if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) { temp.td_flags |= htole32(OHCI_TD_IN); } else { temp.td_flags |= htole32(OHCI_TD_OUT); } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.pc = xfer->frbuffers + x; x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { /* no STATUS stage yet, DATA is last */ if (xfer->flags_int.control_act) { temp.last_frame = 1; temp.setup_alt_next = 0; } } else { temp.last_frame = 1; temp.setup_alt_next = 0; } } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; } else { /* regular data transfer */ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; } ohci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current endpoint * direction. */ /* set endpoint direction and data toggle */ if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) { temp.td_flags = htole32(OHCI_TD_OUT | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); } else { temp.td_flags = htole32(OHCI_TD_IN | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); } temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; temp.last_frame = 1; temp.setup_alt_next = 0; ohci_setup_standard_chain_sub(&temp); } td = temp.td; /* Ensure that last TD is terminating: */ td->td_next = htole32(OHCI_TD_NEXT_END); td->td_flags &= ~htole32(OHCI_TD_INTR_MASK); td->td_flags |= htole32(OHCI_TD_SET_DI(1)); usb_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ xfer->td_transfer_last = td; #ifdef USB_DEBUG if (ohcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->endpoint->toggle_next); ohci_dump_tds(xfer->td_transfer_first); } #endif ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; ed_flags = (OHCI_ED_SET_FA(xfer->address) | OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpointno)) | OHCI_ED_SET_MAXP(xfer->max_frame_size)); ed_flags |= (OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD); if (xfer->xroot->udev->speed == USB_SPEED_LOW) { ed_flags |= OHCI_ED_SPEED; } ed->ed_flags = htole32(ed_flags); td = xfer->td_transfer_first; ed->ed_headp = td->td_self; if (xfer->xroot->udev->flags.self_suspended == 0) { /* the append function will flush the endpoint descriptor */ OHCI_APPEND_QH(ed, *ed_last); if (methods == &ohci_device_bulk_methods) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); } if (methods == &ohci_device_ctrl_methods) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); } } else { usb_pc_cpu_flush(ed->page_cache); } } static void ohci_root_intr(ohci_softc_t *sc) { uint32_t hstatus; uint16_t i; uint16_t m; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* clear any old interrupt data */ memset(sc->sc_hub_idata, 0, sizeof(sc->sc_hub_idata)); hstatus = OREAD4(sc, OHCI_RH_STATUS); DPRINTF("sc=%p hstatus=0x%08x\n", sc, hstatus); /* set bits */ m = (sc->sc_noport + 1); if (m > (8 * sizeof(sc->sc_hub_idata))) { m = (8 * sizeof(sc->sc_hub_idata)); } for (i = 1; i < m; i++) { /* pick out CHANGE bits from the status register */ if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16) { sc->sc_hub_idata[i / 8] |= 1 << (i % 8); DPRINTF("port %d changed\n", i); } } uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void ohci_device_done(struct usb_xfer *xfer, usb_error_t error) { const struct usb_pipe_methods *methods = xfer->endpoint->methods; ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); ohci_ed_t *ed; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (ed) { usb_pc_cpu_invalidate(ed->page_cache); } if (methods == &ohci_device_bulk_methods) { OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last); } if (methods == &ohci_device_ctrl_methods) { OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last); } if (methods == &ohci_device_intr_methods) { OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); } if (methods == &ohci_device_isoc_methods) { OHCI_REMOVE_QH(ed, sc->sc_isoc_p_last); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * ohci bulk support *------------------------------------------------------------------------*/ static void ohci_device_bulk_open(struct usb_xfer *xfer) { return; } static void ohci_device_bulk_close(struct usb_xfer *xfer) { ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_bulk_enter(struct usb_xfer *xfer) { return; } static void ohci_device_bulk_start(struct usb_xfer *xfer) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_bulk_p_last); /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ohci_device_bulk_methods = { .open = ohci_device_bulk_open, .close = ohci_device_bulk_close, .enter = ohci_device_bulk_enter, .start = ohci_device_bulk_start, }; /*------------------------------------------------------------------------* * ohci control support *------------------------------------------------------------------------*/ static void ohci_device_ctrl_open(struct usb_xfer *xfer) { return; } static void ohci_device_ctrl_close(struct usb_xfer *xfer) { ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_ctrl_enter(struct usb_xfer *xfer) { return; } static void ohci_device_ctrl_start(struct usb_xfer *xfer) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_ctrl_p_last); /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ohci_device_ctrl_methods = { .open = ohci_device_ctrl_open, .close = ohci_device_ctrl_close, .enter = ohci_device_ctrl_enter, .start = ohci_device_ctrl_start, }; /*------------------------------------------------------------------------* * ohci interrupt support *------------------------------------------------------------------------*/ static void ohci_device_intr_open(struct usb_xfer *xfer) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); uint16_t best; uint16_t bit; uint16_t x; best = 0; bit = OHCI_NO_EDS / 2; while (bit) { if (xfer->interval >= bit) { x = bit; best = bit; while (x & bit) { if (sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(3, "best=%d interval=%d\n", best, xfer->interval); } static void ohci_device_intr_close(struct usb_xfer *xfer) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); sc->sc_intr_stat[xfer->qh_pos]--; ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_intr_enter(struct usb_xfer *xfer) { return; } static void ohci_device_intr_start(struct usb_xfer *xfer) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ohci_device_intr_methods = { .open = ohci_device_intr_open, .close = ohci_device_intr_close, .enter = ohci_device_intr_enter, .start = ohci_device_intr_start, }; /*------------------------------------------------------------------------* * ohci isochronous support *------------------------------------------------------------------------*/ static void ohci_device_isoc_open(struct usb_xfer *xfer) { return; } static void ohci_device_isoc_close(struct usb_xfer *xfer) { /**/ ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_isoc_enter(struct usb_xfer *xfer) { struct usb_page_search buf_res; ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); struct ohci_hcca *hcca; uint32_t buf_offset; uint32_t nframes; uint32_t ed_flags; uint32_t *plen; uint16_t itd_offset[OHCI_ITD_NOFFSET]; uint16_t length; uint8_t ncur; ohci_itd_t *td; ohci_itd_t *td_last = NULL; ohci_ed_t *ed; hcca = ohci_get_hcca(sc); nframes = le32toh(hcca->hcca_frame_number); DPRINTFN(6, "xfer=%p isoc_next=%u nframes=%u hcca_fn=%u\n", xfer, xfer->endpoint->isoc_next, xfer->nframes, nframes); if ((xfer->endpoint->is_synced == 0) || (((nframes - xfer->endpoint->isoc_next) & 0xFFFF) < xfer->nframes) || (((xfer->endpoint->isoc_next - nframes) & 0xFFFF) >= 128)) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->endpoint->isoc_next = (nframes + 3) & 0xFFFF; xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ buf_offset = ((xfer->endpoint->isoc_next - nframes) & 0xFFFF); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = (usb_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + xfer->nframes); /* get the real number of frames */ nframes = xfer->nframes; buf_offset = 0; plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; ncur = 0; length = 0; while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } itd_offset[ncur] = length; buf_offset += *plen; length += *plen; plen++; ncur++; if ( /* check if the ITD is full */ (ncur == OHCI_ITD_NOFFSET) || /* check if we have put more than 4K into the ITD */ (length & 0xF000) || /* check if it is the last frame */ (nframes == 0)) { /* fill current ITD */ td->itd_flags = htole32( OHCI_ITD_NOCC | OHCI_ITD_SET_SF(xfer->endpoint->isoc_next) | OHCI_ITD_NOINTR | OHCI_ITD_SET_FC(ncur)); td->frames = ncur; xfer->endpoint->isoc_next += ncur; if (length == 0) { /* all zero */ td->itd_bp0 = 0; td->itd_be = ~0; while (ncur--) { td->itd_offset[ncur] = htole16(OHCI_ITD_MK_OFFS(0)); } } else { usbd_get_page(xfer->frbuffers, buf_offset - length, &buf_res); length = OHCI_PAGE_MASK(buf_res.physaddr); buf_res.physaddr = OHCI_PAGE(buf_res.physaddr); td->itd_bp0 = htole32(buf_res.physaddr); usbd_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); td->itd_be = htole32(buf_res.physaddr); while (ncur--) { itd_offset[ncur] += length; itd_offset[ncur] = OHCI_ITD_MK_OFFS(itd_offset[ncur]); td->itd_offset[ncur] = htole16(itd_offset[ncur]); } } ncur = 0; length = 0; td_last = td; td = td->obj_next; if (td) { /* link the last TD with the next one */ td_last->itd_next = td->itd_self; } usb_pc_cpu_flush(td_last->page_cache); } } /* update the last TD */ td_last->itd_flags &= ~htole32(OHCI_ITD_NOINTR); td_last->itd_flags |= htole32(OHCI_ITD_SET_DI(0)); td_last->itd_next = 0; usb_pc_cpu_flush(td_last->page_cache); xfer->td_transfer_last = td_last; #ifdef USB_DEBUG if (ohcidebug > 8) { DPRINTF("data before transfer:\n"); ohci_dump_itds(xfer->td_transfer_first); } #endif ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) ed_flags = (OHCI_ED_DIR_IN | OHCI_ED_FORMAT_ISO); else ed_flags = (OHCI_ED_DIR_OUT | OHCI_ED_FORMAT_ISO); ed_flags |= (OHCI_ED_SET_FA(xfer->address) | OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpointno)) | OHCI_ED_SET_MAXP(xfer->max_frame_size)); if (xfer->xroot->udev->speed == USB_SPEED_LOW) { ed_flags |= OHCI_ED_SPEED; } ed->ed_flags = htole32(ed_flags); td = xfer->td_transfer_first; ed->ed_headp = td->itd_self; /* isochronous transfers are not affected by suspend / resume */ /* the append function will flush the endpoint descriptor */ OHCI_APPEND_QH(ed, sc->sc_isoc_p_last); } static void ohci_device_isoc_start(struct usb_xfer *xfer) { /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods ohci_device_isoc_methods = { .open = ohci_device_isoc_open, .close = ohci_device_isoc_close, .enter = ohci_device_isoc_enter, .start = ohci_device_isoc_start, }; /*------------------------------------------------------------------------* * ohci root control support *------------------------------------------------------------------------* * Simulate a hardware hub by handling all the necessary requests. *------------------------------------------------------------------------*/ static const struct usb_device_descriptor ohci_devd = { sizeof(struct usb_device_descriptor), UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0}, {0}, {0x00, 0x01}, /* device id */ - 1, 2, 0, /* string indicies */ + 1, 2, 0, /* string indexes */ 1 /* # of configurations */ }; static const struct ohci_config_desc ohci_confd = { .confd = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(ohci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, /* max power */ }, .ifcd = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = 0, }, .endpd = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | OHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 32,/* max packet (255 ports) */ .bInterval = 255, }, }; static const struct usb_hub_descriptor ohci_hubd = { .bDescLength = 0, /* dynamic length */ .bDescriptorType = UDESC_HUB, }; static usb_error_t ohci_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { ohci_softc_t *sc = OHCI_BUS2SC(udev->bus); const void *ptr; const char *str_ptr; uint32_t port; uint32_t v; uint16_t len; uint16_t value; uint16_t index; uint8_t l; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_desc.temp; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", req->bmRequestType, req->bRequest, UGETW(req->wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(ohci_devd); ptr = (const void *)&ohci_devd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(ohci_confd); ptr = (const void *)&ohci_confd; break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ str_ptr = "\001"; break; case 1: /* Vendor */ str_ptr = sc->sc_vendor; break; case 2: /* Product */ str_ptr = "OHCI root HUB"; break; default: str_ptr = ""; break; } len = usb_make_str_desc( sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), str_ptr); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= OHCI_MAX_DEVICES) { err = USB_ERR_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if ((value != 0) && (value != 1)) { err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(9, "UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = OHCI_RH_PORT_STATUS(index); switch (value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR); break; case UHF_PORT_POWER: /* Yes, writing to the LOW_SPEED bit clears power. */ OWRITE4(sc, port, UPS_LOW_SPEED); break; case UHF_C_PORT_CONNECTION: OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16); break; case UHF_C_PORT_ENABLE: OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16); break; case UHF_C_PORT_SUSPEND: OWRITE4(sc, port, UPS_C_SUSPEND << 16); break; case UHF_C_PORT_OVER_CURRENT: OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16); break; case UHF_C_PORT_RESET: OWRITE4(sc, port, UPS_C_PORT_RESET << 16); break; default: err = USB_ERR_IOERROR; goto done; } switch (value) { case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* enable RHSC interrupt if condition is cleared. */ if ((OREAD4(sc, port) >> 16) == 0) ohci_rhsc_enable(sc); break; default: break; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); sc->sc_hub_desc.hubd = ohci_hubd; sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL) /* XXX overcurrent */ ); sc->sc_hub_desc.hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v); v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B); for (l = 0; l < sc->sc_noport; l++) { if (v & 1) { sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] |= (1 << (l % 8)); } v >>= 1; } sc->sc_hub_desc.hubd.bDescLength = 8 + ((sc->sc_noport + 7) / 8); len = sc->sc_hub_desc.hubd.bDescLength; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "get port status i=%d\n", index); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } v = OREAD4(sc, OHCI_RH_PORT_STATUS(index)); DPRINTFN(9, "port status=0x%04x\n", v); v &= ~UPS_PORT_MODE_DEVICE; /* force host mode */ USETW(sc->sc_hub_desc.ps.wPortStatus, v); USETW(sc->sc_hub_desc.ps.wPortChange, v >> 16); len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = OHCI_RH_PORT_STATUS(index); switch (value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_PORT_ENABLED); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_SUSPEND); break; case UHF_PORT_RESET: DPRINTFN(6, "reset port %d\n", index); OWRITE4(sc, port, UPS_RESET); for (v = 0;; v++) { if (v < 12) { usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(usb_port_root_reset_delay)); if ((OREAD4(sc, port) & UPS_RESET) == 0) { break; } } else { err = USB_ERR_TIMEOUT; goto done; } } DPRINTFN(9, "ohci port %d reset, status = 0x%04x\n", index, OREAD4(sc, port)); break; case UHF_PORT_POWER: DPRINTFN(3, "set port power %d\n", index); OWRITE4(sc, port, UPS_PORT_POWER); break; default: err = USB_ERR_IOERROR; goto done; } break; default: err = USB_ERR_IOERROR; goto done; } done: *plength = len; *pptr = ptr; return (err); } static void ohci_xfer_setup(struct usb_setup_params *parm) { struct usb_page_search page_info; struct usb_page_cache *pc; ohci_softc_t *sc; struct usb_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t nitd; uint32_t nqh; uint32_t n; sc = OHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = OHCI_PAGE_SIZE; /* * calculate ntd and nqh */ if (parm->methods == &ohci_device_ctrl_methods) { xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nitd = 0; ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_hc_frame_size)); nqh = 1; } else if (parm->methods == &ohci_device_bulk_methods) { xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nitd = 0; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); nqh = 1; } else if (parm->methods == &ohci_device_intr_methods) { xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nitd = 0; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); nqh = 1; } else if (parm->methods == &ohci_device_isoc_methods) { xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); nitd = ((xfer->max_data_length / OHCI_PAGE_SIZE) + howmany(xfer->nframes, OHCI_ITD_NOFFSET) + 1 /* EXTRA */ ); ntd = 0; nqh = 1; } else { usbd_transfer_setup_sub(parm); nitd = 0; ntd = 0; nqh = 0; } alloc_dma_set: if (parm->err) { return; } last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ohci_td_t), OHCI_TD_ALIGN, ntd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != ntd; n++) { ohci_td_t *td; usbd_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->td_self = htole32(page_info.physaddr); td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb_pc_cpu_flush(pc + n); } } if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ohci_itd_t), OHCI_ITD_ALIGN, nitd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nitd; n++) { ohci_itd_t *itd; usbd_get_page(pc + n, 0, &page_info); itd = page_info.buffer; /* init TD */ itd->itd_self = htole32(page_info.physaddr); itd->obj_next = last_obj; itd->page_cache = pc + n; last_obj = itd; usb_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(ohci_ed_t), OHCI_ED_ALIGN, nqh)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqh; n++) { ohci_ed_t *ed; usbd_get_page(pc + n, 0, &page_info); ed = page_info.buffer; /* init QH */ ed->ed_self = htole32(page_info.physaddr); ed->obj_next = last_obj; ed->page_cache = pc + n; last_obj = ed; usb_pc_cpu_flush(pc + n); } } xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static void ohci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { ohci_softc_t *sc = OHCI_BUS2SC(udev->bus); DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode, sc->sc_addr); if (udev->device_index != sc->sc_addr) { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ep->methods = &ohci_device_ctrl_methods; break; case UE_INTERRUPT: ep->methods = &ohci_device_intr_methods; break; case UE_ISOCHRONOUS: if (udev->speed == USB_SPEED_FULL) { ep->methods = &ohci_device_isoc_methods; } break; case UE_BULK: ep->methods = &ohci_device_bulk_methods; break; default: /* do nothing */ break; } } } static void ohci_xfer_unsetup(struct usb_xfer *xfer) { return; } static void ohci_get_dma_delay(struct usb_device *udev, uint32_t *pus) { /* * Wait until hardware has finished any possible use of the * transfer descriptor(s) and QH */ *pus = (1125); /* microseconds */ } static void ohci_device_resume(struct usb_device *udev) { struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); struct usb_xfer *xfer; const struct usb_pipe_methods *methods; ohci_ed_t *ed; DPRINTF("\n"); USB_BUS_LOCK(udev->bus); TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (xfer->xroot->udev == udev) { methods = xfer->endpoint->methods; ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (methods == &ohci_device_bulk_methods) { OHCI_APPEND_QH(ed, sc->sc_bulk_p_last); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); } if (methods == &ohci_device_ctrl_methods) { OHCI_APPEND_QH(ed, sc->sc_ctrl_p_last); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); } if (methods == &ohci_device_intr_methods) { OHCI_APPEND_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); } } } USB_BUS_UNLOCK(udev->bus); return; } static void ohci_device_suspend(struct usb_device *udev) { struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); struct usb_xfer *xfer; const struct usb_pipe_methods *methods; ohci_ed_t *ed; DPRINTF("\n"); USB_BUS_LOCK(udev->bus); TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (xfer->xroot->udev == udev) { methods = xfer->endpoint->methods; ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (methods == &ohci_device_bulk_methods) { OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last); } if (methods == &ohci_device_ctrl_methods) { OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last); } if (methods == &ohci_device_intr_methods) { OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); } } } USB_BUS_UNLOCK(udev->bus); return; } static void ohci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct ohci_softc *sc = OHCI_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: case USB_HW_POWER_SHUTDOWN: ohci_suspend(sc); break; case USB_HW_POWER_RESUME: ohci_resume(sc); break; default: break; } } static void ohci_set_hw_power(struct usb_bus *bus) { struct ohci_softc *sc = OHCI_BUS2SC(bus); uint32_t temp; uint32_t flags; DPRINTF("\n"); USB_BUS_LOCK(bus); flags = bus->hw_power_state; temp = OREAD4(sc, OHCI_CONTROL); temp &= ~(OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE); if (flags & USB_HW_POWER_CONTROL) temp |= OHCI_CLE; if (flags & USB_HW_POWER_BULK) temp |= OHCI_BLE; if (flags & USB_HW_POWER_INTERRUPT) temp |= OHCI_PLE; if (flags & USB_HW_POWER_ISOC) temp |= OHCI_IE | OHCI_PLE; OWRITE4(sc, OHCI_CONTROL, temp); USB_BUS_UNLOCK(bus); return; } static const struct usb_bus_methods ohci_bus_methods = { .endpoint_init = ohci_ep_init, .xfer_setup = ohci_xfer_setup, .xfer_unsetup = ohci_xfer_unsetup, .get_dma_delay = ohci_get_dma_delay, .device_resume = ohci_device_resume, .device_suspend = ohci_device_suspend, .set_hw_power = ohci_set_hw_power, .set_hw_power_sleep = ohci_set_hw_power_sleep, .roothub_exec = ohci_roothub_exec, .xfer_poll = ohci_do_poll, }; Index: head/sys/dev/usb/controller/uhci.c =================================================================== --- head/sys/dev/usb/controller/uhci.c (revision 298931) +++ head/sys/dev/usb/controller/uhci.c (revision 298932) @@ -1,3232 +1,3232 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * USB Universal Host Controller driver. * Handles e.g. PIIX3 and PIIX4. * * UHCI spec: http://developer.intel.com/design/USB/UHCI11D.htm * USB spec: http://www.usb.org/developers/docs/usbspec.zip * PIIXn spec: ftp://download.intel.com/design/intarch/datashts/29055002.pdf * ftp://download.intel.com/design/intarch/datashts/29056201.pdf */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR uhcidebug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include #define alt_next next #define UHCI_BUS2SC(bus) \ ((uhci_softc_t *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((uhci_softc_t *)0)->sc_bus)))) #ifdef USB_DEBUG static int uhcidebug = 0; static int uhcinoloop = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci"); SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RWTUN, &uhcidebug, 0, "uhci debug level"); SYSCTL_INT(_hw_usb_uhci, OID_AUTO, loop, CTLFLAG_RWTUN, &uhcinoloop, 0, "uhci noloop"); static void uhci_dumpregs(uhci_softc_t *sc); static void uhci_dump_tds(uhci_td_t *td); #endif #define UBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define UWRITE1(sc, r, x) \ do { UBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ } while (/*CONSTCOND*/0) #define UWRITE2(sc, r, x) \ do { UBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ } while (/*CONSTCOND*/0) #define UWRITE4(sc, r, x) \ do { UBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ } while (/*CONSTCOND*/0) #define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd) #define UHCISTS(sc) UREAD2(sc, UHCI_STS) #define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */ #define UHCI_INTR_ENDPT 1 struct uhci_mem_layout { struct usb_page_search buf_res; struct usb_page_search fix_res; struct usb_page_cache *buf_pc; struct usb_page_cache *fix_pc; uint32_t buf_offset; uint16_t max_frame_size; }; struct uhci_std_temp { struct uhci_mem_layout ml; uhci_td_t *td; uhci_td_t *td_next; uint32_t average; uint32_t td_status; uint32_t td_token; uint32_t len; uint16_t max_frame_size; uint8_t shortpkt; uint8_t setup_alt_next; uint8_t last_frame; }; static const struct usb_bus_methods uhci_bus_methods; static const struct usb_pipe_methods uhci_device_bulk_methods; static const struct usb_pipe_methods uhci_device_ctrl_methods; static const struct usb_pipe_methods uhci_device_intr_methods; static const struct usb_pipe_methods uhci_device_isoc_methods; static uint8_t uhci_restart(uhci_softc_t *sc); static void uhci_do_poll(struct usb_bus *); static void uhci_device_done(struct usb_xfer *, usb_error_t); static void uhci_transfer_intr_enqueue(struct usb_xfer *); static void uhci_timeout(void *); static uint8_t uhci_check_transfer(struct usb_xfer *); static void uhci_root_intr(uhci_softc_t *sc); void uhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) { struct uhci_softc *sc = UHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, sizeof(uint32_t) * UHCI_FRAMELIST_COUNT, UHCI_FRAMELIST_ALIGN); cb(bus, &sc->sc_hw.ls_ctl_start_pc, &sc->sc_hw.ls_ctl_start_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.fs_ctl_start_pc, &sc->sc_hw.fs_ctl_start_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.last_qh_pc, &sc->sc_hw.last_qh_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.last_td_pc, &sc->sc_hw.last_td_pg, sizeof(uhci_td_t), UHCI_TD_ALIGN); for (i = 0; i != UHCI_VFRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.isoc_start_pc + i, sc->sc_hw.isoc_start_pg + i, sizeof(uhci_td_t), UHCI_TD_ALIGN); } for (i = 0; i != UHCI_IFRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, sizeof(uhci_qh_t), UHCI_QH_ALIGN); } } static void uhci_mem_layout_init(struct uhci_mem_layout *ml, struct usb_xfer *xfer) { ml->buf_pc = xfer->frbuffers + 0; ml->fix_pc = xfer->buf_fixup; ml->buf_offset = 0; ml->max_frame_size = xfer->max_frame_size; } static void uhci_mem_layout_fixup(struct uhci_mem_layout *ml, struct uhci_td *td) { usbd_get_page(ml->buf_pc, ml->buf_offset, &ml->buf_res); if (ml->buf_res.length < td->len) { /* need to do a fixup */ usbd_get_page(ml->fix_pc, 0, &ml->fix_res); td->td_buffer = htole32(ml->fix_res.physaddr); /* * The UHCI driver cannot handle * page crossings, so a fixup is * needed: * * +----+----+ - - - * | YYY|Y | * +----+----+ - - - * \ \ * \ \ * +----+ * |YYYY| (fixup) * +----+ */ if ((td->td_token & htole32(UHCI_TD_PID)) == htole32(UHCI_TD_PID_IN)) { td->fix_pc = ml->fix_pc; usb_pc_cpu_invalidate(ml->fix_pc); } else { td->fix_pc = NULL; /* copy data to fixup location */ usbd_copy_out(ml->buf_pc, ml->buf_offset, ml->fix_res.buffer, td->len); usb_pc_cpu_flush(ml->fix_pc); } /* prepare next fixup */ ml->fix_pc++; } else { td->td_buffer = htole32(ml->buf_res.physaddr); td->fix_pc = NULL; } /* prepare next data location */ ml->buf_offset += td->len; } /* * Return values: * 0: Success * Else: Failure */ static uint8_t uhci_restart(uhci_softc_t *sc) { struct usb_page_search buf_res; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS) { DPRINTFN(2, "Already started\n"); return (0); } DPRINTFN(2, "Restarting\n"); usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); /* Reload fresh base address */ UWRITE4(sc, UHCI_FLBASEADDR, buf_res.physaddr); /* * Assume 64 byte packets at frame end and start HC controller: */ UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); /* wait 10 milliseconds */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); /* check that controller has started */ if (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) { DPRINTFN(2, "Failed\n"); return (1); } return (0); } void uhci_reset(uhci_softc_t *sc) { uint16_t n; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTF("resetting the HC\n"); /* disable interrupts */ UWRITE2(sc, UHCI_INTR, 0); /* global reset */ UHCICMD(sc, UHCI_CMD_GRESET); /* wait */ usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(USB_BUS_RESET_DELAY)); /* terminate all transfers */ UHCICMD(sc, UHCI_CMD_HCRESET); /* the reset bit goes low when the controller is done */ n = UHCI_RESET_TIMEOUT; while (n--) { /* wait one millisecond */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET)) { goto done_1; } } device_printf(sc->sc_bus.bdev, "controller did not reset\n"); done_1: n = 10; while (n--) { /* wait one millisecond */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); /* check if HC is stopped */ if (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) { goto done_2; } } device_printf(sc->sc_bus.bdev, "controller did not stop\n"); done_2: /* reset frame number */ UWRITE2(sc, UHCI_FRNUM, 0); /* set default SOF value */ UWRITE1(sc, UHCI_SOF, 0x40); USB_BUS_UNLOCK(&sc->sc_bus); /* stop root interrupt */ usb_callout_drain(&sc->sc_root_intr); USB_BUS_LOCK(&sc->sc_bus); } static void uhci_start(uhci_softc_t *sc) { USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "enabling\n"); /* enable interrupts */ UWRITE2(sc, UHCI_INTR, (UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | UHCI_INTR_IOCE | UHCI_INTR_SPIE)); if (uhci_restart(sc)) { device_printf(sc->sc_bus.bdev, "cannot start HC controller\n"); } /* start root interrupt */ uhci_root_intr(sc); } static struct uhci_qh * uhci_init_qh(struct usb_page_cache *pc) { struct usb_page_search buf_res; struct uhci_qh *qh; usbd_get_page(pc, 0, &buf_res); qh = buf_res.buffer; qh->qh_self = htole32(buf_res.physaddr) | htole32(UHCI_PTR_QH); qh->page_cache = pc; return (qh); } static struct uhci_td * uhci_init_td(struct usb_page_cache *pc) { struct usb_page_search buf_res; struct uhci_td *td; usbd_get_page(pc, 0, &buf_res); td = buf_res.buffer; td->td_self = htole32(buf_res.physaddr) | htole32(UHCI_PTR_TD); td->page_cache = pc; return (td); } usb_error_t uhci_init(uhci_softc_t *sc) { uint16_t bit; uint16_t x; uint16_t y; DPRINTF("start\n"); usb_callout_init_mtx(&sc->sc_root_intr, &sc->sc_bus.bus_mtx, 0); #ifdef USB_DEBUG if (uhcidebug > 2) { uhci_dumpregs(sc); } #endif /* * Setup QH's */ sc->sc_ls_ctl_p_last = uhci_init_qh(&sc->sc_hw.ls_ctl_start_pc); sc->sc_fs_ctl_p_last = uhci_init_qh(&sc->sc_hw.fs_ctl_start_pc); sc->sc_bulk_p_last = uhci_init_qh(&sc->sc_hw.bulk_start_pc); #if 0 sc->sc_reclaim_qh_p = sc->sc_fs_ctl_p_last; #else /* setup reclaim looping point */ sc->sc_reclaim_qh_p = sc->sc_bulk_p_last; #endif sc->sc_last_qh_p = uhci_init_qh(&sc->sc_hw.last_qh_pc); sc->sc_last_td_p = uhci_init_td(&sc->sc_hw.last_td_pc); for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) { sc->sc_isoc_p_last[x] = uhci_init_td(sc->sc_hw.isoc_start_pc + x); } for (x = 0; x != UHCI_IFRAMELIST_COUNT; x++) { sc->sc_intr_p_last[x] = uhci_init_qh(sc->sc_hw.intr_start_pc + x); } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = UHCI_IFRAMELIST_COUNT / 2; while (bit) { x = bit; while (x & bit) { uhci_qh_t *qh_x; uhci_qh_t *qh_y; y = (x ^ bit) | (bit / 2); /* * the next QH has half the poll interval */ qh_x = sc->sc_intr_p_last[x]; qh_y = sc->sc_intr_p_last[y]; qh_x->h_next = NULL; qh_x->qh_h_next = qh_y->qh_self; qh_x->e_next = NULL; qh_x->qh_e_next = htole32(UHCI_PTR_T); x++; } bit >>= 1; } if (1) { uhci_qh_t *qh_ls; uhci_qh_t *qh_intr; qh_ls = sc->sc_ls_ctl_p_last; qh_intr = sc->sc_intr_p_last[0]; /* start QH for interrupt traffic */ qh_intr->h_next = qh_ls; qh_intr->qh_h_next = qh_ls->qh_self; qh_intr->e_next = 0; qh_intr->qh_e_next = htole32(UHCI_PTR_T); } for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) { uhci_td_t *td_x; uhci_qh_t *qh_intr; td_x = sc->sc_isoc_p_last[x]; qh_intr = sc->sc_intr_p_last[x | (UHCI_IFRAMELIST_COUNT / 2)]; /* start TD for isochronous traffic */ td_x->next = NULL; td_x->td_next = qh_intr->qh_self; td_x->td_status = htole32(UHCI_TD_IOS); td_x->td_token = htole32(0); td_x->td_buffer = htole32(0); } if (1) { uhci_qh_t *qh_ls; uhci_qh_t *qh_fs; qh_ls = sc->sc_ls_ctl_p_last; qh_fs = sc->sc_fs_ctl_p_last; /* start QH where low speed control traffic will be queued */ qh_ls->h_next = qh_fs; qh_ls->qh_h_next = qh_fs->qh_self; qh_ls->e_next = 0; qh_ls->qh_e_next = htole32(UHCI_PTR_T); } if (1) { uhci_qh_t *qh_ctl; uhci_qh_t *qh_blk; uhci_qh_t *qh_lst; uhci_td_t *td_lst; qh_ctl = sc->sc_fs_ctl_p_last; qh_blk = sc->sc_bulk_p_last; /* start QH where full speed control traffic will be queued */ qh_ctl->h_next = qh_blk; qh_ctl->qh_h_next = qh_blk->qh_self; qh_ctl->e_next = 0; qh_ctl->qh_e_next = htole32(UHCI_PTR_T); qh_lst = sc->sc_last_qh_p; /* start QH where bulk traffic will be queued */ qh_blk->h_next = qh_lst; qh_blk->qh_h_next = qh_lst->qh_self; qh_blk->e_next = 0; qh_blk->qh_e_next = htole32(UHCI_PTR_T); td_lst = sc->sc_last_td_p; /* end QH which is used for looping the QHs */ qh_lst->h_next = 0; qh_lst->qh_h_next = htole32(UHCI_PTR_T); /* end of QH chain */ qh_lst->e_next = td_lst; qh_lst->qh_e_next = td_lst->td_self; /* * end TD which hangs from the last QH, to avoid a bug in the PIIX * that makes it run berserk otherwise */ td_lst->next = 0; td_lst->td_next = htole32(UHCI_PTR_T); td_lst->td_status = htole32(0); /* inactive */ td_lst->td_token = htole32(0); td_lst->td_buffer = htole32(0); } if (1) { struct usb_page_search buf_res; uint32_t *pframes; usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); pframes = buf_res.buffer; /* * Setup UHCI framelist * * Execution order: * * pframes -> full speed isochronous -> interrupt QH's -> low * speed control -> full speed control -> bulk transfers * */ for (x = 0; x != UHCI_FRAMELIST_COUNT; x++) { pframes[x] = sc->sc_isoc_p_last[x % UHCI_VFRAMELIST_COUNT]->td_self; } } /* flush all cache into memory */ usb_bus_mem_flush_all(&sc->sc_bus, &uhci_iterate_hw_softc); /* set up the bus struct */ sc->sc_bus.methods = &uhci_bus_methods; USB_BUS_LOCK(&sc->sc_bus); /* reset the controller */ uhci_reset(sc); /* start the controller */ uhci_start(sc); USB_BUS_UNLOCK(&sc->sc_bus); /* catch lost interrupts */ uhci_do_poll(&sc->sc_bus); return (0); } static void uhci_suspend(uhci_softc_t *sc) { #ifdef USB_DEBUG if (uhcidebug > 2) { uhci_dumpregs(sc); } #endif USB_BUS_LOCK(&sc->sc_bus); /* stop the controller */ uhci_reset(sc); /* enter global suspend */ UHCICMD(sc, UHCI_CMD_EGSM); USB_BUS_UNLOCK(&sc->sc_bus); } static void uhci_resume(uhci_softc_t *sc) { USB_BUS_LOCK(&sc->sc_bus); /* reset the controller */ uhci_reset(sc); /* force global resume */ UHCICMD(sc, UHCI_CMD_FGR); /* and start traffic again */ uhci_start(sc); USB_BUS_UNLOCK(&sc->sc_bus); #ifdef USB_DEBUG if (uhcidebug > 2) uhci_dumpregs(sc); #endif /* catch lost interrupts */ uhci_do_poll(&sc->sc_bus); } #ifdef USB_DEBUG static void uhci_dumpregs(uhci_softc_t *sc) { DPRINTFN(0, "%s regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, " "flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n", device_get_nameunit(sc->sc_bus.bdev), UREAD2(sc, UHCI_CMD), UREAD2(sc, UHCI_STS), UREAD2(sc, UHCI_INTR), UREAD2(sc, UHCI_FRNUM), UREAD4(sc, UHCI_FLBASEADDR), UREAD1(sc, UHCI_SOF), UREAD2(sc, UHCI_PORTSC1), UREAD2(sc, UHCI_PORTSC2)); } static uint8_t uhci_dump_td(uhci_td_t *p) { uint32_t td_next; uint32_t td_status; uint32_t td_token; uint8_t temp; usb_pc_cpu_invalidate(p->page_cache); td_next = le32toh(p->td_next); td_status = le32toh(p->td_status); td_token = le32toh(p->td_token); /* * Check whether the link pointer in this TD marks the link pointer * as end of queue: */ temp = ((td_next & UHCI_PTR_T) || (td_next == 0)); printf("TD(%p) at 0x%08x = link=0x%08x status=0x%08x " "token=0x%08x buffer=0x%08x\n", p, le32toh(p->td_self), td_next, td_status, td_token, le32toh(p->td_buffer)); printf("TD(%p) td_next=%s%s%s td_status=%s%s%s%s%s%s%s%s%s%s%s, errcnt=%d, actlen=%d pid=%02x," "addr=%d,endpt=%d,D=%d,maxlen=%d\n", p, (td_next & 1) ? "-T" : "", (td_next & 2) ? "-Q" : "", (td_next & 4) ? "-VF" : "", (td_status & UHCI_TD_BITSTUFF) ? "-BITSTUFF" : "", (td_status & UHCI_TD_CRCTO) ? "-CRCTO" : "", (td_status & UHCI_TD_NAK) ? "-NAK" : "", (td_status & UHCI_TD_BABBLE) ? "-BABBLE" : "", (td_status & UHCI_TD_DBUFFER) ? "-DBUFFER" : "", (td_status & UHCI_TD_STALLED) ? "-STALLED" : "", (td_status & UHCI_TD_ACTIVE) ? "-ACTIVE" : "", (td_status & UHCI_TD_IOC) ? "-IOC" : "", (td_status & UHCI_TD_IOS) ? "-IOS" : "", (td_status & UHCI_TD_LS) ? "-LS" : "", (td_status & UHCI_TD_SPD) ? "-SPD" : "", UHCI_TD_GET_ERRCNT(td_status), UHCI_TD_GET_ACTLEN(td_status), UHCI_TD_GET_PID(td_token), UHCI_TD_GET_DEVADDR(td_token), UHCI_TD_GET_ENDPT(td_token), UHCI_TD_GET_DT(td_token), UHCI_TD_GET_MAXLEN(td_token)); return (temp); } static uint8_t uhci_dump_qh(uhci_qh_t *sqh) { uint8_t temp; uint32_t qh_h_next; uint32_t qh_e_next; usb_pc_cpu_invalidate(sqh->page_cache); qh_h_next = le32toh(sqh->qh_h_next); qh_e_next = le32toh(sqh->qh_e_next); DPRINTFN(0, "QH(%p) at 0x%08x: h_next=0x%08x e_next=0x%08x\n", sqh, le32toh(sqh->qh_self), qh_h_next, qh_e_next); temp = ((((sqh->h_next != NULL) && !(qh_h_next & UHCI_PTR_T)) ? 1 : 0) | (((sqh->e_next != NULL) && !(qh_e_next & UHCI_PTR_T)) ? 2 : 0)); return (temp); } static void uhci_dump_all(uhci_softc_t *sc) { uhci_dumpregs(sc); uhci_dump_qh(sc->sc_ls_ctl_p_last); uhci_dump_qh(sc->sc_fs_ctl_p_last); uhci_dump_qh(sc->sc_bulk_p_last); uhci_dump_qh(sc->sc_last_qh_p); } static void uhci_dump_tds(uhci_td_t *td) { for (; td != NULL; td = td->obj_next) { if (uhci_dump_td(td)) { break; } } } #endif /* * Let the last QH loop back to the full speed control transfer QH. * This is what intel calls "bandwidth reclamation" and improves * USB performance a lot for some devices. * If we are already looping, just count it. */ static void uhci_add_loop(uhci_softc_t *sc) { struct uhci_qh *qh_lst; struct uhci_qh *qh_rec; #ifdef USB_DEBUG if (uhcinoloop) { return; } #endif if (++(sc->sc_loops) == 1) { DPRINTFN(6, "add\n"); qh_lst = sc->sc_last_qh_p; qh_rec = sc->sc_reclaim_qh_p; /* NOTE: we don't loop back the soft pointer */ qh_lst->qh_h_next = qh_rec->qh_self; usb_pc_cpu_flush(qh_lst->page_cache); } } static void uhci_rem_loop(uhci_softc_t *sc) { struct uhci_qh *qh_lst; #ifdef USB_DEBUG if (uhcinoloop) { return; } #endif if (--(sc->sc_loops) == 0) { DPRINTFN(6, "remove\n"); qh_lst = sc->sc_last_qh_p; qh_lst->qh_h_next = htole32(UHCI_PTR_T); usb_pc_cpu_flush(qh_lst->page_cache); } } static void uhci_transfer_intr_enqueue(struct usb_xfer *xfer) { /* check for early completion */ if (uhci_check_transfer(xfer)) { return; } /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usbd_transfer_timeout_ms(xfer, &uhci_timeout, xfer->timeout); } } #define UHCI_APPEND_TD(std,last) (last) = _uhci_append_td(std,last) static uhci_td_t * _uhci_append_td(uhci_td_t *std, uhci_td_t *last) { DPRINTFN(11, "%p to %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->td_next = last->td_next; std->prev = last; usb_pc_cpu_flush(std->page_cache); /* * the last->next->prev is never followed: std->next->prev = std; */ last->next = std; last->td_next = std->td_self; usb_pc_cpu_flush(last->page_cache); return (std); } #define UHCI_APPEND_QH(sqh,last) (last) = _uhci_append_qh(sqh,last) static uhci_qh_t * _uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last) { DPRINTFN(11, "%p to %p\n", sqh, last); if (sqh->h_prev != NULL) { /* should not happen */ DPRINTFN(0, "QH already linked!\n"); return (last); } /* (sc->sc_bus.mtx) must be locked */ sqh->h_next = last->h_next; sqh->qh_h_next = last->qh_h_next; sqh->h_prev = last; usb_pc_cpu_flush(sqh->page_cache); /* * The "last->h_next->h_prev" is never followed: * * "sqh->h_next->h_prev" = sqh; */ last->h_next = sqh; last->qh_h_next = sqh->qh_self; usb_pc_cpu_flush(last->page_cache); return (sqh); } /**/ #define UHCI_REMOVE_TD(std,last) (last) = _uhci_remove_td(std,last) static uhci_td_t * _uhci_remove_td(uhci_td_t *std, uhci_td_t *last) { DPRINTFN(11, "%p from %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->td_next = std->td_next; usb_pc_cpu_flush(std->prev->page_cache); if (std->next) { std->next->prev = std->prev; usb_pc_cpu_flush(std->next->page_cache); } return ((last == std) ? std->prev : last); } #define UHCI_REMOVE_QH(sqh,last) (last) = _uhci_remove_qh(sqh,last) static uhci_qh_t * _uhci_remove_qh(uhci_qh_t *sqh, uhci_qh_t *last) { DPRINTFN(11, "%p from %p\n", sqh, last); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if (sqh->h_prev) { sqh->h_prev->h_next = sqh->h_next; sqh->h_prev->qh_h_next = sqh->qh_h_next; usb_pc_cpu_flush(sqh->h_prev->page_cache); if (sqh->h_next) { sqh->h_next->h_prev = sqh->h_prev; usb_pc_cpu_flush(sqh->h_next->page_cache); } last = ((last == sqh) ? sqh->h_prev : last); sqh->h_prev = 0; usb_pc_cpu_flush(sqh->page_cache); } return (last); } static void uhci_isoc_done(uhci_softc_t *sc, struct usb_xfer *xfer) { struct usb_page_search res; uint32_t nframes = xfer->nframes; uint32_t status; uint32_t offset = 0; uint32_t *plen = xfer->frlengths; uint16_t len = 0; uhci_td_t *td = xfer->td_transfer_first; uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos]; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); /* sync any DMA memory before doing fixups */ usb_bdma_post_sync(xfer); while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_p_last[0]; } #ifdef USB_DEBUG if (uhcidebug > 5) { DPRINTF("isoc TD\n"); uhci_dump_td(td); } #endif usb_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); len = UHCI_TD_GET_ACTLEN(status); if (len > *plen) { len = *plen; } if (td->fix_pc) { usbd_get_page(td->fix_pc, 0, &res); /* copy data from fixup location to real location */ usb_pc_cpu_invalidate(td->fix_pc); usbd_copy_in(xfer->frbuffers, offset, res.buffer, len); } offset += *plen; *plen = len; /* remove TD from schedule */ UHCI_REMOVE_TD(td, *pp_last); pp_last++; plen++; td = td->obj_next; } xfer->aframes = xfer->nframes; } static usb_error_t uhci_non_isoc_done_sub(struct usb_xfer *xfer) { struct usb_page_search res; uhci_td_t *td; uhci_td_t *td_alt_next; uint32_t status; uint32_t token; uint16_t len; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; if (xfer->aframes != xfer->nframes) { usbd_xfer_set_frame_len(xfer, xfer->aframes, 0); } while (1) { usb_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); token = le32toh(td->td_token); /* * Verify the status and add * up the actual length: */ len = UHCI_TD_GET_ACTLEN(status); if (len > td->len) { /* should not happen */ DPRINTF("Invalid status length, " "0x%04x/0x%04x bytes\n", len, td->len); status |= UHCI_TD_STALLED; } else if ((xfer->aframes != xfer->nframes) && (len > 0)) { if (td->fix_pc) { usbd_get_page(td->fix_pc, 0, &res); /* * copy data from fixup location to real * location */ usb_pc_cpu_invalidate(td->fix_pc); usbd_copy_in(xfer->frbuffers + xfer->aframes, xfer->frlengths[xfer->aframes], res.buffer, len); } /* update actual length */ xfer->frlengths[xfer->aframes] += len; } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } if (status & UHCI_TD_STALLED) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (len != td->len) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; /* update data toggle */ xfer->endpoint->toggle_next = (token & UHCI_TD_SET_DT(1)) ? 0 : 1; #ifdef USB_DEBUG if (status & UHCI_TD_ERROR) { DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x " "status=%s%s%s%s%s%s%s%s%s%s%s\n", xfer->address, xfer->endpointno, xfer->aframes, (status & UHCI_TD_BITSTUFF) ? "[BITSTUFF]" : "", (status & UHCI_TD_CRCTO) ? "[CRCTO]" : "", (status & UHCI_TD_NAK) ? "[NAK]" : "", (status & UHCI_TD_BABBLE) ? "[BABBLE]" : "", (status & UHCI_TD_DBUFFER) ? "[DBUFFER]" : "", (status & UHCI_TD_STALLED) ? "[STALLED]" : "", (status & UHCI_TD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]", (status & UHCI_TD_IOC) ? "[IOC]" : "", (status & UHCI_TD_IOS) ? "[IOS]" : "", (status & UHCI_TD_LS) ? "[LS]" : "", (status & UHCI_TD_SPD) ? "[SPD]" : ""); } #endif if (status & UHCI_TD_STALLED) { /* try to separate I/O errors from STALL */ if (UHCI_TD_GET_ERRCNT(status) == 0) return (USB_ERR_IOERROR); return (USB_ERR_STALLED); } return (USB_ERR_NORMAL_COMPLETION); } static void uhci_non_isoc_done(struct usb_xfer *xfer) { usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); #ifdef USB_DEBUG if (uhcidebug > 10) { uhci_dump_tds(xfer->td_transfer_first); } #endif /* sync any DMA memory before doing fixups */ usb_bdma_post_sync(xfer); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = uhci_non_isoc_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = uhci_non_isoc_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = uhci_non_isoc_done_sub(xfer); } done: uhci_device_done(xfer, err); } /*------------------------------------------------------------------------* * uhci_check_transfer_sub * * The main purpose of this function is to update the data-toggle * in case it is wrong. *------------------------------------------------------------------------*/ static void uhci_check_transfer_sub(struct usb_xfer *xfer) { uhci_qh_t *qh; uhci_td_t *td; uhci_td_t *td_alt_next; uint32_t td_token; uint32_t td_self; td = xfer->td_transfer_cache; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; td_token = td->obj_next->td_token; td = td->alt_next; xfer->td_transfer_cache = td; td_self = td->td_self; td_alt_next = td->alt_next; if (xfer->flags_int.control_xfr) goto skip; /* don't touch the DT value! */ if (!((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1)))) goto skip; /* data toggle has correct value */ /* * The data toggle is wrong and we need to toggle it ! */ while (1) { td->td_token ^= htole32(UHCI_TD_SET_DT(1)); usb_pc_cpu_flush(td->page_cache); if (td == xfer->td_transfer_last) { /* last transfer */ break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* next frame */ break; } } skip: /* update the QH */ qh->qh_e_next = td_self; usb_pc_cpu_flush(qh->page_cache); DPRINTFN(13, "xfer=%p following alt next\n", xfer); } /*------------------------------------------------------------------------* * uhci_check_transfer * * Return values: * 0: USB transfer is not finished * Else: USB transfer is finished *------------------------------------------------------------------------*/ static uint8_t uhci_check_transfer(struct usb_xfer *xfer) { uint32_t status; uint32_t token; uhci_td_t *td; DPRINTFN(16, "xfer=%p checking transfer\n", xfer); if (xfer->endpoint->methods == &uhci_device_isoc_methods) { /* isochronous transfer */ td = xfer->td_transfer_last; usb_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); /* check also if the first is complete */ td = xfer->td_transfer_first; usb_pc_cpu_invalidate(td->page_cache); status |= le32toh(td->td_status); if (!(status & UHCI_TD_ACTIVE)) { uhci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } } else { /* non-isochronous transfer */ /* * check whether there is an error somewhere * in the middle, or whether there was a short * packet (SPD and not ACTIVE) */ td = xfer->td_transfer_cache; while (1) { usb_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); token = le32toh(td->td_token); /* * if there is an active TD the transfer isn't done */ if (status & UHCI_TD_ACTIVE) { /* update cache */ xfer->td_transfer_cache = td; goto done; } /* * last transfer descriptor makes the transfer done */ if (((void *)td) == xfer->td_transfer_last) { break; } /* * any kind of error makes the transfer done */ if (status & UHCI_TD_STALLED) { break; } /* * check if we reached the last packet * or if there is a short packet: */ if ((td->td_next == htole32(UHCI_PTR_T)) || (UHCI_TD_GET_ACTLEN(status) < td->len)) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { /* update cache */ xfer->td_transfer_cache = td; uhci_check_transfer_sub(xfer); goto done; } } /* transfer is done */ break; } td = td->obj_next; } uhci_non_isoc_done(xfer); goto transferred; } done: DPRINTFN(13, "xfer=%p is still active\n", xfer); return (0); transferred: return (1); } static void uhci_interrupt_poll(uhci_softc_t *sc) { struct usb_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* * check if transfer is transferred */ if (uhci_check_transfer(xfer)) { /* queue has been modified */ goto repeat; } } } /*------------------------------------------------------------------------* * uhci_interrupt - UHCI interrupt handler * * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, * hence the interrupt handler will be setup before "sc->sc_bus.bdev" * is present ! *------------------------------------------------------------------------*/ void uhci_interrupt(uhci_softc_t *sc) { uint32_t status; USB_BUS_LOCK(&sc->sc_bus); DPRINTFN(16, "real interrupt\n"); #ifdef USB_DEBUG if (uhcidebug > 15) { uhci_dumpregs(sc); } #endif status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; if (status == 0) { /* the interrupt was not for us */ goto done; } if (status & (UHCI_STS_RD | UHCI_STS_HSE | UHCI_STS_HCPE | UHCI_STS_HCH)) { if (status & UHCI_STS_RD) { #ifdef USB_DEBUG printf("%s: resume detect\n", __FUNCTION__); #endif } if (status & UHCI_STS_HSE) { printf("%s: host system error\n", __FUNCTION__); } if (status & UHCI_STS_HCPE) { printf("%s: host controller process error\n", __FUNCTION__); } if (status & UHCI_STS_HCH) { /* no acknowledge needed */ DPRINTF("%s: host controller halted\n", __FUNCTION__); #ifdef USB_DEBUG if (uhcidebug > 0) { uhci_dump_all(sc); } #endif } } /* get acknowledge bits */ status &= (UHCI_STS_USBINT | UHCI_STS_USBEI | UHCI_STS_RD | UHCI_STS_HSE | UHCI_STS_HCPE | UHCI_STS_HCH); if (status == 0) { /* nothing to acknowledge */ goto done; } /* acknowledge interrupts */ UWRITE2(sc, UHCI_STS, status); /* poll all the USB transfers */ uhci_interrupt_poll(sc); done: USB_BUS_UNLOCK(&sc->sc_bus); } /* * called when a request does not complete */ static void uhci_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ uhci_device_done(xfer, USB_ERR_TIMEOUT); } static void uhci_do_poll(struct usb_bus *bus) { struct uhci_softc *sc = UHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); uhci_interrupt_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void uhci_setup_standard_chain_sub(struct uhci_std_temp *temp) { uhci_td_t *td; uhci_td_t *td_next; uhci_td_t *td_alt_next; uint32_t average; uint32_t len_old; uint8_t shortpkt_old; uint8_t precompute; td_alt_next = NULL; shortpkt_old = temp->shortpkt; len_old = temp->len; precompute = 1; /* software is used to detect short incoming transfers */ if ((temp->td_token & htole32(UHCI_TD_PID)) == htole32(UHCI_TD_PID_IN)) { temp->td_status |= htole32(UHCI_TD_SPD); } else { temp->td_status &= ~htole32(UHCI_TD_SPD); } temp->ml.buf_offset = 0; restart: temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0)); temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->average)); td = temp->td; td_next = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) { break; } /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(0)); average = 0; } else { average = temp->average; if (temp->len < average) { temp->shortpkt = 1; temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0)); temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->len)); average = temp->len; } } if (td_next == NULL) { panic("%s: out of UHCI transfer descriptors!", __FUNCTION__); } /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->td_status = temp->td_status; td->td_token = temp->td_token; /* update data toggle */ temp->td_token ^= htole32(UHCI_TD_SET_DT(1)); if (average == 0) { td->len = 0; td->td_buffer = 0; td->fix_pc = NULL; } else { /* update remaining length */ temp->len -= average; td->len = average; /* fill out buffer pointer and do fixup, if any */ uhci_mem_layout_fixup(&temp->ml, td); } td->alt_next = td_alt_next; if ((td_next == td_alt_next) && temp->setup_alt_next) { /* we need to receive these frames one by one ! */ td->td_status |= htole32(UHCI_TD_IOC); td->td_next = htole32(UHCI_PTR_T); } else { if (td_next) { /* link the current TD with the next one */ td->td_next = td_next->td_self; } } usb_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* setup alt next pointer, if any */ if (temp->last_frame) { td_alt_next = NULL; } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } temp->td = td; temp->td_next = td_next; } static uhci_td_t * uhci_setup_standard_chain(struct usb_xfer *xfer) { struct uhci_std_temp temp; uhci_td_t *td; uint32_t x; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); temp.average = xfer->max_frame_size; temp.max_frame_size = xfer->max_frame_size; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; temp.td = NULL; temp.td_next = td; temp.last_frame = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok; uhci_mem_layout_init(&temp.ml, xfer); temp.td_status = htole32(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE)); if (xfer->xroot->udev->speed == USB_SPEED_LOW) { temp.td_status |= htole32(UHCI_TD_LS); } temp.td_token = htole32(UHCI_TD_SET_ENDPT(xfer->endpointno) | UHCI_TD_SET_DEVADDR(xfer->address)); if (xfer->endpoint->toggle_next) { /* DATA1 is next */ temp.td_token |= htole32(UHCI_TD_SET_DT(1)); } /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF)); temp.td_token |= htole32(UHCI_TD_PID_SETUP | UHCI_TD_SET_DT(0)); temp.len = xfer->frlengths[0]; temp.ml.buf_pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) { temp.last_frame = 1; temp.setup_alt_next = 0; } } uhci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.ml.buf_pc = xfer->frbuffers + x; x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { /* no STATUS stage yet, DATA is last */ if (xfer->flags_int.control_act) { temp.last_frame = 1; temp.setup_alt_next = 0; } } else { temp.last_frame = 1; temp.setup_alt_next = 0; } } /* * Keep previous data toggle, * device address and endpoint number: */ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF) | UHCI_TD_SET_DT(1)); if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; } else { /* regular data transfer */ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; } /* set endpoint direction */ temp.td_token |= (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) ? htole32(UHCI_TD_PID_IN) : htole32(UHCI_TD_PID_OUT); uhci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * send a DATA1 message and reverse the current endpoint * direction */ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF) | UHCI_TD_SET_DT(1)); temp.td_token |= (UE_GET_DIR(xfer->endpointno) == UE_DIR_OUT) ? htole32(UHCI_TD_PID_IN | UHCI_TD_SET_DT(1)) : htole32(UHCI_TD_PID_OUT | UHCI_TD_SET_DT(1)); temp.len = 0; temp.ml.buf_pc = NULL; temp.shortpkt = 0; temp.last_frame = 1; temp.setup_alt_next = 0; uhci_setup_standard_chain_sub(&temp); } td = temp.td; /* Ensure that last TD is terminating: */ td->td_next = htole32(UHCI_PTR_T); /* set interrupt bit */ td->td_status |= htole32(UHCI_TD_IOC); usb_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ xfer->td_transfer_last = td; #ifdef USB_DEBUG if (uhcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->endpoint->toggle_next); uhci_dump_tds(xfer->td_transfer_first); } #endif return (xfer->td_transfer_first); } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void uhci_device_done(struct usb_xfer *xfer, usb_error_t error) { const struct usb_pipe_methods *methods = xfer->endpoint->methods; uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); uhci_qh_t *qh; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (qh) { usb_pc_cpu_invalidate(qh->page_cache); } if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; uhci_rem_loop(sc); } if (methods == &uhci_device_bulk_methods) { UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last); } if (methods == &uhci_device_ctrl_methods) { if (xfer->xroot->udev->speed == USB_SPEED_LOW) { UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last); } else { UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last); } } if (methods == &uhci_device_intr_methods) { UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); } /* * Only finish isochronous transfers once * which will update "xfer->frlengths". */ if (xfer->td_transfer_first && xfer->td_transfer_last) { if (methods == &uhci_device_isoc_methods) { uhci_isoc_done(sc, xfer); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * uhci bulk support *------------------------------------------------------------------------*/ static void uhci_device_bulk_open(struct usb_xfer *xfer) { return; } static void uhci_device_bulk_close(struct usb_xfer *xfer) { uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_bulk_enter(struct usb_xfer *xfer) { return; } static void uhci_device_bulk_start(struct usb_xfer *xfer) { uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); uhci_td_t *td; uhci_qh_t *qh; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; qh->e_next = td; qh->qh_e_next = td->td_self; if (xfer->xroot->udev->flags.self_suspended == 0) { UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); uhci_add_loop(sc); xfer->flags_int.bandwidth_reclaimed = 1; } else { usb_pc_cpu_flush(qh->page_cache); } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods uhci_device_bulk_methods = { .open = uhci_device_bulk_open, .close = uhci_device_bulk_close, .enter = uhci_device_bulk_enter, .start = uhci_device_bulk_start, }; /*------------------------------------------------------------------------* * uhci control support *------------------------------------------------------------------------*/ static void uhci_device_ctrl_open(struct usb_xfer *xfer) { return; } static void uhci_device_ctrl_close(struct usb_xfer *xfer) { uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_ctrl_enter(struct usb_xfer *xfer) { return; } static void uhci_device_ctrl_start(struct usb_xfer *xfer) { uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); uhci_qh_t *qh; uhci_td_t *td; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; qh->e_next = td; qh->qh_e_next = td->td_self; /* * NOTE: some devices choke on bandwidth- reclamation for control * transfers */ if (xfer->xroot->udev->flags.self_suspended == 0) { if (xfer->xroot->udev->speed == USB_SPEED_LOW) { UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); } else { UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); } } else { usb_pc_cpu_flush(qh->page_cache); } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods uhci_device_ctrl_methods = { .open = uhci_device_ctrl_open, .close = uhci_device_ctrl_close, .enter = uhci_device_ctrl_enter, .start = uhci_device_ctrl_start, }; /*------------------------------------------------------------------------* * uhci interrupt support *------------------------------------------------------------------------*/ static void uhci_device_intr_open(struct usb_xfer *xfer) { uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); uint16_t best; uint16_t bit; uint16_t x; best = 0; bit = UHCI_IFRAMELIST_COUNT / 2; while (bit) { if (xfer->interval >= bit) { x = bit; best = bit; while (x & bit) { if (sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(3, "best=%d interval=%d\n", best, xfer->interval); } static void uhci_device_intr_close(struct usb_xfer *xfer) { uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); sc->sc_intr_stat[xfer->qh_pos]--; uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_intr_enter(struct usb_xfer *xfer) { return; } static void uhci_device_intr_start(struct usb_xfer *xfer) { uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); uhci_qh_t *qh; uhci_td_t *td; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; qh->e_next = td; qh->qh_e_next = td->td_self; if (xfer->xroot->udev->flags.self_suspended == 0) { /* enter QHs into the controller data structures */ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); } else { usb_pc_cpu_flush(qh->page_cache); } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods uhci_device_intr_methods = { .open = uhci_device_intr_open, .close = uhci_device_intr_close, .enter = uhci_device_intr_enter, .start = uhci_device_intr_start, }; /*------------------------------------------------------------------------* * uhci isochronous support *------------------------------------------------------------------------*/ static void uhci_device_isoc_open(struct usb_xfer *xfer) { uhci_td_t *td; uint32_t td_token; uint8_t ds; td_token = (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) ? UHCI_TD_IN(0, xfer->endpointno, xfer->address, 0) : UHCI_TD_OUT(0, xfer->endpointno, xfer->address, 0); td_token = htole32(td_token); /* initialize all TD's */ for (ds = 0; ds != 2; ds++) { for (td = xfer->td_start[ds]; td; td = td->obj_next) { /* mark TD as inactive */ td->td_status = htole32(UHCI_TD_IOS); td->td_token = td_token; usb_pc_cpu_flush(td->page_cache); } } } static void uhci_device_isoc_close(struct usb_xfer *xfer) { uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_isoc_enter(struct usb_xfer *xfer) { struct uhci_mem_layout ml; uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); uint32_t nframes; uint32_t temp; uint32_t *plen; #ifdef USB_DEBUG uint8_t once = 1; #endif uhci_td_t *td; uhci_td_t *td_last = NULL; uhci_td_t **pp_last; DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->endpoint->isoc_next, xfer->nframes); nframes = UREAD2(sc, UHCI_FRNUM); temp = (nframes - xfer->endpoint->isoc_next) & (UHCI_VFRAMELIST_COUNT - 1); if ((xfer->endpoint->is_synced == 0) || (temp < xfer->nframes)) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->endpoint->isoc_next = (nframes + 3) & (UHCI_VFRAMELIST_COUNT - 1); xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->endpoint->isoc_next - nframes) & (UHCI_VFRAMELIST_COUNT - 1); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb_isoc_time_expand(&sc->sc_bus, nframes) + temp + xfer->nframes; /* get the real number of frames */ nframes = xfer->nframes; uhci_mem_layout_init(&ml, xfer); plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_p_last[xfer->endpoint->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->endpoint->isoc_next; while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_p_last[0]; } if (*plen > xfer->max_frame_size) { #ifdef USB_DEBUG if (once) { once = 0; printf("%s: frame length(%d) exceeds %d " "bytes (frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } /* reuse td_token from last transfer */ td->td_token &= htole32(~UHCI_TD_MAXLEN_MASK); td->td_token |= htole32(UHCI_TD_SET_MAXLEN(*plen)); td->len = *plen; if (td->len == 0) { /* * Do not call "uhci_mem_layout_fixup()" when the * length is zero! */ td->td_buffer = 0; td->fix_pc = NULL; } else { /* fill out buffer pointer and do fixup, if any */ uhci_mem_layout_fixup(&ml, td); } /* update status */ if (nframes == 0) { td->td_status = htole32 (UHCI_TD_ZERO_ACTLEN (UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS | UHCI_TD_IOC)); } else { td->td_status = htole32 (UHCI_TD_ZERO_ACTLEN (UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS)); } usb_pc_cpu_flush(td->page_cache); #ifdef USB_DEBUG if (uhcidebug > 5) { DPRINTF("TD %d\n", nframes); uhci_dump_td(td); } #endif /* insert TD into schedule */ UHCI_APPEND_TD(td, *pp_last); pp_last++; plen++; td_last = td; td = td->obj_next; } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_p_last[0]) & (UHCI_VFRAMELIST_COUNT - 1); } static void uhci_device_isoc_start(struct usb_xfer *xfer) { /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } static const struct usb_pipe_methods uhci_device_isoc_methods = { .open = uhci_device_isoc_open, .close = uhci_device_isoc_close, .enter = uhci_device_isoc_enter, .start = uhci_device_isoc_start, }; /*------------------------------------------------------------------------* * uhci root control support *------------------------------------------------------------------------* * Simulate a hardware hub by handling all the necessary requests. *------------------------------------------------------------------------*/ static const struct usb_device_descriptor uhci_devd = { sizeof(struct usb_device_descriptor), UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0}, {0}, {0x00, 0x01}, /* device id */ - 1, 2, 0, /* string indicies */ + 1, 2, 0, /* string indexes */ 1 /* # of configurations */ }; static const struct uhci_config_desc uhci_confd = { .confd = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(uhci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0 /* max power */ }, .ifcd = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_FSHUB, }, .endpd = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | UHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, /* max packet (63 ports) */ .bInterval = 255, }, }; static const struct usb_hub_descriptor_min uhci_hubd_piix = { .bDescLength = sizeof(uhci_hubd_piix), .bDescriptorType = UDESC_HUB, .bNbrPorts = 2, .wHubCharacteristics = {UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0}, .bPwrOn2PwrGood = 50, }; /* * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also * enables the port, and also states that SET_FEATURE(PORT_ENABLE) * should not be used by the USB subsystem. As we cannot issue a * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port * will be enabled as part of the reset. * * On the VT83C572, the port cannot be successfully enabled until the * outstanding "port enable change" and "connection status change" * events have been reset. */ static usb_error_t uhci_portreset(uhci_softc_t *sc, uint16_t index) { uint16_t port; uint16_t x; uint8_t lim; if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else return (USB_ERR_IOERROR); /* * Before we do anything, turn on SOF messages on the USB * BUS. Some USB devices do not cope without them! */ uhci_restart(sc); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PR); usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(usb_port_root_reset_delay)); DPRINTFN(4, "uhci port %d reset, status0 = 0x%04x\n", index, UREAD2(sc, port)); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); mtx_unlock(&sc->sc_bus.bus_mtx); /* * This delay needs to be exactly 100us, else some USB devices * fail to attach! */ DELAY(100); mtx_lock(&sc->sc_bus.bus_mtx); DPRINTFN(4, "uhci port %d reset, status1 = 0x%04x\n", index, UREAD2(sc, port)); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); for (lim = 0; lim < 12; lim++) { usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); x = UREAD2(sc, port); DPRINTFN(4, "uhci port %d iteration %u, status = 0x%04x\n", index, lim, x); if (!(x & UHCI_PORTSC_CCS)) { /* * No device is connected (or was disconnected * during reset). Consider the port reset. * The delay must be long enough to ensure on * the initial iteration that the device * connection will have been registered. 50ms * appears to be sufficient, but 20ms is not. */ DPRINTFN(4, "uhci port %d loop %u, device detached\n", index, lim); goto done; } if (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) { /* * Port enabled changed and/or connection * status changed were set. Reset either or * both raised flags (by writing a 1 to that * bit), and wait again for state to settle. */ UWRITE2(sc, port, URWMASK(x) | (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC))); continue; } if (x & UHCI_PORTSC_PE) { /* port is enabled */ goto done; } UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE); } DPRINTFN(2, "uhci port %d reset timed out\n", index); return (USB_ERR_TIMEOUT); done: DPRINTFN(4, "uhci port %d reset, status2 = 0x%04x\n", index, UREAD2(sc, port)); sc->sc_isreset = 1; return (USB_ERR_NORMAL_COMPLETION); } static usb_error_t uhci_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { uhci_softc_t *sc = UHCI_BUS2SC(udev->bus); const void *ptr; const char *str_ptr; uint16_t x; uint16_t port; uint16_t value; uint16_t index; uint16_t status; uint16_t change; uint16_t len; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_desc.temp; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", req->bmRequestType, req->bRequest, UGETW(req->wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(uhci_devd); ptr = (const void *)&uhci_devd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(uhci_confd); ptr = (const void *)&uhci_confd; break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ str_ptr = "\001"; break; case 1: /* Vendor */ str_ptr = sc->sc_vendor; break; case 2: /* Product */ str_ptr = "UHCI root HUB"; break; default: str_ptr = ""; break; } len = usb_make_str_desc (sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), str_ptr); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= UHCI_MAX_DEVICES) { err = USB_ERR_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if ((value != 0) && (value != 1)) { err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(4, "UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value); if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { err = USB_ERR_IOERROR; goto done; } switch (value) { case UHF_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~(UHCI_PORTSC_SUSP)); break; case UHF_PORT_RESET: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); break; case UHF_C_PORT_CONNECTION: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_CSC); break; case UHF_C_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_POEDC); break; case UHF_C_PORT_OVER_CURRENT: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_OCIC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; err = USB_ERR_NORMAL_COMPLETION; goto done; case UHF_C_PORT_SUSPEND: sc->sc_isresumed &= ~(1 << index); break; case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_POWER: case UHF_PORT_LOW_SPEED: default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { err = USB_ERR_IOERROR; goto done; } len = 1; sc->sc_hub_desc.temp[0] = ((UREAD2(sc, port) & UHCI_PORTSC_LS) >> UHCI_PORTSC_LS_SHIFT); break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(uhci_hubd_piix); ptr = (const void *)&uhci_hubd_piix; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { err = USB_ERR_IOERROR; goto done; } x = UREAD2(sc, port); status = change = 0; if (x & UHCI_PORTSC_CCS) status |= UPS_CURRENT_CONNECT_STATUS; if (x & UHCI_PORTSC_CSC) change |= UPS_C_CONNECT_STATUS; if (x & UHCI_PORTSC_PE) status |= UPS_PORT_ENABLED; if (x & UHCI_PORTSC_POEDC) change |= UPS_C_PORT_ENABLED; if (x & UHCI_PORTSC_OCI) status |= UPS_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_OCIC) change |= UPS_C_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_LSDA) status |= UPS_LOW_SPEED; if ((x & UHCI_PORTSC_PE) && (x & UHCI_PORTSC_RD)) { /* need to do a write back */ UWRITE2(sc, port, URWMASK(x)); /* wait 20ms for resume sequence to complete */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); /* clear suspend and resume detect */ UWRITE2(sc, port, URWMASK(x) & ~(UHCI_PORTSC_RD | UHCI_PORTSC_SUSP)); /* wait a little bit */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 500); sc->sc_isresumed |= (1 << index); } else if (x & UHCI_PORTSC_SUSP) { status |= UPS_SUSPEND; } status |= UPS_PORT_POWER; if (sc->sc_isresumed & (1 << index)) change |= UPS_C_SUSPEND; if (sc->sc_isreset) change |= UPS_C_PORT_RESET; USETW(sc->sc_hub_desc.ps.wPortStatus, status); USETW(sc->sc_hub_desc.ps.wPortChange, change); len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { err = USB_ERR_IOERROR; goto done; } switch (value) { case UHF_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: err = uhci_portreset(sc, index); goto done; case UHF_PORT_POWER: /* pretend we turned on power */ err = USB_ERR_NORMAL_COMPLETION; goto done; case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_RESET: default: err = USB_ERR_IOERROR; goto done; } break; default: err = USB_ERR_IOERROR; goto done; } done: *plength = len; *pptr = ptr; return (err); } /* * This routine is executed periodically and simulates interrupts from * the root controller interrupt pipe for port status change: */ static void uhci_root_intr(uhci_softc_t *sc) { DPRINTFN(21, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); sc->sc_hub_idata[0] = 0; if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { sc->sc_hub_idata[0] |= 1 << 1; } if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { sc->sc_hub_idata[0] |= 1 << 2; } /* restart timer */ usb_callout_reset(&sc->sc_root_intr, hz, (void *)&uhci_root_intr, sc); if (sc->sc_hub_idata[0] != 0) { uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } } static void uhci_xfer_setup(struct usb_setup_params *parm) { struct usb_page_search page_info; struct usb_page_cache *pc; uhci_softc_t *sc; struct usb_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t nqh; uint32_t nfixup; uint32_t n; uint16_t align; sc = UHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x500; /* * compute ntd and nqh */ if (parm->methods == &uhci_device_ctrl_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usbd_transfer_setup_sub(parm); /* see EHCI HC driver for proof of "ntd" formula */ nqh = 1; ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_frame_size)); } else if (parm->methods == &uhci_device_bulk_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usbd_transfer_setup_sub(parm); nqh = 1; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_frame_size)); } else if (parm->methods == &uhci_device_intr_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usbd_transfer_setup_sub(parm); nqh = 1; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_frame_size)); } else if (parm->methods == &uhci_device_isoc_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usbd_transfer_setup_sub(parm); nqh = 0; ntd = xfer->nframes; } else { usbd_transfer_setup_sub(parm); nqh = 0; ntd = 0; } if (parm->err) { return; } /* * NOTE: the UHCI controller requires that * every packet must be contiguous on * the same USB memory page ! */ nfixup = (parm->bufsize / USB_PAGE_SIZE) + 1; /* * Compute a suitable power of two alignment * for our "max_frame_size" fixup buffer(s): */ align = xfer->max_frame_size; n = 0; while (align) { align >>= 1; n++; } /* check for power of two */ if (!(xfer->max_frame_size & (xfer->max_frame_size - 1))) { n--; } /* * We don't allow alignments of * less than 8 bytes: * * NOTE: Allocating using an aligment * of 1 byte has special meaning! */ if (n < 3) { n = 3; } align = (1 << n); if (usbd_transfer_setup_sub_malloc( parm, &pc, xfer->max_frame_size, align, nfixup)) { parm->err = USB_ERR_NOMEM; return; } xfer->buf_fixup = pc; alloc_dma_set: if (parm->err) { return; } last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(uhci_td_t), UHCI_TD_ALIGN, ntd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != ntd; n++) { uhci_td_t *td; usbd_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ if ((parm->methods == &uhci_device_bulk_methods) || (parm->methods == &uhci_device_ctrl_methods) || (parm->methods == &uhci_device_intr_methods)) { /* set depth first bit */ td->td_self = htole32(page_info.physaddr | UHCI_PTR_TD | UHCI_PTR_VF); } else { td->td_self = htole32(page_info.physaddr | UHCI_PTR_TD); } td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(uhci_qh_t), UHCI_QH_ALIGN, nqh)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqh; n++) { uhci_qh_t *qh; usbd_get_page(pc + n, 0, &page_info); qh = page_info.buffer; /* init QH */ qh->qh_self = htole32(page_info.physaddr | UHCI_PTR_QH); qh->obj_next = last_obj; qh->page_cache = pc + n; last_obj = qh; usb_pc_cpu_flush(pc + n); } } xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static void uhci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { uhci_softc_t *sc = UHCI_BUS2SC(udev->bus); DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode, sc->sc_addr); if (udev->device_index != sc->sc_addr) { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ep->methods = &uhci_device_ctrl_methods; break; case UE_INTERRUPT: ep->methods = &uhci_device_intr_methods; break; case UE_ISOCHRONOUS: if (udev->speed == USB_SPEED_FULL) { ep->methods = &uhci_device_isoc_methods; } break; case UE_BULK: ep->methods = &uhci_device_bulk_methods; break; default: /* do nothing */ break; } } } static void uhci_xfer_unsetup(struct usb_xfer *xfer) { return; } static void uhci_get_dma_delay(struct usb_device *udev, uint32_t *pus) { /* * Wait until hardware has finished any possible use of the * transfer descriptor(s) and QH */ *pus = (1125); /* microseconds */ } static void uhci_device_resume(struct usb_device *udev) { struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); struct usb_xfer *xfer; const struct usb_pipe_methods *methods; uhci_qh_t *qh; DPRINTF("\n"); USB_BUS_LOCK(udev->bus); TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (xfer->xroot->udev == udev) { methods = xfer->endpoint->methods; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (methods == &uhci_device_bulk_methods) { UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); uhci_add_loop(sc); xfer->flags_int.bandwidth_reclaimed = 1; } if (methods == &uhci_device_ctrl_methods) { if (xfer->xroot->udev->speed == USB_SPEED_LOW) { UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); } else { UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); } } if (methods == &uhci_device_intr_methods) { UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); } } } USB_BUS_UNLOCK(udev->bus); return; } static void uhci_device_suspend(struct usb_device *udev) { struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); struct usb_xfer *xfer; const struct usb_pipe_methods *methods; uhci_qh_t *qh; DPRINTF("\n"); USB_BUS_LOCK(udev->bus); TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (xfer->xroot->udev == udev) { methods = xfer->endpoint->methods; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; uhci_rem_loop(sc); } if (methods == &uhci_device_bulk_methods) { UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last); } if (methods == &uhci_device_ctrl_methods) { if (xfer->xroot->udev->speed == USB_SPEED_LOW) { UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last); } else { UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last); } } if (methods == &uhci_device_intr_methods) { UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); } } } USB_BUS_UNLOCK(udev->bus); return; } static void uhci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct uhci_softc *sc = UHCI_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: case USB_HW_POWER_SHUTDOWN: uhci_suspend(sc); break; case USB_HW_POWER_RESUME: uhci_resume(sc); break; default: break; } } static void uhci_set_hw_power(struct usb_bus *bus) { struct uhci_softc *sc = UHCI_BUS2SC(bus); uint32_t flags; DPRINTF("\n"); USB_BUS_LOCK(bus); flags = bus->hw_power_state; /* * WARNING: Some FULL speed USB devices require periodic SOF * messages! If any USB devices are connected through the * UHCI, power save will be disabled! */ if (flags & (USB_HW_POWER_CONTROL | USB_HW_POWER_NON_ROOT_HUB | USB_HW_POWER_BULK | USB_HW_POWER_INTERRUPT | USB_HW_POWER_ISOC)) { DPRINTF("Some USB transfer is " "active on unit %u.\n", device_get_unit(sc->sc_bus.bdev)); uhci_restart(sc); } else { DPRINTF("Power save on unit %u.\n", device_get_unit(sc->sc_bus.bdev)); UHCICMD(sc, UHCI_CMD_MAXP); } USB_BUS_UNLOCK(bus); return; } static const struct usb_bus_methods uhci_bus_methods = { .endpoint_init = uhci_ep_init, .xfer_setup = uhci_xfer_setup, .xfer_unsetup = uhci_xfer_unsetup, .get_dma_delay = uhci_get_dma_delay, .device_resume = uhci_device_resume, .device_suspend = uhci_device_suspend, .set_hw_power = uhci_set_hw_power, .set_hw_power_sleep = uhci_set_hw_power_sleep, .roothub_exec = uhci_roothub_exec, .xfer_poll = uhci_do_poll, }; Index: head/sys/dev/usb/controller/xhcireg.h =================================================================== --- head/sys/dev/usb/controller/xhcireg.h (revision 298931) +++ head/sys/dev/usb/controller/xhcireg.h (revision 298932) @@ -1,224 +1,224 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _XHCIREG_H_ #define _XHCIREG_H_ /* XHCI PCI config registers */ #define PCI_XHCI_CBMEM 0x10 /* configuration base MEM */ #define PCI_XHCI_USBREV 0x60 /* RO USB protocol revision */ #define PCI_USB_REV_3_0 0x30 /* USB 3.0 */ #define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */ #define PCI_XHCI_INTEL_XUSB2PR 0xD0 /* Intel USB2 Port Routing */ #define PCI_XHCI_INTEL_USB2PRM 0xD4 /* Intel USB2 Port Routing Mask */ #define PCI_XHCI_INTEL_USB3_PSSEN 0xD8 /* Intel USB3 Port SuperSpeed Enable */ #define PCI_XHCI_INTEL_USB3PRM 0xDC /* Intel USB3 Port Routing Mask */ /* XHCI capability registers */ #define XHCI_CAPLENGTH 0x00 /* RO capability */ #define XHCI_RESERVED 0x01 /* Reserved */ #define XHCI_HCIVERSION 0x02 /* RO Interface version number */ #define XHCI_HCIVERSION_0_9 0x0090 /* xHCI version 0.9 */ #define XHCI_HCIVERSION_1_0 0x0100 /* xHCI version 1.0 */ -#define XHCI_HCSPARAMS1 0x04 /* RO structual parameters 1 */ +#define XHCI_HCSPARAMS1 0x04 /* RO structural parameters 1 */ #define XHCI_HCS1_DEVSLOT_MAX(x)((x) & 0xFF) #define XHCI_HCS1_IRQ_MAX(x) (((x) >> 8) & 0x3FF) #define XHCI_HCS1_N_PORTS(x) (((x) >> 24) & 0xFF) -#define XHCI_HCSPARAMS2 0x08 /* RO structual parameters 2 */ +#define XHCI_HCSPARAMS2 0x08 /* RO structural parameters 2 */ #define XHCI_HCS2_IST(x) ((x) & 0xF) #define XHCI_HCS2_ERST_MAX(x) (((x) >> 4) & 0xF) #define XHCI_HCS2_SPR(x) (((x) >> 24) & 0x1) #define XHCI_HCS2_SPB_MAX(x) (((x) >> 27) & 0x7F) -#define XHCI_HCSPARAMS3 0x0C /* RO structual parameters 3 */ +#define XHCI_HCSPARAMS3 0x0C /* RO structural parameters 3 */ #define XHCI_HCS3_U1_DEL(x) ((x) & 0xFF) #define XHCI_HCS3_U2_DEL(x) (((x) >> 16) & 0xFFFF) #define XHCI_HCSPARAMS0 0x10 /* RO capability parameters */ #define XHCI_HCS0_AC64(x) ((x) & 0x1) /* 64-bit capable */ #define XHCI_HCS0_BNC(x) (((x) >> 1) & 0x1) /* BW negotiation */ #define XHCI_HCS0_CSZ(x) (((x) >> 2) & 0x1) /* context size */ #define XHCI_HCS0_PPC(x) (((x) >> 3) & 0x1) /* port power control */ #define XHCI_HCS0_PIND(x) (((x) >> 4) & 0x1) /* port indicators */ #define XHCI_HCS0_LHRC(x) (((x) >> 5) & 0x1) /* light HC reset */ #define XHCI_HCS0_LTC(x) (((x) >> 6) & 0x1) /* latency tolerance msg */ #define XHCI_HCS0_NSS(x) (((x) >> 7) & 0x1) /* no secondary sid */ #define XHCI_HCS0_PSA_SZ_MAX(x) (((x) >> 12) & 0xF) /* max pri. stream array size */ #define XHCI_HCS0_XECP(x) (((x) >> 16) & 0xFFFF) /* extended capabilities pointer */ #define XHCI_DBOFF 0x14 /* RO doorbell offset */ #define XHCI_RTSOFF 0x18 /* RO runtime register space offset */ /* XHCI operational registers. Offset given by XHCI_CAPLENGTH register */ #define XHCI_USBCMD 0x00 /* XHCI command */ #define XHCI_CMD_RS 0x00000001 /* RW Run/Stop */ #define XHCI_CMD_HCRST 0x00000002 /* RW Host Controller Reset */ #define XHCI_CMD_INTE 0x00000004 /* RW Interrupter Enable */ #define XHCI_CMD_HSEE 0x00000008 /* RW Host System Error Enable */ #define XHCI_CMD_LHCRST 0x00000080 /* RO/RW Light Host Controller Reset */ #define XHCI_CMD_CSS 0x00000100 /* RW Controller Save State */ #define XHCI_CMD_CRS 0x00000200 /* RW Controller Restore State */ #define XHCI_CMD_EWE 0x00000400 /* RW Enable Wrap Event */ #define XHCI_CMD_EU3S 0x00000800 /* RW Enable U3 MFINDEX Stop */ #define XHCI_USBSTS 0x04 /* XHCI status */ #define XHCI_STS_HCH 0x00000001 /* RO - Host Controller Halted */ #define XHCI_STS_HSE 0x00000004 /* RW - Host System Error */ #define XHCI_STS_EINT 0x00000008 /* RW - Event Interrupt */ #define XHCI_STS_PCD 0x00000010 /* RW - Port Change Detect */ #define XHCI_STS_SSS 0x00000100 /* RO - Save State Status */ #define XHCI_STS_RSS 0x00000200 /* RO - Restore State Status */ #define XHCI_STS_SRE 0x00000400 /* RW - Save/Restore Error */ #define XHCI_STS_CNR 0x00000800 /* RO - Controller Not Ready */ #define XHCI_STS_HCE 0x00001000 /* RO - Host Controller Error */ #define XHCI_PAGESIZE 0x08 /* XHCI page size mask */ #define XHCI_PAGESIZE_4K 0x00000001 /* 4K Page Size */ #define XHCI_PAGESIZE_8K 0x00000002 /* 8K Page Size */ #define XHCI_PAGESIZE_16K 0x00000004 /* 16K Page Size */ #define XHCI_PAGESIZE_32K 0x00000008 /* 32K Page Size */ #define XHCI_PAGESIZE_64K 0x00000010 /* 64K Page Size */ #define XHCI_DNCTRL 0x14 /* XHCI device notification control */ #define XHCI_DNCTRL_MASK(n) (1U << (n)) #define XHCI_CRCR_LO 0x18 /* XHCI command ring control */ #define XHCI_CRCR_LO_RCS 0x00000001 /* RW - consumer cycle state */ #define XHCI_CRCR_LO_CS 0x00000002 /* RW - command stop */ #define XHCI_CRCR_LO_CA 0x00000004 /* RW - command abort */ #define XHCI_CRCR_LO_CRR 0x00000008 /* RW - command ring running */ #define XHCI_CRCR_LO_MASK 0x0000000F #define XHCI_CRCR_HI 0x1C /* XHCI command ring control */ #define XHCI_DCBAAP_LO 0x30 /* XHCI dev context BA pointer */ #define XHCI_DCBAAP_HI 0x34 /* XHCI dev context BA pointer */ #define XHCI_CONFIG 0x38 #define XHCI_CONFIG_SLOTS_MASK 0x000000FF /* RW - number of device slots enabled */ /* XHCI port status registers */ #define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n))) /* XHCI port status */ #define XHCI_PS_CCS 0x00000001 /* RO - current connect status */ #define XHCI_PS_PED 0x00000002 /* RW - port enabled / disabled */ #define XHCI_PS_OCA 0x00000008 /* RO - over current active */ #define XHCI_PS_PR 0x00000010 /* RW - port reset */ #define XHCI_PS_PLS_GET(x) (((x) >> 5) & 0xF) /* RW - port link state */ #define XHCI_PS_PLS_SET(x) (((x) & 0xF) << 5) /* RW - port link state */ #define XHCI_PS_PP 0x00000200 /* RW - port power */ #define XHCI_PS_SPEED_GET(x) (((x) >> 10) & 0xF) /* RO - port speed */ #define XHCI_PS_PIC_GET(x) (((x) >> 14) & 0x3) /* RW - port indicator */ #define XHCI_PS_PIC_SET(x) (((x) & 0x3) << 14) /* RW - port indicator */ #define XHCI_PS_LWS 0x00010000 /* RW - port link state write strobe */ #define XHCI_PS_CSC 0x00020000 /* RW - connect status change */ #define XHCI_PS_PEC 0x00040000 /* RW - port enable/disable change */ #define XHCI_PS_WRC 0x00080000 /* RW - warm port reset change */ #define XHCI_PS_OCC 0x00100000 /* RW - over-current change */ #define XHCI_PS_PRC 0x00200000 /* RW - port reset change */ #define XHCI_PS_PLC 0x00400000 /* RW - port link state change */ #define XHCI_PS_CEC 0x00800000 /* RW - config error change */ #define XHCI_PS_CAS 0x01000000 /* RO - cold attach status */ #define XHCI_PS_WCE 0x02000000 /* RW - wake on connect enable */ #define XHCI_PS_WDE 0x04000000 /* RW - wake on disconnect enable */ #define XHCI_PS_WOE 0x08000000 /* RW - wake on over-current enable */ #define XHCI_PS_DR 0x40000000 /* RO - device removable */ #define XHCI_PS_WPR 0x80000000U /* RW - warm port reset */ #define XHCI_PS_CLEAR 0x80FF01FFU /* command bits */ #define XHCI_PORTPMSC(n) (0x3F4 + (0x10 * (n))) /* XHCI status and control */ #define XHCI_PM3_U1TO_GET(x) (((x) >> 0) & 0xFF) /* RW - U1 timeout */ #define XHCI_PM3_U1TO_SET(x) (((x) & 0xFF) << 0) /* RW - U1 timeout */ #define XHCI_PM3_U2TO_GET(x) (((x) >> 8) & 0xFF) /* RW - U2 timeout */ #define XHCI_PM3_U2TO_SET(x) (((x) & 0xFF) << 8) /* RW - U2 timeout */ #define XHCI_PM3_FLA 0x00010000 /* RW - Force Link PM Accept */ #define XHCI_PM2_L1S_GET(x) (((x) >> 0) & 0x7) /* RO - L1 status */ #define XHCI_PM2_RWE 0x00000008 /* RW - remote wakup enable */ #define XHCI_PM2_HIRD_GET(x) (((x) >> 4) & 0xF) /* RW - host initiated resume duration */ #define XHCI_PM2_HIRD_SET(x) (((x) & 0xF) << 4) /* RW - host initiated resume duration */ #define XHCI_PM2_L1SLOT_GET(x) (((x) >> 8) & 0xFF) /* RW - L1 device slot */ #define XHCI_PM2_L1SLOT_SET(x) (((x) & 0xFF) << 8) /* RW - L1 device slot */ #define XHCI_PM2_HLE 0x00010000 /* RW - hardware LPM enable */ #define XHCI_PORTLI(n) (0x3F8 + (0x10 * (n))) /* XHCI port link info */ #define XHCI_PLI3_ERR_GET(x) (((x) >> 0) & 0xFFFF) /* RO - port link errors */ #define XHCI_PORTRSV(n) (0x3FC + (0x10 * (n))) /* XHCI port reserved */ /* XHCI runtime registers. Offset given by XHCI_CAPLENGTH + XHCI_RTSOFF registers */ #define XHCI_MFINDEX 0x0000 /* RO - microframe index */ #define XHCI_MFINDEX_GET(x) ((x) & 0x3FFF) #define XHCI_IMAN(n) (0x0020 + (0x20 * (n))) /* XHCI interrupt management */ #define XHCI_IMAN_INTR_PEND 0x00000001 /* RW - interrupt pending */ #define XHCI_IMAN_INTR_ENA 0x00000002 /* RW - interrupt enable */ #define XHCI_IMOD(n) (0x0024 + (0x20 * (n))) /* XHCI interrupt moderation */ #define XHCI_IMOD_IVAL_GET(x) (((x) >> 0) & 0xFFFF) /* 250ns unit */ #define XHCI_IMOD_IVAL_SET(x) (((x) & 0xFFFF) << 0) /* 250ns unit */ #define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFF) /* 250ns unit */ #define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFF) << 16) /* 250ns unit */ #define XHCI_IMOD_DEFAULT 0x000001F4U /* 8000 IRQs/second */ #define XHCI_IMOD_DEFAULT_LP 0x000003F8U /* 4000 IRQs/second - LynxPoint */ #define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) /* XHCI event ring segment table size */ #define XHCI_ERSTS_GET(x) ((x) & 0xFFFF) #define XHCI_ERSTS_SET(x) ((x) & 0xFFFF) #define XHCI_ERSTBA_LO(n) (0x0030 + (0x20 * (n))) /* XHCI event ring segment table BA */ #define XHCI_ERSTBA_HI(n) (0x0034 + (0x20 * (n))) /* XHCI event ring segment table BA */ #define XHCI_ERDP_LO(n) (0x0038 + (0x20 * (n))) /* XHCI event ring dequeue pointer */ #define XHCI_ERDP_LO_SINDEX(x) ((x) & 0x7) /* RO - dequeue segment index */ #define XHCI_ERDP_LO_BUSY 0x00000008 /* RW - event handler busy */ #define XHCI_ERDP_HI(n) (0x003C + (0x20 * (n))) /* XHCI event ring dequeue pointer */ /* XHCI doorbell registers. Offset given by XHCI_CAPLENGTH + XHCI_DBOFF registers */ #define XHCI_DOORBELL(n) (0x0000 + (4 * (n))) #define XHCI_DB_TARGET_GET(x) ((x) & 0xFF) /* RW - doorbell target */ #define XHCI_DB_TARGET_SET(x) ((x) & 0xFF) /* RW - doorbell target */ #define XHCI_DB_SID_GET(x) (((x) >> 16) & 0xFFFF) /* RW - doorbell stream ID */ #define XHCI_DB_SID_SET(x) (((x) & 0xFFFF) << 16) /* RW - doorbell stream ID */ /* XHCI legacy support */ #define XHCI_XECP_ID(x) ((x) & 0xFF) #define XHCI_XECP_NEXT(x) (((x) >> 8) & 0xFF) #define XHCI_XECP_BIOS_SEM 0x0002 #define XHCI_XECP_OS_SEM 0x0003 /* XHCI capability ID's */ #define XHCI_ID_USB_LEGACY 0x0001 #define XHCI_ID_PROTOCOLS 0x0002 #define XHCI_ID_POWER_MGMT 0x0003 #define XHCI_ID_VIRTUALIZATION 0x0004 #define XHCI_ID_MSG_IRQ 0x0005 #define XHCI_ID_USB_LOCAL_MEM 0x0006 /* XHCI register R/W wrappers */ #define XREAD1(sc, what, a) \ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off) #define XREAD2(sc, what, a) \ bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off) #define XREAD4(sc, what, a) \ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off) #define XWRITE1(sc, what, a, x) \ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off, (x)) #define XWRITE2(sc, what, a, x) \ bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off, (x)) #define XWRITE4(sc, what, a, x) \ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off, (x)) #endif /* _XHCIREG_H_ */ Index: head/sys/dev/usb/input/ums.c =================================================================== --- head/sys/dev/usb/input/ums.c (revision 298931) +++ head/sys/dev/usb/input/ums.c (revision 298932) @@ -1,1055 +1,1055 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR ums_debug #include #include #include #include #include #include #ifdef USB_DEBUG static int ums_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RWTUN, &ums_debug, 0, "Debug level"); #endif #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) #define UMS_BUF_SIZE 8 /* bytes */ #define UMS_IFQ_MAXLEN 50 /* units */ #define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) #define UMS_INFO_MAX 2 /* maximum number of HID sets */ enum { UMS_INTR_DT, UMS_N_TRANSFER, }; struct ums_info { struct hid_location sc_loc_w; struct hid_location sc_loc_x; struct hid_location sc_loc_y; struct hid_location sc_loc_z; struct hid_location sc_loc_t; struct hid_location sc_loc_btn[UMS_BUTTON_MAX]; uint32_t sc_flags; #define UMS_FLAG_X_AXIS 0x0001 #define UMS_FLAG_Y_AXIS 0x0002 #define UMS_FLAG_Z_AXIS 0x0004 #define UMS_FLAG_T_AXIS 0x0008 #define UMS_FLAG_SBU 0x0010 /* spurious button up events */ #define UMS_FLAG_REVZ 0x0020 /* Z-axis is reversed */ #define UMS_FLAG_W_AXIS 0x0040 uint8_t sc_iid_w; uint8_t sc_iid_x; uint8_t sc_iid_y; uint8_t sc_iid_z; uint8_t sc_iid_t; uint8_t sc_iid_btn[UMS_BUTTON_MAX]; uint8_t sc_buttons; }; struct ums_softc { struct usb_fifo_sc sc_fifo; struct mtx sc_mtx; struct usb_callout sc_callout; struct ums_info sc_info[UMS_INFO_MAX]; mousehw_t sc_hw; mousemode_t sc_mode; mousestatus_t sc_status; struct usb_xfer *sc_xfer[UMS_N_TRANSFER]; int sc_pollrate; int sc_fflags; uint8_t sc_buttons; uint8_t sc_iid; uint8_t sc_temp[64]; }; static void ums_put_queue_timeout(void *__sc); static usb_callback_t ums_intr_callback; static device_probe_t ums_probe; static device_attach_t ums_attach; static device_detach_t ums_detach; static usb_fifo_cmd_t ums_start_read; static usb_fifo_cmd_t ums_stop_read; static usb_fifo_open_t ums_open; static usb_fifo_close_t ums_close; static usb_fifo_ioctl_t ums_ioctl; static void ums_put_queue(struct ums_softc *, int32_t, int32_t, int32_t, int32_t, int32_t); static int ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS); static struct usb_fifo_methods ums_fifo_methods = { .f_open = &ums_open, .f_close = &ums_close, .f_ioctl = &ums_ioctl, .f_start_read = &ums_start_read, .f_stop_read = &ums_stop_read, .basename[0] = "ums", }; static void ums_put_queue_timeout(void *__sc) { struct ums_softc *sc = __sc; mtx_assert(&sc->sc_mtx, MA_OWNED); ums_put_queue(sc, 0, 0, 0, 0, 0); } static void ums_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct ums_softc *sc = usbd_xfer_softc(xfer); struct ums_info *info = &sc->sc_info[0]; struct usb_page_cache *pc; uint8_t *buf = sc->sc_temp; int32_t buttons = 0; int32_t buttons_found = 0; int32_t dw = 0; int32_t dx = 0; int32_t dy = 0; int32_t dz = 0; int32_t dt = 0; uint8_t i; uint8_t id; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(6, "sc=%p actlen=%d\n", sc, len); if (len > (int)sizeof(sc->sc_temp)) { DPRINTFN(6, "truncating large packet to %zu bytes\n", sizeof(sc->sc_temp)); len = sizeof(sc->sc_temp); } if (len == 0) goto tr_setup; pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, len); DPRINTFN(6, "data = %02x %02x %02x %02x " "%02x %02x %02x %02x\n", (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0, (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0, (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0, (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0); if (sc->sc_iid) { id = *buf; len--; buf++; } else { id = 0; if (sc->sc_info[0].sc_flags & UMS_FLAG_SBU) { if ((*buf == 0x14) || (*buf == 0x15)) { goto tr_setup; } } } repeat: if ((info->sc_flags & UMS_FLAG_W_AXIS) && (id == info->sc_iid_w)) dw += hid_get_data(buf, len, &info->sc_loc_w); if ((info->sc_flags & UMS_FLAG_X_AXIS) && (id == info->sc_iid_x)) dx += hid_get_data(buf, len, &info->sc_loc_x); if ((info->sc_flags & UMS_FLAG_Y_AXIS) && (id == info->sc_iid_y)) dy = -hid_get_data(buf, len, &info->sc_loc_y); if ((info->sc_flags & UMS_FLAG_Z_AXIS) && (id == info->sc_iid_z)) { int32_t temp; temp = hid_get_data(buf, len, &info->sc_loc_z); if (info->sc_flags & UMS_FLAG_REVZ) temp = -temp; dz -= temp; } if ((info->sc_flags & UMS_FLAG_T_AXIS) && (id == info->sc_iid_t)) dt -= hid_get_data(buf, len, &info->sc_loc_t); for (i = 0; i < info->sc_buttons; i++) { uint32_t mask; mask = 1UL << UMS_BUT(i); /* check for correct button ID */ if (id != info->sc_iid_btn[i]) continue; /* check for button pressed */ if (hid_get_data(buf, len, &info->sc_loc_btn[i])) buttons |= mask; /* register button mask */ buttons_found |= mask; } if (++info != &sc->sc_info[UMS_INFO_MAX]) goto repeat; /* keep old button value(s) for non-detected buttons */ buttons |= sc->sc_status.button & ~buttons_found; if (dx || dy || dz || dt || dw || (buttons != sc->sc_status.button)) { DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n", dx, dy, dz, dt, dw, buttons); /* translate T-axis into button presses until further */ if (dt > 0) buttons |= 1UL << 3; else if (dt < 0) buttons |= 1UL << 4; sc->sc_status.button = buttons; sc->sc_status.dx += dx; sc->sc_status.dy += dy; sc->sc_status.dz += dz; /* * sc->sc_status.dt += dt; * no way to export this yet */ /* * The Qtronix keyboard has a built in PS/2 * port for a mouse. The firmware once in a * while posts a spurious button up * event. This event we ignore by doing a * timeout for 50 msecs. If we receive * dx=dy=dz=buttons=0 before we add the event * to the queue. In any other case we delete * the timeout event. */ if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) && (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && (dw == 0) && (buttons == 0)) { usb_callout_reset(&sc->sc_callout, hz / 20, &ums_put_queue_timeout, sc); } else { usb_callout_stop(&sc->sc_callout); ums_put_queue(sc, dx, dy, dz, dt, buttons); } } case USB_ST_SETUP: tr_setup: /* check if we can put more data into the FIFO */ if (usb_fifo_put_bytes_max( sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static const struct usb_config ums_config[UMS_N_TRANSFER] = { [UMS_INTR_DT] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &ums_intr_callback, }, }; /* A match on these entries will load ums */ static const STRUCT_USB_HOST_ID __used ums_devs[] = { {USB_IFACE_CLASS(UICLASS_HID), USB_IFACE_SUBCLASS(UISUBCLASS_BOOT), USB_IFACE_PROTOCOL(UIPROTO_MOUSE),}, }; static int ums_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); void *d_ptr; int error; uint16_t d_len; DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bInterfaceClass != UICLASS_HID) return (ENXIO); if (usb_test_quirk(uaa, UQ_UMS_IGNORE)) return (ENXIO); if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) && (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE)) return (BUS_PROBE_DEFAULT); error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); if (error) return (ENXIO); if (hid_is_mouse(d_ptr, d_len)) error = BUS_PROBE_DEFAULT; else error = ENXIO; free(d_ptr, M_TEMP); return (error); } static void ums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf, uint16_t len, uint8_t index) { struct ums_info *info = &sc->sc_info[index]; uint32_t flags; uint8_t i; uint8_t j; if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { info->sc_flags |= UMS_FLAG_X_AXIS; } } if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { info->sc_flags |= UMS_FLAG_Y_AXIS; } } /* Try the wheel first as the Z activator since it's tradition. */ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags, &info->sc_iid_z) || hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags, &info->sc_iid_z)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { info->sc_flags |= UMS_FLAG_Z_AXIS; } /* * We might have both a wheel and Z direction, if so put * put the Z on the W coordinate. */ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, index, &info->sc_loc_w, &flags, &info->sc_iid_w)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { info->sc_flags |= UMS_FLAG_W_AXIS; } } } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, index, &info->sc_loc_z, &flags, &info->sc_iid_z)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { info->sc_flags |= UMS_FLAG_Z_AXIS; } } /* * The Microsoft Wireless Intellimouse 2.0 reports it's wheel * using 0x0048, which is HUG_TWHEEL, and seems to expect you * to know that the byte after the wheel is the tilt axis. * There are no other HID axis descriptors other than X,Y and * TWHEEL */ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, index, &info->sc_loc_t, &flags, &info->sc_iid_t)) { info->sc_loc_t.pos += 8; if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { info->sc_flags |= UMS_FLAG_T_AXIS; } } else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), hid_input, index, &info->sc_loc_t, &flags, &info->sc_iid_t)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) info->sc_flags |= UMS_FLAG_T_AXIS; } /* figure out the number of buttons */ for (i = 0; i < UMS_BUTTON_MAX; i++) { if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), hid_input, index, &info->sc_loc_btn[i], NULL, &info->sc_iid_btn[i])) { break; } } /* detect other buttons */ for (j = 0; (i < UMS_BUTTON_MAX) && (j < 2); i++, j++) { if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)), hid_input, index, &info->sc_loc_btn[i], NULL, &info->sc_iid_btn[i])) { break; } } info->sc_buttons = i; if (i > sc->sc_buttons) sc->sc_buttons = i; if (info->sc_flags == 0) return; /* announce information about the mouse */ device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n", (info->sc_buttons), (info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", (info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", (info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", (info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "", (info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "", info->sc_iid_x); } static int ums_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ums_softc *sc = device_get_softc(dev); struct ums_info *info; void *d_ptr = NULL; int isize; int err; uint16_t d_len; uint8_t i; #ifdef USB_DEBUG uint8_t j; #endif DPRINTFN(11, "sc=%p\n", sc); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE); usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); /* * Force the report (non-boot) protocol. * * Mice without boot protocol support may choose not to implement * Set_Protocol at all; Ignore any error. */ err = usbd_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1); err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config, UMS_N_TRANSFER, sc, &sc->sc_mtx); if (err) { DPRINTF("error=%s\n", usbd_errstr(err)); goto detach; } /* Get HID descriptor */ err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); if (err) { device_printf(dev, "error reading report description\n"); goto detach; } isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); /* * The Microsoft Wireless Notebook Optical Mouse seems to be in worse * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and * all of its other button positions are all off. It also reports that - * it has two addional buttons and a tilt wheel. + * it has two additional buttons and a tilt wheel. */ if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) { sc->sc_iid = 0; info = &sc->sc_info[0]; info->sc_flags = (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS | UMS_FLAG_SBU); info->sc_buttons = 3; isize = 5; /* 1st byte of descriptor report contains garbage */ info->sc_loc_x.pos = 16; info->sc_loc_x.size = 8; info->sc_loc_y.pos = 24; info->sc_loc_y.size = 8; info->sc_loc_z.pos = 32; info->sc_loc_z.size = 8; info->sc_loc_btn[0].pos = 8; info->sc_loc_btn[0].size = 1; info->sc_loc_btn[1].pos = 9; info->sc_loc_btn[1].size = 1; info->sc_loc_btn[2].pos = 10; info->sc_loc_btn[2].size = 1; /* Announce device */ device_printf(dev, "3 buttons and [XYZ] " "coordinates ID=0\n"); } else { /* Search the HID descriptor and announce device */ for (i = 0; i < UMS_INFO_MAX; i++) { ums_hid_parse(sc, dev, d_ptr, d_len, i); } } if (usb_test_quirk(uaa, UQ_MS_REVZ)) { info = &sc->sc_info[0]; /* Some wheels need the Z axis reversed. */ info->sc_flags |= UMS_FLAG_REVZ; } if (isize > (int)usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) { DPRINTF("WARNING: report size, %d bytes, is larger " "than interrupt size, %d bytes!\n", isize, usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])); } free(d_ptr, M_TEMP); d_ptr = NULL; #ifdef USB_DEBUG for (j = 0; j < UMS_INFO_MAX; j++) { info = &sc->sc_info[j]; DPRINTF("sc=%p, index=%d\n", sc, j); DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos, info->sc_loc_x.size, info->sc_iid_x); DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos, info->sc_loc_y.size, info->sc_iid_y); DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos, info->sc_loc_z.size, info->sc_iid_z); DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos, info->sc_loc_t.size, info->sc_iid_t); DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos, info->sc_loc_w.size, info->sc_iid_w); for (i = 0; i < info->sc_buttons; i++) { DPRINTF("B%d\t%d/%d id=%d\n", i + 1, info->sc_loc_btn[i].pos, info->sc_loc_btn[i].size, info->sc_iid_btn[i]); } } DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid); #endif err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, &ums_fifo_methods, &sc->sc_fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); if (err) goto detach; SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "parseinfo", CTLTYPE_STRING|CTLFLAG_RD, sc, 0, ums_sysctl_handler_parseinfo, "", "Dump of parsed HID report descriptor"); return (0); detach: if (d_ptr) { free(d_ptr, M_TEMP); } ums_detach(dev); return (ENOMEM); } static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); DPRINTF("sc=%p\n", sc); usb_fifo_detach(&sc->sc_fifo); usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); usb_callout_drain(&sc->sc_callout); mtx_destroy(&sc->sc_mtx); return (0); } static void ums_start_read(struct usb_fifo *fifo) { struct ums_softc *sc = usb_fifo_softc(fifo); int rate; /* Check if we should override the default polling interval */ rate = sc->sc_pollrate; /* Range check rate */ if (rate > 1000) rate = 1000; /* Check for set rate */ if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) { DPRINTF("Setting pollrate = %d\n", rate); /* Stop current transfer, if any */ usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); /* Set new interval */ usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate); /* Only set pollrate once */ sc->sc_pollrate = 0; } usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]); } static void ums_stop_read(struct usb_fifo *fifo) { struct ums_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); usb_callout_stop(&sc->sc_callout); } #if ((MOUSE_SYS_PACKETSIZE != 8) || \ (MOUSE_MSC_PACKETSIZE != 5)) #error "Software assumptions are not met. Please update code." #endif static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons) { uint8_t buf[8]; if (1) { if (dx > 254) dx = 254; if (dx < -256) dx = -256; if (dy > 254) dy = 254; if (dy < -256) dy = -256; if (dz > 126) dz = 126; if (dz < -128) dz = -128; if (dt > 126) dt = 126; if (dt < -128) dt = -128; buf[0] = sc->sc_mode.syncmask[1]; buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; buf[1] = dx >> 1; buf[2] = dy >> 1; buf[3] = dx - (dx >> 1); buf[4] = dy - (dy >> 1); if (sc->sc_mode.level == 1) { buf[5] = dz >> 1; buf[6] = dz - (dz >> 1); buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); } usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, sc->sc_mode.packetsize, 1); } else { DPRINTF("Buffer full, discarded packet\n"); } } static void ums_reset_buf(struct ums_softc *sc) { /* reset read queue, must be called locked */ usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); } static int ums_open(struct usb_fifo *fifo, int fflags) { struct ums_softc *sc = usb_fifo_softc(fifo); DPRINTFN(2, "\n"); /* check for duplicate open, should not happen */ if (sc->sc_fflags & fflags) return (EBUSY); /* check for first open */ if (sc->sc_fflags == 0) { /* reset all USB mouse parameters */ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_hw.iftype = MOUSE_IF_USB; sc->sc_hw.type = MOUSE_MOUSE; sc->sc_hw.model = MOUSE_MODEL_GENERIC; sc->sc_hw.hwid = 0; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.rate = -1; sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; sc->sc_mode.accelfactor = 0; sc->sc_mode.level = 0; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; /* reset status */ sc->sc_status.flags = 0; sc->sc_status.button = 0; sc->sc_status.obutton = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* sc->sc_status.dt = 0; */ } if (fflags & FREAD) { /* allocate RX buffer */ if (usb_fifo_alloc_buffer(fifo, UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) { return (ENOMEM); } } sc->sc_fflags |= fflags & (FREAD | FWRITE); return (0); } static void ums_close(struct usb_fifo *fifo, int fflags) { struct ums_softc *sc = usb_fifo_softc(fifo); DPRINTFN(2, "\n"); if (fflags & FREAD) usb_fifo_free_buffer(fifo); sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); } static int ums_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { struct ums_softc *sc = usb_fifo_softc(fifo); mousemode_t mode; int error = 0; DPRINTFN(2, "\n"); mtx_lock(&sc->sc_mtx); switch (cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->sc_hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->sc_mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) { /* don't change the current setting */ } else if ((mode.level < 0) || (mode.level > 1)) { error = EINVAL; break; } else { sc->sc_mode.level = mode.level; } /* store polling rate */ sc->sc_pollrate = mode.rate; if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } ums_reset_buf(sc); break; case MOUSE_GETLEVEL: *(int *)addr = sc->sc_mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) { error = EINVAL; break; } sc->sc_mode.level = *(int *)addr; if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } ums_reset_buf(sc); break; case MOUSE_GETSTATUS:{ mousestatus_t *status = (mousestatus_t *)addr; *status = sc->sc_status; sc->sc_status.obutton = sc->sc_status.button; sc->sc_status.button = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* sc->sc_status.dt = 0; */ if (status->dx || status->dy || status->dz /* || status->dt */ ) { status->flags |= MOUSE_POSCHANGED; } if (status->button != status->obutton) { status->flags |= MOUSE_BUTTONSCHANGED; } break; } default: error = ENOTTY; break; } mtx_unlock(&sc->sc_mtx); return (error); } static int ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS) { struct ums_softc *sc = arg1; struct ums_info *info; struct sbuf *sb; int i, j, err, had_output; sb = sbuf_new_auto(); for (i = 0, had_output = 0; i < UMS_INFO_MAX; i++) { info = &sc->sc_info[i]; /* Don't emit empty info */ if ((info->sc_flags & (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS | UMS_FLAG_T_AXIS | UMS_FLAG_W_AXIS)) == 0 && info->sc_buttons == 0) continue; if (had_output) sbuf_printf(sb, "\n"); had_output = 1; sbuf_printf(sb, "i%d:", i + 1); if (info->sc_flags & UMS_FLAG_X_AXIS) sbuf_printf(sb, " X:r%d, p%d, s%d;", (int)info->sc_iid_x, (int)info->sc_loc_x.pos, (int)info->sc_loc_x.size); if (info->sc_flags & UMS_FLAG_Y_AXIS) sbuf_printf(sb, " Y:r%d, p%d, s%d;", (int)info->sc_iid_y, (int)info->sc_loc_y.pos, (int)info->sc_loc_y.size); if (info->sc_flags & UMS_FLAG_Z_AXIS) sbuf_printf(sb, " Z:r%d, p%d, s%d;", (int)info->sc_iid_z, (int)info->sc_loc_z.pos, (int)info->sc_loc_z.size); if (info->sc_flags & UMS_FLAG_T_AXIS) sbuf_printf(sb, " T:r%d, p%d, s%d;", (int)info->sc_iid_t, (int)info->sc_loc_t.pos, (int)info->sc_loc_t.size); if (info->sc_flags & UMS_FLAG_W_AXIS) sbuf_printf(sb, " W:r%d, p%d, s%d;", (int)info->sc_iid_w, (int)info->sc_loc_w.pos, (int)info->sc_loc_w.size); for (j = 0; j < info->sc_buttons; j++) { sbuf_printf(sb, " B%d:r%d, p%d, s%d;", j + 1, (int)info->sc_iid_btn[j], (int)info->sc_loc_btn[j].pos, (int)info->sc_loc_btn[j].size); } } sbuf_finish(sb); err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return (err); } static devclass_t ums_devclass; static device_method_t ums_methods[] = { DEVMETHOD(device_probe, ums_probe), DEVMETHOD(device_attach, ums_attach), DEVMETHOD(device_detach, ums_detach), DEVMETHOD_END }; static driver_t ums_driver = { .name = "ums", .methods = ums_methods, .size = sizeof(struct ums_softc), }; DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0); MODULE_DEPEND(ums, usb, 1, 1, 1); MODULE_VERSION(ums, 1); USB_PNP_HOST_INFO(ums_devs); Index: head/sys/dev/usb/input/wsp.c =================================================================== --- head/sys/dev/usb/input/wsp.c (revision 298931) +++ head/sys/dev/usb/input/wsp.c (revision 298932) @@ -1,1398 +1,1398 @@ /*- * Copyright (c) 2012 Huang Wen Hui * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR wsp_debug #include #include #define WSP_DRIVER_NAME "wsp" #define WSP_BUFFER_MAX 1024 #define WSP_CLAMP(x,low,high) do { \ if ((x) < (low)) \ (x) = (low); \ else if ((x) > (high)) \ (x) = (high); \ } while (0) /* Tunables */ static SYSCTL_NODE(_hw_usb, OID_AUTO, wsp, CTLFLAG_RW, 0, "USB wsp"); #ifdef USB_DEBUG enum wsp_log_level { WSP_LLEVEL_DISABLED = 0, WSP_LLEVEL_ERROR, WSP_LLEVEL_DEBUG, /* for troubleshooting */ WSP_LLEVEL_INFO, /* for diagnostics */ }; static int wsp_debug = WSP_LLEVEL_ERROR;/* the default is to only log errors */ SYSCTL_INT(_hw_usb_wsp, OID_AUTO, debug, CTLFLAG_RWTUN, &wsp_debug, WSP_LLEVEL_ERROR, "WSP debug level"); #endif /* USB_DEBUG */ static struct wsp_tuning { int scale_factor; int z_factor; int pressure_touch_threshold; int pressure_untouch_threshold; int pressure_tap_threshold; int scr_hor_threshold; } wsp_tuning = { .scale_factor = 12, .z_factor = 5, .pressure_touch_threshold = 50, .pressure_untouch_threshold = 10, .pressure_tap_threshold = 120, .scr_hor_threshold = 20, }; static void wsp_runing_rangecheck(struct wsp_tuning *ptun) { WSP_CLAMP(ptun->scale_factor, 1, 63); WSP_CLAMP(ptun->z_factor, 1, 63); WSP_CLAMP(ptun->pressure_touch_threshold, 1, 255); WSP_CLAMP(ptun->pressure_untouch_threshold, 1, 255); WSP_CLAMP(ptun->pressure_tap_threshold, 1, 255); WSP_CLAMP(ptun->scr_hor_threshold, 1, 255); } SYSCTL_INT(_hw_usb_wsp, OID_AUTO, scale_factor, CTLFLAG_RWTUN, &wsp_tuning.scale_factor, 0, "movement scale factor"); SYSCTL_INT(_hw_usb_wsp, OID_AUTO, z_factor, CTLFLAG_RWTUN, &wsp_tuning.z_factor, 0, "Z-axis scale factor"); SYSCTL_INT(_hw_usb_wsp, OID_AUTO, pressure_touch_threshold, CTLFLAG_RWTUN, &wsp_tuning.pressure_touch_threshold, 0, "touch pressure threshold"); SYSCTL_INT(_hw_usb_wsp, OID_AUTO, pressure_untouch_threshold, CTLFLAG_RWTUN, &wsp_tuning.pressure_untouch_threshold, 0, "untouch pressure threshold"); SYSCTL_INT(_hw_usb_wsp, OID_AUTO, pressure_tap_threshold, CTLFLAG_RWTUN, &wsp_tuning.pressure_tap_threshold, 0, "tap pressure threshold"); SYSCTL_INT(_hw_usb_wsp, OID_AUTO, scr_hor_threshold, CTLFLAG_RWTUN, &wsp_tuning.scr_hor_threshold, 0, "horizontal scrolling threshold"); /* * Some tables, structures, definitions and constant values for the * touchpad protocol has been copied from Linux's * "drivers/input/mouse/bcm5974.c" which has the following copyright * holders under GPLv2. All device specific code in this driver has * been written from scratch. The decoding algorithm is based on * output from FreeBSD's usbdump. * * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) * Copyright (C) 2005 Stelian Pop (stelian@popies.net) * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) */ /* button data structure */ struct bt_data { uint8_t unknown1; /* constant */ uint8_t button; /* left button */ uint8_t rel_x; /* relative x coordinate */ uint8_t rel_y; /* relative y coordinate */ } __packed; /* trackpad header types */ enum tp_type { TYPE1, /* plain trackpad */ TYPE2, /* button integrated in trackpad */ TYPE3, /* additional header fields since June 2013 */ TYPE4 /* additional header field for pressure data */ }; /* trackpad finger data offsets, le16-aligned */ #define FINGER_TYPE1 (13 * 2) #define FINGER_TYPE2 (15 * 2) #define FINGER_TYPE3 (19 * 2) #define FINGER_TYPE4 (23 * 2) /* trackpad button data offsets */ #define BUTTON_TYPE2 15 #define BUTTON_TYPE3 23 #define BUTTON_TYPE4 31 /* list of device capability bits */ #define HAS_INTEGRATED_BUTTON 1 /* trackpad finger data block size */ #define FSIZE_TYPE1 (14 * 2) #define FSIZE_TYPE2 (14 * 2) #define FSIZE_TYPE3 (14 * 2) #define FSIZE_TYPE4 (15 * 2) /* trackpad finger header - little endian */ struct tp_header { uint8_t flag; uint8_t sn0; uint16_t wFixed0; uint32_t dwSn1; uint32_t dwFixed1; uint16_t wLength; uint8_t nfinger; uint8_t ibt; int16_t wUnknown[6]; uint8_t q1; uint8_t q2; } __packed; /* trackpad finger structure - little endian */ struct tp_finger { int16_t origin; /* zero when switching track finger */ int16_t abs_x; /* absolute x coodinate */ int16_t abs_y; /* absolute y coodinate */ int16_t rel_x; /* relative x coodinate */ int16_t rel_y; /* relative y coodinate */ int16_t tool_major; /* tool area, major axis */ int16_t tool_minor; /* tool area, minor axis */ int16_t orientation; /* 16384 when point, else 15 bit angle */ int16_t touch_major; /* touch area, major axis */ int16_t touch_minor; /* touch area, minor axis */ int16_t unused[2]; /* zeros */ int16_t pressure; /* pressure on forcetouch touchpad */ int16_t multi; /* one finger: varies, more fingers: * constant */ } __packed; /* trackpad finger data size, empirically at least ten fingers */ #define MAX_FINGERS 16 #define SIZEOF_FINGER sizeof(struct tp_finger) #define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER) #if (WSP_BUFFER_MAX < ((MAX_FINGERS * FSIZE_TYPE4) + FINGER_TYPE4)) #error "WSP_BUFFER_MAX is too small" #endif enum { WSP_FLAG_WELLSPRING1, WSP_FLAG_WELLSPRING2, WSP_FLAG_WELLSPRING3, WSP_FLAG_WELLSPRING4, WSP_FLAG_WELLSPRING4A, WSP_FLAG_WELLSPRING5, WSP_FLAG_WELLSPRING6A, WSP_FLAG_WELLSPRING6, WSP_FLAG_WELLSPRING5A, WSP_FLAG_WELLSPRING7, WSP_FLAG_WELLSPRING7A, WSP_FLAG_WELLSPRING8, WSP_FLAG_WELLSPRING9, WSP_FLAG_MAX, }; /* device-specific configuration */ struct wsp_dev_params { uint8_t caps; /* device capability bitmask */ uint8_t tp_type; /* type of trackpad interface */ uint8_t tp_button; /* offset to button data */ uint8_t tp_offset; /* offset to trackpad finger data */ uint8_t tp_fsize; /* bytes in single finger block */ uint8_t tp_delta; /* offset from header to finger struct */ uint8_t iface_index; uint8_t um_size; /* usb control message length */ uint8_t um_req_val; /* usb control message value */ uint8_t um_req_idx; /* usb control message index */ uint8_t um_switch_idx; /* usb control message mode switch index */ uint8_t um_switch_on; /* usb control message mode switch on */ uint8_t um_switch_off; /* usb control message mode switch off */ }; static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { [WSP_FLAG_WELLSPRING1] = { .caps = 0, .tp_type = TYPE1, .tp_button = 0, .tp_offset = FINGER_TYPE1, .tp_fsize = FSIZE_TYPE1, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING2] = { .caps = 0, .tp_type = TYPE1, .tp_button = 0, .tp_offset = FINGER_TYPE1, .tp_fsize = FSIZE_TYPE1, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING3] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING4] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING4A] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING5] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING6] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING5A] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING6A] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING7] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING7A] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE2, .tp_button = BUTTON_TYPE2, .tp_offset = FINGER_TYPE2, .tp_fsize = FSIZE_TYPE2, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING8] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE3, .tp_button = BUTTON_TYPE3, .tp_offset = FINGER_TYPE3, .tp_fsize = FSIZE_TYPE3, .tp_delta = 0, .iface_index = 0, .um_size = 8, .um_req_val = 0x03, .um_req_idx = 0x00, .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, }, [WSP_FLAG_WELLSPRING9] = { .caps = HAS_INTEGRATED_BUTTON, .tp_type = TYPE4, .tp_button = BUTTON_TYPE4, .tp_offset = FINGER_TYPE4, .tp_fsize = FSIZE_TYPE4, .tp_delta = 2, .iface_index = 2, .um_size = 2, .um_req_val = 0x03, .um_req_idx = 0x02, .um_switch_idx = 1, .um_switch_on = 0x01, .um_switch_off = 0x00, }, }; #define WSP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } static const STRUCT_USB_HOST_ID wsp_devs[] = { /* MacbookAir1.1 */ WSP_DEV(APPLE, WELLSPRING_ANSI, WSP_FLAG_WELLSPRING1), WSP_DEV(APPLE, WELLSPRING_ISO, WSP_FLAG_WELLSPRING1), WSP_DEV(APPLE, WELLSPRING_JIS, WSP_FLAG_WELLSPRING1), /* MacbookProPenryn, aka wellspring2 */ WSP_DEV(APPLE, WELLSPRING2_ANSI, WSP_FLAG_WELLSPRING2), WSP_DEV(APPLE, WELLSPRING2_ISO, WSP_FLAG_WELLSPRING2), WSP_DEV(APPLE, WELLSPRING2_JIS, WSP_FLAG_WELLSPRING2), /* Macbook5,1 (unibody), aka wellspring3 */ WSP_DEV(APPLE, WELLSPRING3_ANSI, WSP_FLAG_WELLSPRING3), WSP_DEV(APPLE, WELLSPRING3_ISO, WSP_FLAG_WELLSPRING3), WSP_DEV(APPLE, WELLSPRING3_JIS, WSP_FLAG_WELLSPRING3), /* MacbookAir3,2 (unibody), aka wellspring4 */ WSP_DEV(APPLE, WELLSPRING4_ANSI, WSP_FLAG_WELLSPRING4), WSP_DEV(APPLE, WELLSPRING4_ISO, WSP_FLAG_WELLSPRING4), WSP_DEV(APPLE, WELLSPRING4_JIS, WSP_FLAG_WELLSPRING4), /* MacbookAir3,1 (unibody), aka wellspring4 */ WSP_DEV(APPLE, WELLSPRING4A_ANSI, WSP_FLAG_WELLSPRING4A), WSP_DEV(APPLE, WELLSPRING4A_ISO, WSP_FLAG_WELLSPRING4A), WSP_DEV(APPLE, WELLSPRING4A_JIS, WSP_FLAG_WELLSPRING4A), /* Macbook8 (unibody, March 2011) */ WSP_DEV(APPLE, WELLSPRING5_ANSI, WSP_FLAG_WELLSPRING5), WSP_DEV(APPLE, WELLSPRING5_ISO, WSP_FLAG_WELLSPRING5), WSP_DEV(APPLE, WELLSPRING5_JIS, WSP_FLAG_WELLSPRING5), /* MacbookAir4,1 (unibody, July 2011) */ WSP_DEV(APPLE, WELLSPRING6A_ANSI, WSP_FLAG_WELLSPRING6A), WSP_DEV(APPLE, WELLSPRING6A_ISO, WSP_FLAG_WELLSPRING6A), WSP_DEV(APPLE, WELLSPRING6A_JIS, WSP_FLAG_WELLSPRING6A), /* MacbookAir4,2 (unibody, July 2011) */ WSP_DEV(APPLE, WELLSPRING6_ANSI, WSP_FLAG_WELLSPRING6), WSP_DEV(APPLE, WELLSPRING6_ISO, WSP_FLAG_WELLSPRING6), WSP_DEV(APPLE, WELLSPRING6_JIS, WSP_FLAG_WELLSPRING6), /* Macbook8,2 (unibody) */ WSP_DEV(APPLE, WELLSPRING5A_ANSI, WSP_FLAG_WELLSPRING5A), WSP_DEV(APPLE, WELLSPRING5A_ISO, WSP_FLAG_WELLSPRING5A), WSP_DEV(APPLE, WELLSPRING5A_JIS, WSP_FLAG_WELLSPRING5A), /* MacbookPro10,1 (unibody, June 2012) */ /* MacbookPro11,1-3 (unibody, June 2013) */ WSP_DEV(APPLE, WELLSPRING7_ANSI, WSP_FLAG_WELLSPRING7), WSP_DEV(APPLE, WELLSPRING7_ISO, WSP_FLAG_WELLSPRING7), WSP_DEV(APPLE, WELLSPRING7_JIS, WSP_FLAG_WELLSPRING7), /* MacbookPro10,2 (unibody, October 2012) */ WSP_DEV(APPLE, WELLSPRING7A_ANSI, WSP_FLAG_WELLSPRING7A), WSP_DEV(APPLE, WELLSPRING7A_ISO, WSP_FLAG_WELLSPRING7A), WSP_DEV(APPLE, WELLSPRING7A_JIS, WSP_FLAG_WELLSPRING7A), /* MacbookAir6,2 (unibody, June 2013) */ WSP_DEV(APPLE, WELLSPRING8_ANSI, WSP_FLAG_WELLSPRING8), WSP_DEV(APPLE, WELLSPRING8_ISO, WSP_FLAG_WELLSPRING8), WSP_DEV(APPLE, WELLSPRING8_JIS, WSP_FLAG_WELLSPRING8), /* MacbookPro12,1 MacbookPro11,4 */ WSP_DEV(APPLE, WELLSPRING9_ANSI, WSP_FLAG_WELLSPRING9), WSP_DEV(APPLE, WELLSPRING9_ISO, WSP_FLAG_WELLSPRING9), WSP_DEV(APPLE, WELLSPRING9_JIS, WSP_FLAG_WELLSPRING9), }; #define WSP_FIFO_BUF_SIZE 8 /* bytes */ #define WSP_FIFO_QUEUE_MAXLEN 50 /* units */ enum { WSP_INTR_DT, WSP_N_TRANSFER, }; struct wsp_softc { struct usb_device *sc_usb_device; struct mtx sc_mutex; /* for synchronization */ struct usb_xfer *sc_xfer[WSP_N_TRANSFER]; struct usb_fifo_sc sc_fifo; const struct wsp_dev_params *sc_params; /* device configuration */ mousehw_t sc_hw; mousemode_t sc_mode; u_int sc_pollrate; mousestatus_t sc_status; u_int sc_state; #define WSP_ENABLED 0x01 struct tp_finger *index[MAX_FINGERS]; /* finger index data */ int16_t pos_x[MAX_FINGERS]; /* position array */ int16_t pos_y[MAX_FINGERS]; /* position array */ u_int sc_touch; /* touch status */ #define WSP_UNTOUCH 0x00 #define WSP_FIRST_TOUCH 0x01 #define WSP_SECOND_TOUCH 0x02 #define WSP_TOUCHING 0x04 int16_t pre_pos_x; /* previous position array */ int16_t pre_pos_y; /* previous position array */ int dx_sum; /* x axis cumulative movement */ int dy_sum; /* y axis cumulative movement */ int dz_sum; /* z axis cumulative movement */ int dz_count; #define WSP_DZ_MAX_COUNT 32 int dt_sum; /* T-axis cumulative movement */ int rdx; /* x axis remainder of divide by scale_factor */ int rdy; /* y axis remainder of divide by scale_factor */ int rdz; /* z axis remainder of divide by scale_factor */ int tp_datalen; uint8_t o_ntouch; /* old touch finger status */ uint8_t finger; /* 0 or 1 *, check which finger moving */ uint16_t intr_count; #define WSP_TAP_THRESHOLD 3 #define WSP_TAP_MAX_COUNT 20 int distance; /* the distance of 2 fingers */ #define MAX_DISTANCE 2500 /* the max allowed distance */ uint8_t ibtn; /* button status in tapping */ uint8_t ntaps; /* finger status in tapping */ uint8_t scr_mode; /* scroll status in movement */ #define WSP_SCR_NONE 0 #define WSP_SCR_VER 1 #define WSP_SCR_HOR 2 uint8_t tp_data[WSP_BUFFER_MAX] __aligned(4); /* trackpad transferred data */ }; /* * function prototypes */ static usb_fifo_cmd_t wsp_start_read; static usb_fifo_cmd_t wsp_stop_read; static usb_fifo_open_t wsp_open; static usb_fifo_close_t wsp_close; static usb_fifo_ioctl_t wsp_ioctl; static struct usb_fifo_methods wsp_fifo_methods = { .f_open = &wsp_open, .f_close = &wsp_close, .f_ioctl = &wsp_ioctl, .f_start_read = &wsp_start_read, .f_stop_read = &wsp_stop_read, .basename[0] = WSP_DRIVER_NAME, }; /* device initialization and shutdown */ static int wsp_enable(struct wsp_softc *sc); static void wsp_disable(struct wsp_softc *sc); /* updating fifo */ static void wsp_reset_buf(struct wsp_softc *sc); static void wsp_add_to_queue(struct wsp_softc *, int, int, int, uint32_t); /* Device methods. */ static device_probe_t wsp_probe; static device_attach_t wsp_attach; static device_detach_t wsp_detach; static usb_callback_t wsp_intr_callback; static const struct usb_config wsp_config[WSP_N_TRANSFER] = { [WSP_INTR_DT] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = { .pipe_bof = 0, .short_xfer_ok = 1, }, .bufsize = WSP_BUFFER_MAX, .callback = &wsp_intr_callback, }, }; static usb_error_t wsp_set_device_mode(struct wsp_softc *sc, uint8_t on) { const struct wsp_dev_params *params = sc->sc_params; uint8_t mode_bytes[8]; usb_error_t err; /* Type 3 does not require a mode switch */ if (params->tp_type == TYPE3) return 0; err = usbd_req_get_report(sc->sc_usb_device, NULL, mode_bytes, params->um_size, params->iface_index, params->um_req_val, params->um_req_idx); if (err != USB_ERR_NORMAL_COMPLETION) { DPRINTF("Failed to read device mode (%d)\n", err); return (err); } /* * XXX Need to wait at least 250ms for hardware to get * ready. The device mode handling appears to be handled * asynchronously and we should not issue these commands too * quickly. */ pause("WHW", hz / 4); mode_bytes[params->um_switch_idx] = on ? params->um_switch_on : params->um_switch_off; return (usbd_req_set_report(sc->sc_usb_device, NULL, mode_bytes, params->um_size, params->iface_index, params->um_req_val, params->um_req_idx)); } static int wsp_enable(struct wsp_softc *sc) { /* reset status */ memset(&sc->sc_status, 0, sizeof(sc->sc_status)); sc->sc_state |= WSP_ENABLED; DPRINTFN(WSP_LLEVEL_INFO, "enabled wsp\n"); return (0); } static void wsp_disable(struct wsp_softc *sc) { sc->sc_state &= ~WSP_ENABLED; DPRINTFN(WSP_LLEVEL_INFO, "disabled wsp\n"); } static int wsp_probe(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct usb_interface_descriptor *id; struct usb_interface *iface; uint8_t i; if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); /* figure out first interface matching */ for (i = 1;; i++) { iface = usbd_get_iface(uaa->device, i); if (iface == NULL || i == 3) return (ENXIO); id = iface->idesc; if ((id == NULL) || (id->bInterfaceClass != UICLASS_HID) || (id->bInterfaceProtocol != 0 && id->bInterfaceProtocol != UIPROTO_MOUSE)) continue; break; } /* check if we are attaching to the first match */ if (uaa->info.bIfaceIndex != i) return (ENXIO); return (usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)); } static int wsp_attach(device_t dev) { struct wsp_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); usb_error_t err; void *d_ptr = NULL; uint16_t d_len; DPRINTFN(WSP_LLEVEL_INFO, "sc=%p\n", sc); /* Get HID descriptor */ err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); if (err == USB_ERR_NORMAL_COMPLETION) { /* Get HID report descriptor length */ sc->tp_datalen = hid_report_size(d_ptr, d_len, hid_input, NULL); free(d_ptr, M_TEMP); if (sc->tp_datalen <= 0 || sc->tp_datalen > WSP_BUFFER_MAX) { DPRINTF("Invalid datalength or too big " "datalength: %d\n", sc->tp_datalen); return (ENXIO); } } else { return (ENXIO); } sc->sc_usb_device = uaa->device; /* get device specific configuration */ sc->sc_params = wsp_dev_params + USB_GET_DRIVER_INFO(uaa); /* * By default the touchpad behaves like a HID device, sending * packets with reportID = 8. Such reports contain only * limited information. They encode movement deltas and button * events, but do not include data from the pressure * sensors. The device input mode can be switched from HID * reports to raw sensor data using vendor-specific USB * control commands: */ /* * During re-enumeration of the device we need to force the * device back into HID mode before switching it to RAW * mode. Else the device does not work like expected. */ err = wsp_set_device_mode(sc, 0); if (err != USB_ERR_NORMAL_COMPLETION) { DPRINTF("Failed to set mode to HID MODE (%d)\n", err); return (ENXIO); } err = wsp_set_device_mode(sc, 1); if (err != USB_ERR_NORMAL_COMPLETION) { DPRINTF("failed to set mode to RAW MODE (%d)\n", err); return (ENXIO); } mtx_init(&sc->sc_mutex, "wspmtx", NULL, MTX_DEF | MTX_RECURSE); err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, wsp_config, WSP_N_TRANSFER, sc, &sc->sc_mutex); if (err) { DPRINTF("error=%s\n", usbd_errstr(err)); goto detach; } if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex, &wsp_fifo_methods, &sc->sc_fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644)) { goto detach; } device_set_usb_desc(dev); sc->sc_hw.buttons = 3; sc->sc_hw.iftype = MOUSE_IF_USB; sc->sc_hw.type = MOUSE_PAD; sc->sc_hw.model = MOUSE_MODEL_GENERIC; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.rate = -1; sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; sc->sc_touch = WSP_UNTOUCH; sc->scr_mode = WSP_SCR_NONE; return (0); detach: wsp_detach(dev); return (ENOMEM); } static int wsp_detach(device_t dev) { struct wsp_softc *sc = device_get_softc(dev); (void) wsp_set_device_mode(sc, 0); mtx_lock(&sc->sc_mutex); if (sc->sc_state & WSP_ENABLED) wsp_disable(sc); mtx_unlock(&sc->sc_mutex); usb_fifo_detach(&sc->sc_fifo); usbd_transfer_unsetup(sc->sc_xfer, WSP_N_TRANSFER); mtx_destroy(&sc->sc_mutex); return (0); } static void wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct wsp_softc *sc = usbd_xfer_softc(xfer); const struct wsp_dev_params *params = sc->sc_params; struct usb_page_cache *pc; struct tp_finger *f; struct tp_header *h; struct wsp_tuning tun = wsp_tuning; int ntouch = 0; /* the finger number in touch */ int ibt = 0; /* button status */ int dx = 0; int dy = 0; int dz = 0; int rdx = 0; int rdy = 0; int rdz = 0; int len; int i; wsp_runing_rangecheck(&tun); if (sc->dz_count == 0) sc->dz_count = WSP_DZ_MAX_COUNT; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* copy out received data */ pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, sc->tp_data, len); if ((len < params->tp_offset + params->tp_fsize) || ((len - params->tp_offset) % params->tp_fsize) != 0) { DPRINTFN(WSP_LLEVEL_INFO, "Invalid length: %d, %x, %x\n", len, sc->tp_data[0], sc->tp_data[1]); goto tr_setup; } if (len < sc->tp_datalen) { /* make sure we don't process old data */ memset(sc->tp_data + len, 0, sc->tp_datalen - len); } h = (struct tp_header *)(sc->tp_data); if (params->tp_type >= TYPE2) { ibt = sc->tp_data[params->tp_button]; ntouch = sc->tp_data[params->tp_button - 1]; } /* range check */ if (ntouch < 0) ntouch = 0; else if (ntouch > MAX_FINGERS) ntouch = MAX_FINGERS; for (i = 0; i != ntouch; i++) { f = (struct tp_finger *)(sc->tp_data + params->tp_offset + params->tp_delta + i * params->tp_fsize); /* swap endianness, if any */ if (le16toh(0x1234) != 0x1234) { f->origin = le16toh((uint16_t)f->origin); f->abs_x = le16toh((uint16_t)f->abs_x); f->abs_y = le16toh((uint16_t)f->abs_y); f->rel_x = le16toh((uint16_t)f->rel_x); f->rel_y = le16toh((uint16_t)f->rel_y); f->tool_major = le16toh((uint16_t)f->tool_major); f->tool_minor = le16toh((uint16_t)f->tool_minor); f->orientation = le16toh((uint16_t)f->orientation); f->touch_major = le16toh((uint16_t)f->touch_major); f->touch_minor = le16toh((uint16_t)f->touch_minor); f->pressure = le16toh((uint16_t)f->pressure); f->multi = le16toh((uint16_t)f->multi); } DPRINTFN(WSP_LLEVEL_INFO, "[%d]ibt=%d, taps=%d, o=%4d, ax=%5d, ay=%5d, " "rx=%5d, ry=%5d, tlmaj=%4d, tlmin=%4d, ot=%4x, " "tchmaj=%4d, tchmin=%4d, presure=%4d, m=%4x\n", i, ibt, ntouch, f->origin, f->abs_x, f->abs_y, f->rel_x, f->rel_y, f->tool_major, f->tool_minor, f->orientation, f->touch_major, f->touch_minor, f->pressure, f->multi); sc->pos_x[i] = f->abs_x; sc->pos_y[i] = -f->abs_y; sc->index[i] = f; } sc->sc_status.flags &= ~MOUSE_POSCHANGED; sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED; sc->sc_status.obutton = sc->sc_status.button; sc->sc_status.button = 0; if (ibt != 0) { sc->sc_status.button |= MOUSE_BUTTON1DOWN; sc->ibtn = 1; } sc->intr_count++; if (sc->ntaps < ntouch) { switch (ntouch) { case 1: if (sc->index[0]->touch_major > tun.pressure_tap_threshold && sc->index[0]->tool_major <= 1200) sc->ntaps = 1; break; case 2: if (sc->index[0]->touch_major > tun.pressure_tap_threshold-30 && sc->index[1]->touch_major > tun.pressure_tap_threshold-30) sc->ntaps = 2; break; case 3: if (sc->index[0]->touch_major > tun.pressure_tap_threshold-40 && sc->index[1]->touch_major > tun.pressure_tap_threshold-40 && sc->index[2]->touch_major > tun.pressure_tap_threshold-40) sc->ntaps = 3; break; default: break; } } if (ntouch == 2) { sc->distance = max(sc->distance, max( abs(sc->pos_x[0] - sc->pos_x[1]), abs(sc->pos_y[0] - sc->pos_y[1]))); } if (sc->index[0]->touch_major < tun.pressure_untouch_threshold && sc->sc_status.button == 0) { sc->sc_touch = WSP_UNTOUCH; if (sc->intr_count < WSP_TAP_MAX_COUNT && sc->intr_count > WSP_TAP_THRESHOLD && sc->ntaps && sc->ibtn == 0) { /* * Add a pair of events (button-down and * button-up). */ switch (sc->ntaps) { case 1: if (!(params->caps & HAS_INTEGRATED_BUTTON)) { wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN); DPRINTFN(WSP_LLEVEL_INFO, "LEFT CLICK!\n"); } break; case 2: DPRINTFN(WSP_LLEVEL_INFO, "sum_x=%5d, sum_y=%5d\n", sc->dx_sum, sc->dy_sum); if (sc->distance < MAX_DISTANCE && abs(sc->dx_sum) < 5 && abs(sc->dy_sum) < 5) { wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN); DPRINTFN(WSP_LLEVEL_INFO, "RIGHT CLICK!\n"); } break; case 3: wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN); break; default: /* we don't handle taps of more than three fingers */ break; } wsp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ } if ((sc->dt_sum / tun.scr_hor_threshold) != 0 && sc->ntaps == 2 && sc->scr_mode == WSP_SCR_HOR) { /* * translate T-axis into button presses * until further */ if (sc->dt_sum > 0) wsp_add_to_queue(sc, 0, 0, 0, 1UL << 3); else if (sc->dt_sum < 0) wsp_add_to_queue(sc, 0, 0, 0, 1UL << 4); } sc->dz_count = WSP_DZ_MAX_COUNT; sc->dz_sum = 0; sc->intr_count = 0; sc->ibtn = 0; sc->ntaps = 0; sc->finger = 0; sc->distance = 0; sc->dt_sum = 0; sc->dx_sum = 0; sc->dy_sum = 0; sc->rdx = 0; sc->rdy = 0; sc->rdz = 0; sc->scr_mode = WSP_SCR_NONE; } else if (sc->index[0]->touch_major >= tun.pressure_touch_threshold && sc->sc_touch == WSP_UNTOUCH) { /* ignore first touch */ sc->sc_touch = WSP_FIRST_TOUCH; } else if (sc->index[0]->touch_major >= tun.pressure_touch_threshold && sc->sc_touch == WSP_FIRST_TOUCH) { /* ignore second touch */ sc->sc_touch = WSP_SECOND_TOUCH; DPRINTFN(WSP_LLEVEL_INFO, "Fist pre_x=%5d, pre_y=%5d\n", sc->pre_pos_x, sc->pre_pos_y); } else { if (sc->sc_touch == WSP_SECOND_TOUCH) sc->sc_touch = WSP_TOUCHING; if (ntouch != 0 && sc->index[0]->touch_major >= tun.pressure_touch_threshold) { dx = sc->pos_x[0] - sc->pre_pos_x; dy = sc->pos_y[0] - sc->pre_pos_y; /* Ignore movement during button is releasing */ if (sc->ibtn != 0 && sc->sc_status.button == 0) dx = dy = 0; /* Ignore movement if ntouch changed */ if (sc->o_ntouch != ntouch) dx = dy = 0; - /* Ignore unexpeted movment when typing */ + /* Ignore unexpeted movement when typing */ if (ntouch == 1 && sc->index[0]->tool_major > 1200) dx = dy = 0; if (sc->ibtn != 0 && ntouch == 1 && sc->intr_count < WSP_TAP_MAX_COUNT && abs(sc->dx_sum) < 1 && abs(sc->dy_sum) < 1 ) dx = dy = 0; if (ntouch == 2 && sc->sc_status.button != 0) { dx = sc->pos_x[sc->finger] - sc->pre_pos_x; dy = sc->pos_y[sc->finger] - sc->pre_pos_y; /* * Ignore movement of switch finger or * movement from ibt=0 to ibt=1 */ if (sc->index[0]->origin == 0 || sc->index[1]->origin == 0 || sc->sc_status.obutton != sc->sc_status.button) { dx = dy = 0; sc->finger = 0; } if ((abs(sc->index[0]->rel_x) + abs(sc->index[0]->rel_y)) < (abs(sc->index[1]->rel_x) + abs(sc->index[1]->rel_y)) && sc->finger == 0) { sc->sc_touch = WSP_SECOND_TOUCH; dx = dy = 0; sc->finger = 1; } if ((abs(sc->index[0]->rel_x) + abs(sc->index[0]->rel_y)) >= (abs(sc->index[1]->rel_x) + abs(sc->index[1]->rel_y)) && sc->finger == 1) { sc->sc_touch = WSP_SECOND_TOUCH; dx = dy = 0; sc->finger = 0; } DPRINTFN(WSP_LLEVEL_INFO, "dx=%5d, dy=%5d, mov=%5d\n", dx, dy, sc->finger); } if (sc->dz_count--) { rdz = (dy + sc->rdz) % tun.scale_factor; sc->dz_sum -= (dy + sc->rdz) / tun.scale_factor; sc->rdz = rdz; } if ((sc->dz_sum / tun.z_factor) != 0) sc->dz_count = 0; } rdx = (dx + sc->rdx) % tun.scale_factor; dx = (dx + sc->rdx) / tun.scale_factor; sc->rdx = rdx; rdy = (dy + sc->rdy) % tun.scale_factor; dy = (dy + sc->rdy) / tun.scale_factor; sc->rdy = rdy; sc->dx_sum += dx; sc->dy_sum += dy; if (ntouch == 2 && sc->sc_status.button == 0) { if (sc->scr_mode == WSP_SCR_NONE && abs(sc->dx_sum) + abs(sc->dy_sum) > tun.scr_hor_threshold) sc->scr_mode = abs(sc->dx_sum) > abs(sc->dy_sum) * 2 ? WSP_SCR_HOR : WSP_SCR_VER; DPRINTFN(WSP_LLEVEL_INFO, "scr_mode=%5d, count=%d, dx_sum=%d, dy_sum=%d\n", sc->scr_mode, sc->intr_count, sc->dx_sum, sc->dy_sum); if (sc->scr_mode == WSP_SCR_HOR) sc->dt_sum += dx; else sc->dt_sum = 0; dx = dy = 0; if (sc->dz_count == 0) dz = sc->dz_sum / tun.z_factor; if (sc->scr_mode == WSP_SCR_HOR || abs(sc->pos_x[0] - sc->pos_x[1]) > MAX_DISTANCE || abs(sc->pos_y[0] - sc->pos_y[1]) > MAX_DISTANCE) dz = 0; } if (ntouch == 3) dx = dy = dz = 0; if (sc->intr_count < WSP_TAP_MAX_COUNT && abs(dx) < 3 && abs(dy) < 3 && abs(dz) < 3) dx = dy = dz = 0; else sc->intr_count = WSP_TAP_MAX_COUNT; if (dx || dy || dz) sc->sc_status.flags |= MOUSE_POSCHANGED; DPRINTFN(WSP_LLEVEL_INFO, "dx=%5d, dy=%5d, dz=%5d, sc_touch=%x, btn=%x\n", dx, dy, dz, sc->sc_touch, sc->sc_status.button); sc->sc_status.dx += dx; sc->sc_status.dy += dy; sc->sc_status.dz += dz; wsp_add_to_queue(sc, dx, -dy, dz, sc->sc_status.button); if (sc->dz_count == 0) { sc->dz_sum = 0; sc->rdz = 0; } } sc->pre_pos_x = sc->pos_x[0]; sc->pre_pos_y = sc->pos_y[0]; if (ntouch == 2 && sc->sc_status.button != 0) { sc->pre_pos_x = sc->pos_x[sc->finger]; sc->pre_pos_y = sc->pos_y[sc->finger]; } sc->o_ntouch = ntouch; case USB_ST_SETUP: tr_setup: /* check if we can put more data into the FIFO */ if (usb_fifo_put_bytes_max( sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { usbd_xfer_set_frame_len(xfer, 0, sc->tp_datalen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void wsp_add_to_queue(struct wsp_softc *sc, int dx, int dy, int dz, uint32_t buttons_in) { uint32_t buttons_out; uint8_t buf[8]; dx = imin(dx, 254); dx = imax(dx, -256); dy = imin(dy, 254); dy = imax(dy, -256); dz = imin(dz, 126); dz = imax(dz, -128); buttons_out = MOUSE_MSC_BUTTONS; if (buttons_in & MOUSE_BUTTON1DOWN) buttons_out &= ~MOUSE_MSC_BUTTON1UP; else if (buttons_in & MOUSE_BUTTON2DOWN) buttons_out &= ~MOUSE_MSC_BUTTON2UP; else if (buttons_in & MOUSE_BUTTON3DOWN) buttons_out &= ~MOUSE_MSC_BUTTON3UP; /* Encode the mouse data in standard format; refer to mouse(4) */ buf[0] = sc->sc_mode.syncmask[1]; buf[0] |= buttons_out; buf[1] = dx >> 1; buf[2] = dy >> 1; buf[3] = dx - (dx >> 1); buf[4] = dy - (dy >> 1); /* Encode extra bytes for level 1 */ if (sc->sc_mode.level == 1) { buf[5] = dz >> 1; /* dz / 2 */ buf[6] = dz - (dz >> 1);/* dz - (dz / 2) */ buf[7] = (((~buttons_in) >> 3) & MOUSE_SYS_EXTBUTTONS); } usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, sc->sc_mode.packetsize, 1); } static void wsp_reset_buf(struct wsp_softc *sc) { /* reset read queue */ usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); } static void wsp_start_read(struct usb_fifo *fifo) { struct wsp_softc *sc = usb_fifo_softc(fifo); int rate; /* Check if we should override the default polling interval */ rate = sc->sc_pollrate; /* Range check rate */ if (rate > 1000) rate = 1000; /* Check for set rate */ if ((rate > 0) && (sc->sc_xfer[WSP_INTR_DT] != NULL)) { /* Stop current transfer, if any */ usbd_transfer_stop(sc->sc_xfer[WSP_INTR_DT]); /* Set new interval */ usbd_xfer_set_interval(sc->sc_xfer[WSP_INTR_DT], 1000 / rate); /* Only set pollrate once */ sc->sc_pollrate = 0; } usbd_transfer_start(sc->sc_xfer[WSP_INTR_DT]); } static void wsp_stop_read(struct usb_fifo *fifo) { struct wsp_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[WSP_INTR_DT]); } static int wsp_open(struct usb_fifo *fifo, int fflags) { DPRINTFN(WSP_LLEVEL_INFO, "\n"); if (fflags & FREAD) { struct wsp_softc *sc = usb_fifo_softc(fifo); int rc; if (sc->sc_state & WSP_ENABLED) return (EBUSY); if (usb_fifo_alloc_buffer(fifo, WSP_FIFO_BUF_SIZE, WSP_FIFO_QUEUE_MAXLEN)) { return (ENOMEM); } rc = wsp_enable(sc); if (rc != 0) { usb_fifo_free_buffer(fifo); return (rc); } } return (0); } static void wsp_close(struct usb_fifo *fifo, int fflags) { if (fflags & FREAD) { struct wsp_softc *sc = usb_fifo_softc(fifo); wsp_disable(sc); usb_fifo_free_buffer(fifo); } } int wsp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { struct wsp_softc *sc = usb_fifo_softc(fifo); mousemode_t mode; int error = 0; mtx_lock(&sc->sc_mutex); switch (cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->sc_hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->sc_mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) /* Don't change the current setting */ ; else if ((mode.level < 0) || (mode.level > 1)) { error = EINVAL; goto done; } sc->sc_mode.level = mode.level; sc->sc_pollrate = mode.rate; sc->sc_hw.buttons = 3; if (sc->sc_mode.level == 0) { sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } wsp_reset_buf(sc); break; case MOUSE_GETLEVEL: *(int *)addr = sc->sc_mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) { error = EINVAL; goto done; } sc->sc_mode.level = *(int *)addr; sc->sc_hw.buttons = 3; if (sc->sc_mode.level == 0) { sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } wsp_reset_buf(sc); break; case MOUSE_GETSTATUS:{ mousestatus_t *status = (mousestatus_t *)addr; *status = sc->sc_status; sc->sc_status.obutton = sc->sc_status.button; sc->sc_status.button = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; if (status->dx || status->dy || status->dz) status->flags |= MOUSE_POSCHANGED; if (status->button != status->obutton) status->flags |= MOUSE_BUTTONSCHANGED; break; } default: error = ENOTTY; } done: mtx_unlock(&sc->sc_mutex); return (error); } static device_method_t wsp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wsp_probe), DEVMETHOD(device_attach, wsp_attach), DEVMETHOD(device_detach, wsp_detach), DEVMETHOD_END }; static driver_t wsp_driver = { .name = WSP_DRIVER_NAME, .methods = wsp_methods, .size = sizeof(struct wsp_softc) }; static devclass_t wsp_devclass; DRIVER_MODULE(wsp, uhub, wsp_driver, wsp_devclass, NULL, 0); MODULE_DEPEND(wsp, usb, 1, 1, 1); MODULE_VERSION(wsp, 1); USB_PNP_HOST_INFO(wsp_devs); Index: head/sys/dev/usb/misc/udbp.c =================================================================== --- head/sys/dev/usb/misc/udbp.c (revision 298931) +++ head/sys/dev/usb/misc/udbp.c (revision 298932) @@ -1,859 +1,859 @@ /*- * Copyright (c) 1996-2000 Whistle Communications, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of author nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY NICK HIBMA AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* Driver for arbitrary double bulk pipe devices. * The driver assumes that there will be the same driver on the other side. * * XXX Some more information on what the framing of the IP packets looks like. * * To take full advantage of bulk transmission, packets should be chosen * between 1k and 5k in size (1k to make sure the sending side starts * streaming, and <5k to avoid overflowing the system with small TDs). */ /* probe/attach/detach: * Connect the driver to the hardware and netgraph * * The reason we submit a bulk in transfer is that USB does not know about * interrupts. The bulk transfer continuously polls the device for data. * While the device has no data available, the device NAKs the TDs. As soon * as there is data, the transfer happens and the data comes flowing in. * * In case you were wondering, interrupt transfers happen exactly that way. * It therefore doesn't make sense to use the interrupt pipe to signal * 'data ready' and then schedule a bulk transfer to fetch it. That would * incur a 2ms delay at least, without reducing bandwidth requirements. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR udbp_debug #include #include #include #include #include #include #include #ifdef USB_DEBUG static int udbp_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RWTUN, &udbp_debug, 0, "udbp debug level"); #endif #define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in * msecs */ #define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one * transfer */ #define UDBP_T_WR 0 #define UDBP_T_RD 1 #define UDBP_T_WR_CS 2 #define UDBP_T_RD_CS 3 #define UDBP_T_MAX 4 #define UDBP_Q_MAXLEN 50 struct udbp_softc { struct mtx sc_mtx; struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ struct usb_xfer *sc_xfer[UDBP_T_MAX]; node_p sc_node; /* back pointer to node */ hook_p sc_hook; /* pointer to the hook */ struct mbuf *sc_bulk_in_buffer; uint32_t sc_packets_in; /* packets in from downstream */ uint32_t sc_packets_out; /* packets out towards downstream */ uint8_t sc_flags; #define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ #define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ uint8_t sc_name[16]; }; /* prototypes */ static int udbp_modload(module_t mod, int event, void *data); static device_probe_t udbp_probe; static device_attach_t udbp_attach; static device_detach_t udbp_detach; static usb_callback_t udbp_bulk_read_callback; static usb_callback_t udbp_bulk_read_clear_stall_callback; static usb_callback_t udbp_bulk_write_callback; static usb_callback_t udbp_bulk_write_clear_stall_callback; static void udbp_bulk_read_complete(node_p, hook_p, void *, int); static ng_constructor_t ng_udbp_constructor; static ng_rcvmsg_t ng_udbp_rcvmsg; static ng_shutdown_t ng_udbp_rmnode; static ng_newhook_t ng_udbp_newhook; static ng_connect_t ng_udbp_connect; static ng_rcvdata_t ng_udbp_rcvdata; static ng_disconnect_t ng_udbp_disconnect; /* Parse type for struct ngudbpstat */ static const struct ng_parse_struct_field ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; static const struct ng_parse_type ng_udbp_stat_type = { &ng_parse_struct_type, &ng_udbp_stat_type_fields }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_udbp_cmdlist[] = { { NGM_UDBP_COOKIE, NGM_UDBP_GET_STATUS, "getstatus", NULL, &ng_udbp_stat_type, }, { NGM_UDBP_COOKIE, NGM_UDBP_SET_FLAG, "setflag", &ng_parse_int32_type, NULL }, {0} }; /* Netgraph node type descriptor */ static struct ng_type ng_udbp_typestruct = { .version = NG_ABI_VERSION, .name = NG_UDBP_NODE_TYPE, .constructor = ng_udbp_constructor, .rcvmsg = ng_udbp_rcvmsg, .shutdown = ng_udbp_rmnode, .newhook = ng_udbp_newhook, .connect = ng_udbp_connect, .rcvdata = ng_udbp_rcvdata, .disconnect = ng_udbp_disconnect, .cmdlist = ng_udbp_cmdlist, }; /* USB config */ static const struct usb_config udbp_config[UDBP_T_MAX] = { [UDBP_T_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UDBP_BUFFERSIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &udbp_bulk_write_callback, .timeout = UDBP_TIMEOUT, }, [UDBP_T_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UDBP_BUFFERSIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &udbp_bulk_read_callback, }, [UDBP_T_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &udbp_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ }, [UDBP_T_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &udbp_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ }, }; static devclass_t udbp_devclass; static device_method_t udbp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, udbp_probe), DEVMETHOD(device_attach, udbp_attach), DEVMETHOD(device_detach, udbp_detach), DEVMETHOD_END }; static driver_t udbp_driver = { .name = "udbp", .methods = udbp_methods, .size = sizeof(struct udbp_softc), }; static const STRUCT_USB_HOST_ID udbp_devs[] = { {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_TURBOCONNECT, 0)}, {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_GADGETZERO, 0)}, {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301, 0)}, {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302, 0)}, {USB_VPI(USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZLINK, 0)}, {USB_VPI(USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB, 0)}, }; DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, udbp_modload, 0); MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); MODULE_DEPEND(udbp, usb, 1, 1, 1); MODULE_VERSION(udbp, 1); USB_PNP_HOST_INFO(udbp_devs); static int udbp_modload(module_t mod, int event, void *data) { int error; switch (event) { case MOD_LOAD: error = ng_newtype(&ng_udbp_typestruct); if (error != 0) { printf("%s: Could not register " "Netgraph node type, error=%d\n", NG_UDBP_NODE_TYPE, error); } break; case MOD_UNLOAD: error = ng_rmtype(&ng_udbp_typestruct); break; default: error = EOPNOTSUPP; break; } return (error); } static int udbp_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != 0) return (ENXIO); return (usbd_lookup_id_by_uaa(udbp_devs, sizeof(udbp_devs), uaa)); } static int udbp_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct udbp_softc *sc = device_get_softc(dev); int error; device_set_usb_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE); error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx); if (error) { DPRINTF("error=%s\n", usbd_errstr(error)); goto detach; } NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN); NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN); /* create Netgraph node */ if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { printf("%s: Could not create Netgraph node\n", sc->sc_name); sc->sc_node = NULL; goto detach; } /* name node */ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { printf("%s: Could not name node\n", sc->sc_name); NG_NODE_UNREF(sc->sc_node); sc->sc_node = NULL; goto detach; } NG_NODE_SET_PRIVATE(sc->sc_node, sc); /* the device is now operational */ return (0); /* success */ detach: udbp_detach(dev); return (ENOMEM); /* failure */ } static int udbp_detach(device_t dev) { struct udbp_softc *sc = device_get_softc(dev); /* destroy Netgraph node */ if (sc->sc_node != NULL) { NG_NODE_SET_PRIVATE(sc->sc_node, NULL); ng_rmnode_self(sc->sc_node); sc->sc_node = NULL; } /* free USB transfers, if any */ usbd_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); mtx_destroy(&sc->sc_mtx); /* destroy queues */ NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq); NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri); /* extra check */ if (sc->sc_bulk_in_buffer) { m_freem(sc->sc_bulk_in_buffer); sc->sc_bulk_in_buffer = NULL; } return (0); /* success */ } static void udbp_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct udbp_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; struct mbuf *m; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* allocate new mbuf */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { goto tr_setup; } if (!(MCLGET(m, M_NOWAIT))) { m_freem(m); goto tr_setup; } m->m_pkthdr.len = m->m_len = actlen; pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, m->m_data, actlen); sc->sc_bulk_in_buffer = m; DPRINTF("received package %d bytes\n", actlen); case USB_ST_SETUP: tr_setup: if (sc->sc_bulk_in_buffer) { ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0); return; } if (sc->sc_flags & UDBP_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); return; } usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDBP_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); } return; } } static void udbp_bulk_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct udbp_softc *sc = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD]; if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UDBP_FLAG_READ_STALL; usbd_transfer_start(xfer_other); } } static void udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); struct mbuf *m; int error; if (sc == NULL) { return; } mtx_lock(&sc->sc_mtx); m = sc->sc_bulk_in_buffer; if (m) { sc->sc_bulk_in_buffer = NULL; if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) { DPRINTF("No upstream hook\n"); goto done; } sc->sc_packets_in++; NG_SEND_DATA_ONLY(error, sc->sc_hook, m); m = NULL; } done: if (m) { m_freem(m); } /* start USB bulk-in transfer, if not already started */ usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); mtx_unlock(&sc->sc_mtx); } static void udbp_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct udbp_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; struct mbuf *m; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->sc_packets_out++; case USB_ST_SETUP: if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); return; } /* get next mbuf, if any */ NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m); if (m == NULL) { NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m); if (m == NULL) { DPRINTF("Data queue is empty\n"); return; } } if (m->m_pkthdr.len > MCLBYTES) { DPRINTF("truncating large packet " "from %d to %d bytes\n", m->m_pkthdr.len, MCLBYTES); m->m_pkthdr.len = MCLBYTES; } pc = usbd_xfer_get_frame(xfer, 0); usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); DPRINTF("packet out: %d bytes\n", m->m_pkthdr.len); m_freem(m); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDBP_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); } return; } } static void udbp_bulk_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct udbp_softc *sc = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR]; if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); } } /*********************************************************************** * Start of Netgraph methods **********************************************************************/ /* * If this is a device node so this work is done in the attach() * routine and the constructor will return EINVAL as you should not be able * to create nodes that depend on hardware (unless you can add the hardware :) */ static int ng_udbp_constructor(node_p node) { return (EINVAL); } /* * Give our ok for a hook to be added... * If we are not running this might kick a device into life. * Possibly decode information out of the hook name. * Add the hook's private info to the hook structure. * (if we had some). In this example, we assume that there is a * an array of structs, called 'channel' in the private info, * one for each active channel. The private * pointer of each hook points to the appropriate UDBP_hookinfo struct * so that the source of an input packet is easily identified. */ static int ng_udbp_newhook(node_p node, hook_p hook, const char *name) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); int32_t error = 0; if (strcmp(name, NG_UDBP_HOOK_NAME)) { return (EINVAL); } mtx_lock(&sc->sc_mtx); if (sc->sc_hook != NULL) { error = EISCONN; } else { sc->sc_hook = hook; NG_HOOK_SET_PRIVATE(hook, NULL); } mtx_unlock(&sc->sc_mtx); return (error); } /* * Get a netgraph control message. * Check it is one we understand. If needed, send a response. * We could save the address for an async action later, but don't here. * Always free the message. * The response should be in a malloc'd region that the caller can 'free'. * A response is not required. * Theoretically you could respond defferently to old message types if * the cookie in the header didn't match what we consider to be current * (so that old userland programs could continue to work). */ static int ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_UDBP_COOKIE: switch (msg->header.cmd) { case NGM_UDBP_GET_STATUS: { struct ngudbpstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stats = (struct ngudbpstat *)resp->data; mtx_lock(&sc->sc_mtx); stats->packets_in = sc->sc_packets_in; stats->packets_out = sc->sc_packets_out; mtx_unlock(&sc->sc_mtx); break; } case NGM_UDBP_SET_FLAG: if (msg->header.arglen != sizeof(uint32_t)) { error = EINVAL; break; } DPRINTF("flags = 0x%08x\n", *((uint32_t *)msg->data)); break; default: error = EINVAL; /* unknown command */ break; } break; default: error = EINVAL; /* unknown cookie type */ break; } /* Take care of synchronous response, if any */ NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /* * Accept data from the hook and queue it for output. */ static int ng_udbp_rcvdata(hook_p hook, item_p item) { struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct ng_bt_mbufq *queue_ptr; struct mbuf *m; struct ng_tag_prio *ptag; int error; if (sc == NULL) { NG_FREE_ITEM(item); return (EHOSTDOWN); } NGI_GET_M(item, m); NG_FREE_ITEM(item); /* * Now queue the data for when it can be sent */ ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, NG_TAG_PRIO, NULL); if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) queue_ptr = &sc->sc_xmitq_hipri; else queue_ptr = &sc->sc_xmitq; mtx_lock(&sc->sc_mtx); if (NG_BT_MBUFQ_FULL(queue_ptr)) { NG_BT_MBUFQ_DROP(queue_ptr); NG_FREE_M(m); error = ENOBUFS; } else { NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); /* * start bulk-out transfer, if not already started: */ usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); error = 0; } mtx_unlock(&sc->sc_mtx); return (error); } /* * Do local shutdown processing.. - * We are a persistant device, we refuse to go away, and + * We are a persistent device, we refuse to go away, and * only remove our links and reset ourself. */ static int ng_udbp_rmnode(node_p node) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); /* Let old node go */ NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); /* forget it ever existed */ if (sc == NULL) { goto done; } /* Create Netgraph node */ if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { printf("%s: Could not create Netgraph node\n", sc->sc_name); sc->sc_node = NULL; goto done; } /* Name node */ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { printf("%s: Could not name Netgraph node\n", sc->sc_name); NG_NODE_UNREF(sc->sc_node); sc->sc_node = NULL; goto done; } NG_NODE_SET_PRIVATE(sc->sc_node, sc); done: if (sc) { mtx_unlock(&sc->sc_mtx); } return (0); } /* * This is called once we've already connected a new hook to the other node. * It gives us a chance to balk at the last minute. */ static int ng_udbp_connect(hook_p hook) { struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); /* probably not at splnet, force outward queueing */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); mtx_lock(&sc->sc_mtx); sc->sc_flags |= (UDBP_FLAG_READ_STALL | UDBP_FLAG_WRITE_STALL); /* start bulk-in transfer */ usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); /* start bulk-out transfer */ usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); mtx_unlock(&sc->sc_mtx); return (0); } /* * Dook disconnection * * For this type, removal of the last link destroys the node */ static int ng_udbp_disconnect(hook_p hook) { struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int error = 0; if (sc != NULL) { mtx_lock(&sc->sc_mtx); if (hook != sc->sc_hook) { error = EINVAL; } else { /* stop bulk-in transfer */ usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD]); /* stop bulk-out transfer */ usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR]); /* cleanup queues */ NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); if (sc->sc_bulk_in_buffer) { m_freem(sc->sc_bulk_in_buffer); sc->sc_bulk_in_buffer = NULL; } sc->sc_hook = NULL; } mtx_unlock(&sc->sc_mtx); } if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (error); } Index: head/sys/dev/usb/net/if_cue.c =================================================================== --- head/sys/dev/usb/net/if_cue.c (revision 298931) +++ head/sys/dev/usb/net/if_cue.c (revision 298932) @@ -1,655 +1,655 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate * adapters and others. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The * RX filter uses a 512-bit multicast hash table, single perfect entry * for the station address, and promiscuous mode. Unlike the ADMtek * and KLSI chips, the CATC ASIC supports read and write combining - * mode where multiple packets can be transfered using a single bulk + * mode where multiple packets can be transferred using a single bulk * transaction, which helps performance a great deal. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR cue_debug #include #include #include #include /* * Various supported device vendors/products. */ /* Belkin F5U111 adapter covered by NETMATE entry */ static const STRUCT_USB_HOST_ID cue_devs[] = { #define CUE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } CUE_DEV(CATC, NETMATE), CUE_DEV(CATC, NETMATE2), CUE_DEV(SMARTBRIDGES, SMARTLINK), #undef CUE_DEV }; /* prototypes */ static device_probe_t cue_probe; static device_attach_t cue_attach; static device_detach_t cue_detach; static usb_callback_t cue_bulk_read_callback; static usb_callback_t cue_bulk_write_callback; static uether_fn_t cue_attach_post; static uether_fn_t cue_init; static uether_fn_t cue_stop; static uether_fn_t cue_start; static uether_fn_t cue_tick; static uether_fn_t cue_setmulti; static uether_fn_t cue_setpromisc; static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t); static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t); static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t); static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int); static int cue_getmac(struct cue_softc *, void *); static uint32_t cue_mchash(const uint8_t *); static void cue_reset(struct cue_softc *); #ifdef USB_DEBUG static int cue_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue"); SYSCTL_INT(_hw_usb_cue, OID_AUTO, debug, CTLFLAG_RWTUN, &cue_debug, 0, "Debug level"); #endif static const struct usb_config cue_config[CUE_N_TRANSFER] = { [CUE_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + 2), .flags = {.pipe_bof = 1,}, .callback = cue_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [CUE_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 2), .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = cue_bulk_read_callback, }, }; static device_method_t cue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cue_probe), DEVMETHOD(device_attach, cue_attach), DEVMETHOD(device_detach, cue_detach), DEVMETHOD_END }; static driver_t cue_driver = { .name = "cue", .methods = cue_methods, .size = sizeof(struct cue_softc), }; static devclass_t cue_devclass; DRIVER_MODULE(cue, uhub, cue_driver, cue_devclass, NULL, 0); MODULE_DEPEND(cue, uether, 1, 1, 1); MODULE_DEPEND(cue, usb, 1, 1, 1); MODULE_DEPEND(cue, ether, 1, 1, 1); MODULE_VERSION(cue, 1); USB_PNP_HOST_INFO(cue_devs); static const struct usb_ether_methods cue_ue_methods = { .ue_attach_post = cue_attach_post, .ue_start = cue_start, .ue_init = cue_init, .ue_stop = cue_stop, .ue_tick = cue_tick, .ue_setmulti = cue_setmulti, .ue_setpromisc = cue_setpromisc, }; #define CUE_SETBIT(sc, reg, x) \ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) #define CUE_CLRBIT(sc, reg, x) \ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) static uint8_t cue_csr_read_1(struct cue_softc *sc, uint16_t reg) { struct usb_device_request req; uint8_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); if (uether_do_request(&sc->sc_ue, &req, &val, 1000)) { /* ignore any errors */ } return (val); } static uint16_t cue_csr_read_2(struct cue_softc *sc, uint8_t reg) { struct usb_device_request req; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 2); (void)uether_do_request(&sc->sc_ue, &req, &val, 1000); return (le16toh(val)); } static int cue_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); return (uether_do_request(&sc->sc_ue, &req, NULL, 1000)); } static int cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int len) { struct usb_device_request req; if (cmd == CUE_CMD_READSRAM) req.bmRequestType = UT_READ_VENDOR_DEVICE; else req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = cmd; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); } static int cue_getmac(struct cue_softc *sc, void *buf) { struct usb_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_GET_MACADDR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, ETHER_ADDR_LEN); return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); } #define CUE_BITS 9 static uint32_t cue_mchash(const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); return (crc & ((1 << CUE_BITS) - 1)); } static void cue_setpromisc(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); CUE_LOCK_ASSERT(sc, MA_OWNED); /* if we want promiscuous mode, set the allframes bit */ if (ifp->if_flags & IFF_PROMISC) CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); else CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); /* write multicast hash-bits */ cue_setmulti(ue); } static void cue_setmulti(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); struct ifmultiaddr *ifma; uint32_t h = 0, i; uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; CUE_LOCK_ASSERT(sc, MA_OWNED); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { for (i = 0; i < 8; i++) hashtbl[i] = 0xff; cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8); return; } /* now program new ones */ if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); hashtbl[h >> 3] |= 1 << (h & 0x7); } if_maddr_runlock(ifp); /* * Also include the broadcast address in the filter * so we can receive broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { h = cue_mchash(ifp->if_broadcastaddr); hashtbl[h >> 3] |= 1 << (h & 0x7); } cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8); } static void cue_reset(struct cue_softc *sc) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_RESET; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 0); if (uether_do_request(&sc->sc_ue, &req, NULL, 1000)) { /* ignore any errors */ } /* * wait a little while for the chip to get its brains in order: */ uether_pause(&sc->sc_ue, hz / 100); } static void cue_attach_post(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); cue_getmac(sc, ue->ue_eaddr); } static int cue_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != CUE_CONFIG_IDX) return (ENXIO); if (uaa->info.bIfaceIndex != CUE_IFACE_IDX) return (ENXIO); return (usbd_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int cue_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct cue_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; uint8_t iface_index; int error; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); iface_index = CUE_IFACE_IDX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB transfers failed\n"); goto detach; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &cue_ue_methods; error = uether_ifattach(ue); if (error) { device_printf(dev, "could not attach interface\n"); goto detach; } return (0); /* success */ detach: cue_detach(dev); return (ENXIO); /* failure */ } static int cue_detach(device_t dev) { struct cue_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; usbd_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER); uether_ifdetach(ue); mtx_destroy(&sc->sc_mtx); return (0); } static void cue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct cue_softc *sc = usbd_xfer_softc(xfer); struct usb_ether *ue = &sc->sc_ue; struct ifnet *ifp = uether_getifp(ue); struct usb_page_cache *pc; uint8_t buf[2]; int len; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen <= (int)(2 + sizeof(struct ether_header))) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, 2); actlen -= 2; len = buf[0] | (buf[1] << 8); len = min(actlen, len); uether_rxbuf(ue, pc, 2, len); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); uether_rxflush(ue); return; default: /* Error */ DPRINTF("bulk read error, %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void cue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct cue_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct usb_page_cache *pc; struct mbuf *m; uint8_t buf[2]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) return; if (m->m_pkthdr.len > MCLBYTES) m->m_pkthdr.len = MCLBYTES; usbd_xfer_set_frame_len(xfer, 0, (m->m_pkthdr.len + 2)); /* the first two bytes are the frame length */ buf[0] = (uint8_t)(m->m_pkthdr.len); buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, buf, 2); usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len); /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m); m_freem(m); usbd_transfer_submit(xfer); return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void cue_tick(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); CUE_LOCK_ASSERT(sc, MA_OWNED); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_SINGLECOLL)); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_MULTICOLL)); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_EXCESSCOLL)); if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } static void cue_start(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); /* * start the USB transfers, if not already started: */ usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]); usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]); } static void cue_init(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); int i; CUE_LOCK_ASSERT(sc, MA_OWNED); /* * Cancel pending I/O and free all RX/TX buffers. */ cue_stop(ue); #if 0 cue_reset(sc); #endif /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(ifp)[i]); /* Enable RX logic. */ cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON); /* Load the multicast filter */ cue_setpromisc(ue); /* * Set the number of RX and TX buffers that we want * to reserve inside the ASIC. */ cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); /* Set advanced operation modes. */ cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */ /* Program the LED operation. */ cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); usbd_xfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]); ifp->if_drv_flags |= IFF_DRV_RUNNING; cue_start(ue); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void cue_stop(struct usb_ether *ue) { struct cue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); CUE_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* * stop all the transfers, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]); usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]); cue_csr_write_1(sc, CUE_ETHCTL, 0); cue_reset(sc); } Index: head/sys/dev/usb/net/if_rue.c =================================================================== --- head/sys/dev/usb/net/if_rue.c (revision 298931) +++ head/sys/dev/usb/net/if_rue.c (revision 298932) @@ -1,922 +1,922 @@ /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * RealTek RTL8150 USB to fast ethernet controller driver. * Datasheet is available from * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR rue_debug #include #include #include #include #ifdef USB_DEBUG static int rue_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue"); SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RWTUN, &rue_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const STRUCT_USB_HOST_ID rue_devs[] = { {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)}, {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)}, {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01, 0)}, }; /* prototypes */ static device_probe_t rue_probe; static device_attach_t rue_attach; static device_detach_t rue_detach; static miibus_readreg_t rue_miibus_readreg; static miibus_writereg_t rue_miibus_writereg; static miibus_statchg_t rue_miibus_statchg; static usb_callback_t rue_intr_callback; static usb_callback_t rue_bulk_read_callback; static usb_callback_t rue_bulk_write_callback; static uether_fn_t rue_attach_post; static uether_fn_t rue_init; static uether_fn_t rue_stop; static uether_fn_t rue_start; static uether_fn_t rue_tick; static uether_fn_t rue_setmulti; static uether_fn_t rue_setpromisc; static int rue_read_mem(struct rue_softc *, uint16_t, void *, int); static int rue_write_mem(struct rue_softc *, uint16_t, void *, int); static uint8_t rue_csr_read_1(struct rue_softc *, uint16_t); static uint16_t rue_csr_read_2(struct rue_softc *, uint16_t); static int rue_csr_write_1(struct rue_softc *, uint16_t, uint8_t); static int rue_csr_write_2(struct rue_softc *, uint16_t, uint16_t); static int rue_csr_write_4(struct rue_softc *, int, uint32_t); static void rue_reset(struct rue_softc *); static int rue_ifmedia_upd(struct ifnet *); static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *); static const struct usb_config rue_config[RUE_N_TRANSFER] = { [RUE_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MCLBYTES, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = rue_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [RUE_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 4), .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = rue_bulk_read_callback, .timeout = 0, /* no timeout */ }, [RUE_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = rue_intr_callback, }, }; static device_method_t rue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rue_probe), DEVMETHOD(device_attach, rue_attach), DEVMETHOD(device_detach, rue_detach), /* MII interface */ DEVMETHOD(miibus_readreg, rue_miibus_readreg), DEVMETHOD(miibus_writereg, rue_miibus_writereg), DEVMETHOD(miibus_statchg, rue_miibus_statchg), DEVMETHOD_END }; static driver_t rue_driver = { .name = "rue", .methods = rue_methods, .size = sizeof(struct rue_softc), }; static devclass_t rue_devclass; DRIVER_MODULE_ORDERED(rue, uhub, rue_driver, rue_devclass, NULL, NULL, SI_ORDER_ANY); DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, NULL, NULL); MODULE_DEPEND(rue, uether, 1, 1, 1); MODULE_DEPEND(rue, usb, 1, 1, 1); MODULE_DEPEND(rue, ether, 1, 1, 1); MODULE_DEPEND(rue, miibus, 1, 1, 1); MODULE_VERSION(rue, 1); USB_PNP_HOST_INFO(rue_devs); static const struct usb_ether_methods rue_ue_methods = { .ue_attach_post = rue_attach_post, .ue_start = rue_start, .ue_init = rue_init, .ue_stop = rue_stop, .ue_tick = rue_tick, .ue_setmulti = rue_setmulti, .ue_setpromisc = rue_setpromisc, .ue_mii_upd = rue_ifmedia_upd, .ue_mii_sts = rue_ifmedia_sts, }; #define RUE_SETBIT(sc, reg, x) \ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x)) #define RUE_CLRBIT(sc, reg, x) \ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x)) static int rue_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len) { struct usb_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); } static int rue_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); } static uint8_t rue_csr_read_1(struct rue_softc *sc, uint16_t reg) { uint8_t val; rue_read_mem(sc, reg, &val, 1); return (val); } static uint16_t rue_csr_read_2(struct rue_softc *sc, uint16_t reg) { uint8_t val[2]; rue_read_mem(sc, reg, &val, 2); return (UGETW(val)); } static int rue_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val) { return (rue_write_mem(sc, reg, &val, 1)); } static int rue_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val) { uint8_t temp[2]; USETW(temp, val); return (rue_write_mem(sc, reg, &temp, 2)); } static int rue_csr_write_4(struct rue_softc *sc, int reg, uint32_t val) { uint8_t temp[4]; USETDW(temp, val); return (rue_write_mem(sc, reg, &temp, 4)); } static int rue_miibus_readreg(device_t dev, int phy, int reg) { struct rue_softc *sc = device_get_softc(dev); uint16_t rval; uint16_t ruereg; int locked; if (phy != 0) /* RTL8150 supports PHY == 0, only */ return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) RUE_LOCK(sc); switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: rval = 0; goto done; default: if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) { rval = rue_csr_read_1(sc, reg); goto done; } device_printf(sc->sc_ue.ue_dev, "bad phy register\n"); rval = 0; goto done; } rval = rue_csr_read_2(sc, ruereg); done: if (!locked) RUE_UNLOCK(sc); return (rval); } static int rue_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rue_softc *sc = device_get_softc(dev); uint16_t ruereg; int locked; if (phy != 0) /* RTL8150 supports PHY == 0, only */ return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) RUE_LOCK(sc); switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: goto done; default: if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) { rue_csr_write_1(sc, reg, data); goto done; } device_printf(sc->sc_ue.ue_dev, " bad phy register\n"); goto done; } rue_csr_write_2(sc, ruereg, data); done: if (!locked) RUE_UNLOCK(sc); return (0); } static void rue_miibus_statchg(device_t dev) { /* * When the code below is enabled the card starts doing weird * things after link going from UP to DOWN and back UP. * * Looks like some of register writes below messes up PHY * interface. * * No visible regressions were found after commenting this code * out, so that disable it for good. */ #if 0 struct rue_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); uint16_t bmcr; int locked; locked = mtx_owned(&sc->sc_mtx); if (!locked) RUE_LOCK(sc); RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); bmcr = rue_csr_read_2(sc, RUE_BMCR); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) bmcr |= RUE_BMCR_SPD_SET; else bmcr &= ~RUE_BMCR_SPD_SET; if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) bmcr |= RUE_BMCR_DUPLEX; else bmcr &= ~RUE_BMCR_DUPLEX; rue_csr_write_2(sc, RUE_BMCR, bmcr); RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); if (!locked) RUE_UNLOCK(sc); #endif } static void rue_setpromisc(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); RUE_LOCK_ASSERT(sc, MA_OWNED); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) RUE_SETBIT(sc, RUE_RCR, RUE_RCR_AAP); else RUE_CLRBIT(sc, RUE_RCR, RUE_RCR_AAP); } /* * Program the 64-bit multicast hash filter. */ static void rue_setmulti(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); uint16_t rxcfg; int h = 0; uint32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; int mcnt = 0; RUE_LOCK_ASSERT(sc, MA_OWNED); rxcfg = rue_csr_read_2(sc, RUE_RCR); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); rxcfg &= ~RUE_RCR_AM; rue_csr_write_2(sc, RUE_RCR, rxcfg); rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ rue_csr_write_4(sc, RUE_MAR0, 0); rue_csr_write_4(sc, RUE_MAR4, 0); /* now program new ones */ if_maddr_rlock(ifp); TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } if_maddr_runlock(ifp); if (mcnt) rxcfg |= RUE_RCR_AM; else rxcfg &= ~RUE_RCR_AM; rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); rue_csr_write_2(sc, RUE_RCR, rxcfg); rue_csr_write_4(sc, RUE_MAR0, hashes[0]); rue_csr_write_4(sc, RUE_MAR4, hashes[1]); } static void rue_reset(struct rue_softc *sc) { int i; rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); for (i = 0; i != RUE_TIMEOUT; i++) { if (uether_pause(&sc->sc_ue, hz / 1000)) break; if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) break; } if (i == RUE_TIMEOUT) device_printf(sc->sc_ue.ue_dev, "reset never completed\n"); uether_pause(&sc->sc_ue, hz / 100); } static void rue_attach_post(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); /* reset the adapter */ rue_reset(sc); /* get station address from the EEPROM */ rue_read_mem(sc, RUE_EEPROM_IDR0, ue->ue_eaddr, ETHER_ADDR_LEN); } /* * Probe for a RTL8150 chip. */ static int rue_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != RUE_CONFIG_IDX) return (ENXIO); if (uaa->info.bIfaceIndex != RUE_IFACE_IDX) return (ENXIO); return (usbd_lookup_id_by_uaa(rue_devs, sizeof(rue_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int rue_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct rue_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; uint8_t iface_index; int error; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); iface_index = RUE_IFACE_IDX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rue_config, RUE_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB transfers failed\n"); goto detach; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &rue_ue_methods; error = uether_ifattach(ue); if (error) { device_printf(dev, "could not attach interface\n"); goto detach; } return (0); /* success */ detach: rue_detach(dev); return (ENXIO); /* failure */ } static int rue_detach(device_t dev) { struct rue_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; usbd_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER); uether_ifdetach(ue); mtx_destroy(&sc->sc_mtx); return (0); } static void rue_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct rue_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct rue_intrpkt pkt; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && actlen >= (int)sizeof(pkt)) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &pkt, sizeof(pkt)); if_inc_counter(ifp, IFCOUNTER_IERRORS, pkt.rue_rxlost_cnt); if_inc_counter(ifp, IFCOUNTER_IERRORS, pkt.rue_crcerr_cnt); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, pkt.rue_col_cnt); } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void rue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct rue_softc *sc = usbd_xfer_softc(xfer); struct usb_ether *ue = &sc->sc_ue; struct ifnet *ifp = uether_getifp(ue); struct usb_page_cache *pc; uint16_t status; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < 4) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, actlen - 4, &status, sizeof(status)); actlen -= 4; - /* check recieve packet was valid or not */ + /* check receive packet was valid or not */ status = le16toh(status); if ((status & RUE_RXSTAT_VALID) == 0) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } uether_rxbuf(ue, pc, 0, actlen); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); uether_rxflush(ue); return; default: /* Error */ DPRINTF("bulk read error, %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void rue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct rue_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct usb_page_cache *pc; struct mbuf *m; int temp_len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if ((sc->sc_flags & RUE_FLAG_LINK) == 0) { /* * don't send anything if there is no link ! */ return; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) return; if (m->m_pkthdr.len > MCLBYTES) m->m_pkthdr.len = MCLBYTES; temp_len = m->m_pkthdr.len; pc = usbd_xfer_get_frame(xfer, 0); usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); /* * This is an undocumented behavior. * RTL8150 chip doesn't send frame length smaller than * RUE_MIN_FRAMELEN (60) byte packet. */ if (temp_len < RUE_MIN_FRAMELEN) { usbd_frame_zero(pc, temp_len, RUE_MIN_FRAMELEN - temp_len); temp_len = RUE_MIN_FRAMELEN; } usbd_xfer_set_frame_len(xfer, 0, temp_len); /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_transfer_submit(xfer); return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void rue_tick(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); struct mii_data *mii = GET_MII(sc); RUE_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); if ((sc->sc_flags & RUE_FLAG_LINK) == 0 && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->sc_flags |= RUE_FLAG_LINK; rue_start(ue); } } static void rue_start(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); /* * start the USB transfers, if not already started: */ usbd_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]); usbd_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]); usbd_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]); } static void rue_init(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); RUE_LOCK_ASSERT(sc, MA_OWNED); /* * Cancel pending I/O */ rue_reset(sc); /* Set MAC address */ rue_write_mem(sc, RUE_IDR0, IF_LLADDR(ifp), ETHER_ADDR_LEN); rue_stop(ue); /* * Set the initial TX and RX configuration. */ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); rue_csr_write_2(sc, RUE_RCR, RUE_RCR_CONFIG|RUE_RCR_AB); /* Load the multicast filter */ rue_setpromisc(ue); /* Load the multicast filter. */ rue_setmulti(ue); /* Enable RX and TX */ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); usbd_xfer_set_stall(sc->sc_xfer[RUE_BULK_DT_WR]); ifp->if_drv_flags |= IFF_DRV_RUNNING; rue_start(ue); } /* * Set media options. */ static int rue_ifmedia_upd(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); struct mii_softc *miisc; int error; RUE_LOCK_ASSERT(sc, MA_OWNED); sc->sc_flags &= ~RUE_FLAG_LINK; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); return (error); } /* * Report current media status. */ static void rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rue_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); RUE_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; RUE_UNLOCK(sc); } static void rue_stop(struct usb_ether *ue) { struct rue_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); RUE_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; sc->sc_flags &= ~RUE_FLAG_LINK; /* * stop all the transfers, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]); usbd_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]); usbd_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]); rue_csr_write_1(sc, RUE_CR, 0x00); rue_reset(sc); } Index: head/sys/dev/usb/net/if_urndis.c =================================================================== --- head/sys/dev/usb/net/if_urndis.c (revision 298931) +++ head/sys/dev/usb/net/if_urndis.c (revision 298932) @@ -1,1054 +1,1054 @@ /* $OpenBSD: if_urndis.c,v 1.46 2013/12/09 15:45:29 pirofti Exp $ */ /* * Copyright (c) 2010 Jonathan Armani * Copyright (c) 2010 Fabien Romano * Copyright (c) 2010 Michael Knudsen * Copyright (c) 2014 Hans Petter Selasky * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR urndis_debug #include #include #include "usb_if.h" #include #include #include static device_probe_t urndis_probe; static device_attach_t urndis_attach; static device_detach_t urndis_detach; static device_suspend_t urndis_suspend; static device_resume_t urndis_resume; static usb_callback_t urndis_bulk_write_callback; static usb_callback_t urndis_bulk_read_callback; static usb_callback_t urndis_intr_read_callback; static uether_fn_t urndis_attach_post; static uether_fn_t urndis_init; static uether_fn_t urndis_stop; static uether_fn_t urndis_start; static uether_fn_t urndis_setmulti; static uether_fn_t urndis_setpromisc; static uint32_t urndis_ctrl_query(struct urndis_softc *sc, uint32_t oid, struct urndis_query_req *msg, uint16_t len, const void **rbuf, uint16_t *rbufsz); static uint32_t urndis_ctrl_set(struct urndis_softc *sc, uint32_t oid, struct urndis_set_req *msg, uint16_t len); static uint32_t urndis_ctrl_handle_init(struct urndis_softc *sc, const struct urndis_comp_hdr *hdr); static uint32_t urndis_ctrl_handle_query(struct urndis_softc *sc, const struct urndis_comp_hdr *hdr, const void **buf, uint16_t *bufsz); static uint32_t urndis_ctrl_handle_reset(struct urndis_softc *sc, const struct urndis_comp_hdr *hdr); static uint32_t urndis_ctrl_init(struct urndis_softc *sc); static uint32_t urndis_ctrl_halt(struct urndis_softc *sc); #ifdef USB_DEBUG static int urndis_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, urndis, CTLFLAG_RW, 0, "USB RNDIS-Ethernet"); SYSCTL_INT(_hw_usb_urndis, OID_AUTO, debug, CTLFLAG_RWTUN, &urndis_debug, 0, "Debug level"); #endif static const struct usb_config urndis_config[URNDIS_N_TRANSFER] = { [URNDIS_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_RX, .if_index = 0, .frames = 1, .bufsize = RNDIS_RX_MAXLEN, .flags = {.short_xfer_ok = 1,}, .callback = urndis_bulk_read_callback, .timeout = 0, /* no timeout */ .usb_mode = USB_MODE_HOST, }, [URNDIS_BULK_TX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_TX, .if_index = 0, .frames = RNDIS_TX_FRAMES_MAX, .bufsize = (RNDIS_TX_FRAMES_MAX * RNDIS_TX_MAXLEN), .flags = { .force_short_xfer = 1, }, .callback = urndis_bulk_write_callback, .timeout = 10000, /* 10 seconds */ .usb_mode = USB_MODE_HOST, }, [URNDIS_INTR_RX] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_RX, .if_index = 1, .bufsize = 0, /* use wMaxPacketSize */ .flags = {.short_xfer_ok = 1,.no_pipe_ok = 1,}, .callback = urndis_intr_read_callback, .timeout = 0, .usb_mode = USB_MODE_HOST, }, }; static device_method_t urndis_methods[] = { /* Device interface */ DEVMETHOD(device_probe, urndis_probe), DEVMETHOD(device_attach, urndis_attach), DEVMETHOD(device_detach, urndis_detach), DEVMETHOD(device_suspend, urndis_suspend), DEVMETHOD(device_resume, urndis_resume), DEVMETHOD_END }; static driver_t urndis_driver = { .name = "urndis", .methods = urndis_methods, .size = sizeof(struct urndis_softc), }; static devclass_t urndis_devclass; static const STRUCT_USB_HOST_ID urndis_host_devs[] = { /* Generic RNDIS class match */ {USB_IFACE_CLASS(UICLASS_CDC), USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), USB_IFACE_PROTOCOL(0xff)}, {USB_IFACE_CLASS(UICLASS_WIRELESS), USB_IFACE_SUBCLASS(UISUBCLASS_RF), USB_IFACE_PROTOCOL(UIPROTO_RNDIS)}, {USB_IFACE_CLASS(UICLASS_IAD), USB_IFACE_SUBCLASS(UISUBCLASS_SYNC), USB_IFACE_PROTOCOL(UIPROTO_ACTIVESYNC)}, /* HP-WebOS */ {USB_VENDOR(USB_VENDOR_PALM), USB_IFACE_CLASS(UICLASS_CDC), USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), USB_IFACE_PROTOCOL(0xff)}, }; DRIVER_MODULE(urndis, uhub, urndis_driver, urndis_devclass, NULL, NULL); MODULE_VERSION(urndis, 1); MODULE_DEPEND(urndis, uether, 1, 1, 1); MODULE_DEPEND(urndis, usb, 1, 1, 1); MODULE_DEPEND(urndis, ether, 1, 1, 1); USB_PNP_HOST_INFO(urndis_host_devs); static const struct usb_ether_methods urndis_ue_methods = { .ue_attach_post = urndis_attach_post, .ue_start = urndis_start, .ue_init = urndis_init, .ue_stop = urndis_stop, .ue_setmulti = urndis_setmulti, .ue_setpromisc = urndis_setpromisc, }; static int urndis_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); return (usbd_lookup_id_by_uaa(urndis_host_devs, sizeof(urndis_host_devs), uaa)); } static void urndis_attach_post(struct usb_ether *ue) { /* no-op */ } static int urndis_attach(device_t dev) { static struct { union { struct urndis_query_req query; struct urndis_set_req set; } hdr; union { uint8_t eaddr[ETHER_ADDR_LEN]; uint32_t filter; } ibuf; } msg; struct urndis_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_cdc_cm_descriptor *cmd; const void *buf; uint16_t bufsz; uint8_t iface_index[2] = { uaa->info.bIfaceIndex + 1, uaa->info.bIfaceIndex }; int error; uint8_t i; sc->sc_ue.ue_udev = uaa->device; sc->sc_ifaceno_ctl = uaa->info.bIfaceNum; cmd = usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_CM, 0xFF); if (cmd != NULL) { DPRINTF("Call Mode Descriptor found, dataif=%d\n", cmd->bDataInterface); iface_index[0] = cmd->bDataInterface; } device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* scan the alternate settings looking for a valid one */ for (i = 0; i != 32; i++) { error = usbd_set_alt_interface_index(uaa->device, iface_index[0], i); if (error != 0) break; error = usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, urndis_config, URNDIS_N_TRANSFER, sc, &sc->sc_mtx); if (error == 0) break; } if ((error != 0) || (i == 32)) { device_printf(dev, "No valid alternate setting found\n"); goto detach; } /* Initialize device - must be done before even querying it */ URNDIS_LOCK(sc); error = urndis_ctrl_init(sc); URNDIS_UNLOCK(sc); if (error != (int)RNDIS_STATUS_SUCCESS) { device_printf(dev, "Unable to initialize hardware\n"); goto detach; } /* Determine MAC address */ memset(msg.ibuf.eaddr, 0, sizeof(msg.ibuf.eaddr)); URNDIS_LOCK(sc); error = urndis_ctrl_query(sc, OID_802_3_PERMANENT_ADDRESS, &msg.hdr.query, sizeof(msg.hdr.query) + sizeof(msg.ibuf.eaddr), &buf, &bufsz); URNDIS_UNLOCK(sc); if (error != (int)RNDIS_STATUS_SUCCESS) { device_printf(dev, "Unable to get hardware address\n"); goto detach; } if (bufsz != ETHER_ADDR_LEN) { device_printf(dev, "Invalid address length: %d bytes\n", bufsz); goto detach; } memcpy(&sc->sc_ue.ue_eaddr, buf, ETHER_ADDR_LEN); /* Initialize packet filter */ sc->sc_filter = RNDIS_PACKET_TYPE_BROADCAST | RNDIS_PACKET_TYPE_ALL_MULTICAST; msg.ibuf.filter = htole32(sc->sc_filter); URNDIS_LOCK(sc); error = urndis_ctrl_set(sc, OID_GEN_CURRENT_PACKET_FILTER, &msg.hdr.set, sizeof(msg.hdr.set) + sizeof(msg.ibuf.filter)); URNDIS_UNLOCK(sc); if (error != (int)RNDIS_STATUS_SUCCESS) { device_printf(dev, "Unable to set data filters\n"); goto detach; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &urndis_ue_methods; error = uether_ifattach(ue); if (error) { device_printf(dev, "Could not attach interface\n"); goto detach; } URNDIS_LOCK(sc); /* start interrupt endpoint, if any */ usbd_transfer_start(sc->sc_xfer[URNDIS_INTR_RX]); URNDIS_UNLOCK(sc); return (0); /* success */ detach: (void)urndis_detach(dev); return (ENXIO); /* failure */ } static int urndis_detach(device_t dev) { struct urndis_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; /* stop all USB transfers first */ usbd_transfer_unsetup(sc->sc_xfer, URNDIS_N_TRANSFER); uether_ifdetach(ue); URNDIS_LOCK(sc); (void)urndis_ctrl_halt(sc); URNDIS_UNLOCK(sc); mtx_destroy(&sc->sc_mtx); return (0); } static void urndis_start(struct usb_ether *ue) { struct urndis_softc *sc = uether_getsc(ue); /* * Start the USB transfers, if not already started: */ usbd_transfer_start(sc->sc_xfer[URNDIS_BULK_TX]); usbd_transfer_start(sc->sc_xfer[URNDIS_BULK_RX]); } static void urndis_init(struct usb_ether *ue) { struct urndis_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); URNDIS_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags |= IFF_DRV_RUNNING; /* stall data write direction, which depends on USB mode */ usbd_xfer_set_stall(sc->sc_xfer[URNDIS_BULK_TX]); /* start data transfers */ urndis_start(ue); } static void urndis_stop(struct usb_ether *ue) { struct urndis_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); URNDIS_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* * stop all the transfers, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[URNDIS_BULK_RX]); usbd_transfer_stop(sc->sc_xfer[URNDIS_BULK_TX]); } static void urndis_setmulti(struct usb_ether *ue) { /* no-op */ } static void urndis_setpromisc(struct usb_ether *ue) { /* no-op */ } static int urndis_suspend(device_t dev) { device_printf(dev, "Suspending\n"); return (0); } static int urndis_resume(device_t dev) { device_printf(dev, "Resuming\n"); return (0); } static usb_error_t urndis_ctrl_msg(struct urndis_softc *sc, uint8_t rt, uint8_t r, uint16_t index, uint16_t value, void *buf, uint16_t buflen) { usb_device_request_t req; req.bmRequestType = rt; req.bRequest = r; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, buflen); return (usbd_do_request_flags(sc->sc_ue.ue_udev, &sc->sc_mtx, &req, buf, (rt & UT_READ) ? USB_SHORT_XFER_OK : 0, NULL, 2000 /* ms */ )); } static usb_error_t urndis_ctrl_send(struct urndis_softc *sc, void *buf, uint16_t len) { usb_error_t err; err = urndis_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, UCDC_SEND_ENCAPSULATED_COMMAND, sc->sc_ifaceno_ctl, 0, buf, len); DPRINTF("%s\n", usbd_errstr(err)); return (err); } static struct urndis_comp_hdr * urndis_ctrl_recv(struct urndis_softc *sc) { struct urndis_comp_hdr *hdr; usb_error_t err; err = urndis_ctrl_msg(sc, UT_READ_CLASS_INTERFACE, UCDC_GET_ENCAPSULATED_RESPONSE, sc->sc_ifaceno_ctl, 0, sc->sc_response_buf, RNDIS_RESPONSE_LEN); if (err != USB_ERR_NORMAL_COMPLETION) return (NULL); hdr = (struct urndis_comp_hdr *)sc->sc_response_buf; DPRINTF("type 0x%x len %u\n", le32toh(hdr->rm_type), le32toh(hdr->rm_len)); if (le32toh(hdr->rm_len) > RNDIS_RESPONSE_LEN) { DPRINTF("ctrl message error: wrong size %u > %u\n", le32toh(hdr->rm_len), RNDIS_RESPONSE_LEN); return (NULL); } return (hdr); } static uint32_t urndis_ctrl_handle(struct urndis_softc *sc, struct urndis_comp_hdr *hdr, const void **buf, uint16_t *bufsz) { uint32_t rval; DPRINTF("\n"); if (buf != NULL && bufsz != NULL) { *buf = NULL; *bufsz = 0; } switch (le32toh(hdr->rm_type)) { case REMOTE_NDIS_INITIALIZE_CMPLT: rval = urndis_ctrl_handle_init(sc, hdr); break; case REMOTE_NDIS_QUERY_CMPLT: rval = urndis_ctrl_handle_query(sc, hdr, buf, bufsz); break; case REMOTE_NDIS_RESET_CMPLT: rval = urndis_ctrl_handle_reset(sc, hdr); break; case REMOTE_NDIS_KEEPALIVE_CMPLT: case REMOTE_NDIS_SET_CMPLT: rval = le32toh(hdr->rm_status); break; default: device_printf(sc->sc_ue.ue_dev, "ctrl message error: unknown event 0x%x\n", le32toh(hdr->rm_type)); rval = RNDIS_STATUS_FAILURE; break; } return (rval); } static uint32_t urndis_ctrl_handle_init(struct urndis_softc *sc, const struct urndis_comp_hdr *hdr) { const struct urndis_init_comp *msg; msg = (const struct urndis_init_comp *)hdr; DPRINTF("len %u rid %u status 0x%x " "ver_major %u ver_minor %u devflags 0x%x medium 0x%x pktmaxcnt %u " "pktmaxsz %u align %u aflistoffset %u aflistsz %u\n", le32toh(msg->rm_len), le32toh(msg->rm_rid), le32toh(msg->rm_status), le32toh(msg->rm_ver_major), le32toh(msg->rm_ver_minor), le32toh(msg->rm_devflags), le32toh(msg->rm_medium), le32toh(msg->rm_pktmaxcnt), le32toh(msg->rm_pktmaxsz), le32toh(msg->rm_align), le32toh(msg->rm_aflistoffset), le32toh(msg->rm_aflistsz)); if (le32toh(msg->rm_status) != RNDIS_STATUS_SUCCESS) { DPRINTF("init failed 0x%x\n", le32toh(msg->rm_status)); return (le32toh(msg->rm_status)); } if (le32toh(msg->rm_devflags) != RNDIS_DF_CONNECTIONLESS) { DPRINTF("wrong device type (current type: 0x%x)\n", le32toh(msg->rm_devflags)); return (RNDIS_STATUS_FAILURE); } if (le32toh(msg->rm_medium) != RNDIS_MEDIUM_802_3) { DPRINTF("medium not 802.3 (current medium: 0x%x)\n", le32toh(msg->rm_medium)); return (RNDIS_STATUS_FAILURE); } sc->sc_lim_pktsz = le32toh(msg->rm_pktmaxsz); return (le32toh(msg->rm_status)); } static uint32_t urndis_ctrl_handle_query(struct urndis_softc *sc, const struct urndis_comp_hdr *hdr, const void **buf, uint16_t *bufsz) { const struct urndis_query_comp *msg; uint64_t limit; msg = (const struct urndis_query_comp *)hdr; DPRINTF("len %u rid %u status 0x%x " "buflen %u bufoff %u\n", le32toh(msg->rm_len), le32toh(msg->rm_rid), le32toh(msg->rm_status), le32toh(msg->rm_infobuflen), le32toh(msg->rm_infobufoffset)); *buf = NULL; *bufsz = 0; if (le32toh(msg->rm_status) != RNDIS_STATUS_SUCCESS) { DPRINTF("query failed 0x%x\n", le32toh(msg->rm_status)); return (le32toh(msg->rm_status)); } limit = le32toh(msg->rm_infobuflen); limit += le32toh(msg->rm_infobufoffset); limit += RNDIS_HEADER_OFFSET; if (limit > (uint64_t)le32toh(msg->rm_len)) { DPRINTF("ctrl message error: invalid query info " "len/offset/end_position(%u/%u/%u) -> " "go out of buffer limit %u\n", le32toh(msg->rm_infobuflen), le32toh(msg->rm_infobufoffset), le32toh(msg->rm_infobuflen) + le32toh(msg->rm_infobufoffset) + RNDIS_HEADER_OFFSET, le32toh(msg->rm_len)); return (RNDIS_STATUS_FAILURE); } *buf = ((const uint8_t *)msg) + RNDIS_HEADER_OFFSET + le32toh(msg->rm_infobufoffset); *bufsz = le32toh(msg->rm_infobuflen); return (le32toh(msg->rm_status)); } static uint32_t urndis_ctrl_handle_reset(struct urndis_softc *sc, const struct urndis_comp_hdr *hdr) { const struct urndis_reset_comp *msg; uint32_t rval; msg = (const struct urndis_reset_comp *)hdr; rval = le32toh(msg->rm_status); DPRINTF("len %u status 0x%x " "adrreset %u\n", le32toh(msg->rm_len), rval, le32toh(msg->rm_adrreset)); if (rval != RNDIS_STATUS_SUCCESS) { DPRINTF("reset failed 0x%x\n", rval); return (rval); } if (msg->rm_adrreset != 0) { struct { struct urndis_set_req hdr; uint32_t filter; } msg_filter; msg_filter.filter = htole32(sc->sc_filter); rval = urndis_ctrl_set(sc, OID_GEN_CURRENT_PACKET_FILTER, &msg_filter.hdr, sizeof(msg_filter)); if (rval != RNDIS_STATUS_SUCCESS) { DPRINTF("unable to reset data filters\n"); return (rval); } } return (rval); } static uint32_t urndis_ctrl_init(struct urndis_softc *sc) { struct urndis_init_req msg; struct urndis_comp_hdr *hdr; uint32_t rval; msg.rm_type = htole32(REMOTE_NDIS_INITIALIZE_MSG); msg.rm_len = htole32(sizeof(msg)); msg.rm_rid = 0; msg.rm_ver_major = htole32(1); msg.rm_ver_minor = htole32(1); msg.rm_max_xfersz = htole32(RNDIS_RX_MAXLEN); DPRINTF("type %u len %u rid %u ver_major %u " "ver_minor %u max_xfersz %u\n", le32toh(msg.rm_type), le32toh(msg.rm_len), le32toh(msg.rm_rid), le32toh(msg.rm_ver_major), le32toh(msg.rm_ver_minor), le32toh(msg.rm_max_xfersz)); rval = urndis_ctrl_send(sc, &msg, sizeof(msg)); if (rval != RNDIS_STATUS_SUCCESS) { DPRINTF("init failed\n"); return (rval); } if ((hdr = urndis_ctrl_recv(sc)) == NULL) { DPRINTF("unable to get init response\n"); return (RNDIS_STATUS_FAILURE); } rval = urndis_ctrl_handle(sc, hdr, NULL, NULL); return (rval); } static uint32_t urndis_ctrl_halt(struct urndis_softc *sc) { struct urndis_halt_req msg; uint32_t rval; msg.rm_type = htole32(REMOTE_NDIS_HALT_MSG); msg.rm_len = htole32(sizeof(msg)); msg.rm_rid = 0; DPRINTF("type %u len %u rid %u\n", le32toh(msg.rm_type), le32toh(msg.rm_len), le32toh(msg.rm_rid)); rval = urndis_ctrl_send(sc, &msg, sizeof(msg)); if (rval != RNDIS_STATUS_SUCCESS) DPRINTF("halt failed\n"); return (rval); } /* - * NB: Querying a device has the requirment of using an input buffer the size + * NB: Querying a device has the requirement of using an input buffer the size * of the expected reply or larger, except for variably sized replies. */ static uint32_t urndis_ctrl_query(struct urndis_softc *sc, uint32_t oid, struct urndis_query_req *msg, uint16_t len, const void **rbuf, uint16_t *rbufsz) { struct urndis_comp_hdr *hdr; uint32_t datalen, rval; msg->rm_type = htole32(REMOTE_NDIS_QUERY_MSG); msg->rm_len = htole32(len); msg->rm_rid = 0; /* XXX */ msg->rm_oid = htole32(oid); datalen = len - sizeof(*msg); msg->rm_infobuflen = htole32(datalen); if (datalen != 0) { msg->rm_infobufoffset = htole32(sizeof(*msg) - RNDIS_HEADER_OFFSET); } else { msg->rm_infobufoffset = 0; } msg->rm_devicevchdl = 0; DPRINTF("type %u len %u rid %u oid 0x%x " "infobuflen %u infobufoffset %u devicevchdl %u\n", le32toh(msg->rm_type), le32toh(msg->rm_len), le32toh(msg->rm_rid), le32toh(msg->rm_oid), le32toh(msg->rm_infobuflen), le32toh(msg->rm_infobufoffset), le32toh(msg->rm_devicevchdl)); rval = urndis_ctrl_send(sc, msg, len); if (rval != RNDIS_STATUS_SUCCESS) { DPRINTF("query failed\n"); return (rval); } if ((hdr = urndis_ctrl_recv(sc)) == NULL) { DPRINTF("unable to get query response\n"); return (RNDIS_STATUS_FAILURE); } rval = urndis_ctrl_handle(sc, hdr, rbuf, rbufsz); return (rval); } static uint32_t urndis_ctrl_set(struct urndis_softc *sc, uint32_t oid, struct urndis_set_req *msg, uint16_t len) { struct urndis_comp_hdr *hdr; uint32_t datalen, rval; msg->rm_type = htole32(REMOTE_NDIS_SET_MSG); msg->rm_len = htole32(len); msg->rm_rid = 0; /* XXX */ msg->rm_oid = htole32(oid); datalen = len - sizeof(*msg); msg->rm_infobuflen = htole32(datalen); if (datalen != 0) { msg->rm_infobufoffset = htole32(sizeof(*msg) - RNDIS_HEADER_OFFSET); } else { msg->rm_infobufoffset = 0; } msg->rm_devicevchdl = 0; DPRINTF("type %u len %u rid %u oid 0x%x " "infobuflen %u infobufoffset %u devicevchdl %u\n", le32toh(msg->rm_type), le32toh(msg->rm_len), le32toh(msg->rm_rid), le32toh(msg->rm_oid), le32toh(msg->rm_infobuflen), le32toh(msg->rm_infobufoffset), le32toh(msg->rm_devicevchdl)); rval = urndis_ctrl_send(sc, msg, len); if (rval != RNDIS_STATUS_SUCCESS) { DPRINTF("set failed\n"); return (rval); } if ((hdr = urndis_ctrl_recv(sc)) == NULL) { DPRINTF("unable to get set response\n"); return (RNDIS_STATUS_FAILURE); } rval = urndis_ctrl_handle(sc, hdr, NULL, NULL); if (rval != RNDIS_STATUS_SUCCESS) DPRINTF("set failed 0x%x\n", rval); return (rval); } static void urndis_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct urndis_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct urndis_packet_msg msg; struct mbuf *m; int actlen; int aframes; int offset; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); DPRINTFN(1, "received %u bytes in %u frames\n", actlen, aframes); for (offset = 0; actlen >= (uint32_t)sizeof(msg);) { /* copy out header */ usbd_copy_out(pc, offset, &msg, sizeof(msg)); if (le32toh(0x1234567U) != 0x1234567U) { /* swap endianness */ msg.rm_type = le32toh(msg.rm_type); msg.rm_len = le32toh(msg.rm_len); msg.rm_dataoffset = le32toh(msg.rm_dataoffset); msg.rm_datalen = le32toh(msg.rm_datalen); msg.rm_oobdataoffset = le32toh(msg.rm_oobdataoffset); msg.rm_oobdatalen = le32toh(msg.rm_oobdatalen); msg.rm_oobdataelements = le32toh(msg.rm_oobdataelements); msg.rm_pktinfooffset = le32toh(msg.rm_pktinfooffset); msg.rm_pktinfolen = le32toh(msg.rm_pktinfolen); msg.rm_vchandle = le32toh(msg.rm_vchandle); msg.rm_reserved = le32toh(msg.rm_reserved); } DPRINTF("len %u data(off:%u len:%u) " "oobdata(off:%u len:%u nb:%u) perpacket(off:%u len:%u)\n", msg.rm_len, msg.rm_dataoffset, msg.rm_datalen, msg.rm_oobdataoffset, msg.rm_oobdatalen, msg.rm_oobdataelements, msg.rm_pktinfooffset, msg.rm_pktinfooffset); /* sanity check the RNDIS header */ if (msg.rm_type != REMOTE_NDIS_PACKET_MSG) { DPRINTF("invalid type 0x%x != 0x%x\n", msg.rm_type, REMOTE_NDIS_PACKET_MSG); goto tr_setup; } else if (msg.rm_len < (uint32_t)sizeof(msg)) { DPRINTF("invalid msg len %u < %u\n", msg.rm_len, (unsigned)sizeof(msg)); goto tr_setup; } else if (msg.rm_len > (uint32_t)actlen) { DPRINTF("invalid msg len %u > buffer " "len %u\n", msg.rm_len, actlen); goto tr_setup; } else if (msg.rm_dataoffset >= (uint32_t)actlen) { DPRINTF("invalid msg dataoffset %u > buffer " "dataoffset %u\n", msg.rm_dataoffset, actlen); goto tr_setup; } else if (msg.rm_datalen > (uint32_t)actlen) { DPRINTF("invalid msg datalen %u > buffer " "datalen %u\n", msg.rm_datalen, actlen); goto tr_setup; } else if ((msg.rm_dataoffset + msg.rm_datalen + (uint32_t)__offsetof(struct urndis_packet_msg, rm_dataoffset)) > (uint32_t)actlen) { DPRINTF("invalid dataoffset %u larger than %u\n", msg.rm_dataoffset + msg.rm_datalen + (uint32_t)__offsetof(struct urndis_packet_msg, rm_dataoffset), actlen); goto tr_setup; } else if (msg.rm_datalen < (uint32_t)sizeof(struct ether_header)) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF("invalid ethernet size " "%u < %u\n", msg.rm_datalen, (unsigned)sizeof(struct ether_header)); goto tr_setup; } else if (msg.rm_datalen > (uint32_t)(MCLBYTES - ETHER_ALIGN)) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF("invalid ethernet size " "%u > %u\n", msg.rm_datalen, (unsigned)MCLBYTES); goto tr_setup; } else if (msg.rm_datalen > (uint32_t)(MHLEN - ETHER_ALIGN)) { m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); } else { m = m_gethdr(M_NOWAIT, MT_DATA); } /* check if we have a buffer */ if (m != NULL) { m->m_len = m->m_pkthdr.len = msg.rm_datalen + ETHER_ALIGN; m_adj(m, ETHER_ALIGN); usbd_copy_out(pc, offset + msg.rm_dataoffset + __offsetof(struct urndis_packet_msg, rm_dataoffset), m->m_data, msg.rm_datalen); /* enqueue */ uether_rxmbuf(&sc->sc_ue, m, msg.rm_datalen); } else { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } offset += msg.rm_len; actlen -= msg.rm_len; } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, RNDIS_RX_MAXLEN); usbd_xfer_set_frames(xfer, 1); usbd_transfer_submit(xfer); uether_rxflush(&sc->sc_ue); /* must be last */ break; default: /* Error */ DPRINTFN(1, "error = %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); usbd_xfer_set_frames(xfer, 0); usbd_transfer_submit(xfer); } break; } } static void urndis_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct urndis_packet_msg msg; struct urndis_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct mbuf *m; unsigned x; int actlen; int aframes; usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); DPRINTFN(1, "\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "%u bytes in %u frames\n", actlen, aframes); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: memset(&msg, 0, sizeof(msg)); for (x = 0; x != RNDIS_TX_FRAMES_MAX; x++) { struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, x); usbd_xfer_set_frame_offset(xfer, x * RNDIS_TX_MAXLEN, x); next_pkt: IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if ((m->m_pkthdr.len + sizeof(msg)) > RNDIS_TX_MAXLEN) { DPRINTF("Too big packet\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* Free buffer */ m_freem(m); goto next_pkt; } msg.rm_type = htole32(REMOTE_NDIS_PACKET_MSG); msg.rm_len = htole32(sizeof(msg) + m->m_pkthdr.len); msg.rm_dataoffset = htole32(RNDIS_DATA_OFFSET); msg.rm_datalen = htole32(m->m_pkthdr.len); /* copy in all data */ usbd_copy_in(pc, 0, &msg, sizeof(msg)); usbd_m_copy_in(pc, sizeof(msg), m, 0, m->m_pkthdr.len); usbd_xfer_set_frame_len(xfer, x, sizeof(msg) + m->m_pkthdr.len); /* * If there's a BPF listener, bounce a copy of * this frame to him: */ BPF_MTAP(ifp, m); /* Free buffer */ m_freem(m); } if (x != 0) { usbd_xfer_set_frames(xfer, x); usbd_transfer_submit(xfer); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); /* count output errors */ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void urndis_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) { int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("Received %d bytes\n", actlen); /* TODO: decode some indications */ /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* start clear stall */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } Index: head/sys/dev/usb/serial/ulpt.c =================================================================== --- head/sys/dev/usb/serial/ulpt.c (revision 298931) +++ head/sys/dev/usb/serial/ulpt.c (revision 298932) @@ -1,762 +1,762 @@ #include __FBSDID("$FreeBSD$"); /* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */ /*- * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR ulpt_debug #include #include #ifdef USB_DEBUG static int ulpt_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt"); SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RWTUN, &ulpt_debug, 0, "Debug level"); #endif #define ULPT_BSIZE (1<<15) /* bytes */ #define ULPT_IFQ_MAXLEN 2 /* units */ #define UR_GET_DEVICE_ID 0x00 #define UR_GET_PORT_STATUS 0x01 #define UR_SOFT_RESET 0x02 #define LPS_NERR 0x08 /* printer no error */ #define LPS_SELECT 0x10 /* printer selected */ #define LPS_NOPAPER 0x20 /* printer out of paper */ #define LPS_INVERT (LPS_SELECT|LPS_NERR) #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) enum { ULPT_BULK_DT_WR, ULPT_BULK_DT_RD, ULPT_INTR_DT_RD, ULPT_N_TRANSFER, }; struct ulpt_softc { struct usb_fifo_sc sc_fifo; struct usb_fifo_sc sc_fifo_noreset; struct mtx sc_mtx; struct usb_callout sc_watchdog; device_t sc_dev; struct usb_device *sc_udev; struct usb_fifo *sc_fifo_open[2]; struct usb_xfer *sc_xfer[ULPT_N_TRANSFER]; int sc_fflags; /* current open flags, FREAD and * FWRITE */ uint8_t sc_iface_no; uint8_t sc_last_status; uint8_t sc_zlps; /* number of consequtive zero length * packets received */ }; /* prototypes */ static device_probe_t ulpt_probe; static device_attach_t ulpt_attach; static device_detach_t ulpt_detach; static usb_callback_t ulpt_write_callback; static usb_callback_t ulpt_read_callback; static usb_callback_t ulpt_status_callback; static void ulpt_reset(struct ulpt_softc *); static void ulpt_watchdog(void *); static usb_fifo_close_t ulpt_close; static usb_fifo_cmd_t ulpt_start_read; static usb_fifo_cmd_t ulpt_start_write; static usb_fifo_cmd_t ulpt_stop_read; static usb_fifo_cmd_t ulpt_stop_write; static usb_fifo_ioctl_t ulpt_ioctl; static usb_fifo_open_t ulpt_open; static usb_fifo_open_t unlpt_open; static struct usb_fifo_methods ulpt_fifo_methods = { .f_close = &ulpt_close, .f_ioctl = &ulpt_ioctl, .f_open = &ulpt_open, .f_start_read = &ulpt_start_read, .f_start_write = &ulpt_start_write, .f_stop_read = &ulpt_stop_read, .f_stop_write = &ulpt_stop_write, .basename[0] = "ulpt", }; static struct usb_fifo_methods unlpt_fifo_methods = { .f_close = &ulpt_close, .f_ioctl = &ulpt_ioctl, .f_open = &unlpt_open, .f_start_read = &ulpt_start_read, .f_start_write = &ulpt_start_write, .f_stop_read = &ulpt_stop_read, .f_stop_write = &ulpt_stop_write, .basename[0] = "unlpt", }; static void ulpt_reset(struct ulpt_softc *sc) { struct usb_device_request req; DPRINTFN(2, "\n"); req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); /* * There was a mistake in the USB printer 1.0 spec that gave the * request type as UT_WRITE_CLASS_OTHER; it should have been * UT_WRITE_CLASS_INTERFACE. Many printers use the old one, * so we try both. */ mtx_lock(&sc->sc_mtx); req.bmRequestType = UT_WRITE_CLASS_OTHER; if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.0 */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.1 */ /* ignore error */ } } mtx_unlock(&sc->sc_mtx); } static void ulpt_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ulpt_softc *sc = usbd_xfer_softc(xfer); struct usb_fifo *f = sc->sc_fifo_open[USB_FIFO_TX]; struct usb_page_cache *pc; int actlen, max; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (f == NULL) { /* should not happen */ DPRINTF("no FIFO\n"); return; } DPRINTF("state=0x%x actlen=%d\n", USB_GET_STATE(xfer), actlen); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: pc = usbd_xfer_get_frame(xfer, 0); max = usbd_xfer_max_len(xfer); if (usb_fifo_get_data(f, pc, 0, max, &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void ulpt_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ulpt_softc *sc = usbd_xfer_softc(xfer); struct usb_fifo *f = sc->sc_fifo_open[USB_FIFO_RX]; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (f == NULL) { /* should not happen */ DPRINTF("no FIFO\n"); return; } DPRINTF("state=0x%x\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen == 0) { if (sc->sc_zlps == 4) { /* enable BULK throttle */ usbd_xfer_set_interval(xfer, 500); /* ms */ } else { sc->sc_zlps++; } } else { /* disable BULK throttle */ usbd_xfer_set_interval(xfer, 0); sc->sc_zlps = 0; } pc = usbd_xfer_get_frame(xfer, 0); usb_fifo_put_data(f, pc, 0, actlen, 1); case USB_ST_SETUP: tr_setup: if (usb_fifo_put_bytes_max(f) != 0) { usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; default: /* Error */ /* disable BULK throttle */ usbd_xfer_set_interval(xfer, 0); sc->sc_zlps = 0; if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void ulpt_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct ulpt_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; uint8_t cur_status; uint8_t new_status; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 1); usbd_copy_out(pc, 0, &cur_status, 1); cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK; new_status = cur_status & ~sc->sc_last_status; sc->sc_last_status = cur_status; if (new_status & LPS_SELECT) log(LOG_NOTICE, "%s: offline\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NOPAPER) log(LOG_NOTICE, "%s: out of paper\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NERR) log(LOG_NOTICE, "%s: output error\n", device_get_nameunit(sc->sc_dev)); break; case USB_ST_SETUP: req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_PORT_STATUS; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); 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, 1); usbd_xfer_set_frames(xfer, 2); usbd_transfer_submit(xfer); break; default: /* Error */ DPRINTF("error=%s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* wait for next watchdog timeout */ } break; } } static const struct usb_config ulpt_config[ULPT_N_TRANSFER] = { [ULPT_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = ULPT_BSIZE, .flags = {.pipe_bof = 1,.proxy_buffer = 1}, .callback = &ulpt_write_callback, }, [ULPT_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = ULPT_BSIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1}, .callback = &ulpt_read_callback, }, [ULPT_INTR_DT_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request) + 1, .callback = &ulpt_status_callback, .timeout = 1000, /* 1 second */ }, }; static void ulpt_start_read(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]); } static void ulpt_stop_read(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]); } static void ulpt_start_write(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]); } static void ulpt_stop_write(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]); } static int ulpt_open(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); /* we assume that open is a serial process */ if (sc->sc_fflags == 0) { - /* reset USB paralell port */ + /* reset USB parallel port */ ulpt_reset(sc); } return (unlpt_open(fifo, fflags)); } static int unlpt_open(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); if (sc->sc_fflags & fflags) { return (EBUSY); } if (fflags & FREAD) { /* clear stall first */ mtx_lock(&sc->sc_mtx); usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]); mtx_unlock(&sc->sc_mtx); if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_RD]), ULPT_IFQ_MAXLEN)) { return (ENOMEM); } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_RX] = fifo; } if (fflags & FWRITE) { /* clear stall first */ mtx_lock(&sc->sc_mtx); usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_WR]), ULPT_IFQ_MAXLEN)) { return (ENOMEM); } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_TX] = fifo; } sc->sc_fflags |= fflags & (FREAD | FWRITE); return (0); } static void ulpt_close(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); if (fflags & (FREAD | FWRITE)) { usb_fifo_free_buffer(fifo); } } static int ulpt_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, int fflags) { return (ENODEV); } static const STRUCT_USB_HOST_ID ulpt_devs[] = { /* Uni-directional USB printer */ {USB_IFACE_CLASS(UICLASS_PRINTER), USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER), USB_IFACE_PROTOCOL(UIPROTO_PRINTER_UNI)}, /* Bi-directional USB printer */ {USB_IFACE_CLASS(UICLASS_PRINTER), USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER), USB_IFACE_PROTOCOL(UIPROTO_PRINTER_BI)}, /* 1284 USB printer */ {USB_IFACE_CLASS(UICLASS_PRINTER), USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER), USB_IFACE_PROTOCOL(UIPROTO_PRINTER_1284)}, }; static int ulpt_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); int error; DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); error = usbd_lookup_id_by_uaa(ulpt_devs, sizeof(ulpt_devs), uaa); if (error) return (error); return (BUS_PROBE_GENERIC); } static int ulpt_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ulpt_softc *sc = device_get_softc(dev); struct usb_interface_descriptor *id; int unit = device_get_unit(dev); int error; uint8_t iface_index = uaa->info.bIfaceIndex; uint8_t alt_index; DPRINTFN(11, "sc=%p\n", sc); sc->sc_dev = dev; sc->sc_udev = uaa->device; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE); usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); /* search through all the descriptors looking for bidir mode */ id = usbd_get_interface_descriptor(uaa->iface); alt_index = 0xFF; while (1) { if (id == NULL) { break; } if ((id->bDescriptorType == UDESC_INTERFACE) && (id->bLength >= sizeof(*id))) { if (id->bInterfaceNumber != uaa->info.bIfaceNum) { break; } else { alt_index++; if ((id->bInterfaceClass == UICLASS_PRINTER) && (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) { goto found; } } } id = (void *)usb_desc_foreach( usbd_get_config_descriptor(uaa->device), (void *)id); } goto detach; found: DPRINTF("setting alternate " "config number: %d\n", alt_index); if (alt_index) { error = usbd_set_alt_interface_index (uaa->device, iface_index, alt_index); if (error) { DPRINTF("could not set alternate " "config, error=%s\n", usbd_errstr(error)); goto detach; } } sc->sc_iface_no = id->bInterfaceNumber; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("error=%s\n", usbd_errstr(error)); goto detach; } device_printf(sc->sc_dev, "using bi-directional mode\n"); #if 0 /* * This code is disabled because for some mysterious reason it causes * printing not to work. But only sometimes, and mostly with * UHCI and less often with OHCI. *sigh* */ { struct usb_config_descriptor *cd = usbd_get_config_descriptor(dev); struct usb_device_request req; int len, alen; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_DEVICE_ID; USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); error = usbd_do_request_flags(dev, &req, devinfo, USB_SHORT_XFER_OK, &alen, USB_DEFAULT_TIMEOUT); if (error) { device_printf(sc->sc_dev, "cannot get device id\n"); } else if (alen <= 2) { device_printf(sc->sc_dev, "empty device id, no " "printer connected?\n"); } else { /* devinfo now contains an IEEE-1284 device ID */ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff); if (len > sizeof devinfo - 3) len = sizeof devinfo - 3; devinfo[len] = 0; printf("%s: device id <", device_get_nameunit(sc->sc_dev)); ieee1284_print_id(devinfo + 2); printf(">\n"); } } #endif error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, &ulpt_fifo_methods, &sc->sc_fifo, unit, -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); if (error) { goto detach; } error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, &unlpt_fifo_methods, &sc->sc_fifo_noreset, unit, -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); if (error) { goto detach; } /* start reading of status */ mtx_lock(&sc->sc_mtx); ulpt_watchdog(sc); mtx_unlock(&sc->sc_mtx); return (0); detach: ulpt_detach(dev); return (ENOMEM); } static int ulpt_detach(device_t dev) { struct ulpt_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); usb_fifo_detach(&sc->sc_fifo); usb_fifo_detach(&sc->sc_fifo_noreset); mtx_lock(&sc->sc_mtx); usb_callout_stop(&sc->sc_watchdog); mtx_unlock(&sc->sc_mtx); usbd_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER); usb_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } #if 0 /* XXX This does not belong here. */ /* * Compare two strings until the second ends. */ static uint8_t ieee1284_compare(const char *a, const char *b) { while (1) { if (*b == 0) { break; } if (*a != *b) { return 1; } b++; a++; } return 0; } /* * Print select parts of an IEEE 1284 device ID. */ void ieee1284_print_id(char *str) { char *p, *q; for (p = str - 1; p; p = strchr(p, ';')) { p++; /* skip ';' */ if (ieee1284_compare(p, "MFG:") == 0 || ieee1284_compare(p, "MANUFACTURER:") == 0 || ieee1284_compare(p, "MDL:") == 0 || ieee1284_compare(p, "MODEL:") == 0) { q = strchr(p, ';'); if (q) printf("%.*s", (int)(q - p + 1), p); } } } #endif static void ulpt_watchdog(void *arg) { struct ulpt_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); /* * Only read status while the device is not opened, due to * possible hardware or firmware bug in some printers. */ if (sc->sc_fflags == 0) usbd_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]); usb_callout_reset(&sc->sc_watchdog, hz, &ulpt_watchdog, sc); } static devclass_t ulpt_devclass; static device_method_t ulpt_methods[] = { DEVMETHOD(device_probe, ulpt_probe), DEVMETHOD(device_attach, ulpt_attach), DEVMETHOD(device_detach, ulpt_detach), DEVMETHOD_END }; static driver_t ulpt_driver = { .name = "ulpt", .methods = ulpt_methods, .size = sizeof(struct ulpt_softc), }; DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, NULL, 0); MODULE_DEPEND(ulpt, usb, 1, 1, 1); MODULE_VERSION(ulpt, 1); USB_PNP_HOST_INFO(ulpt_devs); Index: head/sys/dev/usb/serial/umcs.h =================================================================== --- head/sys/dev/usb/serial/umcs.h (revision 298931) +++ head/sys/dev/usb/serial/umcs.h (revision 298932) @@ -1,644 +1,644 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2010 Lev Serebryakov . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _UMCS7840_H_ #define _UMCS7840_H_ #define UMCS7840_MAX_PORTS 4 #define UMCS7840_READ_LENGTH 1 /* bytes */ #define UMCS7840_CTRL_TIMEOUT 500 /* ms */ /* Read/Wrtire registers vendor commands */ #define MCS7840_RDREQ 0x0d #define MCS7840_WRREQ 0x0e /* Read/Wrtie EEPROM values */ #define MCS7840_EEPROM_RW_WVALUE 0x0900 /* * All these registers are documented only in full datasheet, * which can be requested from MosChip tech support. */ #define MCS7840_DEV_REG_SP1 0x00 /* Options for for UART 1, R/W */ #define MCS7840_DEV_REG_CONTROL1 0x01 /* Control bits for UART 1, * R/W */ #define MCS7840_DEV_REG_PINPONGHIGH 0x02 /* High bits of ping-pong * register, R/W */ #define MCS7840_DEV_REG_PINPONGLOW 0x03 /* Low bits of ping-pong * register, R/W */ /* DCRx_1 Registers goes here (see below, they are documented) */ #define MCS7840_DEV_REG_GPIO 0x07 /* GPIO_0 and GPIO_1 bits, * undocumented, see notes * below R/W */ #define MCS7840_DEV_REG_SP2 0x08 /* Options for for UART 2, R/W */ #define MCS7840_DEV_REG_CONTROL2 0x09 /* Control bits for UART 2, * R/W */ #define MCS7840_DEV_REG_SP3 0x0a /* Options for for UART 3, R/W */ #define MCS7840_DEV_REG_CONTROL3 0x0b /* Control bits for UART 3, * R/W */ #define MCS7840_DEV_REG_SP4 0x0c /* Options for for UART 4, R/W */ #define MCS7840_DEV_REG_CONTROL4 0x0d /* Control bits for UART 4, * R/W */ #define MCS7840_DEV_REG_PLL_DIV_M 0x0e /* Pre-diviedr for PLL, R/W */ #define MCS7840_DEV_REG_UNKNOWN1 0x0f /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_PLL_DIV_N 0x10 /* Loop divider for PLL, R/W */ #define MCS7840_DEV_REG_CLOCK_MUX 0x12 /* PLL input clock & Interrupt * endpoint control, R/W */ #define MCS7840_DEV_REG_UNKNOWN2 0x11 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_CLOCK_SELECT12 0x13 /* Clock source for ports 1 & * 2, R/W */ #define MCS7840_DEV_REG_CLOCK_SELECT34 0x14 /* Clock source for ports 3 & * 4, R/W */ #define MCS7840_DEV_REG_UNKNOWN3 0x15 /* NOT MENTIONED AND NOT USED */ /* DCRx_2-DCRx_4 Registers goes here (see below, they are documented) */ #define MCS7840_DEV_REG_UNKNOWN4 0x1f /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWN5 0x20 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWN6 0x21 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWN7 0x22 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWN8 0x23 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWN9 0x24 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWNA 0x25 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWNB 0x26 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWNC 0x27 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWND 0x28 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWNE 0x29 /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_UNKNOWNF 0x2a /* NOT MENTIONED AND NOT USED */ #define MCS7840_DEV_REG_MODE 0x2b /* Hardware configuration, * R/Only */ #define MCS7840_DEV_REG_SP1_ICG 0x2c /* Inter character gap * configuration for Port 1, * R/W */ #define MCS7840_DEV_REG_SP2_ICG 0x2d /* Inter character gap * configuration for Port 2, * R/W */ #define MCS7840_DEV_REG_SP3_ICG 0x2e /* Inter character gap * configuration for Port 3, * R/W */ #define MCS7840_DEV_REG_SP4_ICG 0x2f /* Inter character gap * configuration for Port 4, * R/W */ #define MCS7840_DEV_REG_RX_SAMPLING12 0x30 /* RX sampling for ports 1 & * 2, R/W */ #define MCS7840_DEV_REG_RX_SAMPLING34 0x31 /* RX sampling for ports 3 & * 4, R/W */ #define MCS7840_DEV_REG_BI_FIFO_STAT1 0x32 /* Bulk-In FIFO Stat for Port * 1, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BO_FIFO_STAT1 0x33 /* Bulk-out FIFO Stat for Port * 1, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BI_FIFO_STAT2 0x34 /* Bulk-In FIFO Stat for Port * 2, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BO_FIFO_STAT2 0x35 /* Bulk-out FIFO Stat for Port * 2, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BI_FIFO_STAT3 0x36 /* Bulk-In FIFO Stat for Port * 3, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BO_FIFO_STAT3 0x37 /* Bulk-out FIFO Stat for Port * 3, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BI_FIFO_STAT4 0x38 /* Bulk-In FIFO Stat for Port * 4, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_BO_FIFO_STAT4 0x39 /* Bulk-out FIFO Stat for Port * 4, contains number of - * availiable bytes, R/Only */ + * available bytes, R/Only */ #define MCS7840_DEV_REG_ZERO_PERIOD1 0x3a /* Period between zero out * frames for Port 1, R/W */ #define MCS7840_DEV_REG_ZERO_PERIOD2 0x3b /* Period between zero out * frames for Port 1, R/W */ #define MCS7840_DEV_REG_ZERO_PERIOD3 0x3c /* Period between zero out * frames for Port 1, R/W */ #define MCS7840_DEV_REG_ZERO_PERIOD4 0x3d /* Period between zero out * frames for Port 1, R/W */ #define MCS7840_DEV_REG_ZERO_ENABLE 0x3e /* Enable/disable of zero out * frames, R/W */ -#define MCS7840_DEV_REG_THR_VAL_LOW1 0x3f /* Low 8 bits of threshhold +#define MCS7840_DEV_REG_THR_VAL_LOW1 0x3f /* Low 8 bits of threshold * value for Bulk-Out for Port * 1, R/W */ -#define MCS7840_DEV_REG_THR_VAL_HIGH1 0x40 /* High 1 bit of threshhold +#define MCS7840_DEV_REG_THR_VAL_HIGH1 0x40 /* High 1 bit of threshold * value for Bulk-Out and * enable flag for Port 1, R/W */ -#define MCS7840_DEV_REG_THR_VAL_LOW2 0x41 /* Low 8 bits of threshhold +#define MCS7840_DEV_REG_THR_VAL_LOW2 0x41 /* Low 8 bits of threshold * value for Bulk-Out for Port * 2, R/W */ -#define MCS7840_DEV_REG_THR_VAL_HIGH2 0x42 /* High 1 bit of threshhold +#define MCS7840_DEV_REG_THR_VAL_HIGH2 0x42 /* High 1 bit of threshold * value for Bulk-Out and * enable flag for Port 2, R/W */ -#define MCS7840_DEV_REG_THR_VAL_LOW3 0x43 /* Low 8 bits of threshhold +#define MCS7840_DEV_REG_THR_VAL_LOW3 0x43 /* Low 8 bits of threshold * value for Bulk-Out for Port * 3, R/W */ -#define MCS7840_DEV_REG_THR_VAL_HIGH3 0x44 /* High 1 bit of threshhold +#define MCS7840_DEV_REG_THR_VAL_HIGH3 0x44 /* High 1 bit of threshold * value for Bulk-Out and * enable flag for Port 3, R/W */ -#define MCS7840_DEV_REG_THR_VAL_LOW4 0x45 /* Low 8 bits of threshhold +#define MCS7840_DEV_REG_THR_VAL_LOW4 0x45 /* Low 8 bits of threshold * value for Bulk-Out for Port * 4, R/W */ -#define MCS7840_DEV_REG_THR_VAL_HIGH4 0x46 /* High 1 bit of threshhold +#define MCS7840_DEV_REG_THR_VAL_HIGH4 0x46 /* High 1 bit of threshold * value for Bulk-Out and * enable flag for Port 4, R/W */ /* Bits for SPx registers */ #define MCS7840_DEV_SPx_LOOP_PIPES 0x01 /* Loop Bulk-Out FIFO to the * Bulk-In FIFO, default = 0 */ #define MCS7840_DEV_SPx_SKIP_ERR_DATA 0x02 /* Drop data bytes from UART, * which were recevied with * errors, default = 0 */ #define MCS7840_DEV_SPx_RESET_OUT_FIFO 0x04 /* Reset Bulk-Out FIFO */ #define MCS7840_DEV_SPx_RESET_IN_FIFO 0x08 /* Reset Bulk-In FIFO */ #define MCS7840_DEV_SPx_CLOCK_MASK 0x70 /* Mask to extract Baud CLK * source */ #define MCS7840_DEV_SPx_CLOCK_X1 0x00 /* CLK = 1.8432Mhz, max speed * = 115200 bps, default */ #define MCS7840_DEV_SPx_CLOCK_X2 0x10 /* CLK = 3.6864Mhz, max speed * = 230400 bps */ #define MCS7840_DEV_SPx_CLOCK_X35 0x20 /* CLK = 6.4512Mhz, max speed * = 403200 bps */ #define MCS7840_DEV_SPx_CLOCK_X4 0x30 /* CLK = 7.3728Mhz, max speed * = 460800 bps */ #define MCS7840_DEV_SPx_CLOCK_X7 0x40 /* CLK = 12.9024Mhz, max speed * = 806400 bps */ #define MCS7840_DEV_SPx_CLOCK_X8 0x50 /* CLK = 14.7456Mhz, max speed * = 921600 bps */ #define MCS7840_DEV_SPx_CLOCK_24MHZ 0x60 /* CLK = 24.0000Mhz, max speed * = 1.5 Mbps */ #define MCS7840_DEV_SPx_CLOCK_48MHZ 0x70 /* CLK = 48.0000Mhz, max speed * = 3.0 Mbps */ #define MCS7840_DEV_SPx_CLOCK_SHIFT 4 /* Value 0..7 can be shifted * to get clock value */ #define MCS7840_DEV_SPx_UART_RESET 0x80 /* Reset UART */ /* Bits for CONTROLx registers */ #define MCS7840_DEV_CONTROLx_HWFC 0x01 /* Enable hardware flow * control (when power * down? It is unclear * in documents), * default = 0 */ #define MCS7840_DEV_CONTROLx_UNUNSED1 0x02 /* Reserved */ #define MCS7840_DEV_CONTROLx_CTS_ENABLE 0x04 /* CTS changes are * translated to MSR, * default = 0 */ #define MCS7840_DEV_CONTROLx_UNUSED2 0x08 /* Reserved for ports * 2,3,4 */ #define MCS7840_DEV_CONTROL1_DRIVER_DONE 0x08 /* USB enumerating is * finished, USB * enumeration memory * can be used as FIFOs */ #define MCS7840_DEV_CONTROLx_RX_NEGATE 0x10 /* Negate RX input, * works for IrDA mode * only, default = 0 */ #define MCS7840_DEV_CONTROLx_RX_DISABLE 0x20 /* Disable RX logic, * works only for * RS-232/RS-485 mode, * default = 0 */ #define MCS7840_DEV_CONTROLx_FSM_CONTROL 0x40 /* Disable RX FSM when * TX is in progress, * works for IrDA mode * only, default = 0 */ #define MCS7840_DEV_CONTROLx_UNUSED3 0x80 /* Reserved */ /* * Bits for PINPONGx registers * These registers control how often two input buffers * for Bulk-In FIFOs are swapped. One of buffers is used * for USB trnasfer, other for receiving data from UART. * Exact meaning of 15 bit value in these registers is unknown */ #define MCS7840_DEV_PINPONGHIGH_MULT 128 /* Only 7 bits in PINPONGLOW * register */ #define MCS7840_DEV_PINPONGLOW_BITS 7 /* Only 7 bits in PINPONGLOW * register */ /* * THIS ONE IS UNDOCUMENTED IN FULL DATASHEET, but e-mail from tech support * confirms, that it is register for GPIO_0 and GPIO_1 data input/output. * Chips has 2 GPIO, but first one (lower bit) MUST be used by device * authors as "number of port" indicator, grounded (0) for two-port * devices and pulled-up to 1 for 4-port devices. */ #define MCS7840_DEV_GPIO_4PORTS 0x01 /* Device has 4 ports * configured */ #define MCS7840_DEV_GPIO_GPIO_0 0x01 /* The same as above */ #define MCS7840_DEV_GPIO_GPIO_1 0x02 /* GPIO_1 data */ /* * Constants for PLL dividers * Ouptut frequency of PLL is: * Fout = (N/M) * Fin. * Default PLL input frequency Fin is 12Mhz (on-chip). */ #define MCS7840_DEV_PLL_DIV_M_BITS 6 /* Number of useful bits for M * divider */ #define MCS7840_DEV_PLL_DIV_M_MASK 0x3f /* Mask for M divider */ #define MCS7840_DEV_PLL_DIV_M_MIN 1 /* Minimum value for M, 0 is * forbidden */ #define MCS7840_DEV_PLL_DIV_M_DEF 1 /* Default value for M */ #define MCS7840_DEV_PLL_DIV_M_MAX 63 /* Maximum value for M */ #define MCS7840_DEV_PLL_DIV_N_BITS 6 /* Number of useful bits for N * divider */ #define MCS7840_DEV_PLL_DIV_N_MASK 0x3f /* Mask for N divider */ #define MCS7840_DEV_PLL_DIV_N_MIN 1 /* Minimum value for N, 0 is * forbidden */ #define MCS7840_DEV_PLL_DIV_N_DEF 8 /* Default value for N */ #define MCS7840_DEV_PLL_DIV_N_MAX 63 /* Maximum value for N */ /* Bits for CLOCK_MUX register */ #define MCS7840_DEV_CLOCK_MUX_INPUTMASK 0x03 /* Mask to extract PLL clock * input */ #define MCS7840_DEV_CLOCK_MUX_IN12MHZ 0x00 /* 12Mhz PLL input, default */ #define MCS7840_DEV_CLOCK_MUX_INEXTRN 0x01 /* External (device-depended) * PLL input */ #define MCS7840_DEV_CLOCK_MUX_INRSV1 0x02 /* Reserved */ #define MCS7840_DEV_CLOCK_MUX_INRSV2 0x03 /* Reserved */ #define MCS7840_DEV_CLOCK_MUX_PLLHIGH 0x04 /* 0 = PLL Output is * 20MHz-100MHz (default), 1 = * 100MHz-300MHz range */ #define MCS7840_DEV_CLOCK_MUX_INTRFIFOS 0x08 /* Enable additional 8 bytes * fro Interrupt USB pipe with * USB FIFOs statuses, default * = 0 */ #define MCS7840_DEV_CLOCK_MUX_RESERVED1 0x10 /* Unused */ #define MCS7840_DEV_CLOCK_MUX_RESERVED2 0x20 /* Unused */ #define MCS7840_DEV_CLOCK_MUX_RESERVED3 0x40 /* Unused */ #define MCS7840_DEV_CLOCK_MUX_RESERVED4 0x80 /* Unused */ /* Bits for CLOCK_SELECTxx registers */ #define MCS7840_DEV_CLOCK_SELECT1_MASK 0x07 /* Bits for port 1 in * CLOCK_SELECT12 */ #define MCS7840_DEV_CLOCK_SELECT1_SHIFT 0 /* Shift for port 1in * CLOCK_SELECT12 */ #define MCS7840_DEV_CLOCK_SELECT2_MASK 0x38 /* Bits for port 2 in * CLOCK_SELECT12 */ #define MCS7840_DEV_CLOCK_SELECT2_SHIFT 3 /* Shift for port 2 in * CLOCK_SELECT12 */ #define MCS7840_DEV_CLOCK_SELECT3_MASK 0x07 /* Bits for port 3 in * CLOCK_SELECT23 */ #define MCS7840_DEV_CLOCK_SELECT3_SHIFT 0 /* Shift for port 3 in * CLOCK_SELECT23 */ #define MCS7840_DEV_CLOCK_SELECT4_MASK 0x38 /* Bits for port 4 in * CLOCK_SELECT23 */ #define MCS7840_DEV_CLOCK_SELECT4_SHIFT 3 /* Shift for port 4 in * CLOCK_SELECT23 */ #define MCS7840_DEV_CLOCK_SELECT_STD 0x00 /* STANDARD baudrate derived * from 96Mhz, default for all * ports */ #define MCS7840_DEV_CLOCK_SELECT_30MHZ 0x01 /* 30Mhz */ #define MCS7840_DEV_CLOCK_SELECT_96MHZ 0x02 /* 96Mhz direct */ #define MCS7840_DEV_CLOCK_SELECT_120MHZ 0x03 /* 120Mhz */ #define MCS7840_DEV_CLOCK_SELECT_PLL 0x04 /* PLL output (see for M and N * dividers) */ #define MCS7840_DEV_CLOCK_SELECT_EXT 0x05 /* External clock input * (device-dependend) */ #define MCS7840_DEV_CLOCK_SELECT_RES1 0x06 /* Unused */ #define MCS7840_DEV_CLOCK_SELECT_RES2 0x07 /* Unused */ /* Bits for MODE register */ #define MCS7840_DEV_MODE_RESERVED1 0x01 /* Unused */ #define MCS7840_DEV_MODE_RESET 0x02 /* 0: RESET = Active High * (default), 1: Reserved (?) */ #define MCS7840_DEV_MODE_SER_PRSNT 0x04 /* 0: Reserved, 1: Do not use * hardocded values (default) * (?) */ #define MCS7840_DEV_MODE_PLLBYPASS 0x08 /* 1: PLL output is bypassed, * default = 0 */ #define MCS7840_DEV_MODE_PORBYPASS 0x10 /* 1: Power-On Reset is * bypassed, default = 0 */ #define MCS7840_DEV_MODE_SELECT24S 0x20 /* 0: 4 Serial Ports / IrDA * active, 1: 2 Serial Ports / * IrDA active */ #define MCS7840_DEV_MODE_EEPROMWR 0x40 /* EEPROM write is enabled, * default */ #define MCS7840_DEV_MODE_IRDA 0x80 /* IrDA mode is activated * (could be turned on), * default */ /* Bits for SPx ICG */ #define MCS7840_DEV_SPx_ICG_DEF 0x24 /* All 8 bits is used as * number of BAUD clocks of * pause */ /* * Bits for RX_SAMPLINGxx registers * These registers control when bit value will be sampled within * the baud period. * 0 is very beginning of period, 15 is very end, 7 is the middle. */ #define MCS7840_DEV_RX_SAMPLING1_MASK 0x0f /* Bits for port 1 in * RX_SAMPLING12 */ #define MCS7840_DEV_RX_SAMPLING1_SHIFT 0 /* Shift for port 1in * RX_SAMPLING12 */ #define MCS7840_DEV_RX_SAMPLING2_MASK 0xf0 /* Bits for port 2 in * RX_SAMPLING12 */ #define MCS7840_DEV_RX_SAMPLING2_SHIFT 4 /* Shift for port 2 in * RX_SAMPLING12 */ #define MCS7840_DEV_RX_SAMPLING3_MASK 0x0f /* Bits for port 3 in * RX_SAMPLING23 */ #define MCS7840_DEV_RX_SAMPLING3_SHIFT 0 /* Shift for port 3 in * RX_SAMPLING23 */ #define MCS7840_DEV_RX_SAMPLING4_MASK 0xf0 /* Bits for port 4 in * RX_SAMPLING23 */ #define MCS7840_DEV_RX_SAMPLING4_SHIFT 4 /* Shift for port 4 in * RX_SAMPLING23 */ #define MCS7840_DEV_RX_SAMPLINGx_MIN 0 /* Max for any RX Sampling */ #define MCS7840_DEV_RX_SAMPLINGx_DEF 7 /* Default for any RX * Sampling, center of period */ #define MCS7840_DEV_RX_SAMPLINGx_MAX 15 /* Min for any RX Sampling */ /* Bits for ZERO_PERIODx */ #define MCS7840_DEV_ZERO_PERIODx_DEF 20 /* Number of Bulk-in requests * befor sending zero-sized * reply */ /* Bits for ZERO_ENABLE */ #define MCS7840_DEV_ZERO_ENABLE_PORT1 0x01 /* Enable of sending * zero-sized replies for port * 1, default */ #define MCS7840_DEV_ZERO_ENABLE_PORT2 0x02 /* Enable of sending * zero-sized replies for port * 2, default */ #define MCS7840_DEV_ZERO_ENABLE_PORT3 0x04 /* Enable of sending * zero-sized replies for port * 3, default */ #define MCS7840_DEV_ZERO_ENABLE_PORT4 0x08 /* Enable of sending * zero-sized replies for port * 4, default */ /* Bits for THR_VAL_HIGHx */ #define MCS7840_DEV_THR_VAL_HIGH_MASK 0x01 /* Only one bit is used */ #define MCS7840_DEV_THR_VAL_HIGH_MUL 256 /* This one bit is means "256" */ #define MCS7840_DEV_THR_VAL_HIGH_SHIFT 8 /* This one bit is means "256" */ #define MCS7840_DEV_THR_VAL_HIGH_ENABLE 0x80 /* Enable threshold */ /* These are documented in "public" datasheet */ #define MCS7840_DEV_REG_DCR0_1 0x04 /* Device contol register 0 for Port * 1, R/W */ #define MCS7840_DEV_REG_DCR1_1 0x05 /* Device contol register 1 for Port * 1, R/W */ #define MCS7840_DEV_REG_DCR2_1 0x06 /* Device contol register 2 for Port * 1, R/W */ #define MCS7840_DEV_REG_DCR0_2 0x16 /* Device contol register 0 for Port * 2, R/W */ #define MCS7840_DEV_REG_DCR1_2 0x17 /* Device contol register 1 for Port * 2, R/W */ #define MCS7840_DEV_REG_DCR2_2 0x18 /* Device contol register 2 for Port * 2, R/W */ #define MCS7840_DEV_REG_DCR0_3 0x19 /* Device contol register 0 for Port * 3, R/W */ #define MCS7840_DEV_REG_DCR1_3 0x1a /* Device contol register 1 for Port * 3, R/W */ #define MCS7840_DEV_REG_DCR2_3 0x1b /* Device contol register 2 for Port * 3, R/W */ #define MCS7840_DEV_REG_DCR0_4 0x1c /* Device contol register 0 for Port * 4, R/W */ #define MCS7840_DEV_REG_DCR1_4 0x1d /* Device contol register 1 for Port * 4, R/W */ #define MCS7840_DEV_REG_DCR2_4 0x1e /* Device contol register 2 for Port * 4, R/W */ /* Bits of DCR0 registers, documented in datasheet */ #define MCS7840_DEV_DCR0_PWRSAVE 0x01 /* Shutdown transiver * when USB Suspend is * engaged, default = 1 */ #define MCS7840_DEV_DCR0_RESERVED1 0x02 /* Unused */ #define MCS7840_DEV_DCR0_GPIO_MODE_MASK 0x0c /* GPIO Mode bits, WORKS * ONLY FOR PORT 1 */ #define MCS7840_DEV_DCR0_GPIO_MODE_IN 0x00 /* GPIO Mode - Input * (0b00), WORKS ONLY * FOR PORT 1 */ #define MCS7840_DEV_DCR0_GPIO_MODE_OUT 0x08 /* GPIO Mode - Input * (0b10), WORKS ONLY * FOR PORT 1 */ #define MCS7840_DEV_DCR0_RTS_ACTIVE_HIGH 0x10 /* RTS Active is HIGH, * default = 0 (low) */ #define MCS7840_DEV_DCR0_RTS_AUTO 0x20 /* RTS is controlled by * state of TX buffer, * default = 0 * (controlled by MCR) */ #define MCS7840_DEV_DCR0_IRDA 0x40 /* IrDA mode */ #define MCS7840_DEV_DCR0_RESERVED2 0x80 /* Unused */ /* Bits of DCR1 registers, documented in datasheet */ #define MCS7840_DEV_DCR1_GPIO_CURRENT_MASK 0x03 /* Mask to extract GPIO * current value, WORKS * ONLY FOR PORT 1 */ #define MCS7840_DEV_DCR1_GPIO_CURRENT_6MA 0x00 /* GPIO output current * 6mA, WORKS ONLY FOR * PORT 1 */ #define MCS7840_DEV_DCR1_GPIO_CURRENT_8MA 0x01 /* GPIO output current * 8mA, defauilt, WORKS * ONLY FOR PORT 1 */ #define MCS7840_DEV_DCR1_GPIO_CURRENT_10MA 0x02 /* GPIO output current * 10mA, WORKS ONLY FOR * PORT 1 */ #define MCS7840_DEV_DCR1_GPIO_CURRENT_12MA 0x03 /* GPIO output current * 12mA, WORKS ONLY FOR * PORT 1 */ #define MCS7840_DEV_DCR1_UART_CURRENT_MASK 0x0c /* Mask to extract UART * signals current value */ #define MCS7840_DEV_DCR1_UART_CURRENT_6MA 0x00 /* UART output current * 6mA */ #define MCS7840_DEV_DCR1_UART_CURRENT_8MA 0x04 /* UART output current * 8mA, defauilt */ #define MCS7840_DEV_DCR1_UART_CURRENT_10MA 0x08 /* UART output current * 10mA */ #define MCS7840_DEV_DCR1_UART_CURRENT_12MA 0x0c /* UART output current * 12mA */ #define MCS7840_DEV_DCR1_WAKEUP_DISABLE 0x10 /* Disable Remote USB * Wakeup */ #define MCS7840_DEV_DCR1_PLLPWRDOWN_DISABLE 0x20 /* Disable PLL power * down when not needed, * WORKS ONLY FOR PORT 1 */ #define MCS7840_DEV_DCR1_LONG_INTERRUPT 0x40 /* Enable 13 bytes of * interrupt data, with * FIFO statistics, * WORKS ONLY FOR PORT 1 */ #define MCS7840_DEV_DCR1_RESERVED1 0x80 /* Unused */ /* * Bits of DCR2 registers, documented in datasheet * Wakeup will work only if DCR0_IRDA = 0 (RS-xxx mode) and * DCR1_WAKEUP_DISABLE = 0 (wakeup enabled). */ #define MCS7840_DEV_DCR2_WAKEUP_CTS 0x01 /* Wakeup on CTS change, * default = 0 */ #define MCS7840_DEV_DCR2_WAKEUP_DCD 0x02 /* Wakeup on DCD change, * default = 0 */ #define MCS7840_DEV_DCR2_WAKEUP_RI 0x04 /* Wakeup on RI change, * default = 1 */ #define MCS7840_DEV_DCR2_WAKEUP_DSR 0x08 /* Wakeup on DSR change, * default = 0 */ #define MCS7840_DEV_DCR2_WAKEUP_RXD 0x10 /* Wakeup on RX Data change, * default = 0 */ #define MCS7840_DEV_DCR2_WAKEUP_RESUME 0x20 /* Wakeup issues RESUME * signal, DISCONNECT * otherwise, default = 1 */ #define MCS7840_DEV_DCR2_RESERVED1 0x40 /* Unused */ #define MCS7840_DEV_DCR2_SHDN_POLARITY 0x80 /* 0: Pin 12 Active Low, 1: * Pin 12 Active High, default * = 0 */ /* Interrupt endpoint bytes & bits */ #define MCS7840_IEP_FIFO_STATUS_INDEX 5 /* * Thesse can be calculated as "1 << portnumber" for Bulk-out and * "1 << (portnumber+1)" for Bulk-in */ #define MCS7840_IEP_BO_PORT1_HASDATA 0x01 #define MCS7840_IEP_BI_PORT1_HASDATA 0x02 #define MCS7840_IEP_BO_PORT2_HASDATA 0x04 #define MCS7840_IEP_BI_PORT2_HASDATA 0x08 #define MCS7840_IEP_BO_PORT3_HASDATA 0x10 #define MCS7840_IEP_BI_PORT3_HASDATA 0x20 #define MCS7840_IEP_BO_PORT4_HASDATA 0x40 #define MCS7840_IEP_BI_PORT4_HASDATA 0x80 /* Documented UART registers (fully compatible with 16550 UART) */ #define MCS7840_UART_REG_THR 0x00 /* Transmitter Holding * Register W/Only */ #define MCS7840_UART_REG_RHR 0x00 /* Receiver Holding Register * R/Only */ #define MCS7840_UART_REG_IER 0x01 /* Interrupt enable register - * R/W */ #define MCS7840_UART_REG_FCR 0x02 /* FIFO Control register - * W/Only */ #define MCS7840_UART_REG_ISR 0x02 /* Interrupt Status Registter * R/Only */ #define MCS7840_UART_REG_LCR 0x03 /* Line control register R/W */ #define MCS7840_UART_REG_MCR 0x04 /* Modem control register R/W */ #define MCS7840_UART_REG_LSR 0x05 /* Line status register R/Only */ #define MCS7840_UART_REG_MSR 0x06 /* Modem status register * R/Only */ #define MCS7840_UART_REG_SCRATCHPAD 0x07 /* Scratch pad register */ #define MCS7840_UART_REG_DLL 0x00 /* Low bits of BAUD divider */ #define MCS7840_UART_REG_DLM 0x01 /* High bits of BAUD divider */ /* IER bits */ #define MCS7840_UART_IER_RXREADY 0x01 /* RX Ready interrumpt mask */ #define MCS7840_UART_IER_TXREADY 0x02 /* TX Ready interrumpt mask */ #define MCS7840_UART_IER_RXSTAT 0x04 /* RX Status interrumpt mask */ #define MCS7840_UART_IER_MODEM 0x08 /* Modem status change * interrumpt mask */ #define MCS7840_UART_IER_SLEEP 0x10 /* SLEEP enable */ /* FCR bits */ #define MCS7840_UART_FCR_ENABLE 0x01 /* Enable FIFO */ #define MCS7840_UART_FCR_FLUSHRHR 0x02 /* Flush RHR and FIFO */ #define MCS7840_UART_FCR_FLUSHTHR 0x04 /* Flush THR and FIFO */ #define MCS7840_UART_FCR_RTLMASK 0xa0 /* Mask to select RHR * Interrupt Trigger level */ #define MCS7840_UART_FCR_RTL_1_1 0x00 /* L1 = 1, L2 = 1 */ #define MCS7840_UART_FCR_RTL_1_4 0x40 /* L1 = 1, L2 = 4 */ #define MCS7840_UART_FCR_RTL_1_8 0x80 /* L1 = 1, L2 = 8 */ #define MCS7840_UART_FCR_RTL_1_14 0xa0 /* L1 = 1, L2 = 14 */ /* ISR bits */ #define MCS7840_UART_ISR_NOPENDING 0x01 /* No interrupt pending */ #define MCS7840_UART_ISR_INTMASK 0x3f /* Mask to select interrupt * source */ #define MCS7840_UART_ISR_RXERR 0x06 /* Recevir error */ #define MCS7840_UART_ISR_RXHASDATA 0x04 /* Recevier has data */ #define MCS7840_UART_ISR_RXTIMEOUT 0x0c /* Recevier timeout */ #define MCS7840_UART_ISR_TXEMPTY 0x02 /* Transmitter empty */ #define MCS7840_UART_ISR_MSCHANGE 0x00 /* Modem status change */ /* LCR bits */ #define MCS7840_UART_LCR_DATALENMASK 0x03 /* Mask for data length */ #define MCS7840_UART_LCR_DATALEN5 0x00 /* 5 data bits */ #define MCS7840_UART_LCR_DATALEN6 0x01 /* 6 data bits */ #define MCS7840_UART_LCR_DATALEN7 0x02 /* 7 data bits */ #define MCS7840_UART_LCR_DATALEN8 0x03 /* 8 data bits */ #define MCS7840_UART_LCR_STOPBMASK 0x04 /* Mask for stop bits */ #define MCS7840_UART_LCR_STOPB1 0x00 /* 1 stop bit in any case */ #define MCS7840_UART_LCR_STOPB2 0x04 /* 1.5-2 stop bits depends on * data length */ #define MCS7840_UART_LCR_PARITYMASK 0x38 /* Mask for all parity data */ #define MCS7840_UART_LCR_PARITYON 0x08 /* Parity ON/OFF - ON */ #define MCS7840_UART_LCR_PARITYODD 0x00 /* Parity Odd */ #define MCS7840_UART_LCR_PARITYEVEN 0x10 /* Parity Even */ #define MCS7840_UART_LCR_PARITYODD 0x00 /* Parity Odd */ #define MCS7840_UART_LCR_PARITYFORCE 0x20 /* Force parity odd/even */ #define MCS7840_UART_LCR_BREAK 0x40 /* Send BREAK */ #define MCS7840_UART_LCR_DIVISORS 0x80 /* Map DLL/DLM instead of * xHR/IER */ /* LSR bits */ #define MCS7840_UART_LSR_RHRAVAIL 0x01 /* Data available for read */ #define MCS7840_UART_LSR_RHROVERRUN 0x02 /* Data FIFO/register overflow */ #define MCS7840_UART_LSR_PARITYERR 0x04 /* Parity error */ #define MCS7840_UART_LSR_FRAMEERR 0x10 /* Framing error */ #define MCS7840_UART_LSR_BREAKERR 0x20 /* BREAK signal received */ #define MCS7840_UART_LSR_THREMPTY 0x40 /* THR register is empty, * ready for transmit */ #define MCS7840_UART_LSR_HASERR 0x80 /* Has error in receiver FIFO */ /* MCR bits */ #define MCS7840_UART_MCR_DTR 0x01 /* Force DTR to be active * (low) */ #define MCS7840_UART_MCR_RTS 0x02 /* Force RTS to be active * (low) */ #define MCS7840_UART_MCR_IE 0x04 /* Enable interrupts (from * code, not documented) */ #define MCS7840_UART_MCR_LOOPBACK 0x10 /* Enable local loopback test * mode */ #define MCS7840_UART_MCR_CTSRTS 0x20 /* Enable CTS/RTS flow control * in 550 (FIFO) mode */ #define MCS7840_UART_MCR_DTRDSR 0x40 /* Enable DTR/DSR flow control * in 550 (FIFO) mode */ #define MCS7840_UART_MCR_DCD 0x80 /* Enable DCD flow control in * 550 (FIFO) mode */ /* MSR bits */ #define MCS7840_UART_MSR_DELTACTS 0x01 /* CTS was changed since last * read */ #define MCS7840_UART_MSR_DELTADSR 0x02 /* DSR was changed since last * read */ #define MCS7840_UART_MSR_DELTARI 0x04 /* RI was changed from low to * high since last read */ #define MCS7840_UART_MSR_DELTADCD 0x08 /* DCD was changed since last * read */ #define MCS7840_UART_MSR_NEGCTS 0x10 /* Negated CTS signal */ #define MCS7840_UART_MSR_NEGDSR 0x20 /* Negated DSR signal */ #define MCS7840_UART_MSR_NEGRI 0x40 /* Negated RI signal */ #define MCS7840_UART_MSR_NEGDCD 0x80 /* Negated DCD signal */ /* SCRATCHPAD bits */ #define MCS7840_UART_SCRATCHPAD_RS232 0x00 /* RS-485 disabled */ #define MCS7840_UART_SCRATCHPAD_RS485_DTRRX 0x80 /* RS-485 mode, DTR High * = RX */ #define MCS7840_UART_SCRATCHPAD_RS485_DTRTX 0xc0 /* RS-485 mode, DTR High * = TX */ #define MCS7840_CONFIG_INDEX 0 #define MCS7840_IFACE_INDEX 0 #endif Index: head/sys/dev/usb/serial/uvisor.c =================================================================== --- head/sys/dev/usb/serial/uvisor.c (revision 298931) +++ head/sys/dev/usb/serial/uvisor.c (revision 298932) @@ -1,677 +1,677 @@ /* $NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $ */ /* $FreeBSD$ */ /* Also already merged from NetBSD: * $NetBSD: uvisor.c,v 1.12 2001/11/13 06:24:57 lukem Exp $ * $NetBSD: uvisor.c,v 1.13 2002/02/11 15:11:49 augustss Exp $ * $NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $ * $NetBSD: uvisor.c,v 1.15 2002/06/16 15:01:31 augustss Exp $ * $NetBSD: uvisor.c,v 1.16 2002/07/11 21:14:36 augustss Exp $ * $NetBSD: uvisor.c,v 1.17 2002/08/13 11:38:15 augustss Exp $ * $NetBSD: uvisor.c,v 1.18 2003/02/05 00:50:14 augustss Exp $ * $NetBSD: uvisor.c,v 1.19 2003/02/07 18:12:37 augustss Exp $ * $NetBSD: uvisor.c,v 1.20 2003/04/11 01:30:10 simonb Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Handspring Visor (Palmpilot compatible PDA) driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR uvisor_debug #include #include #include #ifdef USB_DEBUG static int uvisor_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor"); SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RWTUN, &uvisor_debug, 0, "Debug level"); #endif #define UVISOR_CONFIG_INDEX 0 #define UVISOR_IFACE_INDEX 0 /* * The following buffer sizes are hardcoded due to the way the Palm * firmware works. It looks like the device is not short terminating * the data transferred. */ #define UVISORIBUFSIZE 0 /* Use wMaxPacketSize */ #define UVISOROBUFSIZE 32 /* bytes */ #define UVISOROFRAMES 32 /* units */ /* From the Linux driver */ /* * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that - * are available to be transfered to the host for the specified endpoint. + * are available to be transferred to the host for the specified endpoint. * Currently this is not used, and always returns 0x0001 */ #define UVISOR_REQUEST_BYTES_AVAILABLE 0x01 /* * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host * is now closing the pipe. An empty packet is sent in response. */ #define UVISOR_CLOSE_NOTIFICATION 0x02 /* * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to * get the endpoints used by the connection. */ #define UVISOR_GET_CONNECTION_INFORMATION 0x03 /* * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format */ #define UVISOR_MAX_CONN 8 struct uvisor_connection_info { uWord num_ports; struct { uByte port_function_id; uByte port; } __packed connections[UVISOR_MAX_CONN]; } __packed; #define UVISOR_CONNECTION_INFO_SIZE 18 /* struct uvisor_connection_info.connection[x].port defines: */ #define UVISOR_ENDPOINT_1 0x01 #define UVISOR_ENDPOINT_2 0x02 /* struct uvisor_connection_info.connection[x].port_function_id defines: */ #define UVISOR_FUNCTION_GENERIC 0x00 #define UVISOR_FUNCTION_DEBUGGER 0x01 #define UVISOR_FUNCTION_HOTSYNC 0x02 #define UVISOR_FUNCTION_CONSOLE 0x03 #define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04 /* * Unknown PalmOS stuff. */ #define UVISOR_GET_PALM_INFORMATION 0x04 #define UVISOR_GET_PALM_INFORMATION_LEN 0x44 struct uvisor_palm_connection_info { uByte num_ports; uByte endpoint_numbers_different; uWord reserved1; struct { uDWord port_function_id; uByte port; uByte end_point_info; uWord reserved; } __packed connections[UVISOR_MAX_CONN]; } __packed; enum { UVISOR_BULK_DT_WR, UVISOR_BULK_DT_RD, UVISOR_N_TRANSFER, }; struct uvisor_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UVISOR_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint16_t sc_flag; #define UVISOR_FLAG_PALM4 0x0001 #define UVISOR_FLAG_VISOR 0x0002 #define UVISOR_FLAG_PALM35 0x0004 #define UVISOR_FLAG_SEND_NOTIFY 0x0008 uint8_t sc_iface_no; uint8_t sc_iface_index; }; /* prototypes */ static device_probe_t uvisor_probe; static device_attach_t uvisor_attach; static device_detach_t uvisor_detach; static void uvisor_free_softc(struct uvisor_softc *); static usb_callback_t uvisor_write_callback; static usb_callback_t uvisor_read_callback; static usb_error_t uvisor_init(struct uvisor_softc *, struct usb_device *, struct usb_config *); static void uvisor_free(struct ucom_softc *); static void uvisor_cfg_open(struct ucom_softc *); static void uvisor_cfg_close(struct ucom_softc *); static void uvisor_start_read(struct ucom_softc *); static void uvisor_stop_read(struct ucom_softc *); static void uvisor_start_write(struct ucom_softc *); static void uvisor_stop_write(struct ucom_softc *); static const struct usb_config uvisor_config[UVISOR_N_TRANSFER] = { [UVISOR_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UVISOROBUFSIZE * UVISOROFRAMES, .frames = UVISOROFRAMES, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &uvisor_write_callback, }, [UVISOR_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UVISORIBUFSIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uvisor_read_callback, }, }; static const struct ucom_callback uvisor_callback = { .ucom_cfg_open = &uvisor_cfg_open, .ucom_cfg_close = &uvisor_cfg_close, .ucom_start_read = &uvisor_start_read, .ucom_stop_read = &uvisor_stop_read, .ucom_start_write = &uvisor_start_write, .ucom_stop_write = &uvisor_stop_write, .ucom_free = &uvisor_free, }; static device_method_t uvisor_methods[] = { DEVMETHOD(device_probe, uvisor_probe), DEVMETHOD(device_attach, uvisor_attach), DEVMETHOD(device_detach, uvisor_detach), DEVMETHOD_END }; static devclass_t uvisor_devclass; static driver_t uvisor_driver = { .name = "uvisor", .methods = uvisor_methods, .size = sizeof(struct uvisor_softc), }; static const STRUCT_USB_HOST_ID uvisor_devs[] = { #define UVISOR_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } UVISOR_DEV(ACEECA, MEZ1000, UVISOR_FLAG_PALM4), UVISOR_DEV(ALPHASMART, DANA_SYNC, UVISOR_FLAG_PALM4), UVISOR_DEV(GARMIN, IQUE_3600, UVISOR_FLAG_PALM4), UVISOR_DEV(FOSSIL, WRISTPDA, UVISOR_FLAG_PALM4), UVISOR_DEV(HANDSPRING, VISOR, UVISOR_FLAG_VISOR), UVISOR_DEV(HANDSPRING, TREO, UVISOR_FLAG_PALM4), UVISOR_DEV(HANDSPRING, TREO600, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, M500, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, M505, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, M515, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, I705, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, M125, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, M130, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, TUNGSTEN_Z, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, TUNGSTEN_T, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, ZIRE, UVISOR_FLAG_PALM4), UVISOR_DEV(PALM, ZIRE31, UVISOR_FLAG_PALM4), UVISOR_DEV(SAMSUNG, I500, UVISOR_FLAG_PALM4), UVISOR_DEV(SONY, CLIE_40, 0), UVISOR_DEV(SONY, CLIE_41, 0), UVISOR_DEV(SONY, CLIE_S360, UVISOR_FLAG_PALM4), UVISOR_DEV(SONY, CLIE_NX60, UVISOR_FLAG_PALM4), UVISOR_DEV(SONY, CLIE_35, UVISOR_FLAG_PALM35), /* UVISOR_DEV(SONY, CLIE_25, UVISOR_FLAG_PALM4 ), */ UVISOR_DEV(SONY, CLIE_TJ37, UVISOR_FLAG_PALM4), /* UVISOR_DEV(SONY, CLIE_TH55, UVISOR_FLAG_PALM4 ), See PR 80935 */ UVISOR_DEV(TAPWAVE, ZODIAC, UVISOR_FLAG_PALM4), #undef UVISOR_DEV }; DRIVER_MODULE(uvisor, uhub, uvisor_driver, uvisor_devclass, NULL, 0); MODULE_DEPEND(uvisor, ucom, 1, 1, 1); MODULE_DEPEND(uvisor, usb, 1, 1, 1); MODULE_VERSION(uvisor, 1); USB_PNP_HOST_INFO(uvisor_devs); static int uvisor_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UVISOR_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UVISOR_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(uvisor_devs, sizeof(uvisor_devs), uaa)); } static int uvisor_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uvisor_softc *sc = device_get_softc(dev); struct usb_config uvisor_config_copy[UVISOR_N_TRANSFER]; int error; DPRINTF("sc=%p\n", sc); memcpy(uvisor_config_copy, uvisor_config, sizeof(uvisor_config_copy)); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uvisor", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_udev = uaa->device; /* configure the device */ sc->sc_flag = USB_GET_DRIVER_INFO(uaa); sc->sc_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index = UVISOR_IFACE_INDEX; error = uvisor_init(sc, uaa->device, uvisor_config_copy); if (error) { DPRINTF("init failed, error=%s\n", usbd_errstr(error)); goto detach; } error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index, sc->sc_xfer, uvisor_config_copy, UVISOR_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("could not allocate all pipes\n"); goto detach; } error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uvisor_callback, &sc->sc_mtx); if (error) { DPRINTF("ucom_attach failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: uvisor_detach(dev); return (ENXIO); } static int uvisor_detach(device_t dev) { struct uvisor_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UVISOR_N_TRANSFER); device_claim_softc(dev); uvisor_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uvisor); static void uvisor_free_softc(struct uvisor_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uvisor_free(struct ucom_softc *ucom) { uvisor_free_softc(ucom->sc_parent); } static usb_error_t uvisor_init(struct uvisor_softc *sc, struct usb_device *udev, struct usb_config *config) { usb_error_t err = 0; struct usb_device_request req; struct uvisor_connection_info coninfo; struct uvisor_palm_connection_info pconinfo; uint16_t actlen; uint8_t buffer[256]; if (sc->sc_flag & UVISOR_FLAG_VISOR) { DPRINTF("getting connection info\n"); req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_GET_CONNECTION_INFORMATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); err = usbd_do_request_flags(udev, NULL, &req, &coninfo, USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT); if (err) { goto done; } } #ifdef USB_DEBUG if (sc->sc_flag & UVISOR_FLAG_VISOR) { uint16_t i, np; const char *desc; np = UGETW(coninfo.num_ports); if (np > UVISOR_MAX_CONN) { np = UVISOR_MAX_CONN; } DPRINTF("Number of ports: %d\n", np); for (i = 0; i < np; ++i) { switch (coninfo.connections[i].port_function_id) { case UVISOR_FUNCTION_GENERIC: desc = "Generic"; break; case UVISOR_FUNCTION_DEBUGGER: desc = "Debugger"; break; case UVISOR_FUNCTION_HOTSYNC: desc = "HotSync"; break; case UVISOR_FUNCTION_REMOTE_FILE_SYS: desc = "Remote File System"; break; default: desc = "unknown"; break; } DPRINTF("Port %d is for %s\n", coninfo.connections[i].port, desc); } } #endif if (sc->sc_flag & UVISOR_FLAG_PALM4) { uint8_t port; /* Palm OS 4.0 Hack */ req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_GET_PALM_INFORMATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); err = usbd_do_request_flags (udev, NULL, &req, &pconinfo, USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT); if (err) { goto done; } if (actlen < 12) { DPRINTF("too little data\n"); err = USB_ERR_INVAL; goto done; } if (pconinfo.endpoint_numbers_different) { port = pconinfo.connections[0].end_point_info; config[0].endpoint = (port & 0xF); /* output */ config[1].endpoint = (port >> 4); /* input */ } else { port = pconinfo.connections[0].port; config[0].endpoint = (port & 0xF); /* output */ config[1].endpoint = (port & 0xF); /* input */ } #if 0 req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_GET_PALM_INFORMATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); err = usbd_do_request(udev, &req, buffer); if (err) { goto done; } #endif } if (sc->sc_flag & UVISOR_FLAG_PALM35) { /* get the config number */ DPRINTF("getting config info\n"); req.bmRequestType = UT_READ; req.bRequest = UR_GET_CONFIG; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); err = usbd_do_request(udev, NULL, &req, buffer); if (err) { goto done; } /* get the interface number */ DPRINTF("get the interface number\n"); req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); err = usbd_do_request(udev, NULL, &req, buffer); if (err) { goto done; } } #if 0 uWord wAvail; DPRINTF("getting available bytes\n"); req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE; USETW(req.wValue, 0); USETW(req.wIndex, 5); USETW(req.wLength, sizeof(wAvail)); err = usbd_do_request(udev, NULL, &req, &wAvail); if (err) { goto done; } DPRINTF("avail=%d\n", UGETW(wAvail)); #endif DPRINTF("done\n"); done: return (err); } static void uvisor_cfg_open(struct ucom_softc *ucom) { return; } static void uvisor_cfg_close(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; uint8_t buffer[UVISOR_CONNECTION_INFO_SIZE]; struct usb_device_request req; usb_error_t err; req.bmRequestType = UT_READ_VENDOR_ENDPOINT; /* XXX read? */ req.bRequest = UVISOR_CLOSE_NOTIFICATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, buffer, 0, 1000); if (err) { DPRINTFN(0, "close notification failed, error=%s\n", usbd_errstr(err)); } } static void uvisor_start_read(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_RD]); } static void uvisor_stop_read(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_RD]); } static void uvisor_start_write(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_WR]); } static void uvisor_stop_write(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_WR]); } static void uvisor_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uvisor_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; uint8_t x; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: for (x = 0; x != UVISOROFRAMES; x++) { usbd_xfer_set_frame_offset(xfer, x * UVISOROBUFSIZE, x); pc = usbd_xfer_get_frame(xfer, x); if (ucom_get_data(&sc->sc_ucom, pc, 0, UVISOROBUFSIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, x, actlen); } else { break; } } /* check for data */ if (x != 0) { usbd_xfer_set_frames(xfer, x); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void uvisor_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uvisor_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } Index: head/sys/dev/usb/storage/rio500_usb.h =================================================================== --- head/sys/dev/usb/storage/rio500_usb.h (revision 298931) +++ head/sys/dev/usb/storage/rio500_usb.h (revision 298932) @@ -1,48 +1,48 @@ /*- ---------------------------------------------------------------------- Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) Redistribution and use in source and binary forms, with or without modification, are permitted under any licence of your choise which - meets the open source licence definiton + meets the open source licence definition http://www.opensource.org/opd.html such as the GNU licence or the BSD licence. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License or the BSD license for more details. ---------------------------------------------------------------------- Modified for FreeBSD by Iwasa Kazmi ---------------------------------------------------------------------- */ /* $FreeBSD$ */ #include #ifndef USB_VENDOR_DIAMOND #define USB_VENDOR_DIAMOND 0x841 #endif #ifndef USB_PRODUCT_DIAMOND_RIO500USB #define USB_PRODUCT_DIAMOND_RIO500USB 0x1 #endif struct RioCommand { uint16_t length; int request; int requesttype; int value; int index; void *buffer; int timeout; }; #define RIO_SEND_COMMAND _IOWR('U', 200, struct RioCommand) #define RIO_RECV_COMMAND _IOWR('U', 201, struct RioCommand) #define RIO_DIR_OUT 0x0 #define RIO_DIR_IN 0x1 Index: head/sys/dev/usb/storage/umass.c =================================================================== --- head/sys/dev/usb/storage/umass.c (revision 298931) +++ head/sys/dev/usb/storage/umass.c (revision 298932) @@ -1,3018 +1,3018 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1999 MAEKAWA Masahide , * Nick Hibma * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $ */ /* Also already merged from NetBSD: * $NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $ * $NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $ * $NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $ * $NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $ */ /* * Universal Serial Bus Mass Storage Class specs: * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf */ /* * Ported to NetBSD by Lennart Augustsson . * Parts of the code written by Jason R. Thorpe . */ /* * The driver handles 3 Wire Protocols * - Command/Bulk/Interrupt (CBI) * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI) * - Mass Storage Bulk-Only (BBB) * (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases) * * Over these wire protocols it handles the following command protocols * - SCSI * - UFI (floppy command set) * - 8070i (ATAPI) * * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The * sc->sc_transform method is used to convert the commands into the appropriate * format (if at all necessary). For example, UFI requires all commands to be * 12 bytes in length amongst other things. * * The source code below is marked and can be split into a number of pieces * (in this order): * * - probe/attach/detach * - generic transfer routines * - BBB * - CBI * - CBI_I (in addition to functions from CBI) * - CAM (Common Access Method) * - SCSI * - UFI * - 8070i (ATAPI) * * The protocols are implemented using a state machine, for the transfers as * well as for the resets. The state machine is contained in umass_t_*_callback. * The state machine is started through either umass_command_start() or * umass_reset(). * * The reason for doing this is a) CAM performs a lot better this way and b) it * avoids using tsleep from interrupt context (for example after a failed * transfer). */ /* * The SCSI related part of this driver has been derived from the * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@FreeBSD.org). * * The CAM layer uses so called actions which are messages sent to the host * adapter for completion. The actions come in through umass_cam_action. The * appropriate block of routines is called depending on the transport protocol * in use. When the transfer has finished, these routines call * umass_cam_cb again to complete the CAM command. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DIF(m, x) \ do { \ if (umass_debug & (m)) { x ; } \ } while (0) #define DPRINTF(sc, m, fmt, ...) \ do { \ if (umass_debug & (m)) { \ printf("%s:%s: " fmt, \ (sc) ? (const char *)(sc)->sc_name : \ (const char *)"umassX", \ __FUNCTION__ ,## __VA_ARGS__); \ } \ } while (0) #define UDMASS_GEN 0x00010000 /* general */ #define UDMASS_SCSI 0x00020000 /* scsi */ #define UDMASS_UFI 0x00040000 /* ufi command set */ #define UDMASS_ATAPI 0x00080000 /* 8070i command set */ #define UDMASS_CMD (UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI) #define UDMASS_USB 0x00100000 /* USB general */ #define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */ #define UDMASS_CBI 0x00400000 /* CBI transfers */ #define UDMASS_WIRE (UDMASS_BBB|UDMASS_CBI) #define UDMASS_ALL 0xffff0000 /* all of the above */ static int umass_debug; static int umass_throttle; static SYSCTL_NODE(_hw_usb, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass"); SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RWTUN, &umass_debug, 0, "umass debug level"); SYSCTL_INT(_hw_usb_umass, OID_AUTO, throttle, CTLFLAG_RWTUN, &umass_throttle, 0, "Forced delay between commands in milliseconds"); #else #define DIF(...) do { } while (0) #define DPRINTF(...) do { } while (0) #endif #define UMASS_BULK_SIZE (1 << 17) #define UMASS_CBI_DIAGNOSTIC_CMDLEN 12 /* bytes */ #define UMASS_MAX_CMDLEN MAX(12, CAM_MAX_CDBLEN) /* bytes */ /* USB transfer definitions */ #define UMASS_T_BBB_RESET1 0 /* Bulk-Only */ #define UMASS_T_BBB_RESET2 1 #define UMASS_T_BBB_RESET3 2 #define UMASS_T_BBB_COMMAND 3 #define UMASS_T_BBB_DATA_READ 4 #define UMASS_T_BBB_DATA_RD_CS 5 #define UMASS_T_BBB_DATA_WRITE 6 #define UMASS_T_BBB_DATA_WR_CS 7 #define UMASS_T_BBB_STATUS 8 #define UMASS_T_BBB_MAX 9 #define UMASS_T_CBI_RESET1 0 /* CBI */ #define UMASS_T_CBI_RESET2 1 #define UMASS_T_CBI_RESET3 2 #define UMASS_T_CBI_COMMAND 3 #define UMASS_T_CBI_DATA_READ 4 #define UMASS_T_CBI_DATA_RD_CS 5 #define UMASS_T_CBI_DATA_WRITE 6 #define UMASS_T_CBI_DATA_WR_CS 7 #define UMASS_T_CBI_STATUS 8 #define UMASS_T_CBI_RESET4 9 #define UMASS_T_CBI_MAX 10 #define UMASS_T_MAX MAX(UMASS_T_CBI_MAX, UMASS_T_BBB_MAX) /* Generic definitions */ /* Direction for transfer */ #define DIR_NONE 0 #define DIR_IN 1 #define DIR_OUT 2 /* device name */ #define DEVNAME "umass" #define DEVNAME_SIM "umass-sim" /* Approximate maximum transfer speeds (assumes 33% overhead). */ #define UMASS_FULL_TRANSFER_SPEED 1000 #define UMASS_HIGH_TRANSFER_SPEED 40000 #define UMASS_SUPER_TRANSFER_SPEED 400000 #define UMASS_FLOPPY_TRANSFER_SPEED 20 #define UMASS_TIMEOUT 5000 /* ms */ /* CAM specific definitions */ #define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */ #define UMASS_SCSIID_HOST UMASS_SCSIID_MAX /* Bulk-Only features */ #define UR_BBB_RESET 0xff /* Bulk-Only reset */ #define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */ /* Command Block Wrapper */ typedef struct { uDWord dCBWSignature; #define CBWSIGNATURE 0x43425355 uDWord dCBWTag; uDWord dCBWDataTransferLength; uByte bCBWFlags; #define CBWFLAGS_OUT 0x00 #define CBWFLAGS_IN 0x80 uByte bCBWLUN; uByte bCDBLength; #define CBWCDBLENGTH 16 uByte CBWCDB[CBWCDBLENGTH]; } __packed umass_bbb_cbw_t; #define UMASS_BBB_CBW_SIZE 31 /* Command Status Wrapper */ typedef struct { uDWord dCSWSignature; #define CSWSIGNATURE 0x53425355 #define CSWSIGNATURE_IMAGINATION_DBX1 0x43425355 #define CSWSIGNATURE_OLYMPUS_C1 0x55425355 uDWord dCSWTag; uDWord dCSWDataResidue; uByte bCSWStatus; #define CSWSTATUS_GOOD 0x0 #define CSWSTATUS_FAILED 0x1 #define CSWSTATUS_PHASE 0x2 } __packed umass_bbb_csw_t; #define UMASS_BBB_CSW_SIZE 13 /* CBI features */ #define UR_CBI_ADSC 0x00 typedef union { struct { uint8_t type; #define IDB_TYPE_CCI 0x00 uint8_t value; #define IDB_VALUE_PASS 0x00 #define IDB_VALUE_FAIL 0x01 #define IDB_VALUE_PHASE 0x02 #define IDB_VALUE_PERSISTENT 0x03 #define IDB_VALUE_STATUS_MASK 0x03 } __packed common; struct { uint8_t asc; uint8_t ascq; } __packed ufi; } __packed umass_cbi_sbl_t; struct umass_softc; /* see below */ typedef void (umass_callback_t)(struct umass_softc *sc, union ccb *ccb, uint32_t residue, uint8_t status); #define STATUS_CMD_OK 0 /* everything ok */ #define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */ #define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */ #define STATUS_WIRE_FAILED 3 /* couldn't even get command across */ typedef uint8_t (umass_transform_t)(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len); /* Wire and command protocol */ #define UMASS_PROTO_BBB 0x0001 /* USB wire protocol */ #define UMASS_PROTO_CBI 0x0002 #define UMASS_PROTO_CBI_I 0x0004 #define UMASS_PROTO_WIRE 0x00ff /* USB wire protocol mask */ #define UMASS_PROTO_SCSI 0x0100 /* command protocol */ #define UMASS_PROTO_ATAPI 0x0200 #define UMASS_PROTO_UFI 0x0400 #define UMASS_PROTO_RBC 0x0800 #define UMASS_PROTO_COMMAND 0xff00 /* command protocol mask */ /* Device specific quirks */ #define NO_QUIRKS 0x0000 /* * The drive does not support Test Unit Ready. Convert to Start Unit */ #define NO_TEST_UNIT_READY 0x0001 /* * The drive does not reset the Unit Attention state after REQUEST * SENSE has been sent. The INQUIRY command does not reset the UA * either, and so CAM runs in circles trying to retrieve the initial * INQUIRY data. */ #define RS_NO_CLEAR_UA 0x0002 /* The drive does not support START STOP. */ #define NO_START_STOP 0x0004 /* Don't ask for full inquiry data (255b). */ #define FORCE_SHORT_INQUIRY 0x0008 /* Needs to be initialised the Shuttle way */ #define SHUTTLE_INIT 0x0010 /* Drive needs to be switched to alternate iface 1 */ #define ALT_IFACE_1 0x0020 /* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */ #define FLOPPY_SPEED 0x0040 /* The device can't count and gets the residue of transfers wrong */ #define IGNORE_RESIDUE 0x0080 /* No GetMaxLun call */ #define NO_GETMAXLUN 0x0100 /* The device uses a weird CSWSIGNATURE. */ #define WRONG_CSWSIG 0x0200 /* Device cannot handle INQUIRY so fake a generic response */ #define NO_INQUIRY 0x0400 /* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */ #define NO_INQUIRY_EVPD 0x0800 /* Pad all RBC requests to 12 bytes. */ #define RBC_PAD_TO_12 0x1000 /* * Device reports number of sectors from READ_CAPACITY, not max * sector number. */ #define READ_CAPACITY_OFFBY1 0x2000 /* * Device cannot handle a SCSI synchronize cache command. Normally * this quirk would be handled in the cam layer, but for IDE bridges * we need to associate the quirk with the bridge and not the * underlying disk device. This is handled by faking a success * result. */ #define NO_SYNCHRONIZE_CACHE 0x4000 /* Device does not support 'PREVENT/ALLOW MEDIUM REMOVAL'. */ #define NO_PREVENT_ALLOW 0x8000 struct umass_softc { struct scsi_sense cam_scsi_sense; struct scsi_test_unit_ready cam_scsi_test_unit_ready; struct mtx sc_mtx; struct { uint8_t *data_ptr; union ccb *ccb; umass_callback_t *callback; uint32_t data_len; /* bytes */ uint32_t data_rem; /* bytes */ uint32_t data_timeout; /* ms */ uint32_t actlen; /* bytes */ uint8_t cmd_data[UMASS_MAX_CMDLEN]; uint8_t cmd_len; /* bytes */ uint8_t dir; uint8_t lun; } sc_transfer; /* Bulk specific variables for transfers in progress */ umass_bbb_cbw_t cbw; /* command block wrapper */ umass_bbb_csw_t csw; /* command status wrapper */ /* CBI specific variables for transfers in progress */ umass_cbi_sbl_t sbl; /* status block */ device_t sc_dev; struct usb_device *sc_udev; struct cam_sim *sc_sim; /* SCSI Interface Module */ struct usb_xfer *sc_xfer[UMASS_T_MAX]; /* * The command transform function is used to convert the SCSI * commands into their derivatives, like UFI, ATAPI, and friends. */ umass_transform_t *sc_transform; uint32_t sc_unit; uint32_t sc_quirks; /* they got it almost right */ uint32_t sc_proto; /* wire and cmd protocol */ uint8_t sc_name[16]; uint8_t sc_iface_no; /* interface number */ uint8_t sc_maxlun; /* maximum LUN number, inclusive */ uint8_t sc_last_xfer_index; uint8_t sc_status_try; }; struct umass_probe_proto { uint32_t quirks; uint32_t proto; int error; }; /* prototypes */ static device_probe_t umass_probe; static device_attach_t umass_attach; static device_detach_t umass_detach; static usb_callback_t umass_tr_error; static usb_callback_t umass_t_bbb_reset1_callback; static usb_callback_t umass_t_bbb_reset2_callback; static usb_callback_t umass_t_bbb_reset3_callback; static usb_callback_t umass_t_bbb_command_callback; static usb_callback_t umass_t_bbb_data_read_callback; static usb_callback_t umass_t_bbb_data_rd_cs_callback; static usb_callback_t umass_t_bbb_data_write_callback; static usb_callback_t umass_t_bbb_data_wr_cs_callback; static usb_callback_t umass_t_bbb_status_callback; static usb_callback_t umass_t_cbi_reset1_callback; static usb_callback_t umass_t_cbi_reset2_callback; static usb_callback_t umass_t_cbi_reset3_callback; static usb_callback_t umass_t_cbi_reset4_callback; static usb_callback_t umass_t_cbi_command_callback; static usb_callback_t umass_t_cbi_data_read_callback; static usb_callback_t umass_t_cbi_data_rd_cs_callback; static usb_callback_t umass_t_cbi_data_write_callback; static usb_callback_t umass_t_cbi_data_wr_cs_callback; static usb_callback_t umass_t_cbi_status_callback; static void umass_cancel_ccb(struct umass_softc *); static void umass_init_shuttle(struct umass_softc *); static void umass_reset(struct umass_softc *); static void umass_t_bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t, uint8_t, usb_error_t); static void umass_command_start(struct umass_softc *, uint8_t, void *, uint32_t, uint32_t, umass_callback_t *, union ccb *); static uint8_t umass_bbb_get_max_lun(struct umass_softc *); static void umass_cbi_start_status(struct umass_softc *); static void umass_t_cbi_data_clear_stall_callback(struct usb_xfer *, uint8_t, uint8_t, usb_error_t); static int umass_cam_attach_sim(struct umass_softc *); static void umass_cam_attach(struct umass_softc *); static void umass_cam_detach_sim(struct umass_softc *); static void umass_cam_action(struct cam_sim *, union ccb *); static void umass_cam_poll(struct cam_sim *); static void umass_cam_cb(struct umass_softc *, union ccb *, uint32_t, uint8_t); static void umass_cam_sense_cb(struct umass_softc *, union ccb *, uint32_t, uint8_t); static void umass_cam_quirk_cb(struct umass_softc *, union ccb *, uint32_t, uint8_t); static uint8_t umass_scsi_transform(struct umass_softc *, uint8_t *, uint8_t); static uint8_t umass_rbc_transform(struct umass_softc *, uint8_t *, uint8_t); static uint8_t umass_ufi_transform(struct umass_softc *, uint8_t *, uint8_t); static uint8_t umass_atapi_transform(struct umass_softc *, uint8_t *, uint8_t); static uint8_t umass_no_transform(struct umass_softc *, uint8_t *, uint8_t); static uint8_t umass_std_transform(struct umass_softc *, union ccb *, uint8_t *, uint8_t); #ifdef USB_DEBUG static void umass_bbb_dump_cbw(struct umass_softc *, umass_bbb_cbw_t *); static void umass_bbb_dump_csw(struct umass_softc *, umass_bbb_csw_t *); static void umass_cbi_dump_cmd(struct umass_softc *, void *, uint8_t); static void umass_dump_buffer(struct umass_softc *, uint8_t *, uint32_t, uint32_t); #endif static struct usb_config umass_bbb_config[UMASS_T_BBB_MAX] = { [UMASS_T_BBB_RESET1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_bbb_reset1_callback, .timeout = 5000, /* 5 seconds */ .interval = 500, /* 500 milliseconds */ }, [UMASS_T_BBB_RESET2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_bbb_reset2_callback, .timeout = 5000, /* 5 seconds */ .interval = 50, /* 50 milliseconds */ }, [UMASS_T_BBB_RESET3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_bbb_reset3_callback, .timeout = 5000, /* 5 seconds */ .interval = 50, /* 50 milliseconds */ }, [UMASS_T_BBB_COMMAND] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = sizeof(umass_bbb_cbw_t), .callback = &umass_t_bbb_command_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_DATA_READ] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UMASS_BULK_SIZE, .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,}, .callback = &umass_t_bbb_data_read_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_BBB_DATA_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_bbb_data_rd_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_DATA_WRITE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UMASS_BULK_SIZE, .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,}, .callback = &umass_t_bbb_data_write_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_BBB_DATA_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_bbb_data_wr_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_STATUS] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = sizeof(umass_bbb_csw_t), .flags = {.short_xfer_ok = 1,}, .callback = &umass_t_bbb_status_callback, .timeout = 5000, /* ms */ }, }; static struct usb_config umass_cbi_config[UMASS_T_CBI_MAX] = { [UMASS_T_CBI_RESET1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = (sizeof(struct usb_device_request) + UMASS_CBI_DIAGNOSTIC_CMDLEN), .callback = &umass_t_cbi_reset1_callback, .timeout = 5000, /* 5 seconds */ .interval = 500, /* 500 milliseconds */ }, [UMASS_T_CBI_RESET2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_cbi_reset2_callback, .timeout = 5000, /* 5 seconds */ .interval = 50, /* 50 milliseconds */ }, [UMASS_T_CBI_RESET3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_cbi_reset3_callback, .timeout = 5000, /* 5 seconds */ .interval = 50, /* 50 milliseconds */ }, [UMASS_T_CBI_COMMAND] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = (sizeof(struct usb_device_request) + UMASS_MAX_CMDLEN), .callback = &umass_t_cbi_command_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_DATA_READ] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UMASS_BULK_SIZE, .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,}, .callback = &umass_t_cbi_data_read_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_CBI_DATA_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_cbi_data_rd_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_DATA_WRITE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UMASS_BULK_SIZE, .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,}, .callback = &umass_t_cbi_data_write_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_CBI_DATA_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_cbi_data_wr_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_STATUS] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.short_xfer_ok = 1,.no_pipe_ok = 1,}, .bufsize = sizeof(umass_cbi_sbl_t), .callback = &umass_t_cbi_status_callback, .timeout = 5000, /* ms */ }, [UMASS_T_CBI_RESET4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &umass_t_cbi_reset4_callback, .timeout = 5000, /* ms */ }, }; /* If device cannot return valid inquiry data, fake it */ static const uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = { 0, /* removable */ 0x80, SCSI_REV_2, SCSI_REV_2, /* additional_length */ 31, 0, 0, 0 }; #define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */ #define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */ static devclass_t umass_devclass; static device_method_t umass_methods[] = { /* Device interface */ DEVMETHOD(device_probe, umass_probe), DEVMETHOD(device_attach, umass_attach), DEVMETHOD(device_detach, umass_detach), DEVMETHOD_END }; static driver_t umass_driver = { .name = "umass", .methods = umass_methods, .size = sizeof(struct umass_softc), }; static const STRUCT_USB_HOST_ID __used umass_devs[] = { /* generic mass storage class */ {USB_IFACE_CLASS(UICLASS_MASS),}, }; DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, NULL, 0); MODULE_DEPEND(umass, usb, 1, 1, 1); MODULE_DEPEND(umass, cam, 1, 1, 1); MODULE_VERSION(umass, 1); USB_PNP_HOST_INFO(umass_devs); /* * USB device probe/attach/detach */ static uint16_t umass_get_proto(struct usb_interface *iface) { struct usb_interface_descriptor *id; uint16_t retval; retval = 0; /* Check for a standards compliant device */ id = usbd_get_interface_descriptor(iface); if ((id == NULL) || (id->bInterfaceClass != UICLASS_MASS)) { goto done; } switch (id->bInterfaceSubClass) { case UISUBCLASS_SCSI: retval |= UMASS_PROTO_SCSI; break; case UISUBCLASS_UFI: retval |= UMASS_PROTO_UFI; break; case UISUBCLASS_RBC: retval |= UMASS_PROTO_RBC; break; case UISUBCLASS_SFF8020I: case UISUBCLASS_SFF8070I: retval |= UMASS_PROTO_ATAPI; break; default: goto done; } switch (id->bInterfaceProtocol) { case UIPROTO_MASS_CBI: retval |= UMASS_PROTO_CBI; break; case UIPROTO_MASS_CBI_I: retval |= UMASS_PROTO_CBI_I; break; case UIPROTO_MASS_BBB_OLD: case UIPROTO_MASS_BBB: retval |= UMASS_PROTO_BBB; break; default: goto done; } done: return (retval); } /* * Match the device we are seeing with the devices supported. */ static struct umass_probe_proto umass_probe_proto(device_t dev, struct usb_attach_arg *uaa) { struct umass_probe_proto ret; uint32_t quirks = NO_QUIRKS; uint32_t proto = umass_get_proto(uaa->iface); memset(&ret, 0, sizeof(ret)); ret.error = BUS_PROBE_GENERIC; /* Search for protocol enforcement */ if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_BBB)) { proto &= ~UMASS_PROTO_WIRE; proto |= UMASS_PROTO_BBB; } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_CBI)) { proto &= ~UMASS_PROTO_WIRE; proto |= UMASS_PROTO_CBI; } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_CBI_I)) { proto &= ~UMASS_PROTO_WIRE; proto |= UMASS_PROTO_CBI_I; } if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_SCSI)) { proto &= ~UMASS_PROTO_COMMAND; proto |= UMASS_PROTO_SCSI; } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_ATAPI)) { proto &= ~UMASS_PROTO_COMMAND; proto |= UMASS_PROTO_ATAPI; } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_UFI)) { proto &= ~UMASS_PROTO_COMMAND; proto |= UMASS_PROTO_UFI; } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_RBC)) { proto &= ~UMASS_PROTO_COMMAND; proto |= UMASS_PROTO_RBC; } /* Check if the protocol is invalid */ if ((proto & UMASS_PROTO_COMMAND) == 0) { ret.error = ENXIO; goto done; } if ((proto & UMASS_PROTO_WIRE) == 0) { ret.error = ENXIO; goto done; } /* Search for quirks */ if (usb_test_quirk(uaa, UQ_MSC_NO_TEST_UNIT_READY)) quirks |= NO_TEST_UNIT_READY; if (usb_test_quirk(uaa, UQ_MSC_NO_RS_CLEAR_UA)) quirks |= RS_NO_CLEAR_UA; if (usb_test_quirk(uaa, UQ_MSC_NO_START_STOP)) quirks |= NO_START_STOP; if (usb_test_quirk(uaa, UQ_MSC_NO_GETMAXLUN)) quirks |= NO_GETMAXLUN; if (usb_test_quirk(uaa, UQ_MSC_NO_INQUIRY)) quirks |= NO_INQUIRY; if (usb_test_quirk(uaa, UQ_MSC_NO_INQUIRY_EVPD)) quirks |= NO_INQUIRY_EVPD; if (usb_test_quirk(uaa, UQ_MSC_NO_PREVENT_ALLOW)) quirks |= NO_PREVENT_ALLOW; if (usb_test_quirk(uaa, UQ_MSC_NO_SYNC_CACHE)) quirks |= NO_SYNCHRONIZE_CACHE; if (usb_test_quirk(uaa, UQ_MSC_SHUTTLE_INIT)) quirks |= SHUTTLE_INIT; if (usb_test_quirk(uaa, UQ_MSC_ALT_IFACE_1)) quirks |= ALT_IFACE_1; if (usb_test_quirk(uaa, UQ_MSC_FLOPPY_SPEED)) quirks |= FLOPPY_SPEED; if (usb_test_quirk(uaa, UQ_MSC_IGNORE_RESIDUE)) quirks |= IGNORE_RESIDUE; if (usb_test_quirk(uaa, UQ_MSC_WRONG_CSWSIG)) quirks |= WRONG_CSWSIG; if (usb_test_quirk(uaa, UQ_MSC_RBC_PAD_TO_12)) quirks |= RBC_PAD_TO_12; if (usb_test_quirk(uaa, UQ_MSC_READ_CAP_OFFBY1)) quirks |= READ_CAPACITY_OFFBY1; if (usb_test_quirk(uaa, UQ_MSC_FORCE_SHORT_INQ)) quirks |= FORCE_SHORT_INQUIRY; done: ret.quirks = quirks; ret.proto = proto; return (ret); } static int umass_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct umass_probe_proto temp; if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } temp = umass_probe_proto(dev, uaa); return (temp.error); } static int umass_attach(device_t dev) { struct umass_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct umass_probe_proto temp = umass_probe_proto(dev, uaa); struct usb_interface_descriptor *id; int err; /* * NOTE: the softc struct is cleared in device_set_driver. * We can safely call umass_detach without specifically * initializing the struct. */ sc->sc_dev = dev; sc->sc_udev = uaa->device; sc->sc_proto = temp.proto; sc->sc_quirks = temp.quirks; sc->sc_unit = device_get_unit(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF | MTX_RECURSE); /* get interface index */ id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { device_printf(dev, "failed to get " "interface number\n"); goto detach; } sc->sc_iface_no = id->bInterfaceNumber; #ifdef USB_DEBUG device_printf(dev, " "); switch (sc->sc_proto & UMASS_PROTO_COMMAND) { case UMASS_PROTO_SCSI: printf("SCSI"); break; case UMASS_PROTO_ATAPI: printf("8070i (ATAPI)"); break; case UMASS_PROTO_UFI: printf("UFI"); break; case UMASS_PROTO_RBC: printf("RBC"); break; default: printf("(unknown 0x%02x)", sc->sc_proto & UMASS_PROTO_COMMAND); break; } printf(" over "); switch (sc->sc_proto & UMASS_PROTO_WIRE) { case UMASS_PROTO_BBB: printf("Bulk-Only"); break; case UMASS_PROTO_CBI: /* uses Comand/Bulk pipes */ printf("CBI"); break; case UMASS_PROTO_CBI_I: /* uses Comand/Bulk/Interrupt pipes */ printf("CBI with CCI"); break; default: printf("(unknown 0x%02x)", sc->sc_proto & UMASS_PROTO_WIRE); } printf("; quirks = 0x%04x\n", sc->sc_quirks); #endif if (sc->sc_quirks & ALT_IFACE_1) { err = usbd_set_alt_interface_index (uaa->device, uaa->info.bIfaceIndex, 1); if (err) { DPRINTF(sc, UDMASS_USB, "could not switch to " "Alt Interface 1\n"); goto detach; } } /* allocate all required USB transfers */ if (sc->sc_proto & UMASS_PROTO_BBB) { err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, umass_bbb_config, UMASS_T_BBB_MAX, sc, &sc->sc_mtx); /* skip reset first time */ sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; } else if (sc->sc_proto & (UMASS_PROTO_CBI | UMASS_PROTO_CBI_I)) { err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, umass_cbi_config, UMASS_T_CBI_MAX, sc, &sc->sc_mtx); /* skip reset first time */ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; } else { err = USB_ERR_INVAL; } if (err) { device_printf(dev, "could not setup required " "transfers, %s\n", usbd_errstr(err)); goto detach; } #ifdef USB_DEBUG if (umass_throttle > 0) { uint8_t x; int iv; iv = umass_throttle; if (iv < 1) iv = 1; else if (iv > 8000) iv = 8000; for (x = 0; x != UMASS_T_MAX; x++) { if (sc->sc_xfer[x] != NULL) usbd_xfer_set_interval(sc->sc_xfer[x], iv); } } #endif sc->sc_transform = (sc->sc_proto & UMASS_PROTO_SCSI) ? &umass_scsi_transform : (sc->sc_proto & UMASS_PROTO_UFI) ? &umass_ufi_transform : (sc->sc_proto & UMASS_PROTO_ATAPI) ? &umass_atapi_transform : (sc->sc_proto & UMASS_PROTO_RBC) ? &umass_rbc_transform : &umass_no_transform; /* from here onwards the device can be used. */ if (sc->sc_quirks & SHUTTLE_INIT) { umass_init_shuttle(sc); } /* get the maximum LUN supported by the device */ if (((sc->sc_proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) && !(sc->sc_quirks & NO_GETMAXLUN)) sc->sc_maxlun = umass_bbb_get_max_lun(sc); else sc->sc_maxlun = 0; /* Prepare the SCSI command block */ sc->cam_scsi_sense.opcode = REQUEST_SENSE; sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY; /* register the SIM */ err = umass_cam_attach_sim(sc); if (err) { goto detach; } /* scan the SIM */ umass_cam_attach(sc); DPRINTF(sc, UDMASS_GEN, "Attach finished\n"); return (0); /* success */ detach: umass_detach(dev); return (ENXIO); /* failure */ } static int umass_detach(device_t dev) { struct umass_softc *sc = device_get_softc(dev); DPRINTF(sc, UDMASS_USB, "\n"); /* teardown our statemachine */ usbd_transfer_unsetup(sc->sc_xfer, UMASS_T_MAX); mtx_lock(&sc->sc_mtx); /* cancel any leftover CCB's */ umass_cancel_ccb(sc); umass_cam_detach_sim(sc); mtx_unlock(&sc->sc_mtx); mtx_destroy(&sc->sc_mtx); return (0); /* success */ } static void umass_init_shuttle(struct umass_softc *sc) { struct usb_device_request req; usb_error_t err; uint8_t status[2] = {0, 0}; /* * The Linux driver does this, but no one can tell us what the * command does. */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = 1; /* XXX unknown command */ USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, sizeof(status)); err = usbd_do_request(sc->sc_udev, NULL, &req, &status); DPRINTF(sc, UDMASS_GEN, "Shuttle init returned 0x%02x%02x\n", status[0], status[1]); } /* * Generic functions to handle transfers */ static void umass_transfer_start(struct umass_softc *sc, uint8_t xfer_index) { DPRINTF(sc, UDMASS_GEN, "transfer index = " "%d\n", xfer_index); if (sc->sc_xfer[xfer_index]) { sc->sc_last_xfer_index = xfer_index; usbd_transfer_start(sc->sc_xfer[xfer_index]); } else { umass_cancel_ccb(sc); } } static void umass_reset(struct umass_softc *sc) { DPRINTF(sc, UDMASS_GEN, "resetting device\n"); /* * stop the last transfer, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]); umass_transfer_start(sc, 0); } static void umass_cancel_ccb(struct umass_softc *sc) { union ccb *ccb; mtx_assert(&sc->sc_mtx, MA_OWNED); ccb = sc->sc_transfer.ccb; sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = 0; if (ccb) { (sc->sc_transfer.callback) (sc, ccb, (sc->sc_transfer.data_len - sc->sc_transfer.actlen), STATUS_WIRE_FAILED); } } static void umass_tr_error(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); if (error != USB_ERR_CANCELLED) { DPRINTF(sc, UDMASS_GEN, "transfer error, %s -> " "reset\n", usbd_errstr(error)); } umass_cancel_ccb(sc); } /* * BBB protocol specific functions */ static void umass_t_bbb_reset1_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: umass_transfer_start(sc, UMASS_T_BBB_RESET2); return; case USB_ST_SETUP: /* * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class) * * For Reset Recovery the host shall issue in the following order: * a) a Bulk-Only Mass Storage Reset * b) a Clear Feature HALT to the Bulk-In endpoint * c) a Clear Feature HALT to the Bulk-Out endpoint * * This is done in 3 steps, using 3 transfers: * UMASS_T_BBB_RESET1 * UMASS_T_BBB_RESET2 * UMASS_T_BBB_RESET3 */ DPRINTF(sc, UDMASS_BBB, "BBB reset!\n"); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_BBB_RESET; /* bulk only reset */ USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); 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_frames(xfer, 1); usbd_transfer_submit(xfer); return; default: /* Error */ umass_tr_error(xfer, error); return; } } static void umass_t_bbb_reset2_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_RESET3, UMASS_T_BBB_DATA_READ, error); } static void umass_t_bbb_reset3_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_COMMAND, UMASS_T_BBB_DATA_WRITE, error); } static void umass_t_bbb_data_clear_stall_callback(struct usb_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: umass_transfer_start(sc, next_xfer); return; case USB_ST_SETUP: if (usbd_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) { goto tr_transferred; } return; default: /* Error */ umass_tr_error(xfer, error); return; } } static void umass_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); union ccb *ccb = sc->sc_transfer.ccb; struct usb_page_cache *pc; uint32_t tag; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: umass_transfer_start (sc, ((sc->sc_transfer.dir == DIR_IN) ? UMASS_T_BBB_DATA_READ : (sc->sc_transfer.dir == DIR_OUT) ? UMASS_T_BBB_DATA_WRITE : UMASS_T_BBB_STATUS)); return; case USB_ST_SETUP: sc->sc_status_try = 0; if (ccb) { /* * the initial value is not important, * as long as the values are unique: */ tag = UGETDW(sc->cbw.dCBWTag) + 1; USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); USETDW(sc->cbw.dCBWTag, tag); /* * dCBWDataTransferLength: * This field indicates the number of bytes of data that the host * intends to transfer on the IN or OUT Bulk endpoint(as indicated by * the Direction bit) during the execution of this command. If this * field is set to 0, the device will expect that no data will be * transferred IN or OUT during this command, regardless of the value * of the Direction bit defined in dCBWFlags. */ USETDW(sc->cbw.dCBWDataTransferLength, sc->sc_transfer.data_len); /* * dCBWFlags: * The bits of the Flags field are defined as follows: * Bits 0-6 reserved * Bit 7 Direction - this bit shall be ignored if the * dCBWDataTransferLength field is zero. * 0 = data Out from host to device * 1 = data In from device to host */ sc->cbw.bCBWFlags = ((sc->sc_transfer.dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); sc->cbw.bCBWLUN = sc->sc_transfer.lun; if (sc->sc_transfer.cmd_len > sizeof(sc->cbw.CBWCDB)) { sc->sc_transfer.cmd_len = sizeof(sc->cbw.CBWCDB); DPRINTF(sc, UDMASS_BBB, "Truncating long command!\n"); } sc->cbw.bCDBLength = sc->sc_transfer.cmd_len; /* copy SCSI command data */ memcpy(sc->cbw.CBWCDB, sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len); /* clear remaining command area */ memset(sc->cbw.CBWCDB + sc->sc_transfer.cmd_len, 0, sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len); DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw)); pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &sc->cbw, sizeof(sc->cbw)); usbd_xfer_set_frame_len(xfer, 0, sizeof(sc->cbw)); usbd_transfer_submit(xfer); } return; default: /* Error */ umass_tr_error(xfer, error); return; } } static void umass_t_bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); uint32_t max_bulk = usbd_xfer_max_len(xfer); int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->sc_transfer.data_rem -= actlen; sc->sc_transfer.data_ptr += actlen; sc->sc_transfer.actlen += actlen; if (actlen < sumlen) { /* short transfer */ sc->sc_transfer.data_rem = 0; } case USB_ST_SETUP: DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_transfer_start(sc, UMASS_T_BBB_STATUS); return; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout); usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr, max_bulk); usbd_transfer_submit(xfer); return; default: /* Error */ if (error == USB_ERR_CANCELLED) { umass_tr_error(xfer, error); } else { umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS); } return; } } static void umass_t_bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS, UMASS_T_BBB_DATA_READ, error); } static void umass_t_bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); uint32_t max_bulk = usbd_xfer_max_len(xfer); int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->sc_transfer.data_rem -= actlen; sc->sc_transfer.data_ptr += actlen; sc->sc_transfer.actlen += actlen; if (actlen < sumlen) { /* short transfer */ sc->sc_transfer.data_rem = 0; } case USB_ST_SETUP: DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_transfer_start(sc, UMASS_T_BBB_STATUS); return; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout); usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr, max_bulk); usbd_transfer_submit(xfer); return; default: /* Error */ if (error == USB_ERR_CANCELLED) { umass_tr_error(xfer, error); } else { umass_transfer_start(sc, UMASS_T_BBB_DATA_WR_CS); } return; } } static void umass_t_bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS, UMASS_T_BBB_DATA_WRITE, error); } static void umass_t_bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); union ccb *ccb = sc->sc_transfer.ccb; struct usb_page_cache *pc; uint32_t residue; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* * Do a full reset if there is something wrong with the CSW: */ sc->sc_status_try = 1; /* Zero missing parts of the CSW: */ if (actlen < (int)sizeof(sc->csw)) memset(&sc->csw, 0, sizeof(sc->csw)); pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &sc->csw, actlen); DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw)); residue = UGETDW(sc->csw.dCSWDataResidue); if ((!residue) || (sc->sc_quirks & IGNORE_RESIDUE)) { residue = (sc->sc_transfer.data_len - sc->sc_transfer.actlen); } if (residue > sc->sc_transfer.data_len) { DPRINTF(sc, UDMASS_BBB, "truncating residue from %d " "to %d bytes\n", residue, sc->sc_transfer.data_len); residue = sc->sc_transfer.data_len; } /* translate weird command-status signatures: */ if (sc->sc_quirks & WRONG_CSWSIG) { uint32_t temp = UGETDW(sc->csw.dCSWSignature); if ((temp == CSWSIGNATURE_OLYMPUS_C1) || (temp == CSWSIGNATURE_IMAGINATION_DBX1)) { USETDW(sc->csw.dCSWSignature, CSWSIGNATURE); } } /* check CSW and handle eventual error */ if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) { DPRINTF(sc, UDMASS_BBB, "bad CSW signature 0x%08x != 0x%08x\n", UGETDW(sc->csw.dCSWSignature), CSWSIGNATURE); /* * Invalid CSW: Wrong signature or wrong tag might * indicate that we lost synchronization. Reset the * device. */ goto tr_error; } else if (UGETDW(sc->csw.dCSWTag) != UGETDW(sc->cbw.dCBWTag)) { DPRINTF(sc, UDMASS_BBB, "Invalid CSW: tag 0x%08x should be " "0x%08x\n", UGETDW(sc->csw.dCSWTag), UGETDW(sc->cbw.dCBWTag)); goto tr_error; } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) { DPRINTF(sc, UDMASS_BBB, "Invalid CSW: status %d > %d\n", sc->csw.bCSWStatus, CSWSTATUS_PHASE); goto tr_error; } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) { DPRINTF(sc, UDMASS_BBB, "Phase error, residue = " "%d\n", residue); goto tr_error; } else if (sc->sc_transfer.actlen > sc->sc_transfer.data_len) { DPRINTF(sc, UDMASS_BBB, "Buffer overrun %d > %d\n", sc->sc_transfer.actlen, sc->sc_transfer.data_len); goto tr_error; } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) { DPRINTF(sc, UDMASS_BBB, "Command failed, residue = " "%d\n", residue); sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, STATUS_CMD_FAILED); } else { sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, STATUS_CMD_OK); } return; case USB_ST_SETUP: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: tr_error: DPRINTF(sc, UDMASS_BBB, "Failed to read CSW: %s, try %d\n", usbd_errstr(error), sc->sc_status_try); if ((error == USB_ERR_CANCELLED) || (sc->sc_status_try)) { umass_tr_error(xfer, error); } else { sc->sc_status_try = 1; umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS); } return; } } static void umass_command_start(struct umass_softc *sc, uint8_t dir, void *data_ptr, uint32_t data_len, uint32_t data_timeout, umass_callback_t *callback, union ccb *ccb) { sc->sc_transfer.lun = ccb->ccb_h.target_lun; /* * NOTE: assumes that "sc->sc_transfer.cmd_data" and * "sc->sc_transfer.cmd_len" has been properly * initialized. */ sc->sc_transfer.dir = data_len ? dir : DIR_NONE; sc->sc_transfer.data_ptr = data_ptr; sc->sc_transfer.data_len = data_len; sc->sc_transfer.data_rem = data_len; sc->sc_transfer.data_timeout = (data_timeout + UMASS_TIMEOUT); sc->sc_transfer.actlen = 0; sc->sc_transfer.callback = callback; sc->sc_transfer.ccb = ccb; if (sc->sc_xfer[sc->sc_last_xfer_index]) { usbd_transfer_start(sc->sc_xfer[sc->sc_last_xfer_index]); } else { umass_cancel_ccb(sc); } } static uint8_t umass_bbb_get_max_lun(struct umass_softc *sc) { struct usb_device_request req; usb_error_t err; uint8_t buf = 0; /* The Get Max Lun command is a class-specific request. */ req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_BBB_GET_MAX_LUN; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); err = usbd_do_request(sc->sc_udev, NULL, &req, &buf); if (err) { buf = 0; /* Device doesn't support Get Max Lun request. */ printf("%s: Get Max Lun not supported (%s)\n", sc->sc_name, usbd_errstr(err)); } return (buf); } /* * Command/Bulk/Interrupt (CBI) specific functions */ static void umass_cbi_start_status(struct umass_softc *sc) { if (sc->sc_xfer[UMASS_T_CBI_STATUS]) { umass_transfer_start(sc, UMASS_T_CBI_STATUS); } else { union ccb *ccb = sc->sc_transfer.ccb; sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; (sc->sc_transfer.callback) (sc, ccb, (sc->sc_transfer.data_len - sc->sc_transfer.actlen), STATUS_CMD_UNKNOWN); } } static void umass_t_cbi_reset1_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; uint8_t buf[UMASS_CBI_DIAGNOSTIC_CMDLEN]; uint8_t i; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: umass_transfer_start(sc, UMASS_T_CBI_RESET2); break; case USB_ST_SETUP: /* * Command Block Reset Protocol * * First send a reset request to the device. Then clear * any possibly stalled bulk endpoints. * * This is done in 3 steps, using 3 transfers: * UMASS_T_CBI_RESET1 * UMASS_T_CBI_RESET2 * UMASS_T_CBI_RESET3 * UMASS_T_CBI_RESET4 (only if there is an interrupt endpoint) */ DPRINTF(sc, UDMASS_CBI, "CBI reset!\n"); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_CBI_ADSC; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, UMASS_CBI_DIAGNOSTIC_CMDLEN); /* * The 0x1d code is the SEND DIAGNOSTIC command. To * distinguish between the two, the last 10 bytes of the CBL * is filled with 0xff (section 2.2 of the CBI * specification) */ buf[0] = 0x1d; /* Command Block Reset */ buf[1] = 0x04; for (i = 2; i < UMASS_CBI_DIAGNOSTIC_CMDLEN; i++) { buf[i] = 0xff; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &req, sizeof(req)); pc = usbd_xfer_get_frame(xfer, 1); usbd_copy_in(pc, 0, buf, sizeof(buf)); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, sizeof(buf)); usbd_xfer_set_frames(xfer, 2); usbd_transfer_submit(xfer); break; default: /* Error */ if (error == USB_ERR_CANCELLED) umass_tr_error(xfer, error); else umass_transfer_start(sc, UMASS_T_CBI_RESET2); break; } } static void umass_t_cbi_reset2_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_RESET3, UMASS_T_CBI_DATA_READ, error); } static void umass_t_cbi_reset3_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); umass_t_cbi_data_clear_stall_callback (xfer, (sc->sc_xfer[UMASS_T_CBI_RESET4] && sc->sc_xfer[UMASS_T_CBI_STATUS]) ? UMASS_T_CBI_RESET4 : UMASS_T_CBI_COMMAND, UMASS_T_CBI_DATA_WRITE, error); } static void umass_t_cbi_reset4_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_COMMAND, UMASS_T_CBI_STATUS, error); } static void umass_t_cbi_data_clear_stall_callback(struct usb_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: if (next_xfer == UMASS_T_CBI_STATUS) { umass_cbi_start_status(sc); } else { umass_transfer_start(sc, next_xfer); } break; case USB_ST_SETUP: if (usbd_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) { goto tr_transferred; /* should not happen */ } break; default: /* Error */ umass_tr_error(xfer, error); break; } } static void umass_t_cbi_command_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); union ccb *ccb = sc->sc_transfer.ccb; struct usb_device_request req; struct usb_page_cache *pc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (sc->sc_transfer.dir == DIR_NONE) { umass_cbi_start_status(sc); } else { umass_transfer_start (sc, (sc->sc_transfer.dir == DIR_IN) ? UMASS_T_CBI_DATA_READ : UMASS_T_CBI_DATA_WRITE); } break; case USB_ST_SETUP: if (ccb) { /* * do a CBI transfer with cmd_len bytes from * cmd_data, possibly a data phase of data_len * bytes from/to the device and finally a status * read phase. */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_CBI_ADSC; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; req.wLength[0] = sc->sc_transfer.cmd_len; req.wLength[1] = 0; pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &req, sizeof(req)); pc = usbd_xfer_get_frame(xfer, 1); usbd_copy_in(pc, 0, sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, sc->sc_transfer.cmd_len); usbd_xfer_set_frames(xfer, sc->sc_transfer.cmd_len ? 2 : 1); DIF(UDMASS_CBI, umass_cbi_dump_cmd(sc, sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len)); usbd_transfer_submit(xfer); } break; default: /* Error */ /* * STALL on the control pipe can be result of the command error. * Attempt to clear this STALL same as for bulk pipe also * results in command completion interrupt, but ASC/ASCQ there * look like not always valid, so don't bother about it. */ if ((error == USB_ERR_STALLED) || (sc->sc_transfer.callback == &umass_cam_cb)) { sc->sc_transfer.ccb = NULL; (sc->sc_transfer.callback) (sc, ccb, sc->sc_transfer.data_len, STATUS_CMD_UNKNOWN); } else { umass_tr_error(xfer, error); /* skip reset */ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; } break; } } static void umass_t_cbi_data_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); uint32_t max_bulk = usbd_xfer_max_len(xfer); int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->sc_transfer.data_rem -= actlen; sc->sc_transfer.data_ptr += actlen; sc->sc_transfer.actlen += actlen; if (actlen < sumlen) { /* short transfer */ sc->sc_transfer.data_rem = 0; } case USB_ST_SETUP: DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_cbi_start_status(sc); break; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout); usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr, max_bulk); usbd_transfer_submit(xfer); break; default: /* Error */ if ((error == USB_ERR_CANCELLED) || (sc->sc_transfer.callback != &umass_cam_cb)) { umass_tr_error(xfer, error); } else { umass_transfer_start(sc, UMASS_T_CBI_DATA_RD_CS); } break; } } static void umass_t_cbi_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS, UMASS_T_CBI_DATA_READ, error); } static void umass_t_cbi_data_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); uint32_t max_bulk = usbd_xfer_max_len(xfer); int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->sc_transfer.data_rem -= actlen; sc->sc_transfer.data_ptr += actlen; sc->sc_transfer.actlen += actlen; if (actlen < sumlen) { /* short transfer */ sc->sc_transfer.data_rem = 0; } case USB_ST_SETUP: DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_cbi_start_status(sc); break; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout); usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr, max_bulk); usbd_transfer_submit(xfer); break; default: /* Error */ if ((error == USB_ERR_CANCELLED) || (sc->sc_transfer.callback != &umass_cam_cb)) { umass_tr_error(xfer, error); } else { umass_transfer_start(sc, UMASS_T_CBI_DATA_WR_CS); } break; } } static void umass_t_cbi_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS, UMASS_T_CBI_DATA_WRITE, error); } static void umass_t_cbi_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct umass_softc *sc = usbd_xfer_softc(xfer); union ccb *ccb = sc->sc_transfer.ccb; struct usb_page_cache *pc; uint32_t residue; uint8_t status; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < (int)sizeof(sc->sbl)) { goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &sc->sbl, sizeof(sc->sbl)); residue = (sc->sc_transfer.data_len - sc->sc_transfer.actlen); /* dissect the information in the buffer */ if (sc->sc_proto & UMASS_PROTO_UFI) { /* * Section 3.4.3.1.3 specifies that the UFI command * protocol returns an ASC and ASCQ in the interrupt * data block. */ DPRINTF(sc, UDMASS_CBI, "UFI CCI, ASC = 0x%02x, " "ASCQ = 0x%02x\n", sc->sbl.ufi.asc, sc->sbl.ufi.ascq); status = (((sc->sbl.ufi.asc == 0) && (sc->sbl.ufi.ascq == 0)) ? STATUS_CMD_OK : STATUS_CMD_FAILED); sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, status); break; } else { /* Command Interrupt Data Block */ DPRINTF(sc, UDMASS_CBI, "type=0x%02x, value=0x%02x\n", sc->sbl.common.type, sc->sbl.common.value); if (sc->sbl.common.type == IDB_TYPE_CCI) { status = (sc->sbl.common.value & IDB_VALUE_STATUS_MASK); status = ((status == IDB_VALUE_PASS) ? STATUS_CMD_OK : (status == IDB_VALUE_FAIL) ? STATUS_CMD_FAILED : (status == IDB_VALUE_PERSISTENT) ? STATUS_CMD_FAILED : STATUS_WIRE_FAILED); sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, status); break; } } /* fallthrough */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ DPRINTF(sc, UDMASS_CBI, "Failed to read CSW: %s\n", usbd_errstr(error)); umass_tr_error(xfer, error); break; } } /* * CAM specific functions (used by SCSI, UFI, 8070i (ATAPI)) */ static int umass_cam_attach_sim(struct umass_softc *sc) { struct cam_devq *devq; /* Per device Queue */ /* * A HBA is attached to the CAM layer. * * The CAM layer will then after a while start probing for devices on * the bus. The number of SIMs is limited to one. */ devq = cam_simq_alloc(1 /* maximum openings */ ); if (devq == NULL) { return (ENOMEM); } sc->sc_sim = cam_sim_alloc (&umass_cam_action, &umass_cam_poll, DEVNAME_SIM, sc /* priv */ , sc->sc_unit /* unit number */ , &sc->sc_mtx /* mutex */ , 1 /* maximum device openings */ , 0 /* maximum tagged device openings */ , devq); if (sc->sc_sim == NULL) { cam_simq_free(devq); return (ENOMEM); } mtx_lock(&sc->sc_mtx); if (xpt_bus_register(sc->sc_sim, sc->sc_dev, sc->sc_unit) != CAM_SUCCESS) { mtx_unlock(&sc->sc_mtx); return (ENOMEM); } mtx_unlock(&sc->sc_mtx); return (0); } static void umass_cam_attach(struct umass_softc *sc) { #ifndef USB_DEBUG if (bootverbose) #endif printf("%s:%d:%d: Attached to scbus%d\n", sc->sc_name, cam_sim_path(sc->sc_sim), sc->sc_unit, cam_sim_path(sc->sc_sim)); } /* umass_cam_detach * detach from the CAM layer */ static void umass_cam_detach_sim(struct umass_softc *sc) { if (sc->sc_sim != NULL) { if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) { /* accessing the softc is not possible after this */ sc->sc_sim->softc = NULL; cam_sim_free(sc->sc_sim, /* free_devq */ TRUE); } else { panic("%s: CAM layer is busy\n", sc->sc_name); } sc->sc_sim = NULL; } } /* umass_cam_action * CAM requests for action come through here */ static void umass_cam_action(struct cam_sim *sim, union ccb *ccb) { struct umass_softc *sc = (struct umass_softc *)sim->softc; if (sc == NULL) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; xpt_done(ccb); return; } /* Perform the requested action */ switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: { uint8_t *cmd; uint8_t dir; if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) { cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr); } else { cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes); } DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_SCSI_IO: " "cmd: 0x%02x, flags: 0x%02x, " "%db cmd/%db data/%db sense\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun, cmd[0], ccb->ccb_h.flags & CAM_DIR_MASK, ccb->csio.cdb_len, ccb->csio.dxfer_len, ccb->csio.sense_len); if (sc->sc_transfer.ccb) { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_SCSI_IO: " "I/O in progress, deferring\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun); ccb->ccb_h.status = CAM_SCSI_BUSY; xpt_done(ccb); goto done; } switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: dir = DIR_IN; break; case CAM_DIR_OUT: dir = DIR_OUT; DIF(UDMASS_SCSI, umass_dump_buffer(sc, ccb->csio.data_ptr, ccb->csio.dxfer_len, 48)); break; default: dir = DIR_NONE; } ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED; /* * sc->sc_transform will convert the command to the * command format needed by the specific command set * and return the converted command in * "sc->sc_transfer.cmd_data" */ if (umass_std_transform(sc, ccb, cmd, ccb->csio.cdb_len)) { if (sc->sc_transfer.cmd_data[0] == INQUIRY) { const char *pserial; pserial = usb_get_serial(sc->sc_udev); /* * Umass devices don't generally report their serial numbers * in the usual SCSI way. Emulate it here. */ if ((sc->sc_transfer.cmd_data[1] & SI_EVPD) && (sc->sc_transfer.cmd_data[2] == SVPD_UNIT_SERIAL_NUMBER) && (pserial[0] != '\0')) { struct scsi_vpd_unit_serial_number *vpd_serial; vpd_serial = (struct scsi_vpd_unit_serial_number *)ccb->csio.data_ptr; vpd_serial->length = strlen(pserial); if (vpd_serial->length > sizeof(vpd_serial->serial_num)) vpd_serial->length = sizeof(vpd_serial->serial_num); memcpy(vpd_serial->serial_num, pserial, vpd_serial->length); ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); goto done; } /* * Handle EVPD inquiry for broken devices first * NO_INQUIRY also implies NO_INQUIRY_EVPD */ if ((sc->sc_quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) && (sc->sc_transfer.cmd_data[1] & SI_EVPD)) { scsi_set_sense_data(&ccb->csio.sense_data, /*sense_format*/ SSD_TYPE_NONE, /*current_error*/ 1, /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST, /*asc*/ 0x24, /*ascq*/ 0x00, /*extra args*/ SSD_ELEM_NONE); ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID | CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, 1); xpt_done(ccb); goto done; } /* * Return fake inquiry data for * broken devices */ if (sc->sc_quirks & NO_INQUIRY) { memcpy(ccb->csio.data_ptr, &fake_inq_data, sizeof(fake_inq_data)); ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); goto done; } if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { ccb->csio.dxfer_len = SHORT_INQUIRY_LENGTH; } } else if (sc->sc_transfer.cmd_data[0] == PREVENT_ALLOW) { if (sc->sc_quirks & NO_PREVENT_ALLOW) { ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); goto done; } } else if (sc->sc_transfer.cmd_data[0] == SYNCHRONIZE_CACHE) { if (sc->sc_quirks & NO_SYNCHRONIZE_CACHE) { ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); goto done; } } umass_command_start(sc, dir, ccb->csio.data_ptr, ccb->csio.dxfer_len, ccb->ccb_h.timeout, &umass_cam_cb, ccb); } break; } case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_PATH_INQ:.\n", sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun); /* host specific information */ cpi->version_num = 1; cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = PIM_NO_6_BYTE; cpi->hba_eng_cnt = 0; cpi->max_target = UMASS_SCSIID_MAX; /* one target */ cpi->initiator_id = UMASS_SCSIID_HOST; strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strlcpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN); strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = sc->sc_unit; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->transport = XPORT_USB; cpi->transport_version = 0; if (sc == NULL) { cpi->base_transfer_speed = 0; cpi->max_lun = 0; } else { if (sc->sc_quirks & FLOPPY_SPEED) { cpi->base_transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED; } else { switch (usbd_get_speed(sc->sc_udev)) { case USB_SPEED_SUPER: cpi->base_transfer_speed = UMASS_SUPER_TRANSFER_SPEED; cpi->maxio = MAXPHYS; break; case USB_SPEED_HIGH: cpi->base_transfer_speed = UMASS_HIGH_TRANSFER_SPEED; break; default: cpi->base_transfer_speed = UMASS_FULL_TRANSFER_SPEED; break; } } cpi->max_lun = sc->sc_maxlun; } cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_RESET_DEV: { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_RESET_DEV:.\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun); umass_reset(sc); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_GET_TRAN_SETTINGS:.\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun); cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_USB; cts->transport_version = 0; cts->xport_specific.valid = 0; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_SET_TRAN_SETTINGS: { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_SET_TRAN_SETTINGS:.\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { cam_calc_geometry(&ccb->ccg, /* extended */ 1); xpt_done(ccb); break; } case XPT_NOOP: { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_NOOP:.\n", sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:func_code 0x%04x: " "Not implemented\n", sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, (uintmax_t)ccb->ccb_h.target_lun, ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } done: return; } static void umass_cam_poll(struct cam_sim *sim) { struct umass_softc *sc = (struct umass_softc *)sim->softc; if (sc == NULL) return; DPRINTF(sc, UDMASS_SCSI, "CAM poll\n"); usbd_transfer_poll(sc->sc_xfer, UMASS_T_MAX); } /* umass_cam_cb * finalise a completed CAM command */ static void umass_cam_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, uint8_t status) { ccb->csio.resid = residue; switch (status) { case STATUS_CMD_OK: ccb->ccb_h.status = CAM_REQ_CMP; if ((sc->sc_quirks & READ_CAPACITY_OFFBY1) && (ccb->ccb_h.func_code == XPT_SCSI_IO) && (ccb->csio.cdb_io.cdb_bytes[0] == READ_CAPACITY)) { struct scsi_read_capacity_data *rcap; uint32_t maxsector; rcap = (void *)(ccb->csio.data_ptr); maxsector = scsi_4btoul(rcap->addr) - 1; scsi_ulto4b(maxsector, rcap->addr); } /* * We have to add SVPD_UNIT_SERIAL_NUMBER to the list * of pages supported by the device - otherwise, CAM * will never ask us for the serial number if the * device cannot handle that by itself. */ if (ccb->ccb_h.func_code == XPT_SCSI_IO && sc->sc_transfer.cmd_data[0] == INQUIRY && (sc->sc_transfer.cmd_data[1] & SI_EVPD) && sc->sc_transfer.cmd_data[2] == SVPD_SUPPORTED_PAGE_LIST && (usb_get_serial(sc->sc_udev)[0] != '\0')) { struct ccb_scsiio *csio; struct scsi_vpd_supported_page_list *page_list; csio = &ccb->csio; page_list = (struct scsi_vpd_supported_page_list *)csio->data_ptr; if (page_list->length + 1 < SVPD_SUPPORTED_PAGES_SIZE) { page_list->list[page_list->length] = SVPD_UNIT_SERIAL_NUMBER; page_list->length++; } } xpt_done(ccb); break; case STATUS_CMD_UNKNOWN: case STATUS_CMD_FAILED: /* fetch sense data */ /* the rest of the command was filled in at attach */ sc->cam_scsi_sense.length = ccb->csio.sense_len; DPRINTF(sc, UDMASS_SCSI, "Fetching %d bytes of " "sense data\n", ccb->csio.sense_len); if (umass_std_transform(sc, ccb, &sc->cam_scsi_sense.opcode, sizeof(sc->cam_scsi_sense))) { if ((sc->sc_quirks & FORCE_SHORT_INQUIRY) && (sc->sc_transfer.cmd_data[0] == INQUIRY)) { ccb->csio.sense_len = SHORT_INQUIRY_LENGTH; } umass_command_start(sc, DIR_IN, &ccb->csio.sense_data.error_code, ccb->csio.sense_len, ccb->ccb_h.timeout, &umass_cam_sense_cb, ccb); } break; default: /* * The wire protocol failed and will hopefully have * recovered. We return an error to CAM and let CAM * retry the command if necessary. */ xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; xpt_done(ccb); break; } } /* * Finalise a completed autosense operation */ static void umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, uint8_t status) { uint8_t *cmd; switch (status) { case STATUS_CMD_OK: case STATUS_CMD_UNKNOWN: case STATUS_CMD_FAILED: { int key, sense_len; ccb->csio.sense_resid = residue; sense_len = ccb->csio.sense_len - ccb->csio.sense_resid; key = scsi_get_sense_key(&ccb->csio.sense_data, sense_len, /*show_errors*/ 1); if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) { cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr); } else { cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes); } /* * Getting sense data always succeeds (apart from wire * failures): */ if ((sc->sc_quirks & RS_NO_CLEAR_UA) && (cmd[0] == INQUIRY) && (key == SSD_KEY_UNIT_ATTENTION)) { /* * Ignore unit attention errors in the case where * the Unit Attention state is not cleared on * REQUEST SENSE. They will appear again at the next * command. */ ccb->ccb_h.status = CAM_REQ_CMP; } else if (key == SSD_KEY_NO_SENSE) { /* * No problem after all (in the case of CBI without * CCI) */ ccb->ccb_h.status = CAM_REQ_CMP; } else if ((sc->sc_quirks & RS_NO_CLEAR_UA) && (cmd[0] == READ_CAPACITY) && (key == SSD_KEY_UNIT_ATTENTION)) { /* * Some devices do not clear the unit attention error * on request sense. We insert a test unit ready * command to make sure we clear the unit attention * condition, then allow the retry to proceed as * usual. */ xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID | CAM_DEV_QFRZN; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; #if 0 DELAY(300000); #endif DPRINTF(sc, UDMASS_SCSI, "Doing a sneaky" "TEST_UNIT_READY\n"); /* the rest of the command was filled in at attach */ if ((sc->sc_transform)(sc, &sc->cam_scsi_test_unit_ready.opcode, sizeof(sc->cam_scsi_test_unit_ready)) == 1) { umass_command_start(sc, DIR_NONE, NULL, 0, ccb->ccb_h.timeout, &umass_cam_quirk_cb, ccb); break; } } else { xpt_freeze_devq(ccb->ccb_h.path, 1); if (key >= 0) { ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID | CAM_DEV_QFRZN; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else ccb->ccb_h.status = CAM_AUTOSENSE_FAIL | CAM_DEV_QFRZN; } xpt_done(ccb); break; } default: DPRINTF(sc, UDMASS_SCSI, "Autosense failed, " "status %d\n", status); xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status = CAM_AUTOSENSE_FAIL | CAM_DEV_QFRZN; xpt_done(ccb); } } /* * This completion code just handles the fact that we sent a test-unit-ready * after having previously failed a READ CAPACITY with CHECK_COND. The CCB * status for CAM is already set earlier. */ static void umass_cam_quirk_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, uint8_t status) { DPRINTF(sc, UDMASS_SCSI, "Test unit ready " "returned status %d\n", status); xpt_done(ccb); } /* * SCSI specific functions */ static uint8_t umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return (0); /* failure */ } sc->sc_transfer.cmd_len = cmd_len; switch (cmd_ptr[0]) { case TEST_UNIT_READY: if (sc->sc_quirks & NO_TEST_UNIT_READY) { DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); memset(sc->sc_transfer.cmd_data, 0, cmd_len); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return (1); } break; case INQUIRY: /* * some drives wedge when asked for full inquiry * information. */ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; return (1); } break; } memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); return (1); } static uint8_t umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return (0); /* failure */ } switch (cmd_ptr[0]) { /* these commands are defined in RBC: */ case READ_10: case READ_CAPACITY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: case WRITE_10: case VERIFY_10: case INQUIRY: case MODE_SELECT_10: case MODE_SENSE_10: case TEST_UNIT_READY: case WRITE_BUFFER: /* * The following commands are not listed in my copy of the * RBC specs. CAM however seems to want those, and at least * the Sony DSC device appears to support those as well */ case REQUEST_SENSE: case PREVENT_ALLOW: memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) { memset(sc->sc_transfer.cmd_data + cmd_len, 0, 12 - cmd_len); cmd_len = 12; } sc->sc_transfer.cmd_len = cmd_len; - return (1); /* sucess */ + return (1); /* success */ /* All other commands are not legal in RBC */ default: DPRINTF(sc, UDMASS_SCSI, "Unsupported RBC " "command 0x%02x\n", cmd_ptr[0]); return (0); /* failure */ } } static uint8_t umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return (0); /* failure */ } /* An UFI command is always 12 bytes in length */ sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH; /* Zero the command data */ memset(sc->sc_transfer.cmd_data, 0, UFI_COMMAND_LENGTH); switch (cmd_ptr[0]) { /* * Commands of which the format has been verified. They * should work. Copy the command into the (zeroed out) * destination buffer. */ case TEST_UNIT_READY: if (sc->sc_quirks & NO_TEST_UNIT_READY) { /* * Some devices do not support this command. Start * Stop Unit should give the same results */ DPRINTF(sc, UDMASS_UFI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return (1); } break; case REZERO_UNIT: case REQUEST_SENSE: case FORMAT_UNIT: case INQUIRY: case START_STOP_UNIT: case SEND_DIAGNOSTIC: case PREVENT_ALLOW: case READ_CAPACITY: case READ_10: case WRITE_10: case POSITION_TO_ELEMENT: /* SEEK_10 */ case WRITE_AND_VERIFY: case VERIFY: case MODE_SELECT_10: case MODE_SENSE_10: case READ_12: case WRITE_12: case READ_FORMAT_CAPACITIES: break; /* * SYNCHRONIZE_CACHE isn't supported by UFI, nor should it be * required for UFI devices, so it is appropriate to fake * success. */ case SYNCHRONIZE_CACHE: return (2); default: DPRINTF(sc, UDMASS_SCSI, "Unsupported UFI " "command 0x%02x\n", cmd_ptr[0]); return (0); /* failure */ } memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); return (1); /* success */ } /* * 8070i (ATAPI) specific functions */ static uint8_t umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return (0); /* failure */ } /* An ATAPI command is always 12 bytes in length. */ sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH; /* Zero the command data */ memset(sc->sc_transfer.cmd_data, 0, ATAPI_COMMAND_LENGTH); switch (cmd_ptr[0]) { /* * Commands of which the format has been verified. They * should work. Copy the command into the destination * buffer. */ case INQUIRY: /* * some drives wedge when asked for full inquiry * information. */ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; return (1); } break; case TEST_UNIT_READY: if (sc->sc_quirks & NO_TEST_UNIT_READY) { DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return (1); } break; case REZERO_UNIT: case REQUEST_SENSE: case START_STOP_UNIT: case SEND_DIAGNOSTIC: case PREVENT_ALLOW: case READ_CAPACITY: case READ_10: case WRITE_10: case POSITION_TO_ELEMENT: /* SEEK_10 */ case SYNCHRONIZE_CACHE: case MODE_SELECT_10: case MODE_SENSE_10: case READ_BUFFER: case 0x42: /* READ_SUBCHANNEL */ case 0x43: /* READ_TOC */ case 0x44: /* READ_HEADER */ case 0x47: /* PLAY_MSF (Play Minute/Second/Frame) */ case 0x48: /* PLAY_TRACK */ case 0x49: /* PLAY_TRACK_REL */ case 0x4b: /* PAUSE */ case 0x51: /* READ_DISK_INFO */ case 0x52: /* READ_TRACK_INFO */ case 0x54: /* SEND_OPC */ case 0x59: /* READ_MASTER_CUE */ case 0x5b: /* CLOSE_TR_SESSION */ case 0x5c: /* READ_BUFFER_CAP */ case 0x5d: /* SEND_CUE_SHEET */ case 0xa1: /* BLANK */ case 0xa5: /* PLAY_12 */ case 0xa6: /* EXCHANGE_MEDIUM */ case 0xad: /* READ_DVD_STRUCTURE */ case 0xbb: /* SET_CD_SPEED */ case 0xe5: /* READ_TRACK_INFO_PHILIPS */ break; case READ_12: case WRITE_12: default: DPRINTF(sc, UDMASS_SCSI, "Unsupported ATAPI " "command 0x%02x - trying anyway\n", cmd_ptr[0]); break; } memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); return (1); /* success */ } static uint8_t umass_no_transform(struct umass_softc *sc, uint8_t *cmd, uint8_t cmdlen) { return (0); /* failure */ } static uint8_t umass_std_transform(struct umass_softc *sc, union ccb *ccb, uint8_t *cmd, uint8_t cmdlen) { uint8_t retval; retval = (sc->sc_transform) (sc, cmd, cmdlen); if (retval == 2) { ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return (0); } else if (retval == 0) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status = CAM_REQ_INVALID | CAM_DEV_QFRZN; xpt_done(ccb); return (0); } /* Command should be executed */ return (1); } #ifdef USB_DEBUG static void umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw) { uint8_t *c = cbw->CBWCDB; uint32_t dlen = UGETDW(cbw->dCBWDataTransferLength); uint32_t tag = UGETDW(cbw->dCBWTag); uint8_t clen = cbw->bCDBLength; uint8_t flags = cbw->bCBWFlags; uint8_t lun = cbw->bCBWLUN; DPRINTF(sc, UDMASS_BBB, "CBW %d: cmd = %db " "(0x%02x%02x%02x%02x%02x%02x%s), " "data = %db, lun = %d, dir = %s\n", tag, clen, c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6 ? "..." : ""), dlen, lun, (flags == CBWFLAGS_IN ? "in" : (flags == CBWFLAGS_OUT ? "out" : ""))); } static void umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw) { uint32_t sig = UGETDW(csw->dCSWSignature); uint32_t tag = UGETDW(csw->dCSWTag); uint32_t res = UGETDW(csw->dCSWDataResidue); uint8_t status = csw->bCSWStatus; DPRINTF(sc, UDMASS_BBB, "CSW %d: sig = 0x%08x (%s), tag = 0x%08x, " "res = %d, status = 0x%02x (%s)\n", tag, sig, (sig == CSWSIGNATURE ? "valid" : "invalid"), tag, res, status, (status == CSWSTATUS_GOOD ? "good" : (status == CSWSTATUS_FAILED ? "failed" : (status == CSWSTATUS_PHASE ? "phase" : "")))); } static void umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, uint8_t cmdlen) { uint8_t *c = cmd; uint8_t dir = sc->sc_transfer.dir; DPRINTF(sc, UDMASS_BBB, "cmd = %db " "(0x%02x%02x%02x%02x%02x%02x%s), " "data = %db, dir = %s\n", cmdlen, c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6 ? "..." : ""), sc->sc_transfer.data_len, (dir == DIR_IN ? "in" : (dir == DIR_OUT ? "out" : (dir == DIR_NONE ? "no data phase" : "")))); } static void umass_dump_buffer(struct umass_softc *sc, uint8_t *buffer, uint32_t buflen, uint32_t printlen) { uint32_t i, j; char s1[40]; char s2[40]; char s3[5]; s1[0] = '\0'; s3[0] = '\0'; sprintf(s2, " buffer=%p, buflen=%d", buffer, buflen); for (i = 0; (i < buflen) && (i < printlen); i++) { j = i % 16; if (j == 0 && i != 0) { DPRINTF(sc, UDMASS_GEN, "0x %s%s\n", s1, s2); s2[0] = '\0'; } sprintf(&s1[j * 2], "%02x", buffer[i] & 0xff); } if (buflen > printlen) sprintf(s3, " ..."); DPRINTF(sc, UDMASS_GEN, "0x %s%s%s\n", s1, s2, s3); } #endif Index: head/sys/dev/usb/template/usb_template.c =================================================================== --- head/sys/dev/usb/template/usb_template.c (revision 298931) +++ head/sys/dev/usb/template/usb_template.c (revision 298932) @@ -1,1397 +1,1397 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file contains sub-routines to build up USB descriptors from * USB templates. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ MODULE_DEPEND(usb_template, usb, 1, 1, 1); MODULE_VERSION(usb_template, 1); /* function prototypes */ static void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *); static void usb_make_endpoint_desc(struct usb_temp_setup *, const struct usb_temp_endpoint_desc *); static void usb_make_interface_desc(struct usb_temp_setup *, const struct usb_temp_interface_desc *); static void usb_make_config_desc(struct usb_temp_setup *, const struct usb_temp_config_desc *); static void usb_make_device_desc(struct usb_temp_setup *, const struct usb_temp_device_desc *); static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, uint8_t); static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *, struct usb_hw_ep_scratch_sub *, uint8_t); static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, uint8_t); static usb_error_t usb_hw_ep_resolve(struct usb_device *, struct usb_descriptor *); static const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *); static void *usb_temp_get_device_desc(struct usb_device *); static void *usb_temp_get_qualifier_desc(struct usb_device *); static void *usb_temp_get_config_desc(struct usb_device *, uint16_t *, uint8_t); static const void *usb_temp_get_string_desc(struct usb_device *, uint16_t, uint8_t); static const void *usb_temp_get_vendor_desc(struct usb_device *, const struct usb_device_request *, uint16_t *plen); static const void *usb_temp_get_hub_desc(struct usb_device *); static usb_error_t usb_temp_get_desc(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); static usb_error_t usb_temp_setup_by_index(struct usb_device *, uint16_t index); static void usb_temp_init(void *); /*------------------------------------------------------------------------* * usb_make_raw_desc * * This function will insert a raw USB descriptor into the generated * USB configuration. *------------------------------------------------------------------------*/ static void usb_make_raw_desc(struct usb_temp_setup *temp, const uint8_t *raw) { void *dst; uint8_t len; /* * The first byte of any USB descriptor gives the length. */ if (raw) { len = raw[0]; if (temp->buf) { dst = USB_ADD_BYTES(temp->buf, temp->size); memcpy(dst, raw, len); /* check if we have got a CDC union descriptor */ if ((raw[0] == sizeof(struct usb_cdc_union_descriptor)) && (raw[1] == UDESC_CS_INTERFACE) && (raw[2] == UDESCSUB_CDC_UNION)) { struct usb_cdc_union_descriptor *ud = (void *)dst; /* update the interface numbers */ ud->bMasterInterface += temp->bInterfaceNumber; ud->bSlaveInterface[0] += temp->bInterfaceNumber; } /* check if we have got an interface association descriptor */ if ((raw[0] == sizeof(struct usb_interface_assoc_descriptor)) && (raw[1] == UDESC_IFACE_ASSOC)) { struct usb_interface_assoc_descriptor *iad = (void *)dst; /* update the interface number */ iad->bFirstInterface += temp->bInterfaceNumber; } /* check if we have got a call management descriptor */ if ((raw[0] == sizeof(struct usb_cdc_cm_descriptor)) && (raw[1] == UDESC_CS_INTERFACE) && (raw[2] == UDESCSUB_CDC_CM)) { struct usb_cdc_cm_descriptor *ccd = (void *)dst; /* update the interface number */ ccd->bDataInterface += temp->bInterfaceNumber; } } temp->size += len; } } /*------------------------------------------------------------------------* * usb_make_endpoint_desc * * This function will generate an USB endpoint descriptor from the * given USB template endpoint descriptor, which will be inserted into * the USB configuration. *------------------------------------------------------------------------*/ static void usb_make_endpoint_desc(struct usb_temp_setup *temp, const struct usb_temp_endpoint_desc *ted) { struct usb_endpoint_descriptor *ed; const void **rd; uint16_t old_size; uint16_t mps; uint8_t ea; /* Endpoint Address */ uint8_t et; /* Endpiont Type */ /* Reserve memory */ old_size = temp->size; ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); et = (ted->bmAttributes & UE_XFERTYPE); if (et == UE_ISOCHRONOUS) { /* account for extra byte fields */ temp->size += sizeof(*ed) + 2; } else { temp->size += sizeof(*ed); } /* Scan all Raw Descriptors first */ rd = ted->ppRawDesc; if (rd) { while (*rd) { usb_make_raw_desc(temp, *rd); rd++; } } if (ted->pPacketSize == NULL) { /* not initialized */ temp->err = USB_ERR_INVAL; return; } mps = ted->pPacketSize->mps[temp->usb_speed]; if (mps == 0) { /* not initialized */ temp->err = USB_ERR_INVAL; return; } else if (mps == UE_ZERO_MPS) { /* escape for Zero Max Packet Size */ mps = 0; } /* * Fill out the real USB endpoint descriptor * in case there is a buffer present: */ if (temp->buf) { ed = USB_ADD_BYTES(temp->buf, old_size); if (et == UE_ISOCHRONOUS) ed->bLength = sizeof(*ed) + 2; else ed->bLength = sizeof(*ed); ed->bDescriptorType = UDESC_ENDPOINT; ed->bEndpointAddress = ea; ed->bmAttributes = ted->bmAttributes; USETW(ed->wMaxPacketSize, mps); /* setup bInterval parameter */ if (ted->pIntervals && ted->pIntervals->bInterval[temp->usb_speed]) { ed->bInterval = ted->pIntervals->bInterval[temp->usb_speed]; } else { switch (et) { case UE_BULK: case UE_CONTROL: ed->bInterval = 0; /* not used */ break; case UE_INTERRUPT: switch (temp->usb_speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: ed->bInterval = 1; /* 1 ms */ break; default: ed->bInterval = 4; /* 1 ms */ break; } break; default: /* UE_ISOCHRONOUS */ switch (temp->usb_speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: ed->bInterval = 1; /* 1 ms */ break; default: ed->bInterval = 1; /* 125 us */ break; } break; } } } temp->bNumEndpoints++; } /*------------------------------------------------------------------------* * usb_make_interface_desc * * This function will generate an USB interface descriptor from the * given USB template interface descriptor, which will be inserted * into the USB configuration. *------------------------------------------------------------------------*/ static void usb_make_interface_desc(struct usb_temp_setup *temp, const struct usb_temp_interface_desc *tid) { struct usb_interface_descriptor *id; const struct usb_temp_endpoint_desc **ted; const void **rd; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*id); /* Update interface and alternate interface numbers */ if (tid->isAltInterface == 0) { temp->bAlternateSetting = 0; temp->bInterfaceNumber++; } else { temp->bAlternateSetting++; } /* Scan all Raw Descriptors first */ rd = tid->ppRawDesc; if (rd) { while (*rd) { usb_make_raw_desc(temp, *rd); rd++; } } /* Reset some counters */ temp->bNumEndpoints = 0; /* Scan all Endpoint Descriptors second */ ted = tid->ppEndpoints; if (ted) { while (*ted) { usb_make_endpoint_desc(temp, *ted); ted++; } } /* * Fill out the real USB interface descriptor * in case there is a buffer present: */ if (temp->buf) { id = USB_ADD_BYTES(temp->buf, old_size); id->bLength = sizeof(*id); id->bDescriptorType = UDESC_INTERFACE; id->bInterfaceNumber = temp->bInterfaceNumber; id->bAlternateSetting = temp->bAlternateSetting; id->bNumEndpoints = temp->bNumEndpoints; id->bInterfaceClass = tid->bInterfaceClass; id->bInterfaceSubClass = tid->bInterfaceSubClass; id->bInterfaceProtocol = tid->bInterfaceProtocol; id->iInterface = tid->iInterface; } } /*------------------------------------------------------------------------* * usb_make_config_desc * * This function will generate an USB config descriptor from the given * USB template config descriptor, which will be inserted into the USB * configuration. *------------------------------------------------------------------------*/ static void usb_make_config_desc(struct usb_temp_setup *temp, const struct usb_temp_config_desc *tcd) { struct usb_config_descriptor *cd; const struct usb_temp_interface_desc **tid; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*cd); /* Reset some counters */ temp->bInterfaceNumber = 0xFF; temp->bAlternateSetting = 0; /* Scan all the USB interfaces */ tid = tcd->ppIfaceDesc; if (tid) { while (*tid) { usb_make_interface_desc(temp, *tid); tid++; } } /* * Fill out the real USB config descriptor * in case there is a buffer present: */ if (temp->buf) { cd = USB_ADD_BYTES(temp->buf, old_size); /* compute total size */ old_size = temp->size - old_size; cd->bLength = sizeof(*cd); cd->bDescriptorType = UDESC_CONFIG; USETW(cd->wTotalLength, old_size); cd->bNumInterface = temp->bInterfaceNumber + 1; cd->bConfigurationValue = temp->bConfigurationValue; cd->iConfiguration = tcd->iConfiguration; cd->bmAttributes = tcd->bmAttributes; cd->bMaxPower = tcd->bMaxPower; cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); if (temp->self_powered) { cd->bmAttributes |= UC_SELF_POWERED; } else { cd->bmAttributes &= ~UC_SELF_POWERED; } } } /*------------------------------------------------------------------------* * usb_make_device_desc * * This function will generate an USB device descriptor from the * given USB template device descriptor. *------------------------------------------------------------------------*/ static void usb_make_device_desc(struct usb_temp_setup *temp, const struct usb_temp_device_desc *tdd) { struct usb_temp_data *utd; const struct usb_temp_config_desc **tcd; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*utd); /* Scan all the USB configs */ temp->bConfigurationValue = 1; tcd = tdd->ppConfigDesc; if (tcd) { while (*tcd) { usb_make_config_desc(temp, *tcd); temp->bConfigurationValue++; tcd++; } } /* * Fill out the real USB device descriptor * in case there is a buffer present: */ if (temp->buf) { utd = USB_ADD_BYTES(temp->buf, old_size); /* Store a pointer to our template device descriptor */ utd->tdd = tdd; /* Fill out USB device descriptor */ utd->udd.bLength = sizeof(utd->udd); utd->udd.bDescriptorType = UDESC_DEVICE; utd->udd.bDeviceClass = tdd->bDeviceClass; utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; USETW(utd->udd.idVendor, tdd->idVendor); USETW(utd->udd.idProduct, tdd->idProduct); USETW(utd->udd.bcdDevice, tdd->bcdDevice); utd->udd.iManufacturer = tdd->iManufacturer; utd->udd.iProduct = tdd->iProduct; utd->udd.iSerialNumber = tdd->iSerialNumber; utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; /* * Fill out the USB device qualifier. Pretend that we * don't support any other speeds by setting * "bNumConfigurations" equal to zero. That saves us * generating an extra set of configuration * descriptors. */ utd->udq.bLength = sizeof(utd->udq); utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; utd->udq.bDeviceClass = tdd->bDeviceClass; utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; utd->udq.bNumConfigurations = 0; USETW(utd->udq.bcdUSB, 0x0200); utd->udq.bMaxPacketSize0 = 0; switch (temp->usb_speed) { case USB_SPEED_LOW: USETW(utd->udd.bcdUSB, 0x0110); utd->udd.bMaxPacketSize = 8; break; case USB_SPEED_FULL: USETW(utd->udd.bcdUSB, 0x0110); utd->udd.bMaxPacketSize = 32; break; case USB_SPEED_HIGH: USETW(utd->udd.bcdUSB, 0x0200); utd->udd.bMaxPacketSize = 64; break; case USB_SPEED_VARIABLE: USETW(utd->udd.bcdUSB, 0x0250); utd->udd.bMaxPacketSize = 255; /* 512 bytes */ break; case USB_SPEED_SUPER: USETW(utd->udd.bcdUSB, 0x0300); utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */ break; default: temp->err = USB_ERR_INVAL; break; } } } /*------------------------------------------------------------------------* * usb_hw_ep_match * * Return values: - * 0: The endpoint profile does not match the criterias - * Else: The endpoint profile matches the criterias + * 0: The endpoint profile does not match the criteria + * Else: The endpoint profile matches the criteria *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *pf, uint8_t ep_type, uint8_t ep_dir_in) { if (ep_type == UE_CONTROL) { /* special */ return (pf->support_control); } if ((pf->support_in && ep_dir_in) || (pf->support_out && !ep_dir_in)) { if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || (pf->support_bulk && (ep_type == UE_BULK))) { return (1); } } return (0); } /*------------------------------------------------------------------------* * usb_hw_ep_find_match * * This function is used to find the best matching endpoint profile * for and endpoint belonging to an USB descriptor. * * Return values: * 0: Success. Got a match. * Else: Failure. No match. *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *ues, struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) { const struct usb_hw_ep_profile *pf; uint16_t distance; uint16_t temp; uint16_t max_frame_size; uint8_t n; uint8_t best_n; uint8_t dir_in; uint8_t dir_out; distance = 0xFFFF; best_n = 0; if ((!ep->needs_in) && (!ep->needs_out)) { return (0); /* we are done */ } if (ep->needs_ep_type == UE_CONTROL) { dir_in = 1; dir_out = 1; } else { if (ep->needs_in) { dir_in = 1; dir_out = 0; } else { dir_in = 0; dir_out = 1; } } for (n = 1; n != (USB_EP_MAX / 2); n++) { /* get HW endpoint profile */ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); if (pf == NULL) { /* end of profiles */ break; } /* check if IN-endpoint is reserved */ if (dir_in || pf->is_simplex) { if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { /* mismatch */ continue; } } /* check if OUT-endpoint is reserved */ if (dir_out || pf->is_simplex) { if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { /* mismatch */ continue; } } /* check simplex */ if (pf->is_simplex == is_simplex) { /* mismatch */ continue; } /* check if HW endpoint matches */ if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { /* mismatch */ continue; } /* get maximum frame size */ if (dir_in) max_frame_size = pf->max_in_frame_size; else max_frame_size = pf->max_out_frame_size; /* check if we have a matching profile */ if (max_frame_size >= ep->max_frame_size) { temp = (max_frame_size - ep->max_frame_size); if (distance > temp) { distance = temp; best_n = n; ep->pf = pf; } } } /* see if we got a match */ if (best_n != 0) { /* get the correct profile */ pf = ep->pf; /* reserve IN-endpoint */ if (dir_in) { ues->bmInAlloc[best_n / 8] |= (1 << (best_n % 8)); ep->hw_endpoint_in = best_n | UE_DIR_IN; ep->needs_in = 0; } /* reserve OUT-endpoint */ if (dir_out) { ues->bmOutAlloc[best_n / 8] |= (1 << (best_n % 8)); ep->hw_endpoint_out = best_n | UE_DIR_OUT; ep->needs_out = 0; } return (0); /* got a match */ } return (1); /* failure */ } /*------------------------------------------------------------------------* * usb_hw_ep_get_needs * * This function will figure out the type and number of endpoints * which are needed for an USB configuration. * * Return values: * 0: Success. * Else: Failure. *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, uint8_t ep_type, uint8_t is_complete) { const struct usb_hw_ep_profile *pf; struct usb_hw_ep_scratch_sub *ep_iface; struct usb_hw_ep_scratch_sub *ep_curr; struct usb_hw_ep_scratch_sub *ep_max; struct usb_hw_ep_scratch_sub *ep_end; struct usb_descriptor *desc; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; enum usb_dev_speed speed; uint16_t wMaxPacketSize; uint16_t temp; uint8_t ep_no; ep_iface = ues->ep_max; ep_curr = ues->ep_max; ep_end = ues->ep + USB_EP_MAX; ep_max = ues->ep_max; desc = NULL; speed = usbd_get_speed(ues->udev); repeat: while ((desc = usb_desc_foreach(ues->cd, desc))) { if ((desc->bDescriptorType == UDESC_INTERFACE) && (desc->bLength >= sizeof(*id))) { id = (void *)desc; if (id->bAlternateSetting == 0) { /* going forward */ ep_iface = ep_max; } else { /* reset */ ep_curr = ep_iface; } } if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (void *)desc; goto handle_endpoint_desc; } } ues->ep_max = ep_max; return (0); handle_endpoint_desc: temp = (ed->bmAttributes & UE_XFERTYPE); if (temp == ep_type) { if (ep_curr == ep_end) { /* too many endpoints */ return (1); /* failure */ } wMaxPacketSize = UGETW(ed->wMaxPacketSize); if ((wMaxPacketSize & 0xF800) && (speed == USB_SPEED_HIGH)) { /* handle packet multiplier */ temp = (wMaxPacketSize >> 11) & 3; wMaxPacketSize &= 0x7FF; if (temp == 1) { wMaxPacketSize *= 2; } else { wMaxPacketSize *= 3; } } /* * Check if we have a fixed endpoint number, else the * endpoint number is allocated dynamically: */ ep_no = (ed->bEndpointAddress & UE_ADDR); if (ep_no != 0) { /* get HW endpoint profile */ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, ep_no); if (pf == NULL) { /* HW profile does not exist - failure */ DPRINTFN(0, "Endpoint profile %u " "does not exist\n", ep_no); return (1); } /* reserve fixed endpoint number */ if (ep_type == UE_CONTROL) { ues->bmInAlloc[ep_no / 8] |= (1 << (ep_no % 8)); ues->bmOutAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if ((pf->max_in_frame_size < wMaxPacketSize) || (pf->max_out_frame_size < wMaxPacketSize)) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } else if (ed->bEndpointAddress & UE_DIR_IN) { ues->bmInAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if (pf->max_in_frame_size < wMaxPacketSize) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } else { ues->bmOutAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if (pf->max_out_frame_size < wMaxPacketSize) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } } else if (is_complete) { /* check if we have enough buffer space */ if (wMaxPacketSize > ep_curr->max_frame_size) { return (1); /* failure */ } if (ed->bEndpointAddress & UE_DIR_IN) { ed->bEndpointAddress = ep_curr->hw_endpoint_in; } else { ed->bEndpointAddress = ep_curr->hw_endpoint_out; } } else { /* compute the maximum frame size */ if (ep_curr->max_frame_size < wMaxPacketSize) { ep_curr->max_frame_size = wMaxPacketSize; } if (temp == UE_CONTROL) { ep_curr->needs_in = 1; ep_curr->needs_out = 1; } else { if (ed->bEndpointAddress & UE_DIR_IN) { ep_curr->needs_in = 1; } else { ep_curr->needs_out = 1; } } ep_curr->needs_ep_type = ep_type; } ep_curr++; if (ep_max < ep_curr) { ep_max = ep_curr; } } goto repeat; } /*------------------------------------------------------------------------* * usb_hw_ep_resolve * * This function will try to resolve endpoint requirements by the * given endpoint profiles that the USB hardware reports. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_hw_ep_resolve(struct usb_device *udev, struct usb_descriptor *desc) { struct usb_hw_ep_scratch *ues; struct usb_hw_ep_scratch_sub *ep; const struct usb_hw_ep_profile *pf; const struct usb_bus_methods *methods; struct usb_device_descriptor *dd; uint16_t mps; if (desc == NULL) return (USB_ERR_INVAL); /* get bus methods */ methods = udev->bus->methods; if (methods->get_hw_ep_profile == NULL) return (USB_ERR_INVAL); if (desc->bDescriptorType == UDESC_DEVICE) { if (desc->bLength < sizeof(*dd)) return (USB_ERR_INVAL); dd = (void *)desc; /* get HW control endpoint 0 profile */ (methods->get_hw_ep_profile) (udev, &pf, 0); if (pf == NULL) { return (USB_ERR_INVAL); } if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) { DPRINTFN(0, "Endpoint 0 does not " "support control\n"); return (USB_ERR_INVAL); } mps = dd->bMaxPacketSize; if (udev->speed == USB_SPEED_FULL) { /* * We can optionally choose another packet size ! */ while (1) { /* check if "mps" is ok */ if (pf->max_in_frame_size >= mps) { break; } /* reduce maximum packet size */ mps /= 2; /* check if "mps" is too small */ if (mps < 8) { return (USB_ERR_INVAL); } } dd->bMaxPacketSize = mps; } else { /* We only have one choice */ if (mps == 255) { mps = 512; } /* Check if we support the specified wMaxPacketSize */ if (pf->max_in_frame_size < mps) { return (USB_ERR_INVAL); } } return (0); /* success */ } if (desc->bDescriptorType != UDESC_CONFIG) return (USB_ERR_INVAL); if (desc->bLength < sizeof(*(ues->cd))) return (USB_ERR_INVAL); ues = udev->scratch.hw_ep_scratch; memset(ues, 0, sizeof(*ues)); ues->ep_max = ues->ep; ues->cd = (void *)desc; ues->methods = methods; ues->udev = udev; /* Get all the endpoints we need */ if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || usb_hw_ep_get_needs(ues, UE_CONTROL, 0) || usb_hw_ep_get_needs(ues, UE_BULK, 0)) { DPRINTFN(0, "Could not get needs\n"); return (USB_ERR_INVAL); } for (ep = ues->ep; ep != ues->ep_max; ep++) { while (ep->needs_in || ep->needs_out) { /* * First try to use a simplex endpoint. * Then try to use a duplex endpoint. */ if (usb_hw_ep_find_match(ues, ep, 1) && usb_hw_ep_find_match(ues, ep, 0)) { DPRINTFN(0, "Could not find match\n"); return (USB_ERR_INVAL); } } } ues->ep_max = ues->ep; /* Update all endpoint addresses */ if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || usb_hw_ep_get_needs(ues, UE_CONTROL, 1) || usb_hw_ep_get_needs(ues, UE_BULK, 1)) { DPRINTFN(0, "Could not update endpoint address\n"); return (USB_ERR_INVAL); } return (0); /* success */ } /*------------------------------------------------------------------------* * usb_temp_get_tdd * * Returns: * NULL: No USB template device descriptor found. * Else: Pointer to the USB template device descriptor. *------------------------------------------------------------------------*/ static const struct usb_temp_device_desc * usb_temp_get_tdd(struct usb_device *udev) { if (udev->usb_template_ptr == NULL) { return (NULL); } return (udev->usb_template_ptr->tdd); } /*------------------------------------------------------------------------* * usb_temp_get_device_desc * * Returns: * NULL: No USB device descriptor found. * Else: Pointer to USB device descriptor. *------------------------------------------------------------------------*/ static void * usb_temp_get_device_desc(struct usb_device *udev) { struct usb_device_descriptor *dd; if (udev->usb_template_ptr == NULL) { return (NULL); } dd = &udev->usb_template_ptr->udd; if (dd->bDescriptorType != UDESC_DEVICE) { /* sanity check failed */ return (NULL); } return (dd); } /*------------------------------------------------------------------------* * usb_temp_get_qualifier_desc * * Returns: * NULL: No USB device_qualifier descriptor found. * Else: Pointer to USB device_qualifier descriptor. *------------------------------------------------------------------------*/ static void * usb_temp_get_qualifier_desc(struct usb_device *udev) { struct usb_device_qualifier *dq; if (udev->usb_template_ptr == NULL) { return (NULL); } dq = &udev->usb_template_ptr->udq; if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { /* sanity check failed */ return (NULL); } return (dq); } /*------------------------------------------------------------------------* * usb_temp_get_config_desc * * Returns: * NULL: No USB config descriptor found. * Else: Pointer to USB config descriptor having index "index". *------------------------------------------------------------------------*/ static void * usb_temp_get_config_desc(struct usb_device *udev, uint16_t *pLength, uint8_t index) { struct usb_device_descriptor *dd; struct usb_config_descriptor *cd; uint16_t temp; if (udev->usb_template_ptr == NULL) { return (NULL); } dd = &udev->usb_template_ptr->udd; cd = (void *)(udev->usb_template_ptr + 1); if (index >= dd->bNumConfigurations) { /* out of range */ return (NULL); } while (index--) { if (cd->bDescriptorType != UDESC_CONFIG) { /* sanity check failed */ return (NULL); } temp = UGETW(cd->wTotalLength); cd = USB_ADD_BYTES(cd, temp); } if (pLength) { *pLength = UGETW(cd->wTotalLength); } return (cd); } /*------------------------------------------------------------------------* * usb_temp_get_vendor_desc * * Returns: * NULL: No vendor descriptor found. * Else: Pointer to a vendor descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_vendor_desc(struct usb_device *udev, const struct usb_device_request *req, uint16_t *plen) { const struct usb_temp_device_desc *tdd; tdd = usb_temp_get_tdd(udev); if (tdd == NULL) { return (NULL); } if (tdd->getVendorDesc == NULL) { return (NULL); } return ((tdd->getVendorDesc) (req, plen)); } /*------------------------------------------------------------------------* * usb_temp_get_string_desc * * Returns: * NULL: No string descriptor found. * Else: Pointer to a string descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_string_desc(struct usb_device *udev, uint16_t lang_id, uint8_t string_index) { const struct usb_temp_device_desc *tdd; tdd = usb_temp_get_tdd(udev); if (tdd == NULL) { return (NULL); } if (tdd->getStringDesc == NULL) { return (NULL); } return ((tdd->getStringDesc) (lang_id, string_index)); } /*------------------------------------------------------------------------* * usb_temp_get_hub_desc * * Returns: * NULL: No USB HUB descriptor found. * Else: Pointer to a USB HUB descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_hub_desc(struct usb_device *udev) { return (NULL); /* needs to be implemented */ } /*------------------------------------------------------------------------* * usb_temp_get_desc * * This function is a demultiplexer for local USB device side control * endpoint requests. *------------------------------------------------------------------------*/ static usb_error_t usb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, const void **pPtr, uint16_t *pLength) { const uint8_t *buf; uint16_t len; buf = NULL; len = 0; switch (req->bmRequestType) { case UT_READ_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; default: goto tr_stalled; } case UT_READ_CLASS_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; default: goto tr_stalled; } default: goto tr_stalled; } tr_handle_get_descriptor: switch (req->wValue[1]) { case UDESC_DEVICE: if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_device_desc(udev); goto tr_valid; case UDESC_DEVICE_QUALIFIER: if (udev->speed != USB_SPEED_HIGH) { goto tr_stalled; } if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_qualifier_desc(udev); goto tr_valid; case UDESC_OTHER_SPEED_CONFIGURATION: if (udev->speed != USB_SPEED_HIGH) { goto tr_stalled; } case UDESC_CONFIG: buf = usb_temp_get_config_desc(udev, &len, req->wValue[0]); goto tr_valid; case UDESC_STRING: buf = usb_temp_get_string_desc(udev, UGETW(req->wIndex), req->wValue[0]); goto tr_valid; default: goto tr_stalled; } tr_handle_get_class_descriptor: if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_hub_desc(udev); goto tr_valid; tr_valid: if (buf == NULL) goto tr_stalled; if (len == 0) len = buf[0]; *pPtr = buf; *pLength = len; return (0); /* success */ tr_stalled: /* try to get a vendor specific descriptor */ len = 0; buf = usb_temp_get_vendor_desc(udev, req, &len); if (buf != NULL) goto tr_valid; *pPtr = NULL; *pLength = 0; return (0); /* we ignore failures */ } /*------------------------------------------------------------------------* * usb_temp_setup * * This function generates USB descriptors according to the given USB * template device descriptor. It will also try to figure out the best * matching endpoint addresses using the hardware endpoint profiles. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_temp_setup(struct usb_device *udev, const struct usb_temp_device_desc *tdd) { struct usb_temp_setup *uts; void *buf; usb_error_t error; uint8_t n; uint8_t do_unlock; /* be NULL safe */ if (tdd == NULL) return (0); /* Protect scratch area */ do_unlock = usbd_enum_lock(udev); uts = udev->scratch.temp_setup; memset(uts, 0, sizeof(*uts)); uts->usb_speed = udev->speed; uts->self_powered = udev->flags.self_powered; /* first pass */ usb_make_device_desc(uts, tdd); if (uts->err) { /* some error happened */ goto done; } /* sanity check */ if (uts->size == 0) { uts->err = USB_ERR_INVAL; goto done; } /* allocate zeroed memory */ uts->buf = usbd_alloc_config_desc(udev, uts->size); /* * Allow malloc() to return NULL regardless of M_WAITOK flag. * This helps when porting the software to non-FreeBSD * systems. */ if (uts->buf == NULL) { /* could not allocate memory */ uts->err = USB_ERR_NOMEM; goto done; } /* second pass */ uts->size = 0; usb_make_device_desc(uts, tdd); /* * Store a pointer to our descriptors: */ udev->usb_template_ptr = uts->buf; if (uts->err) { /* some error happened during second pass */ goto done; } /* * Resolve all endpoint addresses ! */ buf = usb_temp_get_device_desc(udev); uts->err = usb_hw_ep_resolve(udev, buf); if (uts->err) { DPRINTFN(0, "Could not resolve endpoints for " "Device Descriptor, error = %s\n", usbd_errstr(uts->err)); goto done; } for (n = 0;; n++) { buf = usb_temp_get_config_desc(udev, NULL, n); if (buf == NULL) { break; } uts->err = usb_hw_ep_resolve(udev, buf); if (uts->err) { DPRINTFN(0, "Could not resolve endpoints for " "Config Descriptor %u, error = %s\n", n, usbd_errstr(uts->err)); goto done; } } done: error = uts->err; if (error) usb_temp_unsetup(udev); if (do_unlock) usbd_enum_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usb_temp_unsetup * * This function frees any memory associated with the currently * setup template, if any. *------------------------------------------------------------------------*/ void usb_temp_unsetup(struct usb_device *udev) { usbd_free_config_desc(udev, udev->usb_template_ptr); udev->usb_template_ptr = NULL; } static usb_error_t usb_temp_setup_by_index(struct usb_device *udev, uint16_t index) { usb_error_t err; switch (index) { case USB_TEMP_MSC: err = usb_temp_setup(udev, &usb_template_msc); break; case USB_TEMP_CDCE: err = usb_temp_setup(udev, &usb_template_cdce); break; case USB_TEMP_MTP: err = usb_temp_setup(udev, &usb_template_mtp); break; case USB_TEMP_MODEM: err = usb_temp_setup(udev, &usb_template_modem); break; case USB_TEMP_AUDIO: err = usb_temp_setup(udev, &usb_template_audio); break; case USB_TEMP_KBD: err = usb_temp_setup(udev, &usb_template_kbd); break; case USB_TEMP_MOUSE: err = usb_temp_setup(udev, &usb_template_mouse); break; case USB_TEMP_PHONE: err = usb_temp_setup(udev, &usb_template_phone); break; case USB_TEMP_SERIALNET: err = usb_temp_setup(udev, &usb_template_serialnet); break; case USB_TEMP_MIDI: err = usb_temp_setup(udev, &usb_template_midi); break; default: return (USB_ERR_INVAL); } return (err); } static void usb_temp_init(void *arg) { /* register our functions */ usb_temp_get_desc_p = &usb_temp_get_desc; usb_temp_setup_by_index_p = &usb_temp_setup_by_index; usb_temp_unsetup_p = &usb_temp_unsetup; } SYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL); SYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL); Index: head/sys/dev/usb/usb_cdc.h =================================================================== --- head/sys/dev/usb/usb_cdc.h (revision 298931) +++ head/sys/dev/usb/usb_cdc.h (revision 298932) @@ -1,288 +1,288 @@ /* $NetBSD: usbcdc.h,v 1.9 2004/10/23 13:24:24 augustss Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USB_CDC_H_ #define _USB_CDC_H_ #define UDESCSUB_CDC_HEADER 0 #define UDESCSUB_CDC_CM 1 /* Call Management */ #define UDESCSUB_CDC_ACM 2 /* Abstract Control Model */ #define UDESCSUB_CDC_DLM 3 /* Direct Line Management */ #define UDESCSUB_CDC_TRF 4 /* Telephone Ringer */ #define UDESCSUB_CDC_TCLSR 5 /* Telephone Call */ #define UDESCSUB_CDC_UNION 6 #define UDESCSUB_CDC_CS 7 /* Country Selection */ #define UDESCSUB_CDC_TOM 8 /* Telephone Operational Modes */ #define UDESCSUB_CDC_USBT 9 /* USB Terminal */ #define UDESCSUB_CDC_NCT 10 #define UDESCSUB_CDC_PUF 11 #define UDESCSUB_CDC_EUF 12 #define UDESCSUB_CDC_MCMF 13 #define UDESCSUB_CDC_CCMF 14 #define UDESCSUB_CDC_ENF 15 #define UDESCSUB_CDC_ANF 16 struct usb_cdc_header_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uWord bcdCDC; } __packed; struct usb_cdc_cm_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bmCapabilities; #define USB_CDC_CM_DOES_CM 0x01 #define USB_CDC_CM_OVER_DATA 0x02 uByte bDataInterface; } __packed; struct usb_cdc_acm_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bmCapabilities; #define USB_CDC_ACM_HAS_FEATURE 0x01 #define USB_CDC_ACM_HAS_LINE 0x02 #define USB_CDC_ACM_HAS_BREAK 0x04 #define USB_CDC_ACM_HAS_NETWORK_CONN 0x08 } __packed; struct usb_cdc_union_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bMasterInterface; uByte bSlaveInterface[1]; } __packed; struct usb_cdc_ethernet_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte iMacAddress; uDWord bmEthernetStatistics; uWord wMaxSegmentSize; uWord wNumberMCFilters; uByte bNumberPowerFilters; } __packed; #define UCDC_SEND_ENCAPSULATED_COMMAND 0x00 #define UCDC_GET_ENCAPSULATED_RESPONSE 0x01 #define UCDC_SET_COMM_FEATURE 0x02 #define UCDC_GET_COMM_FEATURE 0x03 #define UCDC_ABSTRACT_STATE 0x01 #define UCDC_COUNTRY_SETTING 0x02 #define UCDC_CLEAR_COMM_FEATURE 0x04 #define UCDC_SET_LINE_CODING 0x20 #define UCDC_GET_LINE_CODING 0x21 #define UCDC_SET_CONTROL_LINE_STATE 0x22 #define UCDC_LINE_DTR 0x0001 #define UCDC_LINE_RTS 0x0002 #define UCDC_SEND_BREAK 0x23 #define UCDC_BREAK_ON 0xffff #define UCDC_BREAK_OFF 0x0000 struct usb_cdc_abstract_state { uWord wState; #define UCDC_IDLE_SETTING 0x0001 #define UCDC_DATA_MULTIPLEXED 0x0002 } __packed; #define UCDC_ABSTRACT_STATE_LENGTH 2 struct usb_cdc_line_state { uDWord dwDTERate; uByte bCharFormat; #define UCDC_STOP_BIT_1 0 #define UCDC_STOP_BIT_1_5 1 #define UCDC_STOP_BIT_2 2 uByte bParityType; #define UCDC_PARITY_NONE 0 #define UCDC_PARITY_ODD 1 #define UCDC_PARITY_EVEN 2 #define UCDC_PARITY_MARK 3 #define UCDC_PARITY_SPACE 4 uByte bDataBits; } __packed; #define UCDC_LINE_STATE_LENGTH 7 struct usb_cdc_notification { uByte bmRequestType; #define UCDC_NOTIFICATION 0xa1 uByte bNotification; #define UCDC_N_NETWORK_CONNECTION 0x00 #define UCDC_N_RESPONSE_AVAILABLE 0x01 #define UCDC_N_AUX_JACK_HOOK_STATE 0x08 #define UCDC_N_RING_DETECT 0x09 #define UCDC_N_SERIAL_STATE 0x20 #define UCDC_N_CALL_STATE_CHANGED 0x28 #define UCDC_N_LINE_STATE_CHANGED 0x29 #define UCDC_N_CONNECTION_SPEED_CHANGE 0x2a uWord wValue; uWord wIndex; uWord wLength; uByte data[16]; } __packed; #define UCDC_NOTIFICATION_LENGTH 8 /* - * Bits set in the SERIAL STATE notifcation (first byte of data) + * Bits set in the SERIAL STATE notification (first byte of data) */ #define UCDC_N_SERIAL_OVERRUN 0x40 #define UCDC_N_SERIAL_PARITY 0x20 #define UCDC_N_SERIAL_FRAMING 0x10 #define UCDC_N_SERIAL_RI 0x08 #define UCDC_N_SERIAL_BREAK 0x04 #define UCDC_N_SERIAL_DSR 0x02 #define UCDC_N_SERIAL_DCD 0x01 /* Serial state bit masks */ #define UCDC_MDM_RXCARRIER 0x01 #define UCDC_MDM_TXCARRIER 0x02 #define UCDC_MDM_BREAK 0x04 #define UCDC_MDM_RING 0x08 #define UCDC_MDM_FRAMING_ERR 0x10 #define UCDC_MDM_PARITY_ERR 0x20 #define UCDC_MDM_OVERRUN_ERR 0x40 /* * Network Control Model, NCM16 + NCM32, protocol definitions */ struct usb_ncm16_hdr { uDWord dwSignature; uWord wHeaderLength; uWord wSequence; uWord wBlockLength; uWord wDptIndex; } __packed; struct usb_ncm16_dp { uWord wFrameIndex; uWord wFrameLength; } __packed; struct usb_ncm16_dpt { uDWord dwSignature; uWord wLength; uWord wNextNdpIndex; struct usb_ncm16_dp dp[0]; } __packed; struct usb_ncm32_hdr { uDWord dwSignature; uWord wHeaderLength; uWord wSequence; uDWord dwBlockLength; uDWord dwDptIndex; } __packed; struct usb_ncm32_dp { uDWord dwFrameIndex; uDWord dwFrameLength; } __packed; struct usb_ncm32_dpt { uDWord dwSignature; uWord wLength; uWord wReserved6; uDWord dwNextNdpIndex; uDWord dwReserved12; struct usb_ncm32_dp dp[0]; } __packed; /* Communications interface class specific descriptors */ #define UCDC_NCM_FUNC_DESC_SUBTYPE 0x1A struct usb_ncm_func_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bcdNcmVersion[2]; uByte bmNetworkCapabilities; #define UCDC_NCM_CAP_FILTER 0x01 #define UCDC_NCM_CAP_MAC_ADDR 0x02 #define UCDC_NCM_CAP_ENCAP 0x04 #define UCDC_NCM_CAP_MAX_DATA 0x08 #define UCDC_NCM_CAP_CRCMODE 0x10 #define UCDC_NCM_CAP_MAX_DGRAM 0x20 } __packed; /* Communications interface specific class request codes */ #define UCDC_NCM_SET_ETHERNET_MULTICAST_FILTERS 0x40 #define UCDC_NCM_SET_ETHERNET_POWER_MGMT_PATTERN_FILTER 0x41 #define UCDC_NCM_GET_ETHERNET_POWER_MGMT_PATTERN_FILTER 0x42 #define UCDC_NCM_SET_ETHERNET_PACKET_FILTER 0x43 #define UCDC_NCM_GET_ETHERNET_STATISTIC 0x44 #define UCDC_NCM_GET_NTB_PARAMETERS 0x80 #define UCDC_NCM_GET_NET_ADDRESS 0x81 #define UCDC_NCM_SET_NET_ADDRESS 0x82 #define UCDC_NCM_GET_NTB_FORMAT 0x83 #define UCDC_NCM_SET_NTB_FORMAT 0x84 #define UCDC_NCM_GET_NTB_INPUT_SIZE 0x85 #define UCDC_NCM_SET_NTB_INPUT_SIZE 0x86 #define UCDC_NCM_GET_MAX_DATAGRAM_SIZE 0x87 #define UCDC_NCM_SET_MAX_DATAGRAM_SIZE 0x88 #define UCDC_NCM_GET_CRC_MODE 0x89 #define UCDC_NCM_SET_CRC_MODE 0x8A struct usb_ncm_parameters { uWord wLength; uWord bmNtbFormatsSupported; #define UCDC_NCM_FORMAT_NTB16 0x0001 #define UCDC_NCM_FORMAT_NTB32 0x0002 uDWord dwNtbInMaxSize; uWord wNdpInDivisor; uWord wNdpInPayloadRemainder; uWord wNdpInAlignment; uWord wReserved14; uDWord dwNtbOutMaxSize; uWord wNdpOutDivisor; uWord wNdpOutPayloadRemainder; uWord wNdpOutAlignment; uWord wNtbOutMaxDatagrams; } __packed; /* Communications interface specific class notification codes */ #define UCDC_NCM_NOTIF_NETWORK_CONNECTION 0x00 #define UCDC_NCM_NOTIF_RESPONSE_AVAILABLE 0x01 #define UCDC_NCM_NOTIF_CONNECTION_SPEED_CHANGE 0x2A #endif /* _USB_CDC_H_ */ Index: head/sys/dev/usb/usb_dev.c =================================================================== --- head/sys/dev/usb/usb_dev.c (revision 298931) +++ head/sys/dev/usb/usb_dev.c (revision 298932) @@ -1,2474 +1,2474 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * usb_dev.c - An abstraction layer for creating devices under /dev/... */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_fifo_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #if USB_HAVE_UGEN #ifdef USB_DEBUG static int usb_fifo_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_fifo_debug, 0, "Debug Level"); #endif #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) #define USB_UCRED struct ucred *ucred, #else #define USB_UCRED #endif /* prototypes */ static int usb_fifo_open(struct usb_cdev_privdata *, struct usb_fifo *, int); static void usb_fifo_close(struct usb_fifo *, int); static void usb_dev_init(void *); static void usb_dev_init_post(void *); static void usb_dev_uninit(void *); static int usb_fifo_uiomove(struct usb_fifo *, void *, int, struct uio *); static void usb_fifo_check_methods(struct usb_fifo_methods *); static struct usb_fifo *usb_fifo_alloc(struct mtx *); static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, uint8_t); static void usb_loc_fill(struct usb_fs_privdata *, struct usb_cdev_privdata *); static void usb_close(void *); static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int); static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); static void usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); static d_open_t usb_open; static d_ioctl_t usb_ioctl; static d_read_t usb_read; static d_write_t usb_write; static d_poll_t usb_poll; static d_kqfilter_t usb_kqfilter; static d_ioctl_t usb_static_ioctl; static usb_fifo_open_t usb_fifo_dummy_open; static usb_fifo_close_t usb_fifo_dummy_close; static usb_fifo_ioctl_t usb_fifo_dummy_ioctl; static usb_fifo_cmd_t usb_fifo_dummy_cmd; /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ struct cdevsw usb_devsw = { .d_version = D_VERSION, .d_open = usb_open, .d_ioctl = usb_ioctl, .d_name = "usbdev", .d_flags = D_TRACKCLOSE, .d_read = usb_read, .d_write = usb_write, .d_poll = usb_poll, .d_kqfilter = usb_kqfilter, }; static struct cdev* usb_dev = NULL; /* character device structure used for /dev/usb */ static struct cdevsw usb_static_devsw = { .d_version = D_VERSION, .d_ioctl = usb_static_ioctl, .d_name = "usb" }; static TAILQ_HEAD(, usb_symlink) usb_sym_head; static struct sx usb_sym_lock; struct mtx usb_ref_lock; /*------------------------------------------------------------------------* * usb_loc_fill * * This is used to fill out a usb_cdev_privdata structure based on the * device's address as contained in usb_fs_privdata. *------------------------------------------------------------------------*/ static void usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) { cpd->bus_index = pd->bus_index; cpd->dev_index = pd->dev_index; cpd->ep_addr = pd->ep_addr; cpd->fifo_index = pd->fifo_index; } /*------------------------------------------------------------------------* * usb_ref_device * * This function is used to atomically refer an USB device by its * device location. If this function returns success the USB device - * will not dissappear until the USB device is unreferenced. + * will not disappear until the USB device is unreferenced. * * Return values: * 0: Success, refcount incremented on the given USB device. * Else: Failure. *------------------------------------------------------------------------*/ static usb_error_t usb_ref_device(struct usb_cdev_privdata *cpd, struct usb_cdev_refdata *crd, int need_uref) { struct usb_fifo **ppf; struct usb_fifo *f; DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref); /* clear all refs */ memset(crd, 0, sizeof(*crd)); mtx_lock(&usb_ref_lock); cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index); if (cpd->bus == NULL) { DPRINTFN(2, "no bus at %u\n", cpd->bus_index); goto error; } cpd->udev = cpd->bus->devices[cpd->dev_index]; if (cpd->udev == NULL) { DPRINTFN(2, "no device at %u\n", cpd->dev_index); goto error; } if (cpd->udev->state == USB_STATE_DETACHED && (need_uref != 2)) { DPRINTFN(2, "device is detached\n"); goto error; } if (need_uref) { DPRINTFN(2, "ref udev - needed\n"); if (cpd->udev->refcount == USB_DEV_REF_MAX) { DPRINTFN(2, "no dev ref\n"); goto error; } cpd->udev->refcount++; mtx_unlock(&usb_ref_lock); /* * We need to grab the enumeration SX-lock before * grabbing the FIFO refs to avoid deadlock at detach! */ crd->do_unlock = usbd_enum_lock(cpd->udev); mtx_lock(&usb_ref_lock); /* * Set "is_uref" after grabbing the default SX lock */ crd->is_uref = 1; } /* check if we are doing an open */ if (cpd->fflags == 0) { /* use zero defaults */ } else { /* check for write */ if (cpd->fflags & FWRITE) { ppf = cpd->udev->fifo; f = ppf[cpd->fifo_index + USB_FIFO_TX]; crd->txfifo = f; crd->is_write = 1; /* ref */ if (f == NULL || f->refcount == USB_FIFO_REF_MAX) goto error; if (f->curr_cpd != cpd) goto error; /* check if USB-FS is active */ if (f->fs_ep_max != 0) { crd->is_usbfs = 1; } } /* check for read */ if (cpd->fflags & FREAD) { ppf = cpd->udev->fifo; f = ppf[cpd->fifo_index + USB_FIFO_RX]; crd->rxfifo = f; crd->is_read = 1; /* ref */ if (f == NULL || f->refcount == USB_FIFO_REF_MAX) goto error; if (f->curr_cpd != cpd) goto error; /* check if USB-FS is active */ if (f->fs_ep_max != 0) { crd->is_usbfs = 1; } } } /* when everything is OK we increment the refcounts */ if (crd->is_write) { DPRINTFN(2, "ref write\n"); crd->txfifo->refcount++; } if (crd->is_read) { DPRINTFN(2, "ref read\n"); crd->rxfifo->refcount++; } mtx_unlock(&usb_ref_lock); return (0); error: if (crd->do_unlock) usbd_enum_unlock(cpd->udev); if (crd->is_uref) { if (--(cpd->udev->refcount) == 0) cv_broadcast(&cpd->udev->ref_cv); } mtx_unlock(&usb_ref_lock); DPRINTFN(2, "fail\n"); /* clear all refs */ memset(crd, 0, sizeof(*crd)); return (USB_ERR_INVAL); } /*------------------------------------------------------------------------* * usb_usb_ref_device * * This function is used to upgrade an USB reference to include the * USB device reference on a USB location. * * Return values: * 0: Success, refcount incremented on the given USB device. * Else: Failure. *------------------------------------------------------------------------*/ static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *cpd, struct usb_cdev_refdata *crd) { /* * Check if we already got an USB reference on this location: */ if (crd->is_uref) return (0); /* success */ /* * To avoid deadlock at detach we need to drop the FIFO ref * and re-acquire a new ref! */ usb_unref_device(cpd, crd); return (usb_ref_device(cpd, crd, 1 /* need uref */)); } /*------------------------------------------------------------------------* * usb_unref_device * * This function will release the reference count by one unit for the * given USB device. *------------------------------------------------------------------------*/ static void usb_unref_device(struct usb_cdev_privdata *cpd, struct usb_cdev_refdata *crd) { DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref); if (crd->do_unlock) usbd_enum_unlock(cpd->udev); mtx_lock(&usb_ref_lock); if (crd->is_read) { if (--(crd->rxfifo->refcount) == 0) { cv_signal(&crd->rxfifo->cv_drain); } crd->is_read = 0; } if (crd->is_write) { if (--(crd->txfifo->refcount) == 0) { cv_signal(&crd->txfifo->cv_drain); } crd->is_write = 0; } if (crd->is_uref) { crd->is_uref = 0; if (--(cpd->udev->refcount) == 0) cv_broadcast(&cpd->udev->ref_cv); } mtx_unlock(&usb_ref_lock); } static struct usb_fifo * usb_fifo_alloc(struct mtx *mtx) { struct usb_fifo *f; f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); if (f != NULL) { cv_init(&f->cv_io, "FIFO-IO"); cv_init(&f->cv_drain, "FIFO-DRAIN"); f->priv_mtx = mtx; f->refcount = 1; knlist_init_mtx(&f->selinfo.si_note, mtx); } return (f); } /*------------------------------------------------------------------------* * usb_fifo_create *------------------------------------------------------------------------*/ static int usb_fifo_create(struct usb_cdev_privdata *cpd, struct usb_cdev_refdata *crd) { struct usb_device *udev = cpd->udev; struct usb_fifo *f; struct usb_endpoint *ep; uint8_t n; uint8_t is_tx; uint8_t is_rx; uint8_t no_null; uint8_t is_busy; int e = cpd->ep_addr; is_tx = (cpd->fflags & FWRITE) ? 1 : 0; is_rx = (cpd->fflags & FREAD) ? 1 : 0; no_null = 1; is_busy = 0; /* Preallocated FIFO */ if (e < 0) { DPRINTFN(5, "Preallocated FIFO\n"); if (is_tx) { f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; if (f == NULL) return (EINVAL); crd->txfifo = f; } if (is_rx) { f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; if (f == NULL) return (EINVAL); crd->rxfifo = f; } return (0); } KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e)); /* search for a free FIFO slot */ DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e); for (n = 0;; n += 2) { if (n == USB_FIFO_MAX) { if (no_null) { no_null = 0; n = 0; } else { /* end of FIFOs reached */ DPRINTFN(5, "out of FIFOs\n"); return (ENOMEM); } } /* Check for TX FIFO */ if (is_tx) { f = udev->fifo[n + USB_FIFO_TX]; if (f != NULL) { if (f->dev_ep_index != e) { /* wrong endpoint index */ continue; } if (f->curr_cpd != NULL) { /* FIFO is opened */ is_busy = 1; continue; } } else if (no_null) { continue; } } /* Check for RX FIFO */ if (is_rx) { f = udev->fifo[n + USB_FIFO_RX]; if (f != NULL) { if (f->dev_ep_index != e) { /* wrong endpoint index */ continue; } if (f->curr_cpd != NULL) { /* FIFO is opened */ is_busy = 1; continue; } } else if (no_null) { continue; } } break; } if (no_null == 0) { if (e >= (USB_EP_MAX / 2)) { /* we don't create any endpoints in this range */ DPRINTFN(5, "ep out of range\n"); return (is_busy ? EBUSY : EINVAL); } } if ((e != 0) && is_busy) { /* * Only the default control endpoint is allowed to be * opened multiple times! */ DPRINTFN(5, "busy\n"); return (EBUSY); } /* Check TX FIFO */ if (is_tx && (udev->fifo[n + USB_FIFO_TX] == NULL)) { ep = usb_dev_get_ep(udev, e, USB_FIFO_TX); DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX); if (ep == NULL) { DPRINTFN(5, "dev_get_endpoint returned NULL\n"); return (EINVAL); } f = usb_fifo_alloc(&udev->device_mtx); if (f == NULL) { DPRINTFN(5, "could not alloc tx fifo\n"); return (ENOMEM); } /* update some fields */ f->fifo_index = n + USB_FIFO_TX; f->dev_ep_index = e; f->priv_sc0 = ep; f->methods = &usb_ugen_methods; f->iface_index = ep->iface_index; f->udev = udev; mtx_lock(&usb_ref_lock); udev->fifo[n + USB_FIFO_TX] = f; mtx_unlock(&usb_ref_lock); } /* Check RX FIFO */ if (is_rx && (udev->fifo[n + USB_FIFO_RX] == NULL)) { ep = usb_dev_get_ep(udev, e, USB_FIFO_RX); DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX); if (ep == NULL) { DPRINTFN(5, "dev_get_endpoint returned NULL\n"); return (EINVAL); } f = usb_fifo_alloc(&udev->device_mtx); if (f == NULL) { DPRINTFN(5, "could not alloc rx fifo\n"); return (ENOMEM); } /* update some fields */ f->fifo_index = n + USB_FIFO_RX; f->dev_ep_index = e; f->priv_sc0 = ep; f->methods = &usb_ugen_methods; f->iface_index = ep->iface_index; f->udev = udev; mtx_lock(&usb_ref_lock); udev->fifo[n + USB_FIFO_RX] = f; mtx_unlock(&usb_ref_lock); } if (is_tx) { crd->txfifo = udev->fifo[n + USB_FIFO_TX]; } if (is_rx) { crd->rxfifo = udev->fifo[n + USB_FIFO_RX]; } /* fill out fifo index */ DPRINTFN(5, "fifo index = %d\n", n); cpd->fifo_index = n; /* complete */ return (0); } void usb_fifo_free(struct usb_fifo *f) { uint8_t n; if (f == NULL) { /* be NULL safe */ return; } /* destroy symlink devices, if any */ for (n = 0; n != 2; n++) { if (f->symlink[n]) { usb_free_symlink(f->symlink[n]); f->symlink[n] = NULL; } } mtx_lock(&usb_ref_lock); /* delink ourselves to stop calls from userland */ if ((f->fifo_index < USB_FIFO_MAX) && (f->udev != NULL) && (f->udev->fifo[f->fifo_index] == f)) { f->udev->fifo[f->fifo_index] = NULL; } else { DPRINTFN(0, "USB FIFO %p has not been linked\n", f); } /* decrease refcount */ f->refcount--; /* need to wait until all callers have exited */ while (f->refcount != 0) { mtx_unlock(&usb_ref_lock); /* avoid LOR */ mtx_lock(f->priv_mtx); /* prevent write flush, if any */ f->flag_iserror = 1; /* get I/O thread out of any sleep state */ if (f->flag_sleeping) { f->flag_sleeping = 0; cv_broadcast(&f->cv_io); } mtx_unlock(f->priv_mtx); mtx_lock(&usb_ref_lock); /* * Check if the "f->refcount" variable reached zero * during the unlocked time before entering wait: */ if (f->refcount == 0) break; /* wait for sync */ cv_wait(&f->cv_drain, &usb_ref_lock); } mtx_unlock(&usb_ref_lock); /* take care of closing the device here, if any */ usb_fifo_close(f, 0); cv_destroy(&f->cv_io); cv_destroy(&f->cv_drain); knlist_clear(&f->selinfo.si_note, 0); seldrain(&f->selinfo); knlist_destroy(&f->selinfo.si_note); free(f, M_USBDEV); } static struct usb_endpoint * usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir) { struct usb_endpoint *ep; uint8_t ep_dir; if (ep_index == 0) { ep = &udev->ctrl_ep; } else { if (dir == USB_FIFO_RX) { if (udev->flags.usb_mode == USB_MODE_HOST) { ep_dir = UE_DIR_IN; } else { ep_dir = UE_DIR_OUT; } } else { if (udev->flags.usb_mode == USB_MODE_HOST) { ep_dir = UE_DIR_OUT; } else { ep_dir = UE_DIR_IN; } } ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir); } if (ep == NULL) { /* if the endpoint does not exist then return */ return (NULL); } if (ep->edesc == NULL) { /* invalid endpoint */ return (NULL); } return (ep); /* success */ } /*------------------------------------------------------------------------* * usb_fifo_open * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usb_fifo_open(struct usb_cdev_privdata *cpd, struct usb_fifo *f, int fflags) { int err; if (f == NULL) { /* no FIFO there */ DPRINTFN(2, "no FIFO\n"); return (ENXIO); } /* remove FWRITE and FREAD flags */ fflags &= ~(FWRITE | FREAD); /* set correct file flags */ if ((f->fifo_index & 1) == USB_FIFO_TX) { fflags |= FWRITE; } else { fflags |= FREAD; } /* check if we are already opened */ /* we don't need any locks when checking this variable */ if (f->curr_cpd != NULL) { err = EBUSY; goto done; } /* reset short flag before open */ f->flag_short = 0; /* call open method */ err = (f->methods->f_open) (f, fflags); if (err) { goto done; } mtx_lock(f->priv_mtx); /* reset sleep flag */ f->flag_sleeping = 0; /* reset error flag */ f->flag_iserror = 0; /* reset complete flag */ f->flag_iscomplete = 0; /* reset select flag */ f->flag_isselect = 0; /* reset flushing flag */ f->flag_flushing = 0; /* reset ASYNC proc flag */ f->async_p = NULL; mtx_lock(&usb_ref_lock); /* flag the fifo as opened to prevent others */ f->curr_cpd = cpd; mtx_unlock(&usb_ref_lock); /* reset queue */ usb_fifo_reset(f); mtx_unlock(f->priv_mtx); done: return (err); } /*------------------------------------------------------------------------* * usb_fifo_reset *------------------------------------------------------------------------*/ void usb_fifo_reset(struct usb_fifo *f) { struct usb_mbuf *m; if (f == NULL) { return; } while (1) { USB_IF_DEQUEUE(&f->used_q, m); if (m) { USB_IF_ENQUEUE(&f->free_q, m); } else { break; } } /* reset have fragment flag */ f->flag_have_fragment = 0; } /*------------------------------------------------------------------------* * usb_fifo_close *------------------------------------------------------------------------*/ static void usb_fifo_close(struct usb_fifo *f, int fflags) { int err; /* check if we are not opened */ if (f->curr_cpd == NULL) { /* nothing to do - already closed */ return; } mtx_lock(f->priv_mtx); /* clear current cdev private data pointer */ mtx_lock(&usb_ref_lock); f->curr_cpd = NULL; mtx_unlock(&usb_ref_lock); /* check if we are watched by kevent */ KNOTE_LOCKED(&f->selinfo.si_note, 0); /* check if we are selected */ if (f->flag_isselect) { selwakeup(&f->selinfo); f->flag_isselect = 0; } /* check if a thread wants SIGIO */ if (f->async_p != NULL) { PROC_LOCK(f->async_p); kern_psignal(f->async_p, SIGIO); PROC_UNLOCK(f->async_p); f->async_p = NULL; } /* remove FWRITE and FREAD flags */ fflags &= ~(FWRITE | FREAD); /* flush written data, if any */ if ((f->fifo_index & 1) == USB_FIFO_TX) { if (!f->flag_iserror) { /* set flushing flag */ f->flag_flushing = 1; /* get the last packet in */ if (f->flag_have_fragment) { struct usb_mbuf *m; f->flag_have_fragment = 0; USB_IF_DEQUEUE(&f->free_q, m); if (m) { USB_IF_ENQUEUE(&f->used_q, m); } } /* start write transfer, if not already started */ (f->methods->f_start_write) (f); /* check if flushed already */ while (f->flag_flushing && (!f->flag_iserror)) { /* wait until all data has been written */ f->flag_sleeping = 1; err = cv_timedwait_sig(&f->cv_io, f->priv_mtx, USB_MS_TO_TICKS(USB_DEFAULT_TIMEOUT)); if (err) { DPRINTF("signal received\n"); break; } } } fflags |= FWRITE; /* stop write transfer, if not already stopped */ (f->methods->f_stop_write) (f); } else { fflags |= FREAD; /* stop write transfer, if not already stopped */ (f->methods->f_stop_read) (f); } /* check if we are sleeping */ if (f->flag_sleeping) { DPRINTFN(2, "Sleeping at close!\n"); } mtx_unlock(f->priv_mtx); /* call close method */ (f->methods->f_close) (f, fflags); DPRINTF("closed\n"); } /*------------------------------------------------------------------------* * usb_open - cdev callback *------------------------------------------------------------------------*/ static int usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td) { struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; struct usb_cdev_refdata refs; struct usb_cdev_privdata *cpd; int err, ep; DPRINTFN(2, "%s fflags=0x%08x\n", devtoname(dev), fflags); KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); if (((fflags & FREAD) && !(pd->mode & FREAD)) || ((fflags & FWRITE) && !(pd->mode & FWRITE))) { DPRINTFN(2, "access mode not supported\n"); return (EPERM); } cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); ep = cpd->ep_addr = pd->ep_addr; usb_loc_fill(pd, cpd); err = usb_ref_device(cpd, &refs, 1); if (err) { DPRINTFN(2, "cannot ref device\n"); free(cpd, M_USBDEV); return (ENXIO); } cpd->fflags = fflags; /* access mode for open lifetime */ /* create FIFOs, if any */ err = usb_fifo_create(cpd, &refs); /* check for error */ if (err) { DPRINTFN(2, "cannot create fifo\n"); usb_unref_device(cpd, &refs); free(cpd, M_USBDEV); return (err); } if (fflags & FREAD) { err = usb_fifo_open(cpd, refs.rxfifo, fflags); if (err) { DPRINTFN(2, "read open failed\n"); usb_unref_device(cpd, &refs); free(cpd, M_USBDEV); return (err); } } if (fflags & FWRITE) { err = usb_fifo_open(cpd, refs.txfifo, fflags); if (err) { DPRINTFN(2, "write open failed\n"); if (fflags & FREAD) { usb_fifo_close(refs.rxfifo, fflags); } usb_unref_device(cpd, &refs); free(cpd, M_USBDEV); return (err); } } usb_unref_device(cpd, &refs); devfs_set_cdevpriv(cpd, usb_close); return (0); } /*------------------------------------------------------------------------* * usb_close - cdev callback *------------------------------------------------------------------------*/ static void usb_close(void *arg) { struct usb_cdev_refdata refs; struct usb_cdev_privdata *cpd = arg; int err; DPRINTFN(2, "cpd=%p\n", cpd); err = usb_ref_device(cpd, &refs, 2 /* uref and allow detached state */); if (err) { DPRINTFN(2, "Cannot grab USB reference when " "closing USB file handle\n"); goto done; } if (cpd->fflags & FREAD) { usb_fifo_close(refs.rxfifo, cpd->fflags); } if (cpd->fflags & FWRITE) { usb_fifo_close(refs.txfifo, cpd->fflags); } usb_unref_device(cpd, &refs); done: free(cpd, M_USBDEV); } static void usb_dev_init(void *arg) { mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF); sx_init(&usb_sym_lock, "USB sym mutex"); TAILQ_INIT(&usb_sym_head); /* check the UGEN methods */ usb_fifo_check_methods(&usb_ugen_methods); } SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL); static void usb_dev_init_post(void *arg) { /* * Create /dev/usb - this is needed for usbconfig(8), which * needs a well-known device name to access. */ usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR, 0644, USB_DEVICE_NAME); if (usb_dev == NULL) { DPRINTFN(0, "Could not create usb bus device\n"); } } SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL); static void usb_dev_uninit(void *arg) { if (usb_dev != NULL) { destroy_dev(usb_dev); usb_dev = NULL; } mtx_destroy(&usb_ref_lock); sx_destroy(&usb_sym_lock); } SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL); static int usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, struct thread *td) { int error = 0; switch (cmd) { case FIODTYPE: *(int *)addr = 0; /* character device */ break; case FIONBIO: /* handled by upper FS layer */ break; case FIOASYNC: if (*(int *)addr) { if (f->async_p != NULL) { error = EBUSY; break; } f->async_p = USB_TD_GET_PROC(td); } else { f->async_p = NULL; } break; /* XXX this is not the most general solution */ case TIOCSPGRP: if (f->async_p == NULL) { error = EINVAL; break; } if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { error = EPERM; break; } break; default: return (ENOIOCTL); } DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); return (error); } /*------------------------------------------------------------------------* * usb_ioctl - cdev callback *------------------------------------------------------------------------*/ static int usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) { struct usb_cdev_refdata refs; struct usb_cdev_privdata* cpd; struct usb_fifo *f; int fflags; int err; DPRINTFN(2, "cmd=0x%lx\n", cmd); err = devfs_get_cdevpriv((void **)&cpd); if (err != 0) return (err); /* * Performance optimisation: We try to check for IOCTL's that * don't need the USB reference first. Then we grab the USB * reference if we need it! */ err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); if (err) return (ENXIO); fflags = cpd->fflags; f = NULL; /* set default value */ err = ENOIOCTL; /* set default value */ if (fflags & FWRITE) { f = refs.txfifo; err = usb_ioctl_f_sub(f, cmd, addr, td); } if (fflags & FREAD) { f = refs.rxfifo; err = usb_ioctl_f_sub(f, cmd, addr, td); } KASSERT(f != NULL, ("fifo not found")); if (err != ENOIOCTL) goto done; err = (f->methods->f_ioctl) (f, cmd, addr, fflags); DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); if (err != ENOIOCTL) goto done; if (usb_usb_ref_device(cpd, &refs)) { /* we lost the reference */ return (ENXIO); } err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); if (err == ENOIOCTL) err = ENOTTY; if (err) goto done; /* Wait for re-enumeration, if any */ while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) { usb_unref_device(cpd, &refs); usb_pause_mtx(NULL, hz / 128); while (usb_ref_device(cpd, &refs, 1 /* need uref */)) { if (usb_ref_device(cpd, &refs, 0)) { /* device no longer exists */ return (ENXIO); } usb_unref_device(cpd, &refs); usb_pause_mtx(NULL, hz / 128); } } done: usb_unref_device(cpd, &refs); return (err); } static void usb_filter_detach(struct knote *kn) { struct usb_fifo *f = kn->kn_hook; knlist_remove(&f->selinfo.si_note, kn, 0); } static int usb_filter_write(struct knote *kn, long hint) { struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; DPRINTFN(2, "\n"); f = kn->kn_hook; mtx_assert(f->priv_mtx, MA_OWNED); cpd = f->curr_cpd; if (cpd == NULL) { m = (void *)1; } else if (f->fs_ep_max == 0) { if (f->flag_iserror) { /* we got an error */ m = (void *)1; } else { if (f->queue_data == NULL) { /* * start write transfer, if not * already started */ (f->methods->f_start_write) (f); } /* check if any packets are available */ USB_IF_POLL(&f->free_q, m); } } else { if (f->flag_iscomplete) { m = (void *)1; } else { m = NULL; } } return (m ? 1 : 0); } static int usb_filter_read(struct knote *kn, long hint) { struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; DPRINTFN(2, "\n"); f = kn->kn_hook; mtx_assert(f->priv_mtx, MA_OWNED); cpd = f->curr_cpd; if (cpd == NULL) { m = (void *)1; } else if (f->fs_ep_max == 0) { if (f->flag_iserror) { /* we have an error */ m = (void *)1; } else { if (f->queue_data == NULL) { /* * start read transfer, if not * already started */ (f->methods->f_start_read) (f); } /* check if any packets are available */ USB_IF_POLL(&f->used_q, m); /* start reading data, if any */ if (m == NULL) (f->methods->f_start_read) (f); } } else { if (f->flag_iscomplete) { m = (void *)1; } else { m = NULL; } } return (m ? 1 : 0); } static struct filterops usb_filtops_write = { .f_isfd = 1, .f_detach = usb_filter_detach, .f_event = usb_filter_write, }; static struct filterops usb_filtops_read = { .f_isfd = 1, .f_detach = usb_filter_detach, .f_event = usb_filter_read, }; /* ARGSUSED */ static int usb_kqfilter(struct cdev* dev, struct knote *kn) { struct usb_cdev_refdata refs; struct usb_cdev_privdata* cpd; struct usb_fifo *f; int fflags; int err = EINVAL; DPRINTFN(2, "\n"); if (devfs_get_cdevpriv((void **)&cpd) != 0 || usb_ref_device(cpd, &refs, 0) != 0) return (ENXIO); fflags = cpd->fflags; /* Figure out who needs service */ switch (kn->kn_filter) { case EVFILT_WRITE: if (fflags & FWRITE) { f = refs.txfifo; kn->kn_fop = &usb_filtops_write; err = 0; } break; case EVFILT_READ: if (fflags & FREAD) { f = refs.rxfifo; kn->kn_fop = &usb_filtops_read; err = 0; } break; default: err = EOPNOTSUPP; break; } if (err == 0) { kn->kn_hook = f; mtx_lock(f->priv_mtx); knlist_add(&f->selinfo.si_note, kn, 1); mtx_unlock(f->priv_mtx); } usb_unref_device(cpd, &refs); return (err); } /* ARGSUSED */ static int usb_poll(struct cdev* dev, int events, struct thread* td) { struct usb_cdev_refdata refs; struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; int fflags, revents; if (devfs_get_cdevpriv((void **)&cpd) != 0 || usb_ref_device(cpd, &refs, 0) != 0) return (events & (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); fflags = cpd->fflags; /* Figure out who needs service */ revents = 0; if ((events & (POLLOUT | POLLWRNORM)) && (fflags & FWRITE)) { f = refs.txfifo; mtx_lock(f->priv_mtx); if (!refs.is_usbfs) { if (f->flag_iserror) { /* we got an error */ m = (void *)1; } else { if (f->queue_data == NULL) { /* * start write transfer, if not * already started */ (f->methods->f_start_write) (f); } /* check if any packets are available */ USB_IF_POLL(&f->free_q, m); } } else { if (f->flag_iscomplete) { m = (void *)1; } else { m = NULL; } } if (m) { revents |= events & (POLLOUT | POLLWRNORM); } else { f->flag_isselect = 1; selrecord(td, &f->selinfo); } mtx_unlock(f->priv_mtx); } if ((events & (POLLIN | POLLRDNORM)) && (fflags & FREAD)) { f = refs.rxfifo; mtx_lock(f->priv_mtx); if (!refs.is_usbfs) { if (f->flag_iserror) { /* we have an error */ m = (void *)1; } else { if (f->queue_data == NULL) { /* * start read transfer, if not * already started */ (f->methods->f_start_read) (f); } /* check if any packets are available */ USB_IF_POLL(&f->used_q, m); } } else { if (f->flag_iscomplete) { m = (void *)1; } else { m = NULL; } } if (m) { revents |= events & (POLLIN | POLLRDNORM); } else { f->flag_isselect = 1; selrecord(td, &f->selinfo); if (!refs.is_usbfs) { /* start reading data */ (f->methods->f_start_read) (f); } } mtx_unlock(f->priv_mtx); } usb_unref_device(cpd, &refs); return (revents); } static int usb_read(struct cdev *dev, struct uio *uio, int ioflag) { struct usb_cdev_refdata refs; struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; int fflags; int resid; int io_len; int err; uint8_t tr_data = 0; err = devfs_get_cdevpriv((void **)&cpd); if (err != 0) return (err); err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); if (err) return (ENXIO); fflags = cpd->fflags; f = refs.rxfifo; if (f == NULL) { /* should not happen */ usb_unref_device(cpd, &refs); return (EPERM); } resid = uio->uio_resid; mtx_lock(f->priv_mtx); /* check for permanent read error */ if (f->flag_iserror) { err = EIO; goto done; } /* check if USB-FS interface is active */ if (refs.is_usbfs) { /* * The queue is used for events that should be * retrieved using the "USB_FS_COMPLETE" ioctl. */ err = EINVAL; goto done; } while (uio->uio_resid > 0) { USB_IF_DEQUEUE(&f->used_q, m); if (m == NULL) { /* start read transfer, if not already started */ (f->methods->f_start_read) (f); if (ioflag & IO_NDELAY) { if (tr_data) { /* return length before error */ break; } err = EWOULDBLOCK; break; } DPRINTF("sleeping\n"); err = usb_fifo_wait(f); if (err) { break; } continue; } if (f->methods->f_filter_read) { /* * Sometimes it is convenient to process data at the * expense of a userland process instead of a kernel * process. */ (f->methods->f_filter_read) (f, m); } tr_data = 1; io_len = MIN(m->cur_data_len, uio->uio_resid); DPRINTFN(2, "transfer %d bytes from %p\n", io_len, m->cur_data_ptr); err = usb_fifo_uiomove(f, m->cur_data_ptr, io_len, uio); m->cur_data_len -= io_len; m->cur_data_ptr += io_len; if (m->cur_data_len == 0) { uint8_t last_packet; last_packet = m->last_packet; USB_IF_ENQUEUE(&f->free_q, m); if (last_packet) { /* keep framing */ break; } } else { USB_IF_PREPEND(&f->used_q, m); } if (err) { break; } } done: mtx_unlock(f->priv_mtx); usb_unref_device(cpd, &refs); return (err); } static int usb_write(struct cdev *dev, struct uio *uio, int ioflag) { struct usb_cdev_refdata refs; struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; uint8_t *pdata; int fflags; int resid; int io_len; int err; uint8_t tr_data = 0; DPRINTFN(2, "\n"); err = devfs_get_cdevpriv((void **)&cpd); if (err != 0) return (err); err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); if (err) return (ENXIO); fflags = cpd->fflags; f = refs.txfifo; if (f == NULL) { /* should not happen */ usb_unref_device(cpd, &refs); return (EPERM); } resid = uio->uio_resid; mtx_lock(f->priv_mtx); /* check for permanent write error */ if (f->flag_iserror) { err = EIO; goto done; } /* check if USB-FS interface is active */ if (refs.is_usbfs) { /* * The queue is used for events that should be * retrieved using the "USB_FS_COMPLETE" ioctl. */ err = EINVAL; goto done; } if (f->queue_data == NULL) { /* start write transfer, if not already started */ (f->methods->f_start_write) (f); } /* we allow writing zero length data */ do { USB_IF_DEQUEUE(&f->free_q, m); if (m == NULL) { if (ioflag & IO_NDELAY) { if (tr_data) { /* return length before error */ break; } err = EWOULDBLOCK; break; } DPRINTF("sleeping\n"); err = usb_fifo_wait(f); if (err) { break; } continue; } tr_data = 1; if (f->flag_have_fragment == 0) { USB_MBUF_RESET(m); io_len = m->cur_data_len; pdata = m->cur_data_ptr; if (io_len > uio->uio_resid) io_len = uio->uio_resid; m->cur_data_len = io_len; } else { io_len = m->max_data_len - m->cur_data_len; pdata = m->cur_data_ptr + m->cur_data_len; if (io_len > uio->uio_resid) io_len = uio->uio_resid; m->cur_data_len += io_len; } DPRINTFN(2, "transfer %d bytes to %p\n", io_len, pdata); err = usb_fifo_uiomove(f, pdata, io_len, uio); if (err) { f->flag_have_fragment = 0; USB_IF_ENQUEUE(&f->free_q, m); break; } /* check if the buffer is ready to be transmitted */ if ((f->flag_write_defrag == 0) || (m->cur_data_len == m->max_data_len)) { f->flag_have_fragment = 0; /* * Check for write filter: * * Sometimes it is convenient to process data * at the expense of a userland process * instead of a kernel process. */ if (f->methods->f_filter_write) { (f->methods->f_filter_write) (f, m); } /* Put USB mbuf in the used queue */ USB_IF_ENQUEUE(&f->used_q, m); /* Start writing data, if not already started */ (f->methods->f_start_write) (f); } else { /* Wait for more data or close */ f->flag_have_fragment = 1; USB_IF_PREPEND(&f->free_q, m); } } while (uio->uio_resid > 0); done: mtx_unlock(f->priv_mtx); usb_unref_device(cpd, &refs); return (err); } int usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { union { struct usb_read_dir *urd; void* data; } u; int err; u.data = data; switch (cmd) { case USB_READ_DIR: err = usb_read_symlink(u.urd->urd_data, u.urd->urd_startentry, u.urd->urd_maxlen); break; case USB_DEV_QUIRK_GET: case USB_QUIRK_NAME_GET: case USB_DEV_QUIRK_ADD: case USB_DEV_QUIRK_REMOVE: err = usb_quirk_ioctl_p(cmd, data, fflag, td); break; case USB_GET_TEMPLATE: *(int *)data = usb_template; err = 0; break; case USB_SET_TEMPLATE: err = priv_check(curthread, PRIV_DRIVER); if (err) break; usb_template = *(int *)data; break; default: err = ENOTTY; break; } return (err); } static int usb_fifo_uiomove(struct usb_fifo *f, void *cp, int n, struct uio *uio) { int error; mtx_unlock(f->priv_mtx); /* * "uiomove()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things: */ error = uiomove(cp, n, uio); mtx_lock(f->priv_mtx); return (error); } int usb_fifo_wait(struct usb_fifo *f) { int err; mtx_assert(f->priv_mtx, MA_OWNED); if (f->flag_iserror) { /* we are gone */ return (EIO); } f->flag_sleeping = 1; err = cv_wait_sig(&f->cv_io, f->priv_mtx); if (f->flag_iserror) { /* we are gone */ err = EIO; } return (err); } void usb_fifo_signal(struct usb_fifo *f) { if (f->flag_sleeping) { f->flag_sleeping = 0; cv_broadcast(&f->cv_io); } } void usb_fifo_wakeup(struct usb_fifo *f) { usb_fifo_signal(f); KNOTE_LOCKED(&f->selinfo.si_note, 0); if (f->flag_isselect) { selwakeup(&f->selinfo); f->flag_isselect = 0; } if (f->async_p != NULL) { PROC_LOCK(f->async_p); kern_psignal(f->async_p, SIGIO); PROC_UNLOCK(f->async_p); } } static int usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags) { return (0); } static void usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags) { return; } static int usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { return (ENOIOCTL); } static void usb_fifo_dummy_cmd(struct usb_fifo *fifo) { fifo->flag_flushing = 0; /* not flushing */ } static void usb_fifo_check_methods(struct usb_fifo_methods *pm) { /* check that all callback functions are OK */ if (pm->f_open == NULL) pm->f_open = &usb_fifo_dummy_open; if (pm->f_close == NULL) pm->f_close = &usb_fifo_dummy_close; if (pm->f_ioctl == NULL) pm->f_ioctl = &usb_fifo_dummy_ioctl; if (pm->f_ioctl_post == NULL) pm->f_ioctl_post = &usb_fifo_dummy_ioctl; if (pm->f_start_read == NULL) pm->f_start_read = &usb_fifo_dummy_cmd; if (pm->f_stop_read == NULL) pm->f_stop_read = &usb_fifo_dummy_cmd; if (pm->f_start_write == NULL) pm->f_start_write = &usb_fifo_dummy_cmd; if (pm->f_stop_write == NULL) pm->f_stop_write = &usb_fifo_dummy_cmd; } /*------------------------------------------------------------------------* * usb_fifo_attach * * The following function will create a duplex FIFO. * * Return values: * 0: Success. * Else: Failure. *------------------------------------------------------------------------*/ int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode) { struct usb_fifo *f_tx; struct usb_fifo *f_rx; char devname[32]; uint8_t n; f_sc->fp[USB_FIFO_TX] = NULL; f_sc->fp[USB_FIFO_RX] = NULL; if (pm == NULL) return (EINVAL); /* check the methods */ usb_fifo_check_methods(pm); if (priv_mtx == NULL) priv_mtx = &Giant; /* search for a free FIFO slot */ for (n = 0;; n += 2) { if (n == USB_FIFO_MAX) { /* end of FIFOs reached */ return (ENOMEM); } /* Check for TX FIFO */ if (udev->fifo[n + USB_FIFO_TX] != NULL) { continue; } /* Check for RX FIFO */ if (udev->fifo[n + USB_FIFO_RX] != NULL) { continue; } break; } f_tx = usb_fifo_alloc(priv_mtx); f_rx = usb_fifo_alloc(priv_mtx); if ((f_tx == NULL) || (f_rx == NULL)) { usb_fifo_free(f_tx); usb_fifo_free(f_rx); return (ENOMEM); } /* initialise FIFO structures */ f_tx->fifo_index = n + USB_FIFO_TX; f_tx->dev_ep_index = -1; f_tx->priv_sc0 = priv_sc; f_tx->methods = pm; f_tx->iface_index = iface_index; f_tx->udev = udev; f_rx->fifo_index = n + USB_FIFO_RX; f_rx->dev_ep_index = -1; f_rx->priv_sc0 = priv_sc; f_rx->methods = pm; f_rx->iface_index = iface_index; f_rx->udev = udev; f_sc->fp[USB_FIFO_TX] = f_tx; f_sc->fp[USB_FIFO_RX] = f_rx; mtx_lock(&usb_ref_lock); udev->fifo[f_tx->fifo_index] = f_tx; udev->fifo[f_rx->fifo_index] = f_rx; mtx_unlock(&usb_ref_lock); for (n = 0; n != 4; n++) { if (pm->basename[n] == NULL) { continue; } if (subunit < 0) { if (snprintf(devname, sizeof(devname), "%s%u%s", pm->basename[n], unit, pm->postfix[n] ? pm->postfix[n] : "")) { /* ignore */ } } else { if (snprintf(devname, sizeof(devname), "%s%u.%d%s", pm->basename[n], unit, subunit, pm->postfix[n] ? pm->postfix[n] : "")) { /* ignore */ } } /* * Distribute the symbolic links into two FIFO structures: */ if (n & 1) { f_rx->symlink[n / 2] = usb_alloc_symlink(devname); } else { f_tx->symlink[n / 2] = usb_alloc_symlink(devname); } /* Create the device */ f_sc->dev = usb_make_dev(udev, devname, -1, f_tx->fifo_index & f_rx->fifo_index, FREAD|FWRITE, uid, gid, mode); } DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); return (0); } /*------------------------------------------------------------------------* * usb_fifo_alloc_buffer * * Return values: * 0: Success * Else failure *------------------------------------------------------------------------*/ int usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, uint16_t nbuf) { usb_fifo_free_buffer(f); /* allocate an endpoint */ f->free_q.ifq_maxlen = nbuf; f->used_q.ifq_maxlen = nbuf; f->queue_data = usb_alloc_mbufs( M_USBDEV, &f->free_q, bufsize, nbuf); if ((f->queue_data == NULL) && bufsize && nbuf) { return (ENOMEM); } return (0); /* success */ } /*------------------------------------------------------------------------* * usb_fifo_free_buffer * * This function will free the buffers associated with a FIFO. This * function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usb_fifo_free_buffer(struct usb_fifo *f) { if (f->queue_data) { /* free old buffer */ free(f->queue_data, M_USBDEV); f->queue_data = NULL; } /* reset queues */ memset(&f->free_q, 0, sizeof(f->free_q)); memset(&f->used_q, 0, sizeof(f->used_q)); } void usb_fifo_detach(struct usb_fifo_sc *f_sc) { if (f_sc == NULL) { return; } usb_fifo_free(f_sc->fp[USB_FIFO_TX]); usb_fifo_free(f_sc->fp[USB_FIFO_RX]); f_sc->fp[USB_FIFO_TX] = NULL; f_sc->fp[USB_FIFO_RX] = NULL; usb_destroy_dev(f_sc->dev); f_sc->dev = NULL; DPRINTFN(2, "detached %p\n", f_sc); } usb_size_t usb_fifo_put_bytes_max(struct usb_fifo *f) { struct usb_mbuf *m; usb_size_t len; USB_IF_POLL(&f->free_q, m); if (m) { len = m->max_data_len; } else { len = 0; } return (len); } /*------------------------------------------------------------------------* * usb_fifo_put_data * * what: * 0 - normal operation * 1 - set last packet flag to enforce framing *------------------------------------------------------------------------*/ void usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what) { struct usb_mbuf *m; usb_frlength_t io_len; while (len || (what == 1)) { USB_IF_DEQUEUE(&f->free_q, m); if (m) { USB_MBUF_RESET(m); io_len = MIN(len, m->cur_data_len); usbd_copy_out(pc, offset, m->cur_data_ptr, io_len); m->cur_data_len = io_len; offset += io_len; len -= io_len; if ((len == 0) && (what == 1)) { m->last_packet = 1; } USB_IF_ENQUEUE(&f->used_q, m); usb_fifo_wakeup(f); if ((len == 0) || (what == 1)) { break; } } else { break; } } } void usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, usb_size_t len, uint8_t what) { struct usb_mbuf *m; usb_size_t io_len; while (len || (what == 1)) { USB_IF_DEQUEUE(&f->free_q, m); if (m) { USB_MBUF_RESET(m); io_len = MIN(len, m->cur_data_len); memcpy(m->cur_data_ptr, ptr, io_len); m->cur_data_len = io_len; ptr = USB_ADD_BYTES(ptr, io_len); len -= io_len; if ((len == 0) && (what == 1)) { m->last_packet = 1; } USB_IF_ENQUEUE(&f->used_q, m); usb_fifo_wakeup(f); if ((len == 0) || (what == 1)) { break; } } else { break; } } } uint8_t usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->free_q, m); if (m) { m->cur_data_len = len; m->cur_data_ptr = ptr; USB_IF_ENQUEUE(&f->used_q, m); usb_fifo_wakeup(f); return (1); } return (0); } void usb_fifo_put_data_error(struct usb_fifo *f) { f->flag_iserror = 1; usb_fifo_wakeup(f); } /*------------------------------------------------------------------------* * usb_fifo_get_data * * what: * 0 - normal operation * 1 - only get one "usb_mbuf" * * returns: * 0 - no more data * 1 - data in buffer *------------------------------------------------------------------------*/ uint8_t usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what) { struct usb_mbuf *m; usb_frlength_t io_len; uint8_t tr_data = 0; actlen[0] = 0; while (1) { USB_IF_DEQUEUE(&f->used_q, m); if (m) { tr_data = 1; io_len = MIN(len, m->cur_data_len); usbd_copy_in(pc, offset, m->cur_data_ptr, io_len); len -= io_len; offset += io_len; actlen[0] += io_len; m->cur_data_ptr += io_len; m->cur_data_len -= io_len; if ((m->cur_data_len == 0) || (what == 1)) { USB_IF_ENQUEUE(&f->free_q, m); usb_fifo_wakeup(f); if (what == 1) { break; } } else { USB_IF_PREPEND(&f->used_q, m); } } else { if (tr_data) { /* wait for data to be written out */ break; } if (f->flag_flushing) { /* check if we should send a short packet */ if (f->flag_short != 0) { f->flag_short = 0; tr_data = 1; break; } /* flushing complete */ f->flag_flushing = 0; usb_fifo_wakeup(f); } break; } if (len == 0) { break; } } return (tr_data); } uint8_t usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, usb_size_t len, usb_size_t *actlen, uint8_t what) { struct usb_mbuf *m; usb_size_t io_len; uint8_t tr_data = 0; actlen[0] = 0; while (1) { USB_IF_DEQUEUE(&f->used_q, m); if (m) { tr_data = 1; io_len = MIN(len, m->cur_data_len); memcpy(ptr, m->cur_data_ptr, io_len); len -= io_len; ptr = USB_ADD_BYTES(ptr, io_len); actlen[0] += io_len; m->cur_data_ptr += io_len; m->cur_data_len -= io_len; if ((m->cur_data_len == 0) || (what == 1)) { USB_IF_ENQUEUE(&f->free_q, m); usb_fifo_wakeup(f); if (what == 1) { break; } } else { USB_IF_PREPEND(&f->used_q, m); } } else { if (tr_data) { /* wait for data to be written out */ break; } if (f->flag_flushing) { /* check if we should send a short packet */ if (f->flag_short != 0) { f->flag_short = 0; tr_data = 1; break; } /* flushing complete */ f->flag_flushing = 0; usb_fifo_wakeup(f); } break; } if (len == 0) { break; } } return (tr_data); } uint8_t usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) { struct usb_mbuf *m; USB_IF_POLL(&f->used_q, m); if (m) { *plen = m->cur_data_len; *pptr = m->cur_data_ptr; return (1); } return (0); } void usb_fifo_get_data_error(struct usb_fifo *f) { f->flag_iserror = 1; usb_fifo_wakeup(f); } /*------------------------------------------------------------------------* * usb_alloc_symlink * * Return values: * NULL: Failure * Else: Pointer to symlink entry *------------------------------------------------------------------------*/ struct usb_symlink * usb_alloc_symlink(const char *target) { struct usb_symlink *ps; ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); if (ps == NULL) { return (ps); } /* XXX no longer needed */ strlcpy(ps->src_path, target, sizeof(ps->src_path)); ps->src_len = strlen(ps->src_path); strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); ps->dst_len = strlen(ps->dst_path); sx_xlock(&usb_sym_lock); TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry); sx_unlock(&usb_sym_lock); return (ps); } /*------------------------------------------------------------------------* * usb_free_symlink *------------------------------------------------------------------------*/ void usb_free_symlink(struct usb_symlink *ps) { if (ps == NULL) { return; } sx_xlock(&usb_sym_lock); TAILQ_REMOVE(&usb_sym_head, ps, sym_entry); sx_unlock(&usb_sym_lock); free(ps, M_USBDEV); } /*------------------------------------------------------------------------* * usb_read_symlink * * Return value: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ int usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) { struct usb_symlink *ps; uint32_t temp; uint32_t delta = 0; uint8_t len; int error = 0; sx_xlock(&usb_sym_lock); TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) { /* * Compute total length of source and destination symlink * strings pluss one length byte and two NUL bytes: */ temp = ps->src_len + ps->dst_len + 3; if (temp > 255) { /* * Skip entry because this length cannot fit * into one byte: */ continue; } if (startentry != 0) { /* decrement read offset */ startentry--; continue; } if (temp > user_len) { /* out of buffer space */ break; } len = temp; /* copy out total length */ error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); if (error) { break; } delta += 1; /* copy out source string */ error = copyout(ps->src_path, USB_ADD_BYTES(user_ptr, delta), ps->src_len); if (error) { break; } len = 0; delta += ps->src_len; error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); if (error) { break; } delta += 1; /* copy out destination string */ error = copyout(ps->dst_path, USB_ADD_BYTES(user_ptr, delta), ps->dst_len); if (error) { break; } len = 0; delta += ps->dst_len; error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); if (error) { break; } delta += 1; user_len -= temp; } /* a zero length entry indicates the end */ if ((user_len != 0) && (error == 0)) { len = 0; error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); } sx_unlock(&usb_sym_lock); return (error); } void usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) { if (f == NULL) return; /* send a Zero Length Packet, ZLP, before close */ f->flag_short = onoff; } void usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff) { if (f == NULL) return; /* defrag written data */ f->flag_write_defrag = onoff; /* reset defrag state */ f->flag_have_fragment = 0; } void * usb_fifo_softc(struct usb_fifo *f) { return (f->priv_sc0); } #endif /* USB_HAVE_UGEN */ Index: head/sys/dev/usb/usb_device.c =================================================================== --- head/sys/dev/usb/usb_device.c (revision 298931) +++ head/sys/dev/usb/usb_device.c (revision 298932) @@ -1,2888 +1,2888 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #endif #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #include #endif #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /* function prototypes */ static void usb_init_endpoint(struct usb_device *, uint8_t, struct usb_endpoint_descriptor *, struct usb_endpoint_ss_comp_descriptor *, struct usb_endpoint *); static void usb_unconfigure(struct usb_device *, uint8_t); static void usb_detach_device_sub(struct usb_device *, device_t *, char **, uint8_t); static uint8_t usb_probe_and_attach_sub(struct usb_device *, struct usb_attach_arg *); static void usb_init_attach_arg(struct usb_device *, struct usb_attach_arg *); static void usb_suspend_resume_sub(struct usb_device *, device_t, uint8_t); static usb_proc_callback_t usbd_clear_stall_proc; static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t); static void usbd_set_device_strings(struct usb_device *); #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *); #endif #if USB_HAVE_UGEN static void usb_fifo_free_wrap(struct usb_device *, uint8_t, uint8_t); static void usb_cdev_create(struct usb_device *); static void usb_cdev_free(struct usb_device *); #endif /* This variable is global to allow easy access to it: */ #ifdef USB_TEMPLATE int usb_template = USB_TEMPLATE; #else int usb_template; #endif SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RWTUN, &usb_template, 0, "Selected USB device side template"); /* English is default language */ static int usb_lang_id = 0x0009; static int usb_lang_mask = 0x00FF; SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_id, CTLFLAG_RWTUN, &usb_lang_id, 0, "Preferred USB language ID"); SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_mask, CTLFLAG_RWTUN, &usb_lang_mask, 0, "Preferred USB language mask"); static const char* statestr[USB_STATE_MAX] = { [USB_STATE_DETACHED] = "DETACHED", [USB_STATE_ATTACHED] = "ATTACHED", [USB_STATE_POWERED] = "POWERED", [USB_STATE_ADDRESSED] = "ADDRESSED", [USB_STATE_CONFIGURED] = "CONFIGURED", }; const char * usb_statestr(enum usb_dev_state state) { return ((state < USB_STATE_MAX) ? statestr[state] : "UNKNOWN"); } const char * usb_get_manufacturer(struct usb_device *udev) { return (udev->manufacturer ? udev->manufacturer : "Unknown"); } const char * usb_get_product(struct usb_device *udev) { return (udev->product ? udev->product : ""); } const char * usb_get_serial(struct usb_device *udev) { return (udev->serial ? udev->serial : ""); } /*------------------------------------------------------------------------* * usbd_get_ep_by_addr * * This function searches for an USB ep by endpoint address and * direction. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; enum { EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR), }; /* * According to the USB specification not all bits are used * for the endpoint address. Keep defined bits only: */ ea_val &= EA_MASK; /* - * Iterate accross all the USB endpoints searching for a match + * Iterate across all the USB endpoints searching for a match * based on the endpoint address: */ for (; ep != ep_end; ep++) { if (ep->edesc == NULL) { continue; } /* do the mask and check the value */ if ((ep->edesc->bEndpointAddress & EA_MASK) == ea_val) { goto found; } } /* * The default endpoint is always present and is checked separately: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & EA_MASK) == ea_val)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_get_endpoint * * This function searches for an USB endpoint based on the information * given by the passed "struct usb_config" pointer. * * Return values: * NULL: No match. * Else: Pointer to "struct usb_endpoint". *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; uint8_t index = setup->ep_index; uint8_t ea_mask; uint8_t ea_val; uint8_t type_mask; uint8_t type_val; DPRINTFN(10, "udev=%p iface_index=%d address=0x%x " "type=0x%x dir=0x%x index=%d\n", udev, iface_index, setup->endpoint, setup->type, setup->direction, setup->ep_index); /* check USB mode */ if (setup->usb_mode != USB_MODE_DUAL && udev->flags.usb_mode != setup->usb_mode) { /* wrong mode - no endpoint */ return (NULL); } /* setup expected endpoint direction mask and value */ if (setup->direction == UE_DIR_RX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_OUT : UE_DIR_IN; } else if (setup->direction == UE_DIR_TX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_IN : UE_DIR_OUT; } else if (setup->direction == UE_DIR_ANY) { /* match any endpoint direction */ ea_mask = 0; ea_val = 0; } else { /* match the given endpoint direction */ ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT)); } /* setup expected endpoint address */ if (setup->endpoint == UE_ADDR_ANY) { /* match any endpoint address */ } else { /* match the given endpoint address */ ea_mask |= UE_ADDR; ea_val |= (setup->endpoint & UE_ADDR); } /* setup expected endpoint type */ if (setup->type == UE_BULK_INTR) { /* this will match BULK and INTERRUPT endpoints */ type_mask = 2; type_val = 2; } else if (setup->type == UE_TYPE_ANY) { /* match any endpoint type */ type_mask = 0; type_val = 0; } else { /* match the given endpoint type */ type_mask = UE_XFERTYPE; type_val = (setup->type & UE_XFERTYPE); } /* - * Iterate accross all the USB endpoints searching for a match + * Iterate across all the USB endpoints searching for a match * based on the endpoint address. Note that we are searching * the endpoints from the beginning of the "udev->endpoints" array. */ for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* do the masks and check the values */ if (((ep->edesc->bEndpointAddress & ea_mask) == ea_val) && ((ep->edesc->bmAttributes & type_mask) == type_val)) { if (!index--) { goto found; } } } /* * Match against default endpoint last, so that "any endpoint", "any * address" and "any direction" returns the first endpoint of the * interface. "iface_index" and "direction" is ignored: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & ea_mask) == ea_val) && ((udev->ctrl_ep.edesc->bmAttributes & type_mask) == type_val) && (!index)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_interface_count * * This function stores the number of USB interfaces excluding * alternate settings, which the USB config descriptor reports into * the unsigned 8-bit integer pointed to by "count". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count) { if (udev->cdesc == NULL) { *count = 0; return (USB_ERR_NOT_CONFIGURED); } *count = udev->ifaces_max; return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usb_init_endpoint * * This function will initialise the USB endpoint structure pointed to by * the "endpoint" argument. The structure pointed to by "endpoint" must be * zeroed before calling this function. *------------------------------------------------------------------------*/ static void usb_init_endpoint(struct usb_device *udev, uint8_t iface_index, struct usb_endpoint_descriptor *edesc, struct usb_endpoint_ss_comp_descriptor *ecomp, struct usb_endpoint *ep) { const struct usb_bus_methods *methods; usb_stream_t x; methods = udev->bus->methods; (methods->endpoint_init) (udev, edesc, ep); /* initialise USB endpoint structure */ ep->edesc = edesc; ep->ecomp = ecomp; ep->iface_index = iface_index; /* setup USB stream queues */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { TAILQ_INIT(&ep->endpoint_q[x].head); ep->endpoint_q[x].command = &usbd_pipe_start; } /* the pipe is not supported by the hardware */ if (ep->methods == NULL) return; /* check for SUPER-speed streams mode endpoint */ if (udev->speed == USB_SPEED_SUPER && ecomp != NULL && (edesc->bmAttributes & UE_XFERTYPE) == UE_BULK && (UE_GET_BULK_STREAMS(ecomp->bmAttributes) != 0)) { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_STREAMS); } else { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_DEFAULT); } /* clear stall, if any */ if (methods->clear_stall != NULL) { USB_BUS_LOCK(udev->bus); (methods->clear_stall) (udev, ep); USB_BUS_UNLOCK(udev->bus); } } /*-----------------------------------------------------------------------* * usb_endpoint_foreach * * This function will iterate all the USB endpoints except the control * endpoint. This function is NULL safe. * * Return values: * NULL: End of USB endpoints * Else: Pointer to next USB endpoint *------------------------------------------------------------------------*/ struct usb_endpoint * usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep) { struct usb_endpoint *ep_end; /* be NULL safe */ if (udev == NULL) return (NULL); ep_end = udev->endpoints + udev->endpoints_max; /* get next endpoint */ if (ep == NULL) ep = udev->endpoints; else ep++; /* find next allocated ep */ while (ep != ep_end) { if (ep->edesc != NULL) return (ep); ep++; } return (NULL); } /*------------------------------------------------------------------------* * usb_wait_pending_refs * * This function will wait for any USB references to go away before * returning. This function is used before freeing a USB device. *------------------------------------------------------------------------*/ static void usb_wait_pending_refs(struct usb_device *udev) { #if USB_HAVE_UGEN DPRINTF("Refcount = %d\n", (int)udev->refcount); mtx_lock(&usb_ref_lock); udev->refcount--; while (1) { /* wait for any pending references to go away */ if (udev->refcount == 0) { /* prevent further refs being taken, if any */ udev->refcount = USB_DEV_REF_MAX; break; } cv_wait(&udev->ref_cv, &usb_ref_lock); } mtx_unlock(&usb_ref_lock); #endif } /*------------------------------------------------------------------------* * usb_unconfigure * * This function will free all USB interfaces and USB endpoints belonging * to an USB device. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_unconfigure(struct usb_device *udev, uint8_t flag) { uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); /* detach all interface drivers */ usb_detach_device(udev, USB_IFACE_INDEX_ANY, flag); #if USB_HAVE_UGEN /* free all FIFOs except control endpoint FIFOs */ usb_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, flag); /* * Free all cdev's, if any. */ usb_cdev_free(udev); #endif #if USB_HAVE_COMPAT_LINUX /* free Linux compat device, if any */ if (udev->linux_endpoint_start != NULL) { usb_linux_free_device_p(udev); udev->linux_endpoint_start = NULL; } #endif usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_FREE); /* free "cdesc" after "ifaces" and "endpoints", if any */ if (udev->cdesc != NULL) { if (udev->flags.usb_mode != USB_MODE_DEVICE) usbd_free_config_desc(udev, udev->cdesc); udev->cdesc = NULL; } /* set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; if (do_unlock) usbd_enum_unlock(udev); } /*------------------------------------------------------------------------* * usbd_set_config_index * * This function selects configuration by index, independent of the * actual configuration number. This function should not be used by * USB drivers. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_config_index(struct usb_device *udev, uint8_t index) { struct usb_status ds; struct usb_config_descriptor *cdp; uint16_t power; uint16_t max_power; uint8_t selfpowered; uint8_t do_unlock; usb_error_t err; DPRINTFN(6, "udev=%p index=%d\n", udev, index); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); usb_unconfigure(udev, 0); if (index == USB_UNCONFIG_INDEX) { /* * Leave unallocated when unconfiguring the * device. "usb_unconfigure()" will also reset * the current config number and index. */ err = usbd_req_set_config(udev, NULL, USB_UNCONFIG_NO); if (udev->state == USB_STATE_CONFIGURED) usb_set_device_state(udev, USB_STATE_ADDRESSED); goto done; } /* get the full config descriptor */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* save some memory */ err = usbd_req_get_descriptor_ptr(udev, &cdp, (UDESC_CONFIG << 8) | index); } else { /* normal request */ err = usbd_req_get_config_desc_full(udev, NULL, &cdp, index); } if (err) { goto done; } /* set the new config descriptor */ udev->cdesc = cdp; /* Figure out if the device is self or bus powered. */ selfpowered = 0; if ((!udev->flags.uq_bus_powered) && (cdp->bmAttributes & UC_SELF_POWERED) && (udev->flags.usb_mode == USB_MODE_HOST)) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ err = usbd_req_get_device_status(udev, NULL, &ds); if (err) { DPRINTFN(0, "could not read " "device status: %s\n", usbd_errstr(err)); } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) { selfpowered = 1; } DPRINTF("status=0x%04x \n", UGETW(ds.wStatus)); } else selfpowered = 1; } DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, " "selfpowered=%d, power=%d\n", udev, cdp, udev->address, cdp->bConfigurationValue, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2); /* Check if we have enough power. */ power = cdp->bMaxPower * 2; if (udev->parent_hub) { max_power = udev->parent_hub->hub->portpower; } else { max_power = USB_MAX_POWER; } if (power > max_power) { DPRINTFN(0, "power exceeded %d > %d\n", power, max_power); err = USB_ERR_NO_POWER; goto done; } /* Only update "self_powered" in USB Host Mode */ if (udev->flags.usb_mode == USB_MODE_HOST) { udev->flags.self_powered = selfpowered; } udev->power = power; udev->curr_config_no = cdp->bConfigurationValue; udev->curr_config_index = index; usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Set the actual configuration value. */ err = usbd_req_set_config(udev, NULL, cdp->bConfigurationValue); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_ALLOC); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_INIT); if (err) { goto done; } #if USB_HAVE_UGEN /* create device nodes for each endpoint */ usb_cdev_create(udev); #endif done: DPRINTF("error=%s\n", usbd_errstr(err)); if (err) { usb_unconfigure(udev, 0); } if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usb_config_parse * * This function will allocate and free USB interfaces and USB endpoints, * parse the USB configuration structure and initialise the USB endpoints * and interfaces. If "iface_index" is not equal to * "USB_IFACE_INDEX_ANY" then the "cmd" parameter is the * alternate_setting to be selected for the given interface. Else the * "cmd" parameter is defined by "USB_CFG_XXX". "iface_index" can be * "USB_IFACE_INDEX_ANY" or a valid USB interface index. This function * is typically called when setting the configuration or when setting * an alternate interface. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) { struct usb_idesc_parse_state ips; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; struct usb_interface *iface; struct usb_endpoint *ep; usb_error_t err; uint8_t ep_curr; uint8_t ep_max; uint8_t temp; uint8_t do_init; uint8_t alt_index; if (iface_index != USB_IFACE_INDEX_ANY) { /* parameter overload */ alt_index = cmd; cmd = USB_CFG_INIT; } else { /* not used */ alt_index = 0; } err = 0; DPRINTFN(5, "iface_index=%d cmd=%d\n", iface_index, cmd); if (cmd == USB_CFG_FREE) goto cleanup; if (cmd == USB_CFG_INIT) { sx_assert(&udev->enum_sx, SA_LOCKED); /* check for in-use endpoints */ ep = udev->endpoints; ep_max = udev->endpoints_max; while (ep_max--) { /* look for matching endpoints */ if ((iface_index == USB_IFACE_INDEX_ANY) || (iface_index == ep->iface_index)) { if (ep->refcount_alloc != 0) { /* * This typically indicates a * more serious error. */ err = USB_ERR_IN_USE; } else { /* reset endpoint */ memset(ep, 0, sizeof(*ep)); /* make sure we don't zero the endpoint again */ ep->iface_index = USB_IFACE_INDEX_ANY; } } ep++; } if (err) return (err); } memset(&ips, 0, sizeof(ips)); ep_curr = 0; ep_max = 0; while ((id = usb_idesc_foreach(udev->cdesc, &ips))) { iface = udev->ifaces + ips.iface_index; /* check for specific interface match */ if (cmd == USB_CFG_INIT) { if ((iface_index != USB_IFACE_INDEX_ANY) && (iface_index != ips.iface_index)) { /* wrong interface */ do_init = 0; } else if (alt_index != ips.iface_index_alt) { /* wrong alternate setting */ do_init = 0; } else { /* initialise interface */ do_init = 1; } } else do_init = 0; /* check for new interface */ if (ips.iface_index_alt == 0) { /* update current number of endpoints */ ep_curr = ep_max; } /* check for init */ if (do_init) { /* setup the USB interface structure */ iface->idesc = id; /* set alternate index */ iface->alt_index = alt_index; /* set default interface parent */ if (iface_index == USB_IFACE_INDEX_ANY) { iface->parent_iface_index = USB_IFACE_INDEX_ANY; } } DPRINTFN(5, "found idesc nendpt=%d\n", id->bNumEndpoints); ed = (struct usb_endpoint_descriptor *)id; temp = ep_curr; /* iterate all the endpoint descriptors */ while ((ed = usb_edesc_foreach(udev->cdesc, ed))) { /* check if endpoint limit has been reached */ if (temp >= USB_MAX_EP_UNITS) { DPRINTF("Endpoint limit reached\n"); break; } ep = udev->endpoints + temp; if (do_init) { void *ecomp; ecomp = usb_ed_comp_foreach(udev->cdesc, (void *)ed); if (ecomp != NULL) DPRINTFN(5, "Found endpoint companion descriptor\n"); usb_init_endpoint(udev, ips.iface_index, ed, ecomp, ep); } temp ++; /* find maximum number of endpoints */ if (ep_max < temp) ep_max = temp; } } /* NOTE: It is valid to have no interfaces and no endpoints! */ if (cmd == USB_CFG_ALLOC) { udev->ifaces_max = ips.iface_index; #if (USB_HAVE_FIXED_IFACE == 0) udev->ifaces = NULL; if (udev->ifaces_max != 0) { udev->ifaces = malloc(sizeof(*iface) * udev->ifaces_max, M_USB, M_WAITOK | M_ZERO); if (udev->ifaces == NULL) { err = USB_ERR_NOMEM; goto done; } } #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) if (ep_max != 0) { udev->endpoints = malloc(sizeof(*ep) * ep_max, M_USB, M_WAITOK | M_ZERO); if (udev->endpoints == NULL) { err = USB_ERR_NOMEM; goto done; } } else { udev->endpoints = NULL; } #endif USB_BUS_LOCK(udev->bus); udev->endpoints_max = ep_max; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); } #if (USB_HAVE_FIXED_IFACE == 0) || (USB_HAVE_FIXED_ENDPOINT == 0) done: #endif if (err) { if (cmd == USB_CFG_ALLOC) { cleanup: USB_BUS_LOCK(udev->bus); udev->endpoints_max = 0; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); #if (USB_HAVE_FIXED_IFACE == 0) free(udev->ifaces, M_USB); udev->ifaces = NULL; #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) free(udev->endpoints, M_USB); udev->endpoints = NULL; #endif udev->ifaces_max = 0; } } return (err); } /*------------------------------------------------------------------------* * usbd_set_alt_interface_index * * This function will select an alternate interface index for the * given interface index. The interface should not be in use when this * function is called. That means there should not be any open USB * transfers. Else an error is returned. If the alternate setting is * already set this function will simply return success. This function * is called in Host mode and Device mode! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); usb_error_t err; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (iface == NULL) { err = USB_ERR_INVAL; goto done; } if (iface->alt_index == alt_index) { /* * Optimise away duplicate setting of * alternate setting in USB Host Mode! */ err = 0; goto done; } #if USB_HAVE_UGEN /* * Free all generic FIFOs for this interface, except control * endpoint FIFOs: */ usb_fifo_free_wrap(udev, iface_index, 0); #endif err = usb_config_parse(udev, iface_index, alt_index); if (err) { goto done; } if (iface->alt_index != alt_index) { /* the alternate setting does not exist */ err = USB_ERR_INVAL; goto done; } err = usbd_req_set_alt_interface_no(udev, NULL, iface_index, iface->idesc->bAlternateSetting); done: if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usbd_set_endpoint_stall * * This function is used to make a BULK or INTERRUPT endpoint send * STALL tokens in USB device mode. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_endpoint_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t do_stall) { struct usb_xfer *xfer; usb_stream_t x; uint8_t et; uint8_t was_stalled; if (ep == NULL) { /* nothing to do */ DPRINTF("Cannot find endpoint\n"); /* * Pretend that the clear or set stall request is * successful else some USB host stacks can do * strange things, especially when a control endpoint * stalls. */ return (0); } et = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((et != UE_BULK) && (et != UE_INTERRUPT)) { /* * Should not stall control * nor isochronous endpoints. */ DPRINTF("Invalid endpoint\n"); return (0); } USB_BUS_LOCK(udev->bus); /* store current stall state */ was_stalled = ep->is_stalled; /* check for no change */ if (was_stalled && do_stall) { /* if the endpoint is already stalled do nothing */ USB_BUS_UNLOCK(udev->bus); DPRINTF("No change\n"); return (0); } /* set stalled state */ ep->is_stalled = 1; if (do_stall || (!was_stalled)) { if (!was_stalled) { for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* lookup the current USB transfer, if any */ xfer = ep->endpoint_q[x].curr; if (xfer != NULL) { /* * The "xfer_stall" method * will complete the USB * transfer like in case of a * timeout setting the error * code "USB_ERR_STALLED". */ (udev->bus->methods->xfer_stall) (xfer); } } } (udev->bus->methods->set_stall) (udev, ep, &do_stall); } if (!do_stall) { ep->toggle_next = 0; /* reset data toggle */ ep->is_stalled = 0; /* clear stalled state */ (udev->bus->methods->clear_stall) (udev, ep); /* start the current or next transfer, if any */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } USB_BUS_UNLOCK(udev->bus); return (0); } /*------------------------------------------------------------------------* * usb_reset_iface_endpoints - used in USB device side mode *------------------------------------------------------------------------*/ usb_error_t usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index) { struct usb_endpoint *ep; struct usb_endpoint *ep_end; ep = udev->endpoints; ep_end = udev->endpoints + udev->endpoints_max; for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* simulate a clear stall from the peer */ usbd_set_endpoint_stall(udev, ep, 0); } return (0); } /*------------------------------------------------------------------------* * usb_detach_device_sub * * This function will try to detach an USB device. If it fails a panic * will result. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_detach_device_sub(struct usb_device *udev, device_t *ppdev, char **ppnpinfo, uint8_t flag) { device_t dev; char *pnpinfo; int err; dev = *ppdev; if (dev) { /* * NOTE: It is important to clear "*ppdev" before deleting * the child due to some device methods being called late * during the delete process ! */ *ppdev = NULL; if (!rebooting) { device_printf(dev, "at %s, port %d, addr %d " "(disconnected)\n", device_get_nameunit(udev->parent_dev), udev->port_no, udev->address); } if (device_is_attached(dev)) { if (udev->flags.peer_suspended) { err = DEVICE_RESUME(dev); if (err) { device_printf(dev, "Resume failed\n"); } } if (device_detach(dev)) { goto error; } } if (device_delete_child(udev->parent_dev, dev)) { goto error; } } pnpinfo = *ppnpinfo; if (pnpinfo != NULL) { *ppnpinfo = NULL; free(pnpinfo, M_USBDEV); } return; error: /* Detach is not allowed to fail in the USB world */ panic("usb_detach_device_sub: A USB driver would not detach\n"); } /*------------------------------------------------------------------------* * usb_detach_device * * The following function will detach the matching interfaces. * This function is NULL safe. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ void usb_detach_device(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return; } DPRINTFN(4, "udev=%p\n", udev); sx_assert(&udev->enum_sx, SA_LOCKED); /* * First detach the child to give the child's detach routine a * chance to detach the sub-devices in the correct order. * Then delete the child using "device_delete_child()" which * will detach all sub-devices from the bottom and upwards! */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; iface_index = i + 1; } else { i = 0; iface_index = USB_IFACE_MAX; } /* do the detach */ for (; i != iface_index; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_detach_device_sub(udev, &iface->subdev, &iface->pnpinfo, flag); } } /*------------------------------------------------------------------------* * usb_probe_and_attach_sub * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb_probe_and_attach_sub(struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; device_t dev; int err; iface = uaa->iface; if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) { /* leave interface alone */ return (0); } dev = iface->subdev; if (dev) { /* clean up after module unload */ if (device_is_attached(dev)) { /* already a device there */ return (0); } /* clear "iface->subdev" as early as possible */ iface->subdev = NULL; if (device_delete_child(udev->parent_dev, dev)) { /* * Panic here, else one can get a double call * to device_detach(). USB devices should * never fail on detach! */ panic("device_delete_child() failed\n"); } } if (uaa->temp_dev == NULL) { /* create a new child */ uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1); if (uaa->temp_dev == NULL) { device_printf(udev->parent_dev, "Device creation failed\n"); return (1); /* failure */ } device_set_ivars(uaa->temp_dev, uaa); device_quiet(uaa->temp_dev); } /* * Set "subdev" before probe and attach so that "devd" gets * the information it needs. */ iface->subdev = uaa->temp_dev; if (device_probe_and_attach(iface->subdev) == 0) { /* * The USB attach arguments are only available during probe * and attach ! */ uaa->temp_dev = NULL; device_set_ivars(iface->subdev, NULL); if (udev->flags.peer_suspended) { err = DEVICE_SUSPEND(iface->subdev); if (err) device_printf(iface->subdev, "Suspend failed\n"); } return (0); /* success */ } else { /* No USB driver found */ iface->subdev = NULL; } return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_set_parent_iface * * Using this function will lock the alternate interface setting on an * interface. It is typically used for multi interface drivers. In USB * device side mode it is assumed that the alternate interfaces all * have the same endpoint descriptors. The default parent index value * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not * locked. *------------------------------------------------------------------------*/ void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index) { struct usb_interface *iface; if (udev == NULL) { /* nothing to do */ return; } iface = usbd_get_iface(udev, iface_index); if (iface != NULL) iface->parent_iface_index = parent_index; } static void usb_init_attach_arg(struct usb_device *udev, struct usb_attach_arg *uaa) { memset(uaa, 0, sizeof(*uaa)); uaa->device = udev; uaa->usb_mode = udev->flags.usb_mode; uaa->port = udev->port_no; uaa->dev_state = UAA_DEV_READY; uaa->info.idVendor = UGETW(udev->ddesc.idVendor); uaa->info.idProduct = UGETW(udev->ddesc.idProduct); uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice); uaa->info.bDeviceClass = udev->ddesc.bDeviceClass; uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass; uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol; uaa->info.bConfigIndex = udev->curr_config_index; uaa->info.bConfigNum = udev->curr_config_no; } /*------------------------------------------------------------------------* * usb_probe_and_attach * * This function is called from "uhub_explore_sub()", * "usb_handle_set_config()" and "usb_handle_request()". * * Returns: * 0: Success * Else: A control transfer failed *------------------------------------------------------------------------*/ usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) { struct usb_attach_arg uaa; struct usb_interface *iface; uint8_t i; uint8_t j; uint8_t do_unlock; if (udev == NULL) { DPRINTF("udev == NULL\n"); return (USB_ERR_INVAL); } /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->curr_config_index == USB_UNCONFIG_INDEX) { /* do nothing - no configuration has been set */ goto done; } /* setup USB attach arguments */ usb_init_attach_arg(udev, &uaa); /* * If the whole USB device is targeted, invoke the USB event * handler(s): */ if (iface_index == USB_IFACE_INDEX_ANY) { if (usb_test_quirk(&uaa, UQ_MSC_DYMO_EJECT) != 0 && usb_dymo_eject(udev, 0) == 0) { /* success, mark the udev as disappearing */ uaa.dev_state = UAA_DEV_EJECTING; } EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa); if (uaa.dev_state != UAA_DEV_READY) { /* leave device unconfigured */ usb_unconfigure(udev, 0); goto done; } } /* Check if only one interface should be probed: */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; j = i + 1; } else { i = 0; j = USB_IFACE_MAX; } /* Do the probe and attach */ for (; i != j; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* * Looks like the end of the USB * interfaces ! */ DPRINTFN(2, "end of interfaces " "at %u\n", i); break; } if (iface->idesc == NULL) { /* no interface descriptor */ continue; } uaa.iface = iface; uaa.info.bInterfaceClass = iface->idesc->bInterfaceClass; uaa.info.bInterfaceSubClass = iface->idesc->bInterfaceSubClass; uaa.info.bInterfaceProtocol = iface->idesc->bInterfaceProtocol; uaa.info.bIfaceIndex = i; uaa.info.bIfaceNum = iface->idesc->bInterfaceNumber; uaa.driver_info = 0; /* reset driver_info */ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", uaa.info.bInterfaceClass, uaa.info.bInterfaceSubClass, uaa.info.bInterfaceProtocol, uaa.info.bIfaceIndex, uaa.info.bIfaceNum); usb_probe_and_attach_sub(udev, &uaa); /* * Remove the leftover child, if any, to enforce that * a new nomatch devd event is generated for the next * interface if no driver is found: */ if (uaa.temp_dev == NULL) continue; if (device_delete_child(udev->parent_dev, uaa.temp_dev)) DPRINTFN(0, "device delete child failed\n"); uaa.temp_dev = NULL; } done: if (do_unlock) usbd_enum_unlock(udev); return (0); } /*------------------------------------------------------------------------* * usb_suspend_resume_sub * * This function is called when the suspend or resume methods should * be executed on an USB device. *------------------------------------------------------------------------*/ static void usb_suspend_resume_sub(struct usb_device *udev, device_t dev, uint8_t do_suspend) { int err; if (dev == NULL) { return; } if (!device_is_attached(dev)) { return; } if (do_suspend) { err = DEVICE_SUSPEND(dev); } else { err = DEVICE_RESUME(dev); } if (err) { device_printf(dev, "%s failed\n", do_suspend ? "Suspend" : "Resume"); } } /*------------------------------------------------------------------------* * usb_suspend_resume * * The following function will suspend or resume the USB device. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return (0); } DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend); sx_assert(&udev->sr_sx, SA_LOCKED); USB_BUS_LOCK(udev->bus); /* filter the suspend events */ if (udev->flags.peer_suspended == do_suspend) { USB_BUS_UNLOCK(udev->bus); /* nothing to do */ return (0); } udev->flags.peer_suspended = do_suspend; USB_BUS_UNLOCK(udev->bus); /* do the suspend or resume */ for (i = 0; i != USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_suspend_resume_sub(udev, iface->subdev, do_suspend); } return (0); } /*------------------------------------------------------------------------* * usbd_clear_stall_proc * * This function performs generic USB clear stall operations. *------------------------------------------------------------------------*/ static void usbd_clear_stall_proc(struct usb_proc_msg *_pm) { struct usb_udev_msg *pm = (void *)_pm; struct usb_device *udev = pm->udev; /* Change lock */ USB_BUS_UNLOCK(udev->bus); mtx_lock(&udev->device_mtx); /* Start clear stall callback */ usbd_transfer_start(udev->ctrl_xfer[1]); /* Change lock */ mtx_unlock(&udev->device_mtx); USB_BUS_LOCK(udev->bus); } /*------------------------------------------------------------------------* * usb_alloc_device * * This function allocates a new USB device. This function is called * when a new device has been put in the powered state, but not yet in * the addressed state. Get initial descriptor, set the address, get * full descriptor and get strings. * * Return values: * 0: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_device * usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode) { struct usb_attach_arg uaa; struct usb_device *udev; struct usb_device *adev; struct usb_device *hub; uint8_t *scratch_ptr; usb_error_t err; uint8_t device_index; uint8_t config_index; uint8_t config_quirk; uint8_t set_config_failed; uint8_t do_unlock; DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, " "port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n", parent_dev, bus, parent_hub, depth, port_index, port_no, speed, mode); /* * Find an unused device index. In USB Host mode this is the * same as the device address. * * Device index zero is not used and device index 1 should * always be the root hub. */ for (device_index = USB_ROOT_HUB_ADDR; (device_index != bus->devices_max) && (bus->devices[device_index] != NULL); device_index++) /* nop */; if (device_index == bus->devices_max) { device_printf(bus->bdev, "No free USB device index for new device\n"); return (NULL); } if (depth > 0x10) { device_printf(bus->bdev, "Invalid device depth\n"); return (NULL); } udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO); if (udev == NULL) { return (NULL); } /* initialise our SX-lock */ sx_init_flags(&udev->enum_sx, "USB config SX lock", SX_DUPOK); sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_NOWITNESS); cv_init(&udev->ctrlreq_cv, "WCTRL"); cv_init(&udev->ref_cv, "UGONE"); /* initialise our mutex */ mtx_init(&udev->device_mtx, "USB device mutex", NULL, MTX_DEF); /* initialise generic clear stall */ udev->cs_msg[0].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[0].udev = udev; udev->cs_msg[1].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[1].udev = udev; /* initialise some USB device fields */ udev->parent_hub = parent_hub; udev->parent_dev = parent_dev; udev->port_index = port_index; udev->port_no = port_no; udev->depth = depth; udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (usb_ticks_t)ticks; /* * We need to force the power mode to "on" because there are plenty * of USB devices out there that do not work very well with * automatic suspend and resume! */ udev->power_mode = usbd_filter_power_mode(udev, USB_POWER_MODE_ON); udev->pwr_save.last_xfer_time = ticks; /* we are not ready yet */ udev->refcount = 1; /* set up default endpoint descriptor */ udev->ctrl_ep_desc.bLength = sizeof(udev->ctrl_ep_desc); udev->ctrl_ep_desc.bDescriptorType = UDESC_ENDPOINT; udev->ctrl_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; udev->ctrl_ep_desc.bmAttributes = UE_CONTROL; udev->ctrl_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; udev->ctrl_ep_desc.wMaxPacketSize[1] = 0; udev->ctrl_ep_desc.bInterval = 0; /* set up default endpoint companion descriptor */ udev->ctrl_ep_comp_desc.bLength = sizeof(udev->ctrl_ep_comp_desc); udev->ctrl_ep_comp_desc.bDescriptorType = UDESC_ENDPOINT_SS_COMP; udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; udev->speed = speed; udev->flags.usb_mode = mode; /* search for our High Speed USB HUB, if any */ adev = udev; hub = udev->parent_hub; while (hub) { if (hub->speed == USB_SPEED_HIGH) { udev->hs_hub_addr = hub->address; udev->parent_hs_hub = hub; udev->hs_port_no = adev->port_no; break; } adev = hub; hub = hub->parent_hub; } /* init the default endpoint */ usb_init_endpoint(udev, 0, &udev->ctrl_ep_desc, &udev->ctrl_ep_comp_desc, &udev->ctrl_ep); /* set device index */ udev->device_index = device_index; #if USB_HAVE_UGEN /* Create ugen name */ snprintf(udev->ugen_name, sizeof(udev->ugen_name), USB_GENERIC_NAME "%u.%u", device_get_unit(bus->bdev), device_index); LIST_INIT(&udev->pd_list); /* Create the control endpoint device */ udev->ctrl_dev = usb_make_dev(udev, NULL, 0, 0, FREAD|FWRITE, UID_ROOT, GID_OPERATOR, 0600); /* Create a link from /dev/ugenX.X to the default endpoint */ if (udev->ctrl_dev != NULL) make_dev_alias(udev->ctrl_dev->cdev, "%s", udev->ugen_name); #endif /* Initialise device */ if (bus->methods->device_init != NULL) { err = (bus->methods->device_init) (udev); if (err != 0) { DPRINTFN(0, "device init %d failed " "(%s, ignored)\n", device_index, usbd_errstr(err)); goto done; } } /* set powered device state after device init is complete */ usb_set_device_state(udev, USB_STATE_POWERED); if (udev->flags.usb_mode == USB_MODE_HOST) { err = usbd_req_set_address(udev, NULL, device_index); /* * This is the new USB device address from now on, if * the set address request didn't set it already. */ if (udev->address == USB_START_ADDR) udev->address = device_index; /* * We ignore any set-address errors, hence there are * buggy USB devices out there that actually receive * the SETUP PID, but manage to set the address before * the STATUS stage is ACK'ed. If the device responds * to the subsequent get-descriptor at the new * address, then we know that the set-address command * was successful. */ if (err) { DPRINTFN(0, "set address %d failed " "(%s, ignored)\n", udev->address, usbd_errstr(err)); } } else { /* We are not self powered */ udev->flags.self_powered = 0; /* Set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; /* Setup USB descriptors */ err = (usb_temp_setup_by_index_p) (udev, usb_template); if (err) { DPRINTFN(0, "setting up USB template failed maybe the USB " "template module has not been loaded\n"); goto done; } } usb_set_device_state(udev, USB_STATE_ADDRESSED); /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, NULL); if (err != 0) { /* try to enumerate two more times */ err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { goto done; } } } /* * Setup temporary USB attach args so that we can figure out some * basic quirks for this device. */ usb_init_attach_arg(udev, &uaa); if (usb_test_quirk(&uaa, UQ_BUS_POWERED)) { udev->flags.uq_bus_powered = 1; } if (usb_test_quirk(&uaa, UQ_NO_STRINGS)) { udev->flags.no_strings = 1; } /* * Workaround for buggy USB devices. * * It appears that some string-less USB chips will crash and * disappear if any attempts are made to read any string * descriptors. * * Try to detect such chips by checking the strings in the USB * device descriptor. If no strings are present there we * simply disable all USB strings. */ /* Protect scratch area */ do_unlock = usbd_enum_lock(udev); scratch_ptr = udev->scratch.data; if (udev->ddesc.iManufacturer || udev->ddesc.iProduct || udev->ddesc.iSerialNumber) { /* read out the language ID string */ err = usbd_req_get_string_desc(udev, NULL, (char *)scratch_ptr, 4, 0, USB_LANGUAGE_TABLE); } else { err = USB_ERR_INVAL; } if (err || (scratch_ptr[0] < 4)) { udev->flags.no_strings = 1; } else { uint16_t langid; uint16_t pref; uint16_t mask; uint8_t x; /* load preferred value and mask */ pref = usb_lang_id; mask = usb_lang_mask; /* align length correctly */ scratch_ptr[0] &= ~1U; /* fix compiler warning */ langid = 0; /* search for preferred language */ for (x = 2; (x < scratch_ptr[0]); x += 2) { langid = UGETW(scratch_ptr + x); if ((langid & mask) == pref) break; } if (x >= scratch_ptr[0]) { /* pick the first language as the default */ DPRINTFN(1, "Using first language\n"); langid = UGETW(scratch_ptr + 2); } DPRINTFN(1, "Language selected: 0x%04x\n", langid); udev->langid = langid; } if (do_unlock) usbd_enum_unlock(udev); /* assume 100mA bus powered for now. Changed when configured. */ udev->power = USB_MIN_POWER; /* fetch the vendor and product strings from the device */ usbd_set_device_strings(udev); if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* USB device mode setup is complete */ err = 0; goto config_done; } /* * Most USB devices should attach to config index 0 by * default */ if (usb_test_quirk(&uaa, UQ_CFG_INDEX_0)) { config_index = 0; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_1)) { config_index = 1; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_2)) { config_index = 2; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_3)) { config_index = 3; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_4)) { config_index = 4; config_quirk = 1; } else { config_index = 0; config_quirk = 0; } set_config_failed = 0; repeat_set_config: DPRINTF("setting config %u\n", config_index); /* get the USB device configured */ err = usbd_set_config_index(udev, config_index); if (err) { if (udev->ddesc.bNumConfigurations != 0) { if (!set_config_failed) { set_config_failed = 1; /* XXX try to re-enumerate the device */ err = usbd_req_re_enumerate(udev, NULL); if (err == 0) goto repeat_set_config; } DPRINTFN(0, "Failure selecting configuration index %u:" "%s, port %u, addr %u (ignored)\n", config_index, usbd_errstr(err), udev->port_no, udev->address); } /* * Some USB devices do not have any configurations. Ignore any * set config failures! */ err = 0; goto config_done; } if (!config_quirk && config_index + 1 < udev->ddesc.bNumConfigurations) { if ((udev->cdesc->bNumInterface < 2) && usbd_get_no_descriptors(udev->cdesc, UDESC_ENDPOINT) == 0) { DPRINTFN(0, "Found no endpoints, trying next config\n"); config_index++; goto repeat_set_config; } #if USB_HAVE_MSCTEST if (config_index == 0) { /* * Try to figure out if we have an * auto-install disk there: */ if (usb_iface_is_cdrom(udev, 0)) { DPRINTFN(0, "Found possible auto-install " "disk (trying next config)\n"); config_index++; goto repeat_set_config; } } #endif } #if USB_HAVE_MSCTEST if (set_config_failed == 0 && config_index == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_SYNC_CACHE) == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_GETMAXLUN) == 0) { /* * Try to figure out if there are any MSC quirks we * should apply automatically: */ err = usb_msc_auto_quirk(udev, 0); if (err != 0) { set_config_failed = 1; goto repeat_set_config; } } #endif config_done: DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n", udev->address, udev, udev->parent_hub); /* register our device - we are ready */ usb_bus_port_set_device(bus, parent_hub ? parent_hub->hub->ports + port_index : NULL, udev, device_index); #if USB_HAVE_UGEN /* Symlink the ugen device name */ udev->ugen_symlink = usb_alloc_symlink(udev->ugen_name); /* Announce device */ printf("%s: <%s> at %s\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(udev->bus->bdev)); #endif #if USB_HAVE_DEVCTL usb_notify_addq("ATTACH", udev); #endif done: if (err) { /* * Free USB device and all subdevices, if any. */ usb_free_device(udev, 0); udev = NULL; } return (udev); } #if USB_HAVE_UGEN struct usb_fs_privdata * usb_make_dev(struct usb_device *udev, const char *devname, int ep, int fi, int rwmode, uid_t uid, gid_t gid, int mode) { struct usb_fs_privdata* pd; struct make_dev_args args; char buffer[32]; /* Store information to locate ourselves again later */ pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); pd->bus_index = device_get_unit(udev->bus->bdev); pd->dev_index = udev->device_index; pd->ep_addr = ep; pd->fifo_index = fi; pd->mode = rwmode; /* Now, create the device itself */ if (devname == NULL) { devname = buffer; snprintf(buffer, sizeof(buffer), USB_DEVICE_DIR "/%u.%u.%u", pd->bus_index, pd->dev_index, pd->ep_addr); } /* Setup arguments for make_dev_s() */ make_dev_args_init(&args); args.mda_devsw = &usb_devsw; args.mda_uid = uid; args.mda_gid = gid; args.mda_mode = mode; args.mda_si_drv1 = pd; if (make_dev_s(&args, &pd->cdev, "%s", devname) != 0) { DPRINTFN(0, "Failed to create device %s\n", devname); free(pd, M_USBDEV); return (NULL); } return (pd); } void usb_destroy_dev_sync(struct usb_fs_privdata *pd) { DPRINTFN(1, "Destroying device at ugen%d.%d\n", pd->bus_index, pd->dev_index); /* * Destroy character device synchronously. After this * all system calls are returned. Can block. */ destroy_dev(pd->cdev); free(pd, M_USBDEV); } void usb_destroy_dev(struct usb_fs_privdata *pd) { struct usb_bus *bus; if (pd == NULL) return; mtx_lock(&usb_ref_lock); bus = devclass_get_softc(usb_devclass_ptr, pd->bus_index); mtx_unlock(&usb_ref_lock); if (bus == NULL) { usb_destroy_dev_sync(pd); return; } /* make sure we can re-use the device name */ delist_dev(pd->cdev); USB_BUS_LOCK(bus); LIST_INSERT_HEAD(&bus->pd_cleanup_list, pd, pd_next); /* get cleanup going */ usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->cleanup_msg[0], &bus->cleanup_msg[1]); USB_BUS_UNLOCK(bus); } static void usb_cdev_create(struct usb_device *udev) { struct usb_config_descriptor *cd; struct usb_endpoint_descriptor *ed; struct usb_descriptor *desc; struct usb_fs_privdata* pd; int inmode, outmode, inmask, outmask, mode; uint8_t ep; KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("stale cdev entries")); DPRINTFN(2, "Creating device nodes\n"); if (usbd_get_mode(udev) == USB_MODE_DEVICE) { inmode = FWRITE; outmode = FREAD; } else { /* USB_MODE_HOST */ inmode = FREAD; outmode = FWRITE; } inmask = 0; outmask = 0; desc = NULL; /* * Collect all used endpoint numbers instead of just * generating 16 static endpoints. */ cd = usbd_get_config_descriptor(udev); while ((desc = usb_desc_foreach(cd, desc))) { /* filter out all endpoint descriptors */ if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (struct usb_endpoint_descriptor *)desc; /* update masks */ ep = ed->bEndpointAddress; if (UE_GET_DIR(ep) == UE_DIR_OUT) outmask |= 1 << UE_GET_ADDR(ep); else inmask |= 1 << UE_GET_ADDR(ep); } } /* Create all available endpoints except EP0 */ for (ep = 1; ep < 16; ep++) { mode = (inmask & (1 << ep)) ? inmode : 0; mode |= (outmask & (1 << ep)) ? outmode : 0; if (mode == 0) continue; /* no IN or OUT endpoint */ pd = usb_make_dev(udev, NULL, ep, 0, mode, UID_ROOT, GID_OPERATOR, 0600); if (pd != NULL) LIST_INSERT_HEAD(&udev->pd_list, pd, pd_next); } } static void usb_cdev_free(struct usb_device *udev) { struct usb_fs_privdata* pd; DPRINTFN(2, "Freeing device nodes\n"); while ((pd = LIST_FIRST(&udev->pd_list)) != NULL) { KASSERT(pd->cdev->si_drv1 == pd, ("privdata corrupt")); LIST_REMOVE(pd, pd_next); usb_destroy_dev(pd); } } #endif /*------------------------------------------------------------------------* * usb_free_device * * This function is NULL safe and will free an USB device and its * children devices, if any. * * Flag values: Reserved, set to zero. *------------------------------------------------------------------------*/ void usb_free_device(struct usb_device *udev, uint8_t flag) { struct usb_bus *bus; if (udev == NULL) return; /* already freed */ DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); bus = udev->bus; /* set DETACHED state to prevent any further references */ usb_set_device_state(udev, USB_STATE_DETACHED); #if USB_HAVE_DEVCTL usb_notify_addq("DETACH", udev); #endif #if USB_HAVE_UGEN if (!rebooting) { printf("%s: <%s> at %s (disconnected)\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(bus->bdev)); } /* Destroy UGEN symlink, if any */ if (udev->ugen_symlink) { usb_free_symlink(udev->ugen_symlink); udev->ugen_symlink = NULL; } usb_destroy_dev(udev->ctrl_dev); #endif if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* stop receiving any control transfers (Device Side Mode) */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); } /* the following will get the device unconfigured in software */ usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0); /* final device unregister after all character devices are closed */ usb_bus_port_set_device(bus, udev->parent_hub ? udev->parent_hub->hub->ports + udev->port_index : NULL, NULL, USB_ROOT_HUB_ADDR); /* unsetup any leftover default USB transfers */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* template unsetup, if any */ (usb_temp_unsetup_p) (udev); /* * Make sure that our clear-stall messages are not queued * anywhere: */ USB_BUS_LOCK(udev->bus); usb_proc_mwait(USB_BUS_CS_PROC(udev->bus), &udev->cs_msg[0], &udev->cs_msg[1]); USB_BUS_UNLOCK(udev->bus); /* wait for all references to go away */ usb_wait_pending_refs(udev); sx_destroy(&udev->enum_sx); sx_destroy(&udev->sr_sx); cv_destroy(&udev->ctrlreq_cv); cv_destroy(&udev->ref_cv); mtx_destroy(&udev->device_mtx); #if USB_HAVE_UGEN KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("leaked cdev entries")); #endif /* Uninitialise device */ if (bus->methods->device_uninit != NULL) (bus->methods->device_uninit) (udev); /* free device */ free(udev->serial, M_USB); free(udev->manufacturer, M_USB); free(udev->product, M_USB); free(udev, M_USB); } /*------------------------------------------------------------------------* * usbd_get_iface * * This function is the safe way to get the USB interface structure * pointer by interface index. * * Return values: * NULL: Interface not present. * Else: Pointer to USB interface structure. *------------------------------------------------------------------------*/ struct usb_interface * usbd_get_iface(struct usb_device *udev, uint8_t iface_index) { struct usb_interface *iface = udev->ifaces + iface_index; if (iface_index >= udev->ifaces_max) return (NULL); return (iface); } /*------------------------------------------------------------------------* * usbd_find_descriptor * * This function will lookup the first descriptor that matches the * criteria given by the arguments "type" and "subtype". Descriptors * will only be searched within the interface having the index * "iface_index". If the "id" argument points to an USB descriptor, * it will be skipped before the search is started. This allows * searching for multiple descriptors using the same criteria. Else * the search is started after the interface descriptor. * * Return values: * NULL: End of descriptors * Else: A descriptor matching the criteria *------------------------------------------------------------------------*/ void * usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask) { struct usb_descriptor *desc; struct usb_config_descriptor *cd; struct usb_interface *iface; cd = usbd_get_config_descriptor(udev); if (cd == NULL) { return (NULL); } if (id == NULL) { iface = usbd_get_iface(udev, iface_index); if (iface == NULL) { return (NULL); } id = usbd_get_interface_descriptor(iface); if (id == NULL) { return (NULL); } } desc = (void *)id; while ((desc = usb_desc_foreach(cd, desc))) { if (desc->bDescriptorType == UDESC_INTERFACE) { break; } if (((desc->bDescriptorType & type_mask) == type) && ((desc->bDescriptorSubtype & subtype_mask) == subtype)) { return (desc); } } return (NULL); } /*------------------------------------------------------------------------* * usb_devinfo * * This function will dump information from the device descriptor * belonging to the USB device pointed to by "udev", to the string * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes * including the terminating zero. *------------------------------------------------------------------------*/ void usb_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len) { struct usb_device_descriptor *udd = &udev->ddesc; uint16_t bcdDevice; uint16_t bcdUSB; bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); if (udd->bDeviceClass != 0xFF) { snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), udd->bDeviceClass, udd->bDeviceSubClass, (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } else { snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } } #ifdef USB_VERBOSE /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { uint16_t vendor; uint16_t product; uint32_t flags; const char *vendorname; const char *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include "usbdevs.h" #include "usbdevs_data.h" #endif /* USB_VERBOSE */ static void usbd_set_device_strings(struct usb_device *udev) { struct usb_device_descriptor *udd = &udev->ddesc; #ifdef USB_VERBOSE const struct usb_knowndev *kdp; #endif char *temp_ptr; size_t temp_size; uint16_t vendor_id; uint16_t product_id; uint8_t do_unlock; /* Protect scratch area */ do_unlock = usbd_enum_lock(udev); temp_ptr = (char *)udev->scratch.data; temp_size = sizeof(udev->scratch.data); vendor_id = UGETW(udd->idVendor); product_id = UGETW(udd->idProduct); /* get serial number string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iSerialNumber); udev->serial = strdup(temp_ptr, M_USB); /* get manufacturer string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iManufacturer); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->manufacturer = strdup(temp_ptr, M_USB); /* get product string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iProduct); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->product = strdup(temp_ptr, M_USB); #ifdef USB_VERBOSE if (udev->manufacturer == NULL || udev->product == NULL) { for (kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == vendor_id && (kdp->product == product_id || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { /* XXX should use pointer to knowndevs string */ if (udev->manufacturer == NULL) { udev->manufacturer = strdup(kdp->vendorname, M_USB); } if (udev->product == NULL && (kdp->flags & USB_KNOWNDEV_NOPROD) == 0) { udev->product = strdup(kdp->productname, M_USB); } } } #endif /* Provide default strings if none were found */ if (udev->manufacturer == NULL) { snprintf(temp_ptr, temp_size, "vendor 0x%04x", vendor_id); udev->manufacturer = strdup(temp_ptr, M_USB); } if (udev->product == NULL) { snprintf(temp_ptr, temp_size, "product 0x%04x", product_id); udev->product = strdup(temp_ptr, M_USB); } if (do_unlock) usbd_enum_unlock(udev); } /* * Returns: * See: USB_MODE_XXX */ enum usb_hc_mode usbd_get_mode(struct usb_device *udev) { return (udev->flags.usb_mode); } /* * Returns: * See: USB_SPEED_XXX */ enum usb_dev_speed usbd_get_speed(struct usb_device *udev) { return (udev->speed); } uint32_t usbd_get_isoc_fps(struct usb_device *udev) { ; /* indent fix */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: return (1000); default: return (8000); } } struct usb_device_descriptor * usbd_get_device_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (&udev->ddesc); } struct usb_config_descriptor * usbd_get_config_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (udev->cdesc); } /*------------------------------------------------------------------------* * usb_test_quirk - test a device for a given quirk * * Return values: * 0: The USB device does not have the given quirk. * Else: The USB device has the given quirk. *------------------------------------------------------------------------*/ uint8_t usb_test_quirk(const struct usb_attach_arg *uaa, uint16_t quirk) { uint8_t found; uint8_t x; if (quirk == UQ_NONE) return (0); /* search the automatic per device quirks first */ for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (uaa->device->autoQuirk[x] == quirk) return (1); } /* search global quirk table, if any */ found = (usb_test_quirk_p) (&uaa->info, quirk); return (found); } struct usb_interface_descriptor * usbd_get_interface_descriptor(struct usb_interface *iface) { if (iface == NULL) return (NULL); /* be NULL safe */ return (iface->idesc); } uint8_t usbd_get_interface_altindex(struct usb_interface *iface) { return (iface->alt_index); } uint8_t usbd_get_bus_index(struct usb_device *udev) { return ((uint8_t)device_get_unit(udev->bus->bdev)); } uint8_t usbd_get_device_index(struct usb_device *udev) { return (udev->device_index); } #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *udev) { struct usb_interface *iface; struct sbuf *sb; int i; /* announce the device */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "port=%u " #if USB_HAVE_UGEN "parent=%s" #endif "", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", udev->port_no #if USB_HAVE_UGEN , udev->parent_hub != NULL ? udev->parent_hub->ugen_name : device_get_nameunit(device_get_parent(udev->bus->bdev)) #endif ); sbuf_finish(sb); devctl_notify("USB", "DEVICE", type, sbuf_data(sb)); sbuf_delete(sb); /* announce each interface */ for (i = 0; i < USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) break; /* end of interfaces */ if (iface->idesc == NULL) continue; /* no interface descriptor */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "interface=%d " "endpoints=%d " "intclass=0x%02x " "intsubclass=0x%02x " "intprotocol=0x%02x", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", iface->idesc->bInterfaceNumber, iface->idesc->bNumEndpoints, iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass, iface->idesc->bInterfaceProtocol); sbuf_finish(sb); devctl_notify("USB", "INTERFACE", type, sbuf_data(sb)); sbuf_delete(sb); } } #endif #if USB_HAVE_UGEN /*------------------------------------------------------------------------* * usb_fifo_free_wrap * * This function will free the FIFOs. * * Description of "flag" argument: If the USB_UNCFG_FLAG_FREE_EP0 flag * is set and "iface_index" is set to "USB_IFACE_INDEX_ANY", we free * all FIFOs. If the USB_UNCFG_FLAG_FREE_EP0 flag is not set and * "iface_index" is set to "USB_IFACE_INDEX_ANY", we free all non * control endpoint FIFOs. If "iface_index" is not set to * "USB_IFACE_INDEX_ANY" the flag has no effect. *------------------------------------------------------------------------*/ static void usb_fifo_free_wrap(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_fifo *f; uint16_t i; /* * Free any USB FIFOs on the given interface: */ for (i = 0; i != USB_FIFO_MAX; i++) { f = udev->fifo[i]; if (f == NULL) { continue; } /* Check if the interface index matches */ if (iface_index == f->iface_index) { if (f->methods != &usb_ugen_methods) { /* * Don't free any non-generic FIFOs in * this case. */ continue; } if ((f->dev_ep_index == 0) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else if (iface_index == USB_IFACE_INDEX_ANY) { if ((f->methods == &usb_ugen_methods) && (f->dev_ep_index == 0) && (!(flag & USB_UNCFG_FLAG_FREE_EP0)) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else { /* no need to free this FIFO */ continue; } /* free this FIFO */ usb_fifo_free(f); } } #endif /*------------------------------------------------------------------------* * usb_peer_can_wakeup * * Return values: * 0: Peer cannot do resume signalling. * Else: Peer can do resume signalling. *------------------------------------------------------------------------*/ uint8_t usb_peer_can_wakeup(struct usb_device *udev) { const struct usb_config_descriptor *cdp; cdp = udev->cdesc; if ((cdp != NULL) && (udev->flags.usb_mode == USB_MODE_HOST)) { return (cdp->bmAttributes & UC_REMOTE_WAKEUP); } return (0); /* not supported */ } void usb_set_device_state(struct usb_device *udev, enum usb_dev_state state) { KASSERT(state < USB_STATE_MAX, ("invalid udev state")); DPRINTF("udev %p state %s -> %s\n", udev, usb_statestr(udev->state), usb_statestr(state)); #if USB_HAVE_UGEN mtx_lock(&usb_ref_lock); #endif udev->state = state; #if USB_HAVE_UGEN mtx_unlock(&usb_ref_lock); #endif if (udev->bus->methods->device_state_change != NULL) (udev->bus->methods->device_state_change) (udev); } enum usb_dev_state usb_get_device_state(struct usb_device *udev) { if (udev == NULL) return (USB_STATE_DETACHED); return (udev->state); } uint8_t usbd_device_attached(struct usb_device *udev) { return (udev->state > USB_STATE_DETACHED); } /* * The following function locks enumerating the given USB device. If * the lock is already grabbed this function returns zero. Else a * non-zero value is returned. */ uint8_t usbd_enum_lock(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); sx_xlock(&udev->enum_sx); sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); return (1); } /* The following function unlocks enumerating the given USB device. */ void usbd_enum_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->enum_sx); sx_xunlock(&udev->sr_sx); } /* The following function locks suspend and resume. */ void usbd_sr_lock(struct usb_device *udev) { sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); } /* The following function unlocks suspend and resume. */ void usbd_sr_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->sr_sx); } /* * The following function checks the enumerating lock for the given * USB device. */ uint8_t usbd_enum_is_locked(struct usb_device *udev) { return (sx_xlocked(&udev->enum_sx)); } /* * The following function is used to set the per-interface specific * plug and play information. The string referred to by the pnpinfo * argument can safely be freed after calling this function. The * pnpinfo of an interface will be reset at device detach or when * passing a NULL argument to this function. This function * returns zero on success, else a USB_ERR_XXX failure code. */ usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo) { struct usb_interface *iface; iface = usbd_get_iface(udev, iface_index); if (iface == NULL) return (USB_ERR_INVAL); if (iface->pnpinfo != NULL) { free(iface->pnpinfo, M_USBDEV); iface->pnpinfo = NULL; } if (pnpinfo == NULL || pnpinfo[0] == 0) return (0); /* success */ iface->pnpinfo = strdup(pnpinfo, M_USBDEV); if (iface->pnpinfo == NULL) return (USB_ERR_NOMEM); return (0); /* success */ } usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk) { uint8_t x; for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (udev->autoQuirk[x] == 0 || udev->autoQuirk[x] == quirk) { udev->autoQuirk[x] = quirk; return (0); /* success */ } } return (USB_ERR_NOMEM); } /* * The following function is used to select the endpoint mode. It * should not be called outside enumeration context. */ usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode) { usb_error_t error; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->bus->methods->set_endpoint_mode != NULL) { error = (udev->bus->methods->set_endpoint_mode) ( udev, ep, ep_mode); } else if (ep_mode != USB_EP_MODE_DEFAULT) { error = USB_ERR_INVAL; } else { error = 0; } /* only set new mode regardless of error */ ep->ep_mode = ep_mode; if (do_unlock) usbd_enum_unlock(udev); return (error); } uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep) { return (ep->ep_mode); } Index: head/sys/dev/usb/usb_handle_request.c =================================================================== --- head/sys/dev/usb/usb_handle_request.c (revision 298931) +++ head/sys/dev/usb/usb_handle_request.c (revision 298932) @@ -1,811 +1,811 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /* function prototypes */ static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t); static usb_error_t usb_handle_remote_wakeup(struct usb_xfer *, uint8_t); static usb_error_t usb_handle_request(struct usb_xfer *); static usb_error_t usb_handle_set_config(struct usb_xfer *, uint8_t); static usb_error_t usb_handle_set_stall(struct usb_xfer *, uint8_t, uint8_t); static usb_error_t usb_handle_iface_request(struct usb_xfer *, void **, uint16_t *, struct usb_device_request, uint16_t, uint8_t); /*------------------------------------------------------------------------* * usb_handle_request_callback * * This function is the USB callback for generic USB Device control * transfers. *------------------------------------------------------------------------*/ void usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error) { usb_error_t err; /* check the current transfer state */ switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: /* handle the request */ err = usb_handle_request(xfer); if (err) { if (err == USB_ERR_BAD_CONTEXT) { /* we need to re-setup the control transfer */ usb_needs_explore(xfer->xroot->bus, 0); break; } goto tr_restart; } usbd_transfer_submit(xfer); break; default: /* check if a control transfer is active */ if (xfer->flags_int.control_rem != 0xFFFF) { /* handle the request */ err = usb_handle_request(xfer); } if (xfer->error != USB_ERR_CANCELLED) { /* should not happen - try stalling */ goto tr_restart; } break; } return; tr_restart: /* * If a control transfer is active, stall it, and wait for the * next control transfer. */ usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request)); xfer->nframes = 1; xfer->flags.manual_status = 1; xfer->flags.force_short_xfer = 0; usbd_xfer_set_stall(xfer); /* cancel previous transfer, if any */ usbd_transfer_submit(xfer); } /*------------------------------------------------------------------------* * usb_handle_set_config * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) { struct usb_device *udev = xfer->xroot->udev; usb_error_t err = 0; uint8_t do_unlock; /* * We need to protect against other threads doing probe and * attach: */ USB_XFER_UNLOCK(xfer); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (conf_no == USB_UNCONFIG_NO) { conf_no = USB_UNCONFIG_INDEX; } else { /* * The relationship between config number and config index * is very simple in our case: */ conf_no--; } if (usbd_set_config_index(udev, conf_no)) { DPRINTF("set config %d failed\n", conf_no); err = USB_ERR_STALLED; goto done; } if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { DPRINTF("probe and attach failed\n"); err = USB_ERR_STALLED; goto done; } done: if (do_unlock) usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (err); } static usb_error_t usb_check_alt_setting(struct usb_device *udev, struct usb_interface *iface, uint8_t alt_index) { uint8_t do_unlock; usb_error_t err = 0; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) err = USB_ERR_INVAL; if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usb_handle_iface_request * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_iface_request(struct usb_xfer *xfer, void **ppdata, uint16_t *plen, struct usb_device_request req, uint16_t off, uint8_t state) { struct usb_interface *iface; struct usb_interface *iface_parent; /* parent interface */ struct usb_device *udev = xfer->xroot->udev; int error; uint8_t iface_index; uint8_t temp_state; uint8_t do_unlock; if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { iface_index = req.wIndex[0]; /* unicast */ } else { iface_index = 0; /* broadcast */ } /* * We need to protect against other threads doing probe and * attach: */ USB_XFER_UNLOCK(xfer); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); error = ENXIO; tr_repeat: iface = usbd_get_iface(udev, iface_index); if ((iface == NULL) || (iface->idesc == NULL)) { /* end of interfaces non-existing interface */ goto tr_stalled; } /* set initial state */ temp_state = state; /* forward request to interface, if any */ if ((error != 0) && (error != ENOTTY) && (iface->subdev != NULL) && device_is_attached(iface->subdev)) { #if 0 DEVMETHOD(usb_handle_request, NULL); /* dummy */ #endif error = USB_HANDLE_REQUEST(iface->subdev, &req, ppdata, plen, off, &temp_state); } iface_parent = usbd_get_iface(udev, iface->parent_iface_index); if ((iface_parent == NULL) || (iface_parent->idesc == NULL)) { /* non-existing interface */ iface_parent = NULL; } /* forward request to parent interface, if any */ if ((error != 0) && (error != ENOTTY) && (iface_parent != NULL) && (iface_parent->subdev != NULL) && ((req.bmRequestType & 0x1F) == UT_INTERFACE) && (iface_parent->subdev != iface->subdev) && device_is_attached(iface_parent->subdev)) { error = USB_HANDLE_REQUEST(iface_parent->subdev, &req, ppdata, plen, off, &temp_state); } if (error == 0) { /* negativly adjust pointer and length */ *ppdata = ((uint8_t *)(*ppdata)) - off; *plen += off; if ((state == USB_HR_NOT_COMPLETE) && (temp_state == USB_HR_COMPLETE_OK)) goto tr_short; else goto tr_valid; } else if (error == ENOTTY) { goto tr_stalled; } if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { iface_index++; /* iterate */ goto tr_repeat; } if (state != USB_HR_NOT_COMPLETE) { /* we are complete */ goto tr_valid; } switch (req.bmRequestType) { case UT_WRITE_INTERFACE: switch (req.bRequest) { case UR_SET_INTERFACE: /* * We assume that the endpoints are the same - * accross the alternate settings. + * across the alternate settings. * * Reset the endpoints, because re-attaching * only a part of the device is not possible. */ error = usb_check_alt_setting(udev, iface, req.wValue[0]); if (error) { DPRINTF("alt setting does not exist %s\n", usbd_errstr(error)); goto tr_stalled; } error = usb_reset_iface_endpoints(udev, iface_index); if (error) { DPRINTF("alt setting failed %s\n", usbd_errstr(error)); goto tr_stalled; } /* update the current alternate setting */ iface->alt_index = req.wValue[0]; break; default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (req.bRequest) { case UR_GET_INTERFACE: *ppdata = &iface->alt_index; *plen = 1; break; default: goto tr_stalled; } break; default: goto tr_stalled; } tr_valid: if (do_unlock) usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (0); tr_short: if (do_unlock) usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (USB_ERR_SHORT_XFER); tr_stalled: if (do_unlock) usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (USB_ERR_STALLED); } /*------------------------------------------------------------------------* * usb_handle_stall * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) { struct usb_device *udev = xfer->xroot->udev; usb_error_t err; USB_XFER_UNLOCK(xfer); err = usbd_set_endpoint_stall(udev, usbd_get_ep_by_addr(udev, ep), do_stall); USB_XFER_LOCK(xfer); return (err); } /*------------------------------------------------------------------------* * usb_handle_get_stall * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val) { struct usb_endpoint *ep; uint8_t halted; ep = usbd_get_ep_by_addr(udev, ea_val); if (ep == NULL) { /* nothing to do */ return (0); } USB_BUS_LOCK(udev->bus); halted = ep->is_stalled; USB_BUS_UNLOCK(udev->bus); return (halted); } /*------------------------------------------------------------------------* * usb_handle_remote_wakeup * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) { struct usb_device *udev; struct usb_bus *bus; udev = xfer->xroot->udev; bus = udev->bus; USB_BUS_LOCK(bus); if (is_on) { udev->flags.remote_wakeup = 1; } else { udev->flags.remote_wakeup = 0; } USB_BUS_UNLOCK(bus); #if USB_HAVE_POWERD /* In case we are out of sync, update the power state. */ usb_bus_power_update(udev->bus); #endif return (0); /* success */ } /*------------------------------------------------------------------------* * usb_handle_request * * Internal state sequence: * * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR * * Returns: * 0: Ready to start hardware * Else: Stall current transfer, if any *------------------------------------------------------------------------*/ static usb_error_t usb_handle_request(struct usb_xfer *xfer) { struct usb_device_request req; struct usb_device *udev; const void *src_zcopy; /* zero-copy source pointer */ const void *src_mcopy; /* non zero-copy source pointer */ uint16_t off; /* data offset */ uint16_t rem; /* data remainder */ uint16_t max_len; /* max fragment length */ uint16_t wValue; uint8_t state; uint8_t is_complete = 1; usb_error_t err; union { uWord wStatus; uint8_t buf[2]; } temp; /* * Filter the USB transfer state into * something which we understand: */ switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: state = USB_HR_NOT_COMPLETE; if (!xfer->flags_int.control_act) { /* nothing to do */ goto tr_stalled; } break; case USB_ST_TRANSFERRED: if (!xfer->flags_int.control_act) { state = USB_HR_COMPLETE_OK; } else { state = USB_HR_NOT_COMPLETE; } break; default: state = USB_HR_COMPLETE_ERR; break; } /* reset frame stuff */ usbd_xfer_set_frame_len(xfer, 0, 0); usbd_xfer_set_frame_offset(xfer, 0, 0); usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); /* get the current request, if any */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); if (xfer->flags_int.control_rem == 0xFFFF) { /* first time - not initialised */ rem = UGETW(req.wLength); off = 0; } else { /* not first time - initialised */ rem = xfer->flags_int.control_rem; off = UGETW(req.wLength) - rem; } /* set some defaults */ max_len = 0; src_zcopy = NULL; src_mcopy = NULL; udev = xfer->xroot->udev; /* get some request fields decoded */ wValue = UGETW(req.wValue); DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, req.bRequest, wValue, UGETW(req.wIndex), off, rem, state); /* demultiplex the control request */ switch (req.bmRequestType) { case UT_READ_DEVICE: if (state != USB_HR_NOT_COMPLETE) { break; } switch (req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (req.bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: switch (wValue) { case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (wValue) { case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (req.bRequest) { case UR_CLEAR_FEATURE: switch (wValue) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (wValue) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; default: goto tr_stalled; } break; default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (req.bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; default: /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ err = usb_handle_iface_request(xfer, USB_ADD_BYTES(&src_zcopy, 0), &max_len, req, off, state); if (err == 0) { is_complete = 0; goto tr_valid; } else if (err == USB_ERR_SHORT_XFER) { goto tr_valid; } /* * Reset zero-copy pointer and max length * variable in case they were unintentionally * set: */ src_zcopy = NULL; max_len = 0; /* * Check if we have a vendor specific * descriptor: */ goto tr_handle_get_descriptor; } goto tr_valid; tr_handle_get_descriptor: err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); if (err) goto tr_stalled; if (src_zcopy == NULL) goto tr_stalled; goto tr_valid; tr_handle_get_config: temp.buf[0] = udev->curr_config_no; src_mcopy = temp.buf; max_len = 1; goto tr_valid; tr_handle_get_status: wValue = 0; USB_BUS_LOCK(udev->bus); if (udev->flags.remote_wakeup) { wValue |= UDS_REMOTE_WAKEUP; } if (udev->flags.self_powered) { wValue |= UDS_SELF_POWERED; } USB_BUS_UNLOCK(udev->bus); USETW(temp.wStatus, wValue); src_mcopy = temp.wStatus; max_len = sizeof(temp.wStatus); goto tr_valid; tr_handle_set_address: if (state == USB_HR_NOT_COMPLETE) { if (wValue >= 0x80) { /* invalid value */ goto tr_stalled; } else if (udev->curr_config_no != 0) { /* we are configured ! */ goto tr_stalled; } } else if (state != USB_HR_NOT_COMPLETE) { udev->address = (wValue & 0x7F); goto tr_bad_context; } goto tr_valid; tr_handle_set_config: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_set_config(xfer, req.wValue[0])) { goto tr_stalled; } } goto tr_valid; tr_handle_clear_halt: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { goto tr_stalled; } } goto tr_valid; tr_handle_clear_wakeup: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_remote_wakeup(xfer, 0)) { goto tr_stalled; } } goto tr_valid; tr_handle_set_halt: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { goto tr_stalled; } } goto tr_valid; tr_handle_set_wakeup: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_remote_wakeup(xfer, 1)) { goto tr_stalled; } } goto tr_valid; tr_handle_get_ep_status: if (state == USB_HR_NOT_COMPLETE) { temp.wStatus[0] = usb_handle_get_stall(udev, req.wIndex[0]); temp.wStatus[1] = 0; src_mcopy = temp.wStatus; max_len = sizeof(temp.wStatus); } goto tr_valid; tr_valid: if (state != USB_HR_NOT_COMPLETE) { goto tr_stalled; } /* subtract offset from length */ max_len -= off; /* Compute the real maximum data length */ if (max_len > xfer->max_data_length) { max_len = usbd_xfer_max_len(xfer); } if (max_len > rem) { max_len = rem; } /* * If the remainder is greater than the maximum data length, * we need to truncate the value for the sake of the * comparison below: */ if (rem > xfer->max_data_length) { rem = usbd_xfer_max_len(xfer); } if ((rem != max_len) && (is_complete != 0)) { /* * If we don't transfer the data we can transfer, then * the transfer is short ! */ xfer->flags.force_short_xfer = 1; xfer->nframes = 2; } else { /* * Default case */ xfer->flags.force_short_xfer = 0; xfer->nframes = max_len ? 2 : 1; } if (max_len > 0) { if (src_mcopy) { src_mcopy = USB_ADD_BYTES(src_mcopy, off); usbd_copy_in(xfer->frbuffers + 1, 0, src_mcopy, max_len); usbd_xfer_set_frame_len(xfer, 1, max_len); } else { usbd_xfer_set_frame_data(xfer, 1, USB_ADD_BYTES(src_zcopy, off), max_len); } } else { /* the end is reached, send status */ xfer->flags.manual_status = 0; usbd_xfer_set_frame_len(xfer, 1, 0); } DPRINTF("success\n"); return (0); /* success */ tr_stalled: DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? "complete" : "stalled"); return (USB_ERR_STALLED); tr_bad_context: DPRINTF("bad context\n"); return (USB_ERR_BAD_CONTEXT); } Index: head/sys/dev/usb/usb_hid.c =================================================================== --- head/sys/dev/usb/usb_hid.c (revision 298931) +++ head/sys/dev/usb/usb_hid.c (revision 298932) @@ -1,925 +1,925 @@ /* $FreeBSD$ */ /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ static void hid_clear_local(struct hid_item *); static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); #define MAXUSAGE 64 #define MAXPUSH 4 #define MAXID 16 struct hid_pos_data { int32_t rid; uint32_t pos; }; struct hid_data { const uint8_t *start; const uint8_t *end; const uint8_t *p; struct hid_item cur[MAXPUSH]; struct hid_pos_data last_pos[MAXID]; int32_t usages_min[MAXUSAGE]; int32_t usages_max[MAXUSAGE]; int32_t usage_last; /* last seen usage */ uint32_t loc_size; /* last seen size */ uint32_t loc_count; /* last seen count */ uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ uint8_t pushlevel; /* current pushlevel */ uint8_t ncount; /* end usage item count */ uint8_t icount; /* current usage item count */ uint8_t nusage; /* end "usages_min/max" index */ uint8_t iusage; /* current "usages_min/max" index */ uint8_t ousage; /* current "usages_min/max" offset */ uint8_t susage; /* usage set flags */ }; /*------------------------------------------------------------------------* * hid_clear_local *------------------------------------------------------------------------*/ static void hid_clear_local(struct hid_item *c) { c->loc.count = 0; c->loc.size = 0; c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; c->designator_index = 0; c->designator_minimum = 0; c->designator_maximum = 0; c->string_index = 0; c->string_minimum = 0; c->string_maximum = 0; c->set_delimiter = 0; } static void hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) { uint8_t i; /* check for same report ID - optimise */ if (c->report_ID == next_rID) return; /* save current position for current rID */ if (c->report_ID == 0) { i = 0; } else { for (i = 1; i != MAXID; i++) { if (s->last_pos[i].rid == c->report_ID) break; if (s->last_pos[i].rid == 0) break; } } if (i != MAXID) { s->last_pos[i].rid = c->report_ID; s->last_pos[i].pos = c->loc.pos; } /* store next report ID */ c->report_ID = next_rID; /* lookup last position for next rID */ if (next_rID == 0) { i = 0; } else { for (i = 1; i != MAXID; i++) { if (s->last_pos[i].rid == next_rID) break; if (s->last_pos[i].rid == 0) break; } } if (i != MAXID) { s->last_pos[i].rid = next_rID; c->loc.pos = s->last_pos[i].pos; } else { DPRINTF("Out of RID entries, position is set to zero!\n"); c->loc.pos = 0; } } /*------------------------------------------------------------------------* * hid_start_parse *------------------------------------------------------------------------*/ struct hid_data * hid_start_parse(const void *d, usb_size_t len, int kindset) { struct hid_data *s; if ((kindset-1) & kindset) { DPRINTFN(0, "Only one bit can be " "set in the kindset\n"); return (NULL); } s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); s->start = s->p = d; s->end = ((const uint8_t *)d) + len; s->kindset = kindset; return (s); } /*------------------------------------------------------------------------* * hid_end_parse *------------------------------------------------------------------------*/ void hid_end_parse(struct hid_data *s) { if (s == NULL) return; free(s, M_TEMP); } /*------------------------------------------------------------------------* * get byte from HID descriptor *------------------------------------------------------------------------*/ static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize) { const uint8_t *ptr; uint8_t retval; ptr = s->p; /* check if end is reached */ if (ptr == s->end) return (0); /* read out a byte */ retval = *ptr; /* check if data pointer can be advanced by "wSize" bytes */ if ((s->end - ptr) < wSize) ptr = s->end; else ptr += wSize; /* update pointer */ s->p = ptr; return (retval); } /*------------------------------------------------------------------------* * hid_get_item *------------------------------------------------------------------------*/ int hid_get_item(struct hid_data *s, struct hid_item *h) { struct hid_item *c; unsigned int bTag, bType, bSize; uint32_t oldpos; int32_t mask; int32_t dval; if (s == NULL) return (0); c = &s->cur[s->pushlevel]; top: /* check if there is an array of items */ if (s->icount < s->ncount) { /* get current usage */ if (s->iusage < s->nusage) { dval = s->usages_min[s->iusage] + s->ousage; c->usage = dval; s->usage_last = dval; if (dval == s->usages_max[s->iusage]) { s->iusage ++; s->ousage = 0; } else { s->ousage ++; } } else { DPRINTFN(1, "Using last usage\n"); dval = s->usage_last; } s->icount ++; /* * Only copy HID item, increment position and return * if correct kindset! */ if (s->kindset & (1 << c->kind)) { *h = *c; DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, h->loc.size, h->loc.count); c->loc.pos += c->loc.size * c->loc.count; return (1); } } /* reset state variables */ s->icount = 0; s->ncount = 0; s->iusage = 0; s->nusage = 0; s->susage = 0; s->ousage = 0; hid_clear_local(c); /* get next item */ while (s->p != s->end) { bSize = hid_get_byte(s, 1); if (bSize == 0xfe) { /* long item */ bSize = hid_get_byte(s, 1); bSize |= hid_get_byte(s, 1) << 8; bTag = hid_get_byte(s, 1); bType = 0xff; /* XXX what should it be */ } else { /* short item */ bTag = bSize >> 4; bType = (bSize >> 2) & 3; bSize &= 3; if (bSize == 3) bSize = 4; } switch (bSize) { case 0: dval = 0; mask = 0; break; case 1: dval = (int8_t)hid_get_byte(s, 1); mask = 0xFF; break; case 2: dval = hid_get_byte(s, 1); dval |= hid_get_byte(s, 1) << 8; dval = (int16_t)dval; mask = 0xFFFF; break; case 4: dval = hid_get_byte(s, 1); dval |= hid_get_byte(s, 1) << 8; dval |= hid_get_byte(s, 1) << 16; dval |= hid_get_byte(s, 1) << 24; mask = 0xFFFFFFFF; break; default: dval = hid_get_byte(s, bSize); DPRINTFN(0, "bad length %u (data=0x%02x)\n", bSize, dval); continue; } switch (bType) { case 0: /* Main */ switch (bTag) { case 8: /* Input */ c->kind = hid_input; c->flags = dval; ret: c->loc.count = s->loc_count; c->loc.size = s->loc_size; if (c->flags & HIO_VARIABLE) { /* range check usage count */ if (c->loc.count > 255) { DPRINTFN(0, "Number of " "items(%u) truncated to 255\n", (unsigned)(c->loc.count)); s->ncount = 255; } else s->ncount = c->loc.count; /* * The "top" loop will return * one and one item: */ c->loc.count = 1; } else { s->ncount = 1; } goto top; case 9: /* Output */ c->kind = hid_output; c->flags = dval; goto ret; case 10: /* Collection */ c->kind = hid_collection; c->collection = dval; c->collevel++; c->usage = s->usage_last; *h = *c; return (1); case 11: /* Feature */ c->kind = hid_feature; c->flags = dval; goto ret; case 12: /* End collection */ c->kind = hid_endcollection; if (c->collevel == 0) { DPRINTFN(0, "invalid end collection\n"); return (0); } c->collevel--; *h = *c; return (1); default: DPRINTFN(0, "Main bTag=%d\n", bTag); break; } break; case 1: /* Global */ switch (bTag) { case 0: c->_usage_page = dval << 16; break; case 1: c->logical_minimum = dval; break; case 2: c->logical_maximum = dval; break; case 3: c->physical_minimum = dval; break; case 4: c->physical_maximum = dval; break; case 5: c->unit_exponent = dval; break; case 6: c->unit = dval; break; case 7: /* mask because value is unsigned */ s->loc_size = dval & mask; break; case 8: hid_switch_rid(s, c, dval & mask); break; case 9: /* mask because value is unsigned */ s->loc_count = dval & mask; break; case 10: /* Push */ s->pushlevel ++; if (s->pushlevel < MAXPUSH) { s->cur[s->pushlevel] = *c; /* store size and count */ c->loc.size = s->loc_size; c->loc.count = s->loc_count; /* update current item pointer */ c = &s->cur[s->pushlevel]; } else { DPRINTFN(0, "Cannot push " "item @ %d\n", s->pushlevel); } break; case 11: /* Pop */ s->pushlevel --; if (s->pushlevel < MAXPUSH) { /* preserve position */ oldpos = c->loc.pos; c = &s->cur[s->pushlevel]; /* restore size and count */ s->loc_size = c->loc.size; s->loc_count = c->loc.count; /* set default item location */ c->loc.pos = oldpos; c->loc.size = 0; c->loc.count = 0; } else { DPRINTFN(0, "Cannot pop " "item @ %d\n", s->pushlevel); } break; default: DPRINTFN(0, "Global bTag=%d\n", bTag); break; } break; case 2: /* Local */ switch (bTag) { case 0: if (bSize != 4) dval = (dval & mask) | c->_usage_page; /* set last usage, in case of a collection */ s->usage_last = dval; if (s->nusage < MAXUSAGE) { s->usages_min[s->nusage] = dval; s->usages_max[s->nusage] = dval; s->nusage ++; } else { DPRINTFN(0, "max usage reached\n"); } /* clear any pending usage sets */ s->susage = 0; break; case 1: s->susage |= 1; if (bSize != 4) dval = (dval & mask) | c->_usage_page; c->usage_minimum = dval; goto check_set; case 2: s->susage |= 2; if (bSize != 4) dval = (dval & mask) | c->_usage_page; c->usage_maximum = dval; check_set: if (s->susage != 3) break; /* sanity check */ if ((s->nusage < MAXUSAGE) && (c->usage_minimum <= c->usage_maximum)) { /* add usage range */ s->usages_min[s->nusage] = c->usage_minimum; s->usages_max[s->nusage] = c->usage_maximum; s->nusage ++; } else { DPRINTFN(0, "Usage set dropped\n"); } s->susage = 0; break; case 3: c->designator_index = dval; break; case 4: c->designator_minimum = dval; break; case 5: c->designator_maximum = dval; break; case 7: c->string_index = dval; break; case 8: c->string_minimum = dval; break; case 9: c->string_maximum = dval; break; case 10: c->set_delimiter = dval; break; default: DPRINTFN(0, "Local bTag=%d\n", bTag); break; } break; default: DPRINTFN(0, "default bType=%d\n", bType); break; } } return (0); } /*------------------------------------------------------------------------* * hid_report_size *------------------------------------------------------------------------*/ int hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) { struct hid_data *d; struct hid_item h; uint32_t temp; uint32_t hpos; uint32_t lpos; uint8_t any_id; any_id = 0; hpos = 0; lpos = 0xFFFFFFFF; for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { if (h.kind == k) { - /* check for ID-byte presense */ + /* check for ID-byte presence */ if ((h.report_ID != 0) && !any_id) { if (id != NULL) *id = h.report_ID; any_id = 1; } /* compute minimum */ if (lpos > h.loc.pos) lpos = h.loc.pos; /* compute end position */ temp = h.loc.pos + (h.loc.size * h.loc.count); /* compute maximum */ if (hpos < temp) hpos = temp; } } hid_end_parse(d); /* safety check - can happen in case of currupt descriptors */ if (lpos > hpos) temp = 0; else temp = hpos - lpos; /* check for ID byte */ if (any_id) temp += 8; else if (id != NULL) *id = 0; /* return length in bytes rounded up */ return ((temp + 7) / 8); } /*------------------------------------------------------------------------* * hid_locate *------------------------------------------------------------------------*/ int hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) { struct hid_data *d; struct hid_item h; for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { if (index--) continue; if (loc != NULL) *loc = h.loc; if (flags != NULL) *flags = h.flags; if (id != NULL) *id = h.report_ID; hid_end_parse(d); return (1); } } if (loc != NULL) loc->size = 0; if (flags != NULL) *flags = 0; if (id != NULL) *id = 0; hid_end_parse(d); return (0); } /*------------------------------------------------------------------------* * hid_get_data *------------------------------------------------------------------------*/ static uint32_t hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc, int is_signed) { uint32_t hpos = loc->pos; uint32_t hsize = loc->size; uint32_t data; uint32_t rpos; uint8_t n; DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); /* Range check and limit */ if (hsize == 0) return (0); if (hsize > 32) hsize = 32; /* Get data in a safe way */ data = 0; rpos = (hpos / 8); n = (hsize + 7) / 8; rpos += n; while (n--) { rpos--; if (rpos < len) data |= buf[rpos] << (8 * n); } /* Correctly shift down data */ data = (data >> (hpos % 8)); n = 32 - hsize; /* Mask and sign extend in one */ if (is_signed != 0) data = (int32_t)((int32_t)data << n) >> n; else data = (uint32_t)((uint32_t)data << n) >> n; DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", loc->pos, loc->size, (long)data); return (data); } int32_t hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) { return (hid_get_data_sub(buf, len, loc, 1)); } uint32_t hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc) { return (hid_get_data_sub(buf, len, loc, 0)); } /*------------------------------------------------------------------------* * hid_put_data *------------------------------------------------------------------------*/ void hid_put_data_unsigned(uint8_t *buf, usb_size_t len, struct hid_location *loc, unsigned int value) { uint32_t hpos = loc->pos; uint32_t hsize = loc->size; uint64_t data; uint64_t mask; uint32_t rpos; uint8_t n; DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); /* Range check and limit */ if (hsize == 0) return; if (hsize > 32) hsize = 32; /* Put data in a safe way */ rpos = (hpos / 8); n = (hsize + 7) / 8; data = ((uint64_t)value) << (hpos % 8); mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); rpos += n; while (n--) { rpos--; if (rpos < len) { buf[rpos] &= ~(mask >> (8 * n)); buf[rpos] |= (data >> (8 * n)); } } } /*------------------------------------------------------------------------* * hid_is_collection *------------------------------------------------------------------------*/ int hid_is_collection(const void *desc, usb_size_t size, int32_t usage) { struct hid_data *hd; struct hid_item hi; int err; hd = hid_start_parse(desc, size, hid_input); if (hd == NULL) return (0); while ((err = hid_get_item(hd, &hi))) { if (hi.kind == hid_collection && hi.usage == usage) break; } hid_end_parse(hd); return (err); } /*------------------------------------------------------------------------* * hid_get_descriptor_from_usb * * This function will search for a HID descriptor between two USB * interface descriptors. * * Return values: * NULL: No more HID descriptors. * Else: Pointer to HID descriptor. *------------------------------------------------------------------------*/ struct usb_hid_descriptor * hid_get_descriptor_from_usb(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id) { struct usb_descriptor *desc = (void *)id; if (desc == NULL) { return (NULL); } while ((desc = usb_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_HID) && (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { return (void *)desc; } if (desc->bDescriptorType == UDESC_INTERFACE) { break; } } return (NULL); } /*------------------------------------------------------------------------* * usbd_req_get_hid_desc * * This function will read out an USB report descriptor from the USB * device. * * Return values: * NULL: Failure. * Else: Success. The pointer should eventually be passed to free(). *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, struct malloc_type *mem, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_hid_descriptor *hid; usb_error_t err; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } hid = hid_get_descriptor_from_usb (usbd_get_config_descriptor(udev), iface->idesc); if (hid == NULL) { return (USB_ERR_IOERROR); } *sizep = UGETW(hid->descrs[0].wDescriptorLength); if (*sizep == 0) { return (USB_ERR_IOERROR); } if (mtx) mtx_unlock(mtx); *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK); if (mtx) mtx_lock(mtx); if (*descp == NULL) { return (USB_ERR_NOMEM); } err = usbd_req_get_report_descriptor (udev, mtx, *descp, *sizep, iface_index); if (err) { free(*descp, mem); *descp = NULL; return (err); } return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * hid_is_mouse * * This function will decide if a USB descriptor belongs to a USB mouse. * * Return values: * Zero: Not a USB mouse. * Else: Is a USB mouse. *------------------------------------------------------------------------*/ int hid_is_mouse(const void *d_ptr, uint16_t d_len) { struct hid_data *hd; struct hid_item hi; int mdepth; int found; hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); if (hd == NULL) return (0); mdepth = 0; found = 0; while (hid_get_item(hd, &hi)) { switch (hi.kind) { case hid_collection: if (mdepth != 0) mdepth++; else if (hi.collection == 1 && hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) mdepth++; break; case hid_endcollection: if (mdepth != 0) mdepth--; break; case hid_input: if (mdepth == 0) break; if (hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) found++; if (hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) found++; break; default: break; } } hid_end_parse(hd); return (found); } /*------------------------------------------------------------------------* * hid_is_keyboard * * This function will decide if a USB descriptor belongs to a USB keyboard. * * Return values: * Zero: Not a USB keyboard. * Else: Is a USB keyboard. *------------------------------------------------------------------------*/ int hid_is_keyboard(const void *d_ptr, uint16_t d_len) { if (hid_is_collection(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) return (1); return (0); } Index: head/sys/dev/usb/usb_hub.c =================================================================== --- head/sys/dev/usb/usb_hub.c (revision 298931) +++ head/sys/dev/usb/usb_hub.c (revision 298932) @@ -1,2924 +1,2924 @@ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR uhub_debug #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #define UHUB_INTR_INTERVAL 250 /* ms */ enum { UHUB_INTR_TRANSFER, #if USB_HAVE_TT_SUPPORT UHUB_RESET_TT_TRANSFER, #endif UHUB_N_TRANSFER, }; #ifdef USB_DEBUG static int uhub_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB"); SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RWTUN, &uhub_debug, 0, "Debug level"); #endif #if USB_HAVE_POWERD static int usb_power_timeout = 30; /* seconds */ SYSCTL_INT(_hw_usb, OID_AUTO, power_timeout, CTLFLAG_RWTUN, &usb_power_timeout, 0, "USB power timeout"); #endif #if USB_HAVE_DISABLE_ENUM static int usb_disable_enumeration = 0; SYSCTL_INT(_hw_usb, OID_AUTO, disable_enumeration, CTLFLAG_RWTUN, &usb_disable_enumeration, 0, "Set to disable all USB device enumeration."); static int usb_disable_port_power = 0; SYSCTL_INT(_hw_usb, OID_AUTO, disable_port_power, CTLFLAG_RWTUN, &usb_disable_port_power, 0, "Set to disable all USB port power."); #endif struct uhub_current_state { uint16_t port_change; uint16_t port_status; }; struct uhub_softc { struct uhub_current_state sc_st;/* current state */ #if (USB_HAVE_FIXED_PORT != 0) struct usb_hub sc_hub; #endif device_t sc_dev; /* base device */ struct mtx sc_mtx; /* our mutex */ struct usb_device *sc_udev; /* USB device */ struct usb_xfer *sc_xfer[UHUB_N_TRANSFER]; /* interrupt xfer */ #if USB_HAVE_DISABLE_ENUM int sc_disable_enumeration; int sc_disable_port_power; #endif uint8_t sc_flags; #define UHUB_FLAG_DID_EXPLORE 0x01 }; #define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol) #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) #define UHUB_IS_MULTI_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBMTT) #define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB) /* prototypes for type checking: */ static device_probe_t uhub_probe; static device_attach_t uhub_attach; static device_detach_t uhub_detach; static device_suspend_t uhub_suspend; static device_resume_t uhub_resume; static bus_driver_added_t uhub_driver_added; static bus_child_location_str_t uhub_child_location_string; static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string; static usb_callback_t uhub_intr_callback; #if USB_HAVE_TT_SUPPORT static usb_callback_t uhub_reset_tt_callback; #endif static void usb_dev_resume_peer(struct usb_device *udev); static void usb_dev_suspend_peer(struct usb_device *udev); static uint8_t usb_peer_should_wakeup(struct usb_device *udev); static const struct usb_config uhub_config[UHUB_N_TRANSFER] = { [UHUB_INTR_TRANSFER] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_ANY, .timeout = 0, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uhub_intr_callback, .interval = UHUB_INTR_INTERVAL, }, #if USB_HAVE_TT_SUPPORT [UHUB_RESET_TT_TRANSFER] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &uhub_reset_tt_callback, .timeout = 1000, /* 1 second */ .usb_mode = USB_MODE_HOST, }, #endif }; /* * driver instance for "hub" connected to "usb" * and "hub" connected to "hub" */ static devclass_t uhub_devclass; static device_method_t uhub_methods[] = { DEVMETHOD(device_probe, uhub_probe), DEVMETHOD(device_attach, uhub_attach), DEVMETHOD(device_detach, uhub_detach), DEVMETHOD(device_suspend, uhub_suspend), DEVMETHOD(device_resume, uhub_resume), DEVMETHOD(bus_child_location_str, uhub_child_location_string), DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string), DEVMETHOD(bus_driver_added, uhub_driver_added), DEVMETHOD_END }; static driver_t uhub_driver = { .name = "uhub", .methods = uhub_methods, .size = sizeof(struct uhub_softc) }; DRIVER_MODULE(uhub, usbus, uhub_driver, uhub_devclass, 0, 0); DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, NULL, 0); MODULE_VERSION(uhub, 1); static void uhub_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhub_softc *sc = usbd_xfer_softc(xfer); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(2, "\n"); /* * This is an indication that some port * has changed status. Notify the bus * event handler thread that we need * to be explored again: */ usb_needs_explore(sc->sc_udev->bus, 0); case USB_ST_SETUP: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* * Do a clear-stall. The "stall_pipe" flag * will get cleared before next callback by * the USB stack. */ usbd_xfer_set_stall(xfer); usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; } } /*------------------------------------------------------------------------* * uhub_reset_tt_proc * * This function starts the TT reset USB request *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT static void uhub_reset_tt_proc(struct usb_proc_msg *_pm) { struct usb_udev_msg *pm = (void *)_pm; struct usb_device *udev = pm->udev; struct usb_hub *hub; struct uhub_softc *sc; hub = udev->hub; if (hub == NULL) return; sc = hub->hubsoftc; if (sc == NULL) return; /* Change lock */ USB_BUS_UNLOCK(udev->bus); mtx_lock(&sc->sc_mtx); /* Start transfer */ usbd_transfer_start(sc->sc_xfer[UHUB_RESET_TT_TRANSFER]); /* Change lock */ mtx_unlock(&sc->sc_mtx); USB_BUS_LOCK(udev->bus); } #endif /*------------------------------------------------------------------------* * uhub_tt_buffer_reset_async_locked * * This function queues a TT reset for the given USB device and endpoint. *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT void uhub_tt_buffer_reset_async_locked(struct usb_device *child, struct usb_endpoint *ep) { struct usb_device_request req; struct usb_device *udev; struct usb_hub *hub; struct usb_port *up; uint16_t wValue; uint8_t port; if (child == NULL || ep == NULL) return; udev = child->parent_hs_hub; port = child->hs_port_no; if (udev == NULL) return; hub = udev->hub; if ((hub == NULL) || (udev->speed != USB_SPEED_HIGH) || (child->speed != USB_SPEED_LOW && child->speed != USB_SPEED_FULL) || (child->flags.usb_mode != USB_MODE_HOST) || (port == 0) || (ep->edesc == NULL)) { /* not applicable */ return; } USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); up = hub->ports + port - 1; if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; /* if we already received a clear buffer request, reset the whole TT */ if (up->req_reset_tt.bRequest != 0) { req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_RESET_TT; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); } else { wValue = (ep->edesc->bEndpointAddress & 0xF) | ((child->address & 0x7F) << 4) | ((ep->edesc->bEndpointAddress & 0x80) << 8) | ((ep->edesc->bmAttributes & 3) << 12); req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_TT_BUFFER; USETW(req.wValue, wValue); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); } up->req_reset_tt = req; /* get reset transfer started */ usb_proc_msignal(USB_BUS_TT_PROC(udev->bus), &hub->tt_msg[0], &hub->tt_msg[1]); } #endif #if USB_HAVE_TT_SUPPORT static void uhub_reset_tt_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhub_softc *sc; struct usb_device *udev; struct usb_port *up; uint8_t x; DPRINTF("TT buffer reset\n"); sc = usbd_xfer_softc(xfer); udev = sc->sc_udev; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: USB_BUS_LOCK(udev->bus); /* find first port which needs a TT reset */ for (x = 0; x != udev->hub->nports; x++) { up = udev->hub->ports + x; if (up->req_reset_tt.bRequest == 0) continue; /* copy in the transfer */ usbd_copy_in(xfer->frbuffers, 0, &up->req_reset_tt, sizeof(up->req_reset_tt)); /* reset buffer */ memset(&up->req_reset_tt, 0, sizeof(up->req_reset_tt)); /* set length */ usbd_xfer_set_frame_len(xfer, 0, sizeof(up->req_reset_tt)); xfer->nframes = 1; USB_BUS_UNLOCK(udev->bus); usbd_transfer_submit(xfer); return; } USB_BUS_UNLOCK(udev->bus); break; default: if (error == USB_ERR_CANCELLED) break; DPRINTF("TT buffer reset failed (%s)\n", usbd_errstr(error)); goto tr_setup; } } #endif /*------------------------------------------------------------------------* * uhub_count_active_host_ports * * This function counts the number of active ports at the given speed. *------------------------------------------------------------------------*/ uint8_t uhub_count_active_host_ports(struct usb_device *udev, enum usb_dev_speed speed) { struct uhub_softc *sc; struct usb_device *child; struct usb_hub *hub; struct usb_port *up; uint8_t retval = 0; uint8_t x; if (udev == NULL) goto done; hub = udev->hub; if (hub == NULL) goto done; sc = hub->hubsoftc; if (sc == NULL) goto done; for (x = 0; x != hub->nports; x++) { up = hub->ports + x; child = usb_bus_port_get_device(udev->bus, up); if (child != NULL && child->flags.usb_mode == USB_MODE_HOST && child->speed == speed) retval++; } done: return (retval); } void uhub_explore_handle_re_enumerate(struct usb_device *child) { uint8_t do_unlock; usb_error_t err; /* check if device should be re-enumerated */ if (child->flags.usb_mode != USB_MODE_HOST) return; do_unlock = usbd_enum_lock(child); switch (child->re_enumerate_wait) { case USB_RE_ENUM_START: err = usbd_set_config_index(child, USB_UNCONFIG_INDEX); if (err != 0) { DPRINTF("Unconfigure failed: %s: Ignored.\n", usbd_errstr(err)); } if (child->parent_hub == NULL) { /* the root HUB cannot be re-enumerated */ DPRINTFN(6, "cannot reset root HUB\n"); err = 0; } else { err = usbd_req_re_enumerate(child, NULL); } if (err == 0) err = usbd_set_config_index(child, 0); if (err == 0) { err = usb_probe_and_attach(child, USB_IFACE_INDEX_ANY); } child->re_enumerate_wait = USB_RE_ENUM_DONE; break; case USB_RE_ENUM_PWR_OFF: /* get the device unconfigured */ err = usbd_set_config_index(child, USB_UNCONFIG_INDEX); if (err) { DPRINTFN(0, "Could not unconfigure " "device (ignored)\n"); } if (child->parent_hub == NULL) { /* the root HUB cannot be re-enumerated */ DPRINTFN(6, "cannot set port feature\n"); err = 0; } else { /* clear port enable */ err = usbd_req_clear_port_feature(child->parent_hub, NULL, child->port_no, UHF_PORT_ENABLE); if (err) { DPRINTFN(0, "Could not disable port " "(ignored)\n"); } } child->re_enumerate_wait = USB_RE_ENUM_DONE; break; case USB_RE_ENUM_SET_CONFIG: err = usbd_set_config_index(child, child->next_config_index); if (err != 0) { DPRINTF("Configure failed: %s: Ignored.\n", usbd_errstr(err)); } else { err = usb_probe_and_attach(child, USB_IFACE_INDEX_ANY); } child->re_enumerate_wait = USB_RE_ENUM_DONE; break; default: child->re_enumerate_wait = USB_RE_ENUM_DONE; break; } if (do_unlock) usbd_enum_unlock(child); } /*------------------------------------------------------------------------* * uhub_explore_sub - subroutine * * Return values: * 0: Success * Else: A control transaction failed *------------------------------------------------------------------------*/ static usb_error_t uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) { struct usb_bus *bus; struct usb_device *child; uint8_t refcount; usb_error_t err; bus = sc->sc_udev->bus; err = 0; /* get driver added refcount from USB bus */ refcount = bus->driver_added_refcount; /* get device assosiated with the given port */ child = usb_bus_port_get_device(bus, up); if (child == NULL) { /* nothing to do */ goto done; } uhub_explore_handle_re_enumerate(child); /* check if probe and attach should be done */ if (child->driver_added_refcount != refcount) { child->driver_added_refcount = refcount; err = usb_probe_and_attach(child, USB_IFACE_INDEX_ANY); if (err) { goto done; } } /* start control transfer, if device mode */ if (child->flags.usb_mode == USB_MODE_DEVICE) usbd_ctrl_transfer_setup(child); /* if a HUB becomes present, do a recursive HUB explore */ if (child->hub) err = (child->hub->explore) (child); done: return (err); } /*------------------------------------------------------------------------* * uhub_read_port_status - factored out code *------------------------------------------------------------------------*/ static usb_error_t uhub_read_port_status(struct uhub_softc *sc, uint8_t portno) { struct usb_port_status ps; usb_error_t err; err = usbd_req_get_port_status( sc->sc_udev, NULL, &ps, portno); /* update status regardless of error */ sc->sc_st.port_status = UGETW(ps.wPortStatus); sc->sc_st.port_change = UGETW(ps.wPortChange); /* debugging print */ DPRINTFN(4, "port %d, wPortStatus=0x%04x, " "wPortChange=0x%04x, err=%s\n", portno, sc->sc_st.port_status, sc->sc_st.port_change, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * uhub_reattach_port * * Returns: * 0: Success * Else: A control transaction failed *------------------------------------------------------------------------*/ static usb_error_t uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) { struct usb_device *child; struct usb_device *udev; enum usb_dev_speed speed; enum usb_hc_mode mode; usb_error_t err; uint16_t power_mask; uint8_t timeout; DPRINTF("reattaching port %d\n", portno); timeout = 0; udev = sc->sc_udev; child = usb_bus_port_get_device(udev->bus, udev->hub->ports + portno - 1); repeat: /* first clear the port connection change bit */ err = usbd_req_clear_port_feature(udev, NULL, portno, UHF_C_PORT_CONNECTION); if (err) goto error; /* check if there is a child */ if (child != NULL) { /* * Free USB device and all subdevices, if any. */ usb_free_device(child, 0); child = NULL; } /* get fresh status */ err = uhub_read_port_status(sc, portno); if (err) goto error; #if USB_HAVE_DISABLE_ENUM /* check if we should skip enumeration from this USB HUB */ if (usb_disable_enumeration != 0 || sc->sc_disable_enumeration != 0) { DPRINTF("Enumeration is disabled!\n"); goto error; } #endif /* check if nothing is connected to the port */ if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) goto error; /* check if there is no power on the port and print a warning */ switch (udev->speed) { case USB_SPEED_HIGH: case USB_SPEED_FULL: case USB_SPEED_LOW: power_mask = UPS_PORT_POWER; break; case USB_SPEED_SUPER: if (udev->parent_hub == NULL) power_mask = UPS_PORT_POWER; else power_mask = UPS_PORT_POWER_SS; break; default: power_mask = 0; break; } if (!(sc->sc_st.port_status & power_mask)) { DPRINTF("WARNING: strange, connected port %d " "has no power\n", portno); } /* check if the device is in Host Mode */ if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) { DPRINTF("Port %d is in Host Mode\n", portno); if (sc->sc_st.port_status & UPS_SUSPEND) { /* * NOTE: Should not get here in SuperSpeed * mode, because the HUB should report this * bit as zero. */ DPRINTF("Port %d was still " "suspended, clearing.\n", portno); err = usbd_req_clear_port_feature(udev, NULL, portno, UHF_PORT_SUSPEND); } /* USB Host Mode */ /* wait for maximum device power up time */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_powerup_delay)); /* reset port, which implies enabling it */ err = usbd_req_reset_port(udev, NULL, portno); if (err) { DPRINTFN(0, "port %d reset " "failed, error=%s\n", portno, usbd_errstr(err)); goto error; } /* get port status again, it might have changed during reset */ err = uhub_read_port_status(sc, portno); if (err) { goto error; } /* check if something changed during port reset */ if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) || (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) { if (timeout) { DPRINTFN(0, "giving up port reset " "- device vanished\n"); goto error; } timeout = 1; goto repeat; } } else { DPRINTF("Port %d is in Device Mode\n", portno); } /* * Figure out the device speed */ switch (udev->speed) { case USB_SPEED_HIGH: if (sc->sc_st.port_status & UPS_HIGH_SPEED) speed = USB_SPEED_HIGH; else if (sc->sc_st.port_status & UPS_LOW_SPEED) speed = USB_SPEED_LOW; else speed = USB_SPEED_FULL; break; case USB_SPEED_FULL: if (sc->sc_st.port_status & UPS_LOW_SPEED) speed = USB_SPEED_LOW; else speed = USB_SPEED_FULL; break; case USB_SPEED_LOW: speed = USB_SPEED_LOW; break; case USB_SPEED_SUPER: if (udev->parent_hub == NULL) { /* Root HUB - special case */ switch (sc->sc_st.port_status & UPS_OTHER_SPEED) { case 0: speed = USB_SPEED_FULL; break; case UPS_LOW_SPEED: speed = USB_SPEED_LOW; break; case UPS_HIGH_SPEED: speed = USB_SPEED_HIGH; break; default: speed = USB_SPEED_SUPER; break; } } else { speed = USB_SPEED_SUPER; } break; default: /* same speed like parent */ speed = udev->speed; break; } if (speed == USB_SPEED_SUPER) { err = usbd_req_set_hub_u1_timeout(udev, NULL, portno, 128 - (2 * udev->depth)); if (err) { DPRINTFN(0, "port %d U1 timeout " "failed, error=%s\n", portno, usbd_errstr(err)); } err = usbd_req_set_hub_u2_timeout(udev, NULL, portno, 128 - (2 * udev->depth)); if (err) { DPRINTFN(0, "port %d U2 timeout " "failed, error=%s\n", portno, usbd_errstr(err)); } } /* * Figure out the device mode * * NOTE: This part is currently FreeBSD specific. */ if (udev->parent_hub != NULL) { /* inherit mode from the parent HUB */ mode = udev->parent_hub->flags.usb_mode; } else if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) mode = USB_MODE_DEVICE; else mode = USB_MODE_HOST; /* need to create a new child */ child = usb_alloc_device(sc->sc_dev, udev->bus, udev, udev->depth + 1, portno - 1, portno, speed, mode); if (child == NULL) { DPRINTFN(0, "could not allocate new device\n"); goto error; } return (0); /* success */ error: if (child != NULL) { /* * Free USB device and all subdevices, if any. */ usb_free_device(child, 0); child = NULL; } if (err == 0) { if (sc->sc_st.port_status & UPS_PORT_ENABLED) { err = usbd_req_clear_port_feature( sc->sc_udev, NULL, portno, UHF_PORT_ENABLE); } } if (err) { DPRINTFN(0, "device problem (%s), " "disabling port %d\n", usbd_errstr(err), portno); } return (err); } /*------------------------------------------------------------------------* * usb_device_20_compatible * * Returns: * 0: HUB does not support suspend and resume * Else: HUB supports suspend and resume *------------------------------------------------------------------------*/ static uint8_t usb_device_20_compatible(struct usb_device *udev) { if (udev == NULL) return (0); switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: return (1); default: return (0); } } /*------------------------------------------------------------------------* * uhub_suspend_resume_port * * Returns: * 0: Success * Else: A control transaction failed *------------------------------------------------------------------------*/ static usb_error_t uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) { struct usb_device *child; struct usb_device *udev; uint8_t is_suspend; usb_error_t err; DPRINTF("port %d\n", portno); udev = sc->sc_udev; child = usb_bus_port_get_device(udev->bus, udev->hub->ports + portno - 1); /* first clear the port suspend change bit */ if (usb_device_20_compatible(udev)) { err = usbd_req_clear_port_feature(udev, NULL, portno, UHF_C_PORT_SUSPEND); } else { err = usbd_req_clear_port_feature(udev, NULL, portno, UHF_C_PORT_LINK_STATE); } if (err) { DPRINTF("clearing suspend failed.\n"); goto done; } /* get fresh status */ err = uhub_read_port_status(sc, portno); if (err) { DPRINTF("reading port status failed.\n"); goto done; } /* convert current state */ if (usb_device_20_compatible(udev)) { if (sc->sc_st.port_status & UPS_SUSPEND) { is_suspend = 1; } else { is_suspend = 0; } } else { switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) { case UPS_PORT_LS_U3: is_suspend = 1; break; case UPS_PORT_LS_SS_INA: usbd_req_warm_reset_port(udev, NULL, portno); is_suspend = 0; break; default: is_suspend = 0; break; } } DPRINTF("suspended=%u\n", is_suspend); /* do the suspend or resume */ if (child) { /* * This code handle two cases: 1) Host Mode - we can only * receive resume here 2) Device Mode - we can receive * suspend and resume here */ if (is_suspend == 0) usb_dev_resume_peer(child); else if (child->flags.usb_mode == USB_MODE_DEVICE) usb_dev_suspend_peer(child); } done: return (err); } /*------------------------------------------------------------------------* * uhub_root_interrupt * * This function is called when a Root HUB interrupt has * happened. "ptr" and "len" makes up the Root HUB interrupt * packet. This function is called having the "bus_mtx" locked. *------------------------------------------------------------------------*/ void uhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len) { USB_BUS_LOCK_ASSERT(bus, MA_OWNED); usb_needs_explore(bus, 0); } static uint8_t uhub_is_too_deep(struct usb_device *udev) { switch (udev->speed) { case USB_SPEED_FULL: case USB_SPEED_LOW: case USB_SPEED_HIGH: if (udev->depth > USB_HUB_MAX_DEPTH) return (1); break; case USB_SPEED_SUPER: if (udev->depth > USB_SS_HUB_DEPTH_MAX) return (1); break; default: break; } return (0); } /*------------------------------------------------------------------------* * uhub_explore * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t uhub_explore(struct usb_device *udev) { struct usb_hub *hub; struct uhub_softc *sc; struct usb_port *up; usb_error_t err; uint8_t portno; uint8_t x; uint8_t do_unlock; hub = udev->hub; sc = hub->hubsoftc; DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address); /* ignore devices that are too deep */ if (uhub_is_too_deep(udev)) return (USB_ERR_TOO_DEEP); /* check if device is suspended */ if (udev->flags.self_suspended) { /* need to wait until the child signals resume */ DPRINTF("Device is suspended!\n"); return (0); } /* * Make sure we don't race against user-space applications * like LibUSB: */ do_unlock = usbd_enum_lock(udev); for (x = 0; x != hub->nports; x++) { up = hub->ports + x; portno = x + 1; err = uhub_read_port_status(sc, portno); if (err) { /* most likely the HUB is gone */ break; } if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) { DPRINTF("Overcurrent on port %u.\n", portno); err = usbd_req_clear_port_feature( udev, NULL, portno, UHF_C_PORT_OVER_CURRENT); if (err) { /* most likely the HUB is gone */ break; } } if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) { /* * Fake a connect status change so that the * status gets checked initially! */ sc->sc_st.port_change |= UPS_C_CONNECT_STATUS; } if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) { err = usbd_req_clear_port_feature( udev, NULL, portno, UHF_C_PORT_ENABLE); if (err) { /* most likely the HUB is gone */ break; } if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { /* * Ignore the port error if the device * has vanished ! */ } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) { DPRINTFN(0, "illegal enable change, " "port %d\n", portno); } else { if (up->restartcnt == USB_RESTART_MAX) { /* XXX could try another speed ? */ DPRINTFN(0, "port error, giving up " "port %d\n", portno); } else { sc->sc_st.port_change |= UPS_C_CONNECT_STATUS; up->restartcnt++; } } } if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { err = uhub_reattach_port(sc, portno); if (err) { /* most likely the HUB is gone */ break; } } if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) { err = uhub_suspend_resume_port(sc, portno); if (err) { /* most likely the HUB is gone */ break; } } err = uhub_explore_sub(sc, up); if (err) { /* no device(s) present */ continue; } /* explore succeeded - reset restart counter */ up->restartcnt = 0; } if (do_unlock) usbd_enum_unlock(udev); /* initial status checked */ sc->sc_flags |= UHUB_FLAG_DID_EXPLORE; /* return success */ return (USB_ERR_NORMAL_COMPLETION); } static int uhub_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); /* * The subclass for USB HUBs is currently ignored because it * is 0 for some and 1 for others. */ if (uaa->info.bConfigIndex == 0 && uaa->info.bDeviceClass == UDCLASS_HUB) return (0); return (ENXIO); } /* NOTE: The information returned by this function can be wrong. */ usb_error_t uhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt) { struct usb_hub_descriptor hubdesc20; struct usb_hub_ss_descriptor hubdesc30; usb_error_t err; uint8_t nports; uint8_t tt; if (udev->ddesc.bDeviceClass != UDCLASS_HUB) return (USB_ERR_INVAL); nports = 0; tt = 0; switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: /* assuming that there is one port */ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1); if (err) { DPRINTFN(0, "getting USB 2.0 HUB descriptor failed," "error=%s\n", usbd_errstr(err)); break; } nports = hubdesc20.bNbrPorts; if (nports > 127) nports = 127; if (udev->speed == USB_SPEED_HIGH) tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3; break; case USB_SPEED_SUPER: err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1); if (err) { DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed," "error=%s\n", usbd_errstr(err)); break; } nports = hubdesc30.bNbrPorts; if (nports > 16) nports = 16; break; default: err = USB_ERR_INVAL; break; } if (pnports != NULL) *pnports = nports; if (ptt != NULL) *ptt = tt; return (err); } static int uhub_attach(device_t dev) { struct uhub_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_device *udev = uaa->device; struct usb_device *parent_hub = udev->parent_hub; struct usb_hub *hub; struct usb_hub_descriptor hubdesc20; struct usb_hub_ss_descriptor hubdesc30; #if USB_HAVE_DISABLE_ENUM struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; #endif uint16_t pwrdly; uint16_t nports; uint8_t x; uint8_t portno; uint8_t removable; uint8_t iface_index; usb_error_t err; sc->sc_udev = udev; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "USB HUB mutex", NULL, MTX_DEF); device_set_usb_desc(dev); DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, " "parent->selfpowered=%d\n", udev->depth, udev->flags.self_powered, parent_hub, parent_hub ? parent_hub->flags.self_powered : 0); if (uhub_is_too_deep(udev)) { DPRINTFN(0, "HUB at depth %d, " "exceeds maximum. HUB ignored\n", (int)udev->depth); goto error; } if (!udev->flags.self_powered && parent_hub && !parent_hub->flags.self_powered) { DPRINTFN(0, "Bus powered HUB connected to " "bus powered HUB. HUB ignored\n"); goto error; } if (UHUB_IS_MULTI_TT(sc)) { err = usbd_set_alt_interface_index(udev, 0, 1); if (err) { device_printf(dev, "MTT could not be enabled\n"); goto error; } device_printf(dev, "MTT enabled\n"); } /* get HUB descriptor */ DPRINTFN(2, "Getting HUB descriptor\n"); switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: /* assuming that there is one port */ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1); if (err) { DPRINTFN(0, "getting USB 2.0 HUB descriptor failed," "error=%s\n", usbd_errstr(err)); goto error; } /* get number of ports */ nports = hubdesc20.bNbrPorts; /* get power delay */ pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + usb_extra_power_up_time); /* get complete HUB descriptor */ if (nports >= 8) { /* check number of ports */ if (nports > 127) { DPRINTFN(0, "Invalid number of USB 2.0 ports," "error=%s\n", usbd_errstr(err)); goto error; } /* get complete HUB descriptor */ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports); if (err) { DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed," "error=%s\n", usbd_errstr(err)); goto error; } if (hubdesc20.bNbrPorts != nports) { DPRINTFN(0, "Number of ports changed\n"); goto error; } } break; case USB_SPEED_SUPER: if (udev->parent_hub != NULL) { err = usbd_req_set_hub_depth(udev, NULL, udev->depth - 1); if (err) { DPRINTFN(0, "Setting USB 3.0 HUB depth failed," "error=%s\n", usbd_errstr(err)); goto error; } } err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1); if (err) { DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed," "error=%s\n", usbd_errstr(err)); goto error; } /* get number of ports */ nports = hubdesc30.bNbrPorts; /* get power delay */ pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + usb_extra_power_up_time); /* get complete HUB descriptor */ if (nports >= 8) { /* check number of ports */ if (nports > ((udev->parent_hub != NULL) ? 15 : 127)) { DPRINTFN(0, "Invalid number of USB 3.0 ports," "error=%s\n", usbd_errstr(err)); goto error; } /* get complete HUB descriptor */ err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, nports); if (err) { DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed," "error=%s\n", usbd_errstr(err)); goto error; } if (hubdesc30.bNbrPorts != nports) { DPRINTFN(0, "Number of ports changed\n"); goto error; } } break; default: DPRINTF("Assuming HUB has only one port\n"); /* default number of ports */ nports = 1; /* default power delay */ pwrdly = ((10 * UHD_PWRON_FACTOR) + usb_extra_power_up_time); break; } if (nports == 0) { DPRINTFN(0, "portless HUB\n"); goto error; } if (nports > USB_MAX_PORTS) { DPRINTF("Port limit exceeded\n"); goto error; } #if (USB_HAVE_FIXED_PORT == 0) hub = malloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports), M_USBDEV, M_WAITOK | M_ZERO); if (hub == NULL) goto error; #else hub = &sc->sc_hub; #endif udev->hub = hub; /* initialize HUB structure */ hub->hubsoftc = sc; hub->explore = &uhub_explore; hub->nports = nports; hub->hubudev = udev; #if USB_HAVE_TT_SUPPORT hub->tt_msg[0].hdr.pm_callback = &uhub_reset_tt_proc; hub->tt_msg[0].udev = udev; hub->tt_msg[1].hdr.pm_callback = &uhub_reset_tt_proc; hub->tt_msg[1].udev = udev; #endif /* if self powered hub, give ports maximum current */ if (udev->flags.self_powered) { hub->portpower = USB_MAX_POWER; } else { hub->portpower = USB_MIN_POWER; } /* set up interrupt pipe */ iface_index = 0; if (udev->parent_hub == NULL) { /* root HUB is special */ err = 0; } else { /* normal HUB */ err = usbd_transfer_setup(udev, &iface_index, sc->sc_xfer, uhub_config, UHUB_N_TRANSFER, sc, &sc->sc_mtx); } if (err) { DPRINTFN(0, "cannot setup interrupt transfer, " "errstr=%s\n", usbd_errstr(err)); goto error; } /* wait with power off for a while */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME)); #if USB_HAVE_DISABLE_ENUM /* Add device sysctls */ sysctl_ctx = device_get_sysctl_ctx(dev); sysctl_tree = device_get_sysctl_tree(dev); if (sysctl_ctx != NULL && sysctl_tree != NULL) { (void) SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "disable_enumeration", CTLFLAG_RWTUN, &sc->sc_disable_enumeration, 0, "Set to disable enumeration on this USB HUB."); (void) SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "disable_port_power", CTLFLAG_RWTUN, &sc->sc_disable_port_power, 0, "Set to disable USB port power on this USB HUB."); } #endif /* * To have the best chance of success we do things in the exact same * order as Windoze98. This should not be necessary, but some * devices do not follow the USB specs to the letter. * * These are the events on the bus when a hub is attached: * Get device and config descriptors (see attach code) * Get hub descriptor (see above) * For all ports * turn on power * wait for power to become stable * (all below happens in explore code) * For all ports * clear C_PORT_CONNECTION * For all ports * get port status * if device connected * wait 100 ms * turn on reset * wait * clear C_PORT_RESET * get port status * proceed with device attachment */ /* XXX should check for none, individual, or ganged power? */ removable = 0; for (x = 0; x != nports; x++) { /* set up data structures */ struct usb_port *up = hub->ports + x; up->device_index = 0; up->restartcnt = 0; portno = x + 1; /* check if port is removable */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: if (!UHD_NOT_REMOV(&hubdesc20, portno)) removable++; break; case USB_SPEED_SUPER: if (!UHD_NOT_REMOV(&hubdesc30, portno)) removable++; break; default: DPRINTF("Assuming removable port\n"); removable++; break; } if (err == 0) { #if USB_HAVE_DISABLE_ENUM /* check if we should disable USB port power or not */ if (usb_disable_port_power != 0 || sc->sc_disable_port_power != 0) { /* turn the power off */ DPRINTFN(2, "Turning port %d power off\n", portno); err = usbd_req_clear_port_feature(udev, NULL, portno, UHF_PORT_POWER); } else { #endif /* turn the power on */ DPRINTFN(2, "Turning port %d power on\n", portno); err = usbd_req_set_port_feature(udev, NULL, portno, UHF_PORT_POWER); #if USB_HAVE_DISABLE_ENUM } #endif } if (err != 0) { DPRINTFN(0, "port %d power on or off failed, %s\n", portno, usbd_errstr(err)); } DPRINTF("turn on port %d power\n", portno); /* wait for stable power */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(pwrdly)); } device_printf(dev, "%d port%s with %d " "removable, %s powered\n", nports, (nports != 1) ? "s" : "", removable, udev->flags.self_powered ? "self" : "bus"); /* Start the interrupt endpoint, if any */ mtx_lock(&sc->sc_mtx); usbd_transfer_start(sc->sc_xfer[UHUB_INTR_TRANSFER]); mtx_unlock(&sc->sc_mtx); /* Enable automatic power save on all USB HUBs */ usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); return (0); error: usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); #if (USB_HAVE_FIXED_PORT == 0) free(udev->hub, M_USBDEV); #endif udev->hub = NULL; mtx_destroy(&sc->sc_mtx); return (ENXIO); } /* * Called from process context when the hub is gone. * Detach all devices on active ports. */ static int uhub_detach(device_t dev) { struct uhub_softc *sc = device_get_softc(dev); struct usb_hub *hub = sc->sc_udev->hub; struct usb_bus *bus = sc->sc_udev->bus; struct usb_device *child; uint8_t x; if (hub == NULL) /* must be partially working */ return (0); /* Make sure interrupt transfer is gone. */ usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); /* Detach all ports */ for (x = 0; x != hub->nports; x++) { child = usb_bus_port_get_device(bus, hub->ports + x); if (child == NULL) { continue; } /* * Free USB device and all subdevices, if any. */ usb_free_device(child, 0); } #if USB_HAVE_TT_SUPPORT /* Make sure our TT messages are not queued anywhere */ USB_BUS_LOCK(bus); usb_proc_mwait(USB_BUS_TT_PROC(bus), &hub->tt_msg[0], &hub->tt_msg[1]); USB_BUS_UNLOCK(bus); #endif #if (USB_HAVE_FIXED_PORT == 0) free(hub, M_USBDEV); #endif sc->sc_udev->hub = NULL; mtx_destroy(&sc->sc_mtx); return (0); } static int uhub_suspend(device_t dev) { DPRINTF("\n"); /* Sub-devices are not suspended here! */ return (0); } static int uhub_resume(device_t dev) { DPRINTF("\n"); /* Sub-devices are not resumed here! */ return (0); } static void uhub_driver_added(device_t dev, driver_t *driver) { usb_needs_explore_all(); } struct hub_result { struct usb_device *udev; uint8_t portno; uint8_t iface_index; }; static void uhub_find_iface_index(struct usb_hub *hub, device_t child, struct hub_result *res) { struct usb_interface *iface; struct usb_device *udev; uint8_t nports; uint8_t x; uint8_t i; nports = hub->nports; for (x = 0; x != nports; x++) { udev = usb_bus_port_get_device(hub->hubudev->bus, hub->ports + x); if (!udev) { continue; } for (i = 0; i != USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface && (iface->subdev == child)) { res->iface_index = i; res->udev = udev; res->portno = x + 1; return; } } } res->iface_index = 0; res->udev = NULL; res->portno = 0; } static int uhub_child_location_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc; struct usb_hub *hub; struct hub_result res; if (!device_is_attached(parent)) { if (buflen) buf[0] = 0; return (0); } sc = device_get_softc(parent); hub = sc->sc_udev->hub; mtx_lock(&Giant); uhub_find_iface_index(hub, child, &res); if (!res.udev) { DPRINTF("device not on hub\n"); if (buflen) { buf[0] = '\0'; } goto done; } snprintf(buf, buflen, "bus=%u hubaddr=%u port=%u devaddr=%u" " interface=%u" #if USB_HAVE_UGEN " ugen=%s" #endif , device_get_unit(res.udev->bus->bdev) , (res.udev->parent_hub != NULL) ? res.udev->parent_hub->device_index : 0 , res.portno, res.udev->device_index, res.iface_index #if USB_HAVE_UGEN , res.udev->ugen_name #endif ); done: mtx_unlock(&Giant); return (0); } static int uhub_child_pnpinfo_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc; struct usb_hub *hub; struct usb_interface *iface; struct hub_result res; if (!device_is_attached(parent)) { if (buflen) buf[0] = 0; return (0); } sc = device_get_softc(parent); hub = sc->sc_udev->hub; mtx_lock(&Giant); uhub_find_iface_index(hub, child, &res); if (!res.udev) { DPRINTF("device not on hub\n"); if (buflen) { buf[0] = '\0'; } goto done; } iface = usbd_get_iface(res.udev, res.iface_index); if (iface && iface->idesc) { snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "devproto=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "intclass=0x%02x intsubclass=0x%02x " "intprotocol=0x%02x" "%s%s", UGETW(res.udev->ddesc.idVendor), UGETW(res.udev->ddesc.idProduct), res.udev->ddesc.bDeviceClass, res.udev->ddesc.bDeviceSubClass, res.udev->ddesc.bDeviceProtocol, usb_get_serial(res.udev), UGETW(res.udev->ddesc.bcdDevice), (res.udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass, iface->idesc->bInterfaceProtocol, iface->pnpinfo ? " " : "", iface->pnpinfo ? iface->pnpinfo : ""); } else { if (buflen) { buf[0] = '\0'; } goto done; } done: mtx_unlock(&Giant); return (0); } /* * The USB Transaction Translator: * =============================== * - * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed + * When doing LOW- and FULL-speed USB transfers across a HIGH-speed * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT * USB transfers. To utilize bandwidth dynamically the "scatter and * gather" principle must be applied. This means that bandwidth must * be divided into equal parts of bandwidth. With regard to USB all * data is transferred in smaller packets with length * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is * not a constant! * * The bandwidth scheduler which I have implemented will simply pack * the USB transfers back to back until there is no more space in the * schedule. Out of the 8 microframes which the USB 2.0 standard * provides, only 6 are available for non-HIGH-speed devices. I have * reserved the first 4 microframes for ISOCHRONOUS transfers. The * last 2 microframes I have reserved for INTERRUPT transfers. Without * this division, it is very difficult to allocate and free bandwidth * dynamically. * * NOTE about the Transaction Translator in USB HUBs: * * USB HUBs have a very simple Transaction Translator, that will * simply pipeline all the SPLIT transactions. That means that the * transactions will be executed in the order they are queued! * */ /*------------------------------------------------------------------------* * usb_intr_find_best_slot * * Return value: * The best Transaction Translation slot for an interrupt endpoint. *------------------------------------------------------------------------*/ static uint8_t usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, uint8_t end, uint8_t mask) { usb_size_t min = (usb_size_t)-1; usb_size_t sum; uint8_t x; uint8_t y; uint8_t z; y = 0; /* find the last slot with lesser used bandwidth */ for (x = start; x < end; x++) { sum = 0; /* compute sum of bandwidth */ for (z = x; z < end; z++) { if (mask & (1U << (z - x))) sum += ptr[z]; } /* check if the current multi-slot is more optimal */ if (min >= sum) { min = sum; y = x; } /* check if the mask is about to be shifted out */ if (mask & (1U << (end - 1 - x))) break; } return (y); } /*------------------------------------------------------------------------* * usb_hs_bandwidth_adjust * - * This function will update the bandwith usage for the microframe + * This function will update the bandwidth usage for the microframe * having index "slot" by "len" bytes. "len" can be negative. If the * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX" * the "slot" argument will be replaced by the slot having least used * bandwidth. The "mask" argument is used for multi-slot allocations. * * Returns: * The slot in which the bandwidth update was done: 0..7 *------------------------------------------------------------------------*/ static uint8_t usb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len, uint8_t slot, uint8_t mask) { struct usb_bus *bus = udev->bus; struct usb_hub *hub; enum usb_dev_speed speed; uint8_t x; USB_BUS_LOCK_ASSERT(bus, MA_OWNED); speed = usbd_get_speed(udev); switch (speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: if (speed == USB_SPEED_LOW) { len *= 8; } /* * The Host Controller Driver should have * performed checks so that the lookup * below does not result in a NULL pointer * access. */ hub = udev->parent_hs_hub->hub; if (slot >= USB_HS_MICRO_FRAMES_MAX) { slot = usb_intr_find_best_slot(hub->uframe_usage, USB_FS_ISOC_UFRAME_MAX, 6, mask); } for (x = slot; x < 8; x++) { if (mask & (1U << (x - slot))) { hub->uframe_usage[x] += len; bus->uframe_usage[x] += len; } } break; default: if (slot >= USB_HS_MICRO_FRAMES_MAX) { slot = usb_intr_find_best_slot(bus->uframe_usage, 0, USB_HS_MICRO_FRAMES_MAX, mask); } for (x = slot; x < 8; x++) { if (mask & (1U << (x - slot))) { bus->uframe_usage[x] += len; } } break; } return (slot); } /*------------------------------------------------------------------------* * usb_hs_bandwidth_alloc * * This function is a wrapper function for "usb_hs_bandwidth_adjust()". *------------------------------------------------------------------------*/ void usb_hs_bandwidth_alloc(struct usb_xfer *xfer) { struct usb_device *udev; uint8_t slot; uint8_t mask; uint8_t speed; udev = xfer->xroot->udev; if (udev->flags.usb_mode != USB_MODE_HOST) return; /* not supported */ xfer->endpoint->refcount_bw++; if (xfer->endpoint->refcount_bw != 1) return; /* already allocated */ speed = usbd_get_speed(udev); switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: /* allocate a microframe slot */ mask = 0x01; slot = usb_hs_bandwidth_adjust(udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask); xfer->endpoint->usb_uframe = slot; xfer->endpoint->usb_smask = mask << slot; if ((speed != USB_SPEED_FULL) && (speed != USB_SPEED_LOW)) { xfer->endpoint->usb_cmask = 0x00 ; } else { xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE; } break; case UE_ISOCHRONOUS: switch (usbd_xfer_get_fps_shift(xfer)) { case 0: mask = 0xFF; break; case 1: mask = 0x55; break; case 2: mask = 0x11; break; default: mask = 0x01; break; } /* allocate a microframe multi-slot */ slot = usb_hs_bandwidth_adjust(udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask); xfer->endpoint->usb_uframe = slot; xfer->endpoint->usb_cmask = 0; xfer->endpoint->usb_smask = mask << slot; break; default: xfer->endpoint->usb_uframe = 0; xfer->endpoint->usb_cmask = 0; xfer->endpoint->usb_smask = 0; break; } DPRINTFN(11, "slot=%d, mask=0x%02x\n", xfer->endpoint->usb_uframe, xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe); } /*------------------------------------------------------------------------* * usb_hs_bandwidth_free * * This function is a wrapper function for "usb_hs_bandwidth_adjust()". *------------------------------------------------------------------------*/ void usb_hs_bandwidth_free(struct usb_xfer *xfer) { struct usb_device *udev; uint8_t slot; uint8_t mask; udev = xfer->xroot->udev; if (udev->flags.usb_mode != USB_MODE_HOST) return; /* not supported */ xfer->endpoint->refcount_bw--; if (xfer->endpoint->refcount_bw != 0) return; /* still allocated */ switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_ISOCHRONOUS: slot = xfer->endpoint->usb_uframe; mask = xfer->endpoint->usb_smask; /* free microframe slot(s): */ usb_hs_bandwidth_adjust(udev, -xfer->max_frame_size, slot, mask >> slot); DPRINTFN(11, "slot=%d, mask=0x%02x\n", slot, mask >> slot); xfer->endpoint->usb_uframe = 0; xfer->endpoint->usb_cmask = 0; xfer->endpoint->usb_smask = 0; break; default: break; } } /*------------------------------------------------------------------------* * usb_isoc_time_expand * * This function will expand the time counter from 7-bit to 16-bit. * * Returns: * 16-bit isochronous time counter. *------------------------------------------------------------------------*/ uint16_t usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr) { uint16_t rem; USB_BUS_LOCK_ASSERT(bus, MA_OWNED); rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1); isoc_time_curr &= (USB_ISOC_TIME_MAX - 1); if (isoc_time_curr < rem) { /* the time counter wrapped around */ bus->isoc_time_last += USB_ISOC_TIME_MAX; } /* update the remainder */ bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1); bus->isoc_time_last |= isoc_time_curr; return (bus->isoc_time_last); } /*------------------------------------------------------------------------* * usbd_fs_isoc_schedule_alloc_slot * * This function will allocate bandwidth for an isochronous FULL speed * transaction in the FULL speed schedule. * * Returns: * <8: Success * Else: Error *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT uint8_t usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time) { struct usb_xfer *xfer; struct usb_xfer *pipe_xfer; struct usb_bus *bus; usb_frlength_t len; usb_frlength_t data_len; uint16_t delta; uint16_t slot; uint8_t retval; data_len = 0; slot = 0; bus = isoc_xfer->xroot->bus; TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) { /* skip self, if any */ if (xfer == isoc_xfer) continue; /* check if this USB transfer is going through the same TT */ if (xfer->xroot->udev->parent_hs_hub != isoc_xfer->xroot->udev->parent_hs_hub) { continue; } if ((isoc_xfer->xroot->udev->parent_hs_hub-> ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) && (xfer->xroot->udev->hs_port_no != isoc_xfer->xroot->udev->hs_port_no)) { continue; } if (xfer->endpoint->methods != isoc_xfer->endpoint->methods) continue; /* check if isoc_time is part of this transfer */ delta = xfer->isoc_time_complete - isoc_time; if (delta > 0 && delta <= xfer->nframes) { delta = xfer->nframes - delta; len = xfer->frlengths[delta]; len += 8; len *= 7; len /= 6; data_len += len; } /* * Check double buffered transfers. Only stream ID * equal to zero is valid here! */ TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q[0].head, wait_entry) { /* skip self, if any */ if (pipe_xfer == isoc_xfer) continue; /* check if isoc_time is part of this transfer */ delta = pipe_xfer->isoc_time_complete - isoc_time; if (delta > 0 && delta <= pipe_xfer->nframes) { delta = pipe_xfer->nframes - delta; len = pipe_xfer->frlengths[delta]; len += 8; len *= 7; len /= 6; data_len += len; } } } while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) { data_len -= USB_FS_BYTES_PER_HS_UFRAME; slot++; } /* check for overflow */ if (slot >= USB_FS_ISOC_UFRAME_MAX) return (255); retval = slot; delta = isoc_xfer->isoc_time_complete - isoc_time; if (delta > 0 && delta <= isoc_xfer->nframes) { delta = isoc_xfer->nframes - delta; len = isoc_xfer->frlengths[delta]; len += 8; len *= 7; len /= 6; data_len += len; } while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) { data_len -= USB_FS_BYTES_PER_HS_UFRAME; slot++; } /* check for overflow */ if (slot >= USB_FS_ISOC_UFRAME_MAX) return (255); return (retval); } #endif /*------------------------------------------------------------------------* * usb_bus_port_get_device * * This function is NULL safe. *------------------------------------------------------------------------*/ struct usb_device * usb_bus_port_get_device(struct usb_bus *bus, struct usb_port *up) { if ((bus == NULL) || (up == NULL)) { /* be NULL safe */ return (NULL); } if (up->device_index == 0) { /* nothing to do */ return (NULL); } return (bus->devices[up->device_index]); } /*------------------------------------------------------------------------* * usb_bus_port_set_device * * This function is NULL safe. *------------------------------------------------------------------------*/ void usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up, struct usb_device *udev, uint8_t device_index) { if (bus == NULL) { /* be NULL safe */ return; } /* * There is only one case where we don't * have an USB port, and that is the Root Hub! */ if (up) { if (udev) { up->device_index = device_index; } else { device_index = up->device_index; up->device_index = 0; } } /* * Make relationships to our new device */ if (device_index != 0) { #if USB_HAVE_UGEN mtx_lock(&usb_ref_lock); #endif bus->devices[device_index] = udev; #if USB_HAVE_UGEN mtx_unlock(&usb_ref_lock); #endif } /* * Debug print */ DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev); } /*------------------------------------------------------------------------* * usb_needs_explore * * This functions is called when the USB event thread needs to run. *------------------------------------------------------------------------*/ void usb_needs_explore(struct usb_bus *bus, uint8_t do_probe) { uint8_t do_unlock; DPRINTF("\n"); if (bus == NULL) { DPRINTF("No bus pointer!\n"); return; } if ((bus->devices == NULL) || (bus->devices[USB_ROOT_HUB_ADDR] == NULL)) { DPRINTF("No root HUB\n"); return; } if (mtx_owned(&bus->bus_mtx)) { do_unlock = 0; } else { USB_BUS_LOCK(bus); do_unlock = 1; } if (do_probe) { bus->do_probe = 1; } if (usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->explore_msg[0], &bus->explore_msg[1])) { /* ignore */ } if (do_unlock) { USB_BUS_UNLOCK(bus); } } /*------------------------------------------------------------------------* * usb_needs_explore_all * * This function is called whenever a new driver is loaded and will * cause that all USB busses are re-explored. *------------------------------------------------------------------------*/ void usb_needs_explore_all(void) { struct usb_bus *bus; devclass_t dc; device_t dev; int max; DPRINTFN(3, "\n"); dc = usb_devclass_ptr; if (dc == NULL) { DPRINTFN(0, "no devclass\n"); return; } /* - * Explore all USB busses in parallell. + * Explore all USB busses in parallel. */ max = devclass_get_maxunit(dc); while (max >= 0) { dev = devclass_get_device(dc, max); if (dev) { bus = device_get_softc(dev); if (bus) { usb_needs_explore(bus, 1); } } max--; } } /*------------------------------------------------------------------------* * usb_bus_power_update * * This function will ensure that all USB devices on the given bus are * properly suspended or resumed according to the device transfer * state. *------------------------------------------------------------------------*/ #if USB_HAVE_POWERD void usb_bus_power_update(struct usb_bus *bus) { usb_needs_explore(bus, 0 /* no probe */ ); } #endif /*------------------------------------------------------------------------* * usbd_transfer_power_ref * * This function will modify the power save reference counts and * wakeup the USB device associated with the given USB transfer, if * needed. *------------------------------------------------------------------------*/ #if USB_HAVE_POWERD void usbd_transfer_power_ref(struct usb_xfer *xfer, int val) { static const usb_power_mask_t power_mask[4] = { [UE_CONTROL] = USB_HW_POWER_CONTROL, [UE_BULK] = USB_HW_POWER_BULK, [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT, [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC, }; struct usb_device *udev; uint8_t needs_explore; uint8_t needs_hw_power; uint8_t xfer_type; udev = xfer->xroot->udev; if (udev->device_index == USB_ROOT_HUB_ADDR) { /* no power save for root HUB */ return; } USB_BUS_LOCK(udev->bus); xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE; udev->pwr_save.last_xfer_time = ticks; udev->pwr_save.type_refs[xfer_type] += val; if (xfer->flags_int.control_xfr) { udev->pwr_save.read_refs += val; if (xfer->flags_int.usb_mode == USB_MODE_HOST) { /* * It is not allowed to suspend during a * control transfer: */ udev->pwr_save.write_refs += val; } } else if (USB_GET_DATA_ISREAD(xfer)) { udev->pwr_save.read_refs += val; } else { udev->pwr_save.write_refs += val; } if (val > 0) { if (udev->flags.self_suspended) needs_explore = usb_peer_should_wakeup(udev); else needs_explore = 0; if (!(udev->bus->hw_power_state & power_mask[xfer_type])) { DPRINTF("Adding type %u to power state\n", xfer_type); udev->bus->hw_power_state |= power_mask[xfer_type]; needs_hw_power = 1; } else { needs_hw_power = 0; } } else { needs_explore = 0; needs_hw_power = 0; } USB_BUS_UNLOCK(udev->bus); if (needs_explore) { DPRINTF("update\n"); usb_bus_power_update(udev->bus); } else if (needs_hw_power) { DPRINTF("needs power\n"); if (udev->bus->methods->set_hw_power != NULL) { (udev->bus->methods->set_hw_power) (udev->bus); } } } #endif /*------------------------------------------------------------------------* * usb_peer_should_wakeup * * This function returns non-zero if the current device should wake up. *------------------------------------------------------------------------*/ static uint8_t usb_peer_should_wakeup(struct usb_device *udev) { return (((udev->power_mode == USB_POWER_MODE_ON) && (udev->flags.usb_mode == USB_MODE_HOST)) || (udev->driver_added_refcount != udev->bus->driver_added_refcount) || (udev->re_enumerate_wait != USB_RE_ENUM_DONE) || (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || (udev->pwr_save.write_refs != 0) || ((udev->pwr_save.read_refs != 0) && (udev->flags.usb_mode == USB_MODE_HOST) && (usb_peer_can_wakeup(udev) == 0))); } /*------------------------------------------------------------------------* * usb_bus_powerd * * This function implements the USB power daemon and is called * regularly from the USB explore thread. *------------------------------------------------------------------------*/ #if USB_HAVE_POWERD void usb_bus_powerd(struct usb_bus *bus) { struct usb_device *udev; usb_ticks_t temp; usb_ticks_t limit; usb_ticks_t mintime; usb_size_t type_refs[5]; uint8_t x; limit = usb_power_timeout; if (limit == 0) limit = hz; else if (limit > 255) limit = 255 * hz; else limit = limit * hz; DPRINTF("bus=%p\n", bus); USB_BUS_LOCK(bus); /* * The root HUB device is never suspended * and we simply skip it. */ for (x = USB_ROOT_HUB_ADDR + 1; x != bus->devices_max; x++) { udev = bus->devices[x]; if (udev == NULL) continue; temp = ticks - udev->pwr_save.last_xfer_time; if (usb_peer_should_wakeup(udev)) { /* check if we are suspended */ if (udev->flags.self_suspended != 0) { USB_BUS_UNLOCK(bus); usb_dev_resume_peer(udev); USB_BUS_LOCK(bus); } } else if ((temp >= limit) && (udev->flags.usb_mode == USB_MODE_HOST) && (udev->flags.self_suspended == 0)) { /* try to do suspend */ USB_BUS_UNLOCK(bus); usb_dev_suspend_peer(udev); USB_BUS_LOCK(bus); } } /* reset counters */ mintime = (usb_ticks_t)-1; type_refs[0] = 0; type_refs[1] = 0; type_refs[2] = 0; type_refs[3] = 0; type_refs[4] = 0; /* Re-loop all the devices to get the actual state */ for (x = USB_ROOT_HUB_ADDR + 1; x != bus->devices_max; x++) { udev = bus->devices[x]; if (udev == NULL) continue; /* we found a non-Root-Hub USB device */ type_refs[4] += 1; /* "last_xfer_time" can be updated by a resume */ temp = ticks - udev->pwr_save.last_xfer_time; /* * Compute minimum time since last transfer for the complete * bus: */ if (temp < mintime) mintime = temp; if (udev->flags.self_suspended == 0) { type_refs[0] += udev->pwr_save.type_refs[0]; type_refs[1] += udev->pwr_save.type_refs[1]; type_refs[2] += udev->pwr_save.type_refs[2]; type_refs[3] += udev->pwr_save.type_refs[3]; } } if (mintime >= (usb_ticks_t)(1 * hz)) { /* recompute power masks */ DPRINTF("Recomputing power masks\n"); bus->hw_power_state = 0; if (type_refs[UE_CONTROL] != 0) bus->hw_power_state |= USB_HW_POWER_CONTROL; if (type_refs[UE_BULK] != 0) bus->hw_power_state |= USB_HW_POWER_BULK; if (type_refs[UE_INTERRUPT] != 0) bus->hw_power_state |= USB_HW_POWER_INTERRUPT; if (type_refs[UE_ISOCHRONOUS] != 0) bus->hw_power_state |= USB_HW_POWER_ISOC; if (type_refs[4] != 0) bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB; } USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power != NULL) { /* always update hardware power! */ (bus->methods->set_hw_power) (bus); } return; } #endif /*------------------------------------------------------------------------* * usb_dev_resume_peer * * This function will resume an USB peer and do the required USB * signalling to get an USB device out of the suspended state. *------------------------------------------------------------------------*/ static void usb_dev_resume_peer(struct usb_device *udev) { struct usb_bus *bus; int err; /* be NULL safe */ if (udev == NULL) return; /* check if already resumed */ if (udev->flags.self_suspended == 0) return; /* we need a parent HUB to do resume */ if (udev->parent_hub == NULL) return; DPRINTF("udev=%p\n", udev); if ((udev->flags.usb_mode == USB_MODE_DEVICE) && (udev->flags.remote_wakeup == 0)) { /* * If the host did not set the remote wakeup feature, we can * not wake it up either! */ DPRINTF("remote wakeup is not set!\n"); return; } /* get bus pointer */ bus = udev->bus; /* resume parent hub first */ usb_dev_resume_peer(udev->parent_hub); /* reduce chance of instant resume failure by waiting a little bit */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(20)); if (usb_device_20_compatible(udev)) { /* resume current port (Valid in Host and Device Mode) */ err = usbd_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); if (err) { DPRINTFN(0, "Resuming port failed\n"); return; } } else { /* resume current port (Valid in Host and Device Mode) */ err = usbd_req_set_port_link_state(udev->parent_hub, NULL, udev->port_no, UPS_PORT_LS_U0); if (err) { DPRINTFN(0, "Resuming port failed\n"); return; } } /* resume settle time */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay)); if (bus->methods->device_resume != NULL) { /* resume USB device on the USB controller */ (bus->methods->device_resume) (udev); } USB_BUS_LOCK(bus); /* set that this device is now resumed */ udev->flags.self_suspended = 0; #if USB_HAVE_POWERD /* make sure that we don't go into suspend right away */ udev->pwr_save.last_xfer_time = ticks; /* make sure the needed power masks are on */ if (udev->pwr_save.type_refs[UE_CONTROL] != 0) bus->hw_power_state |= USB_HW_POWER_CONTROL; if (udev->pwr_save.type_refs[UE_BULK] != 0) bus->hw_power_state |= USB_HW_POWER_BULK; if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0) bus->hw_power_state |= USB_HW_POWER_INTERRUPT; if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) bus->hw_power_state |= USB_HW_POWER_ISOC; #endif USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power != NULL) { /* always update hardware power! */ (bus->methods->set_hw_power) (bus); } usbd_sr_lock(udev); /* notify all sub-devices about resume */ err = usb_suspend_resume(udev, 0); usbd_sr_unlock(udev); /* check if peer has wakeup capability */ if (usb_peer_can_wakeup(udev)) { /* clear remote wakeup */ err = usbd_req_clear_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); if (err) { DPRINTFN(0, "Clearing device " "remote wakeup failed: %s\n", usbd_errstr(err)); } } } /*------------------------------------------------------------------------* * usb_dev_suspend_peer * * This function will suspend an USB peer and do the required USB * signalling to get an USB device into the suspended state. *------------------------------------------------------------------------*/ static void usb_dev_suspend_peer(struct usb_device *udev) { struct usb_device *child; int err; uint8_t x; uint8_t nports; repeat: /* be NULL safe */ if (udev == NULL) return; /* check if already suspended */ if (udev->flags.self_suspended) return; /* we need a parent HUB to do suspend */ if (udev->parent_hub == NULL) return; DPRINTF("udev=%p\n", udev); /* check if the current device is a HUB */ if (udev->hub != NULL) { nports = udev->hub->nports; /* check if all devices on the HUB are suspended */ for (x = 0; x != nports; x++) { child = usb_bus_port_get_device(udev->bus, udev->hub->ports + x); if (child == NULL) continue; if (child->flags.self_suspended) continue; DPRINTFN(1, "Port %u is busy on the HUB!\n", x + 1); return; } } if (usb_peer_can_wakeup(udev)) { /* * This request needs to be done before we set * "udev->flags.self_suspended": */ /* allow device to do remote wakeup */ err = usbd_req_set_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); if (err) { DPRINTFN(0, "Setting device " "remote wakeup failed\n"); } } USB_BUS_LOCK(udev->bus); /* * Checking for suspend condition and setting suspended bit * must be atomic! */ err = usb_peer_should_wakeup(udev); if (err == 0) { /* * Set that this device is suspended. This variable * must be set before calling USB controller suspend * callbacks. */ udev->flags.self_suspended = 1; } USB_BUS_UNLOCK(udev->bus); if (err != 0) { if (usb_peer_can_wakeup(udev)) { /* allow device to do remote wakeup */ err = usbd_req_clear_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); if (err) { DPRINTFN(0, "Setting device " "remote wakeup failed\n"); } } if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* resume parent HUB first */ usb_dev_resume_peer(udev->parent_hub); /* reduce chance of instant resume failure by waiting a little bit */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(20)); /* resume current port (Valid in Host and Device Mode) */ err = usbd_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); /* resume settle time */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay)); } DPRINTF("Suspend was cancelled!\n"); return; } usbd_sr_lock(udev); /* notify all sub-devices about suspend */ err = usb_suspend_resume(udev, 1); usbd_sr_unlock(udev); if (udev->bus->methods->device_suspend != NULL) { usb_timeout_t temp; /* suspend device on the USB controller */ (udev->bus->methods->device_suspend) (udev); /* do DMA delay */ temp = usbd_get_dma_delay(udev); if (temp != 0) usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp)); } if (usb_device_20_compatible(udev)) { /* suspend current port */ err = usbd_req_set_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); if (err) { DPRINTFN(0, "Suspending port failed\n"); return; } } else { /* suspend current port */ err = usbd_req_set_port_link_state(udev->parent_hub, NULL, udev->port_no, UPS_PORT_LS_U3); if (err) { DPRINTFN(0, "Suspending port failed\n"); return; } } udev = udev->parent_hub; goto repeat; } /*------------------------------------------------------------------------* * usbd_set_power_mode * * This function will set the power mode, see USB_POWER_MODE_XXX for a * USB device. *------------------------------------------------------------------------*/ void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode) { /* filter input argument */ if ((power_mode != USB_POWER_MODE_ON) && (power_mode != USB_POWER_MODE_OFF)) power_mode = USB_POWER_MODE_SAVE; power_mode = usbd_filter_power_mode(udev, power_mode); udev->power_mode = power_mode; /* update copy of power mode */ #if USB_HAVE_POWERD usb_bus_power_update(udev->bus); #else usb_needs_explore(udev->bus, 0 /* no probe */ ); #endif } /*------------------------------------------------------------------------* * usbd_filter_power_mode * * This function filters the power mode based on hardware requirements. *------------------------------------------------------------------------*/ uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode) { const struct usb_bus_methods *mtod; int8_t temp; mtod = udev->bus->methods; temp = -1; if (mtod->get_power_mode != NULL) (mtod->get_power_mode) (udev, &temp); /* check if we should not filter */ if (temp < 0) return (power_mode); /* use fixed power mode given by hardware driver */ return (temp); } /*------------------------------------------------------------------------* * usbd_start_re_enumerate * * This function starts re-enumeration of the given USB device. This * function does not need to be called BUS-locked. This function does * not wait until the re-enumeration is completed. *------------------------------------------------------------------------*/ void usbd_start_re_enumerate(struct usb_device *udev) { if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) { udev->re_enumerate_wait = USB_RE_ENUM_START; usb_needs_explore(udev->bus, 0); } } /*-----------------------------------------------------------------------* * usbd_start_set_config * * This function starts setting a USB configuration. This function * does not need to be called BUS-locked. This function does not wait * until the set USB configuratino is completed. *------------------------------------------------------------------------*/ usb_error_t usbd_start_set_config(struct usb_device *udev, uint8_t index) { if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) { if (udev->curr_config_index == index) { /* no change needed */ return (0); } udev->next_config_index = index; udev->re_enumerate_wait = USB_RE_ENUM_SET_CONFIG; usb_needs_explore(udev->bus, 0); return (0); } else if (udev->re_enumerate_wait == USB_RE_ENUM_SET_CONFIG) { if (udev->next_config_index == index) { /* no change needed */ return (0); } } return (USB_ERR_PENDING_REQUESTS); } Index: head/sys/dev/usb/usb_transfer.c =================================================================== --- head/sys/dev/usb/usb_transfer.c (revision 298931) +++ head/sys/dev/usb/usb_transfer.c (revision 298932) @@ -1,3538 +1,3538 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { const struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * If the "align" argument is equal to "1" a non-contiguous allocation * can happen. Else if the "align" argument is greater than "1", the * allocation will always be contiguous in memory. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_dma_pg; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (align == 1) { /* special case - non-cached multi page DMA memory */ n_dma_pc = count; n_dma_pg = (2 + (size / USB_PAGE_SIZE)); n_obj = 1; } else if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_dma_pg = 1; n_obj = 1; } else { /* compute number of objects per page */ #ifdef USB_DMA_SINGLE_ALLOC n_obj = 1; #else n_obj = (USB_PAGE_SIZE / size); #endif /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = howmany(count, n_obj); n_dma_pg = 1; } /* * DMA memory is allocated once, but mapped twice. That's why * there is one list for auto-free and another list for * non-auto-free which only holds the mapping and not the * allocation. */ if (parm->buf == NULL) { /* reserve memory (auto-free) */ parm->dma_page_ptr += n_dma_pc * n_dma_pg; parm->dma_page_cache_ptr += n_dma_pc; /* reserve memory (no-auto-free) */ parm->dma_page_ptr += count * n_dma_pg; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc != NULL) { if (n_obj != 1) *ppc = parm->xfer_page_cache_ptr; else *ppc = parm->dma_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; if (n_obj == 1) { /* * Avoid mapping memory twice if only a single object * should be allocated per page cache: */ for (x = 0; x != n_dma_pc; x++) { if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; } } else { for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint16_t maxp_old; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* store max packet size value before filtering */ maxp_old = xfer->max_packet_size; /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* * Check if the max packet size was outside its allowed range * and clamped to a valid value: */ if (maxp_old != xfer->max_packet_size) xfer->flags_int.maxp_was_clamped = 1; /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; if (xfer->flags.pre_scale_frames != 0) xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence - * this leads to alot of extra code in the USB kernel. + * this leads to a lot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usbd_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* * Setup "frlengths" and shadow "frlengths" for keeping the * initial frame lengths when a USB transfer is complete. This * information is useful when computing isochronous offsets. */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { #if USB_HAVE_BUSDMA struct usb_page_search page_info; struct usb_page_cache *pc; if (usbd_transfer_setup_sub_malloc(parm, &pc, parm->bufsize, 1, 1)) { parm->err = USB_ERR_NOMEM; } else if (parm->buf != NULL) { usbd_get_page(pc, 0, &page_info); xfer->local_buffer = page_info.buffer; usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } #else /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf != NULL) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); #endif } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } static uint8_t usbd_transfer_setup_has_bulk(const struct usb_config *setup_start, uint16_t n_setup) { while (n_setup--) { uint8_t type = setup_start[n_setup].type; if (type == UE_BULK || type == UE_BULK_INTR || type == UE_TYPE_ANY) return (1); } return (0); } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; usb_error_t error = 0; uint16_t n; uint16_t refcount; uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == NULL) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* more sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (error) return (error); /* Protect scratch area */ do_unlock = usbd_enum_lock(udev); refcount = 0; info = NULL; parm = &udev->scratch.xfer_setup[0].parm; memset(parm, 0, sizeof(*parm)); parm->udev = udev; parm->speed = usbd_get_speed(udev); parm->hc_max_packet_count = 1; if (parm->speed >= USB_SPEED_MAX) { parm->err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits, parm->dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg) info->done_p = USB_BUS_CONTROL_XFER_PROC(udev->bus); else if (xfer_mtx == &Giant) info->done_p = USB_BUS_GIANT_PROC(udev->bus); else if (usbd_transfer_setup_has_bulk(setup_start, n_setup)) info->done_p = USB_BUS_NON_GIANT_BULK_PROC(udev->bus); else info->done_p = USB_BUS_NON_GIANT_ISOC_PROC(udev->bus); } /* reset sizes */ parm->size[0] = 0; parm->buf = buf; parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); /* * Check that the USB PIPE is valid and that * the endpoint mode is proper. * * Make sure we don't allocate a streams * transfer when such a combination is not * valid. */ if ((ep == NULL) || (ep->methods == NULL) || ((ep->ep_mode != USB_EP_MODE_STREAMS) && (ep->ep_mode != USB_EP_MODE_DEFAULT)) || (setup->stream_id != 0 && (setup->stream_id >= USB_MAX_EP_STREAMS || (ep->ep_mode != USB_EP_MODE_STREAMS)))) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &udev->scratch.xfer_setup[0].dummy; memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; /* set transfer stream ID */ xfer->stream_id = setup->stream_id; parm->size[0] += sizeof(xfer[0]); parm->methods = xfer->endpoint->methods; parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (parm); /* check for error */ if (parm->err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm->err) goto done; } if (buf != NULL || parm->err != 0) goto done; /* if no transfers, nothing to do */ if (refcount == 0) goto done; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is three: * 1) for loading memory * 2) for allocating memory * 3) for fixing memory [UHCI] */ parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm->dma_tag_max += 8; parm->dma_tag_p += parm->dma_tag_max; parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[3] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[4] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[5] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[2] = parm->size[0]; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); parm->size[6] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm->size[0]); goto done; } parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } /* check if any errors happened */ if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); error = parm->err; if (do_unlock) usbd_enum_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usbd_control_transfer_did_data * * This function returns non-zero if a control endpoint has * transferred the first DATA packet after the SETUP packet. * Else it returns zero. *------------------------------------------------------------------------*/ static uint8_t usbd_control_transfer_did_data(struct usb_xfer *xfer) { struct usb_device_request req; /* SETUP packet is not yet sent */ if (xfer->flags_int.control_hdr != 0) return (0); /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* compare remainder to the initial value */ return (xfer->flags_int.control_rem != UGETW(req.wLength)); } /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* update did data flag */ xfer->flags_int.control_did_data = usbd_control_transfer_did_data(xfer); /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint-> endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { /* make a copy of the frlenghts[] */ xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* enter the transfer */ (ep->methods->enter) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper( &ep->endpoint_q[xfer->stream_id], NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } void * usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) { struct usb_page_search page_info; KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); return (page_info.buffer); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } /*------------------------------------------------------------------------* * usbd_xfer_old_frame_length * * This function returns the framelength of the given frame at the * time the transfer was submitted. This function can be used to * compute the starting data pointer of the next isochronous frame * when an isochronous transfer has completed. *------------------------------------------------------------------------*/ usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex + xfer->max_frame_count]); } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if ((pq->recurse_3 != 0 || mtx_owned(info->xfer_mtx) == 0) && SCHEDULER_STOPPED() == 0) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. * 6) HW completed transfer during callback */ DPRINTFN(3, "case 5 and 6\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed! * * Postponing the callback also ensures that other USB * transfer queues get a chance. */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } #if USB_HAVE_PF if (xfer->usb_state != USB_ST_SETUP) { USB_BUS_LOCK(info->bus); usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); USB_BUS_UNLOCK(info->bus); } #endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB - * transfer. This code path is ususally only used when there is an USB + * transfer. This code path is usually only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error, if not already set */ if (xfer->error == USB_ERR_NORMAL_COMPLETION) xfer->error = error; /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(info->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &info->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { info->bus->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { info->bus->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( USB_BUS_CS_PROC(info->bus), &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; #if USB_HAVE_TT_SUPPORT switch (xfer->error) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: /* nothing to do */ break; default: /* try to reset the TT, if any */ USB_BUS_LOCK(bus); uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint); USB_BUS_UNLOCK(bus); break; } #endif /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL); if (ep->endpoint_q[xfer->stream_id].curr != NULL || TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { /* clear third recurse flag */ pq->recurse_3 = 0; do { /* set two first recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); /* * Set third recurse flag to indicate * recursion happened: */ pq->recurse_3 = 1; } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usbd_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Reset clear stall error counter. */ udev->clear_stall_errors = 0; /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; uint16_t n; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ if (udev->bus == NULL) continue; /* no BUS structure */ if (udev->bus->methods == NULL) continue; /* no BUS methods */ if (udev->bus->methods->xfer_poll == NULL) continue; /* no poll method */ /* make sure that the BUS mutex is not locked */ drop_bus = 0; while (mtx_owned(&xroot->udev->bus->bus_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(&xroot->udev->bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ drop_xfer = 0; while (mtx_owned(xroot->xfer_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } /* Make sure cv_signal() and cv_broadcast() is not called */ USB_BUS_CONTROL_XFER_PROC(udev->bus)->up_msleep = 0; USB_BUS_EXPLORE_PROC(udev->bus)->up_msleep = 0; USB_BUS_GIANT_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_ISOC_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_BULK_PROC(udev->bus)->up_msleep = 0; /* poll USB hardware */ (udev->bus->methods->xfer_poll) (udev->bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&xroot->udev->bus->bus_mtx); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } /* * The following function returns non-zero if the max packet size * field was clamped to a valid value. Else it returns zero. */ uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer) { return (xfer->flags_int.maxp_was_clamped); } Index: head/sys/dev/usb/usbdevs =================================================================== --- head/sys/dev/usb/usbdevs (revision 298931) +++ head/sys/dev/usb/usbdevs (revision 298932) @@ -1,4706 +1,4706 @@ $FreeBSD$ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ /*- * Copyright (c) 1998-2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * List of known USB vendors * * USB.org publishes a VID list of USB-IF member companies at * http://www.usb.org/developers/tools * Note that it does not show companies that have obtained a Vendor ID * without becoming full members. * * Please note that these IDs do not do anything. Adding an ID here and * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name * available to the source code and does not change any functionality, nor * does it make your device available to a specific driver. * It will however make the descriptive string available if a device does not * provide the string itself. * * After adding a vendor ID VNDR and a product ID PRDCT you will have the * following extra defines: * #define USB_VENDOR_VNDR 0x???? * #define USB_PRODUCT_VNDR_PRDCT 0x???? * * You may have to add these defines to the respective probe routines to * make the device recognised by the appropriate device driver. */ vendor UNKNOWN1 0x0053 Unknown vendor vendor UNKNOWN2 0x0105 Unknown vendor vendor EGALAX2 0x0123 eGalax, Inc. vendor CHIPSBANK 0x0204 Chipsbank Microelectronics Co. vendor HUMAX 0x02ad HUMAX vendor LTS 0x0386 LTS vendor BWCT 0x03da Bernd Walter Computer Technology vendor AOX 0x03e8 AOX vendor THESYS 0x03e9 Thesys vendor DATABROADCAST 0x03ea Data Broadcasting vendor ATMEL 0x03eb Atmel vendor IWATSU 0x03ec Iwatsu America vendor MITSUMI 0x03ee Mitsumi vendor HP 0x03f0 Hewlett Packard vendor GENOA 0x03f1 Genoa vendor OAK 0x03f2 Oak vendor ADAPTEC 0x03f3 Adaptec vendor DIEBOLD 0x03f4 Diebold vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical vendor EPSONIMAGING 0x03f8 Epson Imaging vendor KEYTRONIC 0x03f9 KeyTronic vendor OPTI 0x03fb OPTi vendor ELITEGROUP 0x03fc Elitegroup vendor XILINX 0x03fd Xilinx vendor FARALLON 0x03fe Farallon Communications vendor NATIONAL 0x0400 National Semiconductor vendor NATIONALREG 0x0401 National Registry vendor ACERLABS 0x0402 Acer Labs vendor FTDI 0x0403 Future Technology Devices vendor NCR 0x0404 NCR vendor SYNOPSYS2 0x0405 Synopsys vendor FUJITSUICL 0x0406 Fujitsu-ICL vendor FUJITSU2 0x0407 Fujitsu Personal Systems vendor QUANTA 0x0408 Quanta vendor NEC 0x0409 NEC vendor KODAK 0x040a Eastman Kodak vendor WELTREND 0x040b Weltrend vendor VIA 0x040d VIA vendor MCCI 0x040e MCCI vendor MELCO 0x0411 Melco vendor LEADTEK 0x0413 Leadtek vendor WINBOND 0x0416 Winbond vendor PHOENIX 0x041a Phoenix vendor CREATIVE 0x041e Creative Labs vendor NOKIA 0x0421 Nokia vendor ADI 0x0422 ADI Systems vendor CATC 0x0423 Computer Access Technology vendor SMC2 0x0424 Standard Microsystems vendor MOTOROLA_HK 0x0425 Motorola HK vendor GRAVIS 0x0428 Advanced Gravis Computer vendor CIRRUSLOGIC 0x0429 Cirrus Logic vendor INNOVATIVE 0x042c Innovative Semiconductors vendor MOLEX 0x042f Molex vendor SUN 0x0430 Sun Microsystems vendor UNISYS 0x0432 Unisys vendor TAUGA 0x0436 Taugagreining HF vendor AMD 0x0438 Advanced Micro Devices vendor LEXMARK 0x043d Lexmark International vendor LG 0x043e LG Electronics vendor NANAO 0x0440 NANAO vendor GATEWAY 0x0443 Gateway 2000 vendor NMB 0x0446 NMB vendor ALPS 0x044e Alps Electric vendor THRUST 0x044f Thrustmaster vendor TI 0x0451 Texas Instruments vendor ANALOGDEVICES 0x0456 Analog Devices vendor SIS 0x0457 Silicon Integrated Systems Corp. vendor KYE 0x0458 KYE Systems vendor DIAMOND2 0x045a Diamond (Supra) vendor RENESAS 0x045b Renesas vendor MICROSOFT 0x045e Microsoft vendor PRIMAX 0x0461 Primax Electronics vendor MGE 0x0463 MGE UPS Systems vendor AMP 0x0464 AMP vendor CHERRY 0x046a Cherry Mikroschalter vendor MEGATRENDS 0x046b American Megatrends vendor LOGITECH 0x046d Logitech vendor BTC 0x046e Behavior Tech. Computer vendor PHILIPS 0x0471 Philips -vendor SUN2 0x0472 Sun Microsystems (offical) +vendor SUN2 0x0472 Sun Microsystems (official) vendor SANYO 0x0474 Sanyo Electric vendor SEAGATE 0x0477 Seagate vendor CONNECTIX 0x0478 Connectix vendor SEMTECH 0x047a Semtech vendor KENSINGTON 0x047d Kensington vendor LUCENT 0x047e Lucent vendor PLANTRONICS 0x047f Plantronics vendor KYOCERA 0x0482 Kyocera Wireless Corp. vendor STMICRO 0x0483 STMicroelectronics vendor FOXCONN 0x0489 Foxconn vendor MEIZU 0x0492 Meizu Electronics vendor YAMAHA 0x0499 YAMAHA vendor COMPAQ 0x049f Compaq vendor HITACHI 0x04a4 Hitachi vendor ACERP 0x04a5 Acer Peripherals vendor DAVICOM 0x04a6 Davicom vendor VISIONEER 0x04a7 Visioneer vendor CANON 0x04a9 Canon vendor NIKON 0x04b0 Nikon vendor PAN 0x04b1 Pan International vendor IBM 0x04b3 IBM vendor CYPRESS 0x04b4 Cypress Semiconductor vendor ROHM 0x04b5 ROHM vendor COMPAL 0x04b7 Compal vendor EPSON 0x04b8 Seiko Epson vendor RAINBOW 0x04b9 Rainbow Technologies vendor IODATA 0x04bb I-O Data vendor TDK 0x04bf TDK vendor 3COMUSR 0x04c1 U.S. Robotics vendor METHODE 0x04c2 Methode Electronics Far East vendor MAXISWITCH 0x04c3 Maxi Switch vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research vendor FUJITSU 0x04c5 Fujitsu vendor TOSHIBAAM 0x04c6 Toshiba America vendor MICROMACRO 0x04c7 Micro Macro Technologies vendor KONICA 0x04c8 Konica vendor LITEON 0x04ca Lite-On Technology vendor FUJIPHOTO 0x04cb Fuji Photo Film vendor PHILIPSSEMI 0x04cc Philips Semiconductors vendor TATUNG 0x04cd Tatung Co. Of America vendor SCANLOGIC 0x04ce ScanLogic vendor MYSON 0x04cf Myson Technology vendor DIGI2 0x04d0 Digi vendor ITTCANON 0x04d1 ITT Canon vendor ALTEC 0x04d2 Altec Lansing vendor LSI 0x04d4 LSI vendor MENTORGRAPHICS 0x04d6 Mentor Graphics vendor ITUNERNET 0x04d8 I-Tuner Networks vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc. vendor PANASONIC 0x04da Panasonic (Matsushita) vendor HUANHSIN 0x04dc Huan Hsin vendor SHARP 0x04dd Sharp vendor IIYAMA 0x04e1 Iiyama vendor SHUTTLE 0x04e6 Shuttle Technology vendor ELO 0x04e7 Elo TouchSystems vendor SAMSUNG 0x04e8 Samsung Electronics vendor NORTHSTAR 0x04eb Northstar vendor TOKYOELECTRON 0x04ec Tokyo Electron vendor ANNABOOKS 0x04ed Annabooks vendor JVC 0x04f1 JVC vendor CHICONY 0x04f2 Chicony Electronics vendor ELAN 0x04f3 Elan vendor NEWNEX 0x04f7 Newnex vendor BROTHER 0x04f9 Brother Industries vendor DALLAS 0x04fa Dallas Semiconductor vendor AIPTEK2 0x04fc AIPTEK International vendor PFU 0x04fe PFU vendor FUJIKURA 0x0501 Fujikura/DDK vendor ACER 0x0502 Acer vendor 3COM 0x0506 3Com vendor HOSIDEN 0x0507 Hosiden Corporation vendor AZTECH 0x0509 Aztech Systems vendor BELKIN 0x050d Belkin Components vendor KAWATSU 0x050f Kawatsu Semiconductor vendor FCI 0x0514 FCI vendor LONGWELL 0x0516 Longwell vendor COMPOSITE 0x0518 Composite vendor STAR 0x0519 Star Micronics vendor APC 0x051d American Power Conversion vendor SCIATLANTA 0x051e Scientific Atlanta vendor TSM 0x0520 TSM vendor CONNECTEK 0x0522 Advanced Connectek USA vendor NETCHIP 0x0525 NetChip Technology vendor ALTRA 0x0527 ALTRA vendor ATI 0x0528 ATI Technologies vendor AKS 0x0529 Aladdin Knowledge Systems vendor TEKOM 0x052b Tekom vendor CANONDEV 0x052c Canon vendor WACOMTECH 0x0531 Wacom vendor INVENTEC 0x0537 Inventec vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG vendor SYNOPSYS 0x053f Synopsys vendor UNIACCESS 0x0540 Universal Access vendor VIEWSONIC 0x0543 ViewSonic vendor XIRLINK 0x0545 Xirlink vendor ANCHOR 0x0547 Anchor Chips vendor SONY 0x054c Sony vendor FUJIXEROX 0x0550 Fuji Xerox vendor VISION 0x0553 VLSI Vision vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems vendor ATEN 0x0557 ATEN International vendor SAMSUNG2 0x055d Samsung Electronics vendor MUSTEK 0x055f Mustek Systems vendor TELEX 0x0562 Telex Communications vendor CHINON 0x0564 Chinon vendor PERACOM 0x0565 Peracom Networks vendor ALCOR2 0x0566 Alcor Micro vendor XYRATEX 0x0567 Xyratex vendor WACOM 0x056a WACOM vendor ETEK 0x056c e-TEK Labs vendor EIZO 0x056d EIZO vendor ELECOM 0x056e Elecom vendor CONEXANT 0x0572 Conexant vendor HAUPPAUGE 0x0573 Hauppauge Computer Works vendor BAFO 0x0576 BAFO/Quality Computer Accessories vendor YEDATA 0x057b Y-E Data vendor AVM 0x057c AVM vendor QUICKSHOT 0x057f Quickshot vendor ROLAND 0x0582 Roland vendor ROCKFIRE 0x0583 Rockfire vendor RATOC 0x0584 RATOC Systems vendor ZYXEL 0x0586 ZyXEL Communication vendor INFINEON 0x058b Infineon vendor MICREL 0x058d Micrel vendor ALCOR 0x058f Alcor Micro vendor OMRON 0x0590 OMRON vendor ZORAN 0x0595 Zoran Microelectronics vendor NIIGATA 0x0598 Niigata vendor IOMEGA 0x059b Iomega vendor ATREND 0x059c A-Trend Technology vendor AID 0x059d Advanced Input Devices vendor LACIE 0x059f LaCie vendor FUJIFILM 0x05a2 Fuji Film vendor ARC 0x05a3 ARC vendor ORTEK 0x05a4 Ortek vendor CISCOLINKSYS3 0x05a6 Cisco-Linksys vendor BOSE 0x05a7 Bose vendor OMNIVISION 0x05a9 OmniVision vendor INSYSTEM 0x05ab In-System Design vendor APPLE 0x05ac Apple Computer vendor YCCABLE 0x05ad Y.C. Cable vendor DIGITALPERSONA 0x05ba DigitalPersona vendor 3G 0x05bc 3G Green Green Globe vendor RAFI 0x05bd RAFI vendor TYCO 0x05be Tyco vendor KAWASAKI 0x05c1 Kawasaki vendor DIGI 0x05c5 Digi International vendor QUALCOMM2 0x05c6 Qualcomm vendor QTRONIX 0x05c7 Qtronix vendor FOXLINK 0x05c8 Foxlink vendor RICOH 0x05ca Ricoh vendor ELSA 0x05cc ELSA vendor SCIWORX 0x05ce sci-worx vendor BRAINBOXES 0x05d1 Brainboxes Limited vendor ULTIMA 0x05d8 Ultima vendor AXIOHM 0x05d9 Axiohm Transaction Solutions vendor MICROTEK 0x05da Microtek vendor SUNTAC 0x05db SUN Corporation vendor LEXAR 0x05dc Lexar Media vendor ADDTRON 0x05dd Addtron vendor SYMBOL 0x05e0 Symbol Technologies vendor SYNTEK 0x05e1 Syntek vendor GENESYS 0x05e3 Genesys Logic vendor FUJI 0x05e5 Fuji Electric vendor KEITHLEY 0x05e6 Keithley Instruments vendor EIZONANAO 0x05e7 EIZO Nanao vendor KLSI 0x05e9 Kawasaki LSI vendor FFC 0x05eb FFC vendor ANKO 0x05ef Anko Electronic vendor PIENGINEERING 0x05f3 P.I. Engineering vendor AOC 0x05f6 AOC International vendor CHIC 0x05fe Chic Technology vendor BARCO 0x0600 Barco Display Systems vendor BRIDGE 0x0607 Bridge Information vendor SOLIDYEAR 0x060b Solid Year vendor BIORAD 0x0614 Bio-Rad Laboratories vendor MACALLY 0x0618 Macally vendor ACTLABS 0x061c Act Labs vendor ALARIS 0x0620 Alaris vendor APEX 0x0624 Apex vendor CREATIVE3 0x062a Creative Labs vendor MICRON 0x0634 Micron Technology vendor VIVITAR 0x0636 Vivitar vendor GUNZE 0x0637 Gunze Electronics USA vendor AVISION 0x0638 Avision vendor TEAC 0x0644 TEAC vendor ACTON 0x0647 Acton Research Corp. vendor OPTO 0x065a Optoelectronics Co., Ltd vendor SGI 0x065e Silicon Graphics vendor SANWASUPPLY 0x0663 Sanwa Supply vendor MEGATEC 0x0665 Megatec vendor LINKSYS 0x066b Linksys vendor ACERSA 0x066e Acer Semiconductor America vendor SIGMATEL 0x066f Sigmatel vendor DRAYTEK 0x0675 DrayTek vendor AIWA 0x0677 Aiwa vendor ACARD 0x0678 ACARD Technology vendor PROLIFIC 0x067b Prolific Technology vendor SIEMENS 0x067c Siemens vendor AVANCELOGIC 0x0680 Avance Logic vendor SIEMENS2 0x0681 Siemens vendor MINOLTA 0x0686 Minolta vendor CHPRODUCTS 0x068e CH Products vendor HAGIWARA 0x0693 Hagiwara Sys-Com vendor CTX 0x0698 Chuntex vendor ASKEY 0x069a Askey Computer vendor SAITEK 0x06a3 Saitek vendor ALCATELT 0x06b9 Alcatel Telecom vendor AGFA 0x06bd AGFA-Gevaert vendor ASIAMD 0x06be Asia Microelectronic Development vendor BIZLINK 0x06c4 Bizlink International vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc. vendor CONTEC 0x06ce Contec products vendor AASHIMA 0x06d6 Aashima Technology vendor LIEBERT 0x06da Liebert vendor MULTITECH 0x06e0 MultiTech vendor ADS 0x06e1 ADS Technologies vendor ALCATELM 0x06e4 Alcatel Microelectronics vendor SIRIUS 0x06ea Sirius Technologies vendor GUILLEMOT 0x06f8 Guillemot vendor BOSTON 0x06fd Boston Acoustics vendor SMC 0x0707 Standard Microsystems vendor PUTERCOM 0x0708 Putercom vendor MCT 0x0711 MCT vendor IMATION 0x0718 Imation vendor TECLAST 0x071b Teclast vendor SONYERICSSON 0x0731 Sony Ericsson vendor EICON 0x0734 Eicon Networks vendor SYNTECH 0x0745 Syntech Information vendor DIGITALSTREAM 0x074e Digital Stream vendor AUREAL 0x0755 Aureal Semiconductor vendor MAUDIO 0x0763 M-Audio vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc. vendor SURECOM 0x0769 Surecom Technology vendor HIDGLOBAL 0x076b HID Global vendor LINKSYS2 0x077b Linksys vendor GRIFFIN 0x077d Griffin Technology vendor SANDISK 0x0781 SanDisk vendor JENOPTIK 0x0784 Jenoptik vendor LOGITEC 0x0789 Logitec vendor NOKIA2 0x078b Nokia vendor BRIMAX 0x078e Brimax vendor AXIS 0x0792 Axis Communications vendor ABL 0x0794 ABL Electronics vendor SAGEM 0x079b Sagem vendor SUNCOMM 0x079c Sun Communications, Inc. vendor ALFADATA 0x079d Alfadata Computer vendor NATIONALTECH 0x07a2 National Technical Systems vendor ONNTO 0x07a3 Onnto vendor BE 0x07a4 Be vendor ADMTEK 0x07a6 ADMtek vendor COREGA 0x07aa Corega vendor FREECOM 0x07ab Freecom vendor MICROTECH 0x07af Microtech vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola) vendor OLYMPUS 0x07b4 Olympus vendor ABOCOM 0x07b8 AboCom Systems vendor KEISOKUGIKEN 0x07c1 Keisokugiken vendor ONSPEC 0x07c4 OnSpec vendor APG 0x07c5 APG Cash Drawer vendor BUG 0x07c8 B.U.G. vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International vendor AVERMEDIA 0x07ca AVerMedia Technologies vendor SIIG 0x07cc SIIG vendor CASIO 0x07cf CASIO vendor DLINK2 0x07d1 D-Link vendor APTIO 0x07d2 Aptio Products vendor ARASAN 0x07da Arasan Chip Systems vendor ALLIEDCABLE 0x07e6 Allied Cable vendor STSN 0x07ef STSN vendor CENTURY 0x07f7 Century Corp vendor NEWLINK 0x07ff NEWlink vendor MAGTEK 0x0801 Mag-Tek vendor ZOOM 0x0803 Zoom Telephonics vendor PCS 0x0810 Personal Communication Systems vendor ALPHASMART 0x081e AlphaSmart, Inc. vendor BROADLOGIC 0x0827 BroadLogic vendor HANDSPRING 0x082d Handspring vendor PALM 0x0830 Palm Computing vendor SOURCENEXT 0x0833 SOURCENEXT vendor ACTIONSTAR 0x0835 Action Star Enterprise vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin vendor ACCTON 0x083a Accton Technology vendor DIAMOND 0x0841 Diamond vendor NETGEAR 0x0846 BayNETGEAR vendor TOPRE 0x0853 Topre Corporation vendor ACTIVEWIRE 0x0854 ActiveWire vendor BBELECTRONICS 0x0856 B&B Electronics vendor PORTGEAR 0x085a PortGear vendor NETGEAR2 0x0864 Netgear vendor SYSTEMTALKS 0x086e System Talks vendor METRICOM 0x0870 Metricom vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America vendor JATON 0x087d Jaton vendor APT 0x0880 APT Technologies vendor BOCARESEARCH 0x0885 Boca Research vendor ANDREA 0x08a8 Andrea Electronics vendor BURRBROWN 0x08bb Burr-Brown Japan vendor 2WIRE 0x08c8 2Wire vendor AIPTEK 0x08ca AIPTEK International vendor SMARTBRIDGES 0x08d1 SmartBridges vendor FUJITSUSIEMENS 0x08d4 Fujitsu-Siemens vendor BILLIONTON 0x08dd Billionton Systems vendor GEMALTO 0x08e6 Gemalto SA vendor EXTENDED 0x08e9 Extended Systems vendor MSYSTEMS 0x08ec M-Systems vendor DIGIANSWER 0x08fd Digianswer vendor AUTHENTEC 0x08ff AuthenTec vendor AUDIOTECHNICA 0x0909 Audio-Technica vendor TRUMPION 0x090a Trumpion Microelectronics vendor FEIYA 0x090c Feiya vendor ALATION 0x0910 Alation Systems vendor GLOBESPAN 0x0915 Globespan vendor CONCORDCAMERA 0x0919 Concord Camera vendor GARMIN 0x091e Garmin International vendor GOHUBS 0x0921 GoHubs vendor DYMO 0x0922 DYMO vendor XEROX 0x0924 Xerox vendor BIOMETRIC 0x0929 American Biometric Company vendor TOSHIBA 0x0930 Toshiba vendor PLEXTOR 0x093b Plextor vendor INTREPIDCS 0x093c Intrepid vendor YANO 0x094f Yano vendor KINGSTON 0x0951 Kingston Technology vendor BLUEWATER 0x0956 BlueWater Systems vendor AGILENT 0x0957 Agilent Technologies vendor GUDE 0x0959 Gude ADS vendor PORTSMITH 0x095a Portsmith vendor ACERW 0x0967 Acer vendor ADIRONDACK 0x0976 Adirondack Wire & Cable vendor BECKHOFF 0x0978 Beckhoff vendor MINDSATWORK 0x097a Minds At Work vendor POINTCHIPS 0x09a6 PointChips vendor INTERSIL 0x09aa Intersil vendor ALTIUS 0x09b3 Altius Solutions vendor ARRIS 0x09c1 Arris Interactive vendor ACTIVCARD 0x09c3 ACTIVCARD vendor ACTISYS 0x09c4 ACTiSYS vendor NOVATEL2 0x09d7 Novatel Wireless vendor AFOURTECH 0x09da A-FOUR TECH vendor AIMEX 0x09dc AIMEX vendor ADDONICS 0x09df Addonics Technologies vendor AKAI 0x09e8 AKAI professional M.I. vendor ARESCOM 0x09f5 ARESCOM vendor BAY 0x09f9 Bay Associates vendor ALTERA 0x09fb Altera vendor CSR 0x0a12 Cambridge Silicon Radio vendor TREK 0x0a16 Trek Technology vendor ASAHIOPTICAL 0x0a17 Asahi Optical vendor BOCASYSTEMS 0x0a43 Boca Systems vendor SHANTOU 0x0a46 ShanTou vendor MEDIAGEAR 0x0a48 MediaGear vendor BROADCOM 0x0a5c Broadcom vendor GREENHOUSE 0x0a6b GREENHOUSE vendor MEDELI 0x0a67 Medeli vendor GEOCAST 0x0a79 Geocast Network Systems vendor EGO 0x0a92 EGO systems vendor IDQUANTIQUE 0x0aba ID Quantique vendor IDTECH 0x0acd ID TECH vendor ZYDAS 0x0ace Zydas Technology Corporation vendor NEODIO 0x0aec Neodio vendor OPTION 0x0af0 Option N.V. vendor ASUS 0x0b05 ASUSTeK Computer vendor TODOS 0x0b0c Todos Data System vendor SIIG2 0x0b39 SIIG vendor TEKRAM 0x0b3b Tekram Technology vendor HAL 0x0b41 HAL Corporation vendor EMS 0x0b43 EMS Production vendor NEC2 0x0b62 NEC vendor ADLINK 0x0b63 ADLINK Technoligy, Inc. vendor ATI2 0x0b6f ATI vendor ZEEVO 0x0b7a Zeevo, Inc. vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc. vendor SMART 0x0b8c Smart Technologies vendor ASIX 0x0b95 ASIX Electronics vendor O2MICRO 0x0b97 O2 Micro, Inc. vendor USR 0x0baf U.S. Robotics vendor AMBIT 0x0bb2 Ambit Microsystems vendor HTC 0x0bb4 HTC vendor REALTEK 0x0bda Realtek vendor ERICSSON2 0x0bdb Ericsson vendor MEI 0x0bed MEI vendor ADDONICS2 0x0bf6 Addonics Technology vendor FSC 0x0bf8 Fujitsu Siemens Computers vendor AGATE 0x0c08 Agate Technologies vendor DMI 0x0c0b DMI vendor CANYON 0x0c10 Canyon vendor ICOM 0x0c26 Icom Inc. vendor GNOTOMETRICS 0x0c33 GN Otometrics vendor CHICONY2 0x0c45 Chicony / Microdia / Sonix Technology Co., Ltd. vendor REINERSCT 0x0c4b Reiner-SCT vendor SEALEVEL 0x0c52 Sealevel System vendor JETI 0x0c6c Jeti vendor LUWEN 0x0c76 Luwen vendor ELEKTOR 0x0c7d ELEKTOR Electronics vendor KYOCERA2 0x0c88 Kyocera Wireless Corp. vendor ZCOM 0x0cde Z-Com vendor ATHEROS2 0x0cf3 Atheros Communications vendor POSIFLEX 0x0d3a POSIFLEX vendor TANGTOP 0x0d3d Tangtop vendor KOBIL 0x0d46 KOBIL vendor SMC3 0x0d5c Standard Microsystems vendor ADDON 0x0d7d Add-on Technology vendor ACDC 0x0d7e American Computer & Digital Components vendor CMEDIA 0x0d8c CMEDIA vendor CONCEPTRONIC 0x0d8e Conceptronic vendor SKANHEX 0x0d96 Skanhex Technology, Inc. vendor MSI 0x0db0 Micro Star International vendor ELCON 0x0db7 ELCON Systemtechnik vendor UNKNOWN4 0x0dcd Unknown vendor vendor NETAC 0x0dd8 Netac vendor SITECOMEU 0x0df6 Sitecom Europe vendor MOBILEACTION 0x0df7 Mobile Action vendor AMIGO 0x0e0b Amigo Technology vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia vendor HAWKING 0x0e66 Hawking vendor FOSSIL 0x0e67 Fossil, Inc vendor GMATE 0x0e7e G.Mate, Inc vendor MEDIATEK 0x0e8d MediaTek, Inc. vendor OTI 0x0ea0 Ours Technology vendor YISO 0x0eab Yiso Wireless Co. vendor PILOTECH 0x0eaf Pilotech vendor NOVATECH 0x0eb0 NovaTech vendor ITEGNO 0x0eba iTegno vendor WINMAXGROUP 0x0ed1 WinMaxGroup vendor TOD 0x0ede TOD vendor EGALAX 0x0eef eGalax, Inc. vendor AIRPRIME 0x0f3d AirPrime, Inc. vendor MICROTUNE 0x0f4d Microtune vendor VTECH 0x0f88 VTech vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH vendor RIM 0x0fca Research In Motion vendor DYNASTREAM 0x0fcf Dynastream Innovations vendor LARSENBRUSGAARD 0x0fd8 Larsen and Brusgaard vendor OWL 0x0fde OWL vendor KONTRON 0x0fe6 Kontron AG vendor QUALCOMM 0x1004 Qualcomm vendor APACER 0x1005 Apacer vendor MOTOROLA4 0x100d Motorola vendor HP3 0x103c Hewlett Packard vendor AIRPLUS 0x1011 Airplus vendor DESKNOTE 0x1019 Desknote vendor NEC3 0x1033 NEC vendor TTI 0x103e Thurlby Thandar Instruments vendor GIGABYTE 0x1044 GIGABYTE vendor WESTERN 0x1058 Western Digital vendor MOTOROLA 0x1063 Motorola vendor CCYU 0x1065 CCYU Technology vendor CURITEL 0x106c Curitel Communications Inc vendor SILABS2 0x10a6 SILABS2 vendor USI 0x10ab USI vendor LIEBERT2 0x10af Liebert vendor PLX 0x10b5 PLX vendor ASANTE 0x10bd Asante vendor SILABS 0x10c4 Silicon Labs vendor SILABS3 0x10c5 Silicon Labs vendor SILABS4 0x10ce Silicon Labs vendor ACTIONS 0x10d6 Actions vendor ANALOG 0x1110 Analog Devices vendor TENX 0x1130 Ten X Technology, Inc. vendor ISSC 0x1131 Integrated System Solution Corp. vendor JRC 0x1145 Japan Radio Company vendor SPHAIRON 0x114b Sphairon Access Systems GmbH vendor DELORME 0x1163 DeLorme vendor SERVERWORKS 0x1166 ServerWorks vendor DLINK3 0x1186 Dlink vendor ACERCM 0x1189 Acer Communications & Multimedia vendor SIERRA 0x1199 Sierra Wireless vendor SANWA 0x11ad Sanwa Electric Instrument Co., Ltd. vendor TOPFIELD 0x11db Topfield Co., Ltd vendor SIEMENS3 0x11f5 Siemens vendor NETINDEX 0x11f6 NetIndex vendor ALCATEL 0x11f7 Alcatel vendor INTERBIOMETRICS 0x1209 Interbiometrics vendor UNKNOWN3 0x1233 Unknown vendor vendor TSUNAMI 0x1241 Tsunami vendor PHEENET 0x124a Pheenet vendor TARGUS 0x1267 Targus vendor TWINMOS 0x126f TwinMOS vendor TENDA 0x1286 Tenda vendor TESTO 0x128d Testo products vendor CREATIVE2 0x1292 Creative Labs vendor BELKIN2 0x1293 Belkin Components vendor CYBERTAN 0x129b CyberTAN Technology vendor HUAWEI 0x12d1 Huawei Technologies vendor ARANEUS 0x12d8 Araneus Information Systems vendor TAPWAVE 0x12ef Tapwave vendor AINCOMM 0x12fd Aincomm vendor MOBILITY 0x1342 Mobility vendor DICKSMITH 0x1371 Dick Smith Electronics vendor NETGEAR3 0x1385 Netgear vendor BALTECH 0x13ad Baltech vendor CISCOLINKSYS 0x13b1 Cisco-Linksys vendor SHARK 0x13d2 Shark vendor AZUREWAVE 0x13d3 AsureWave vendor INITIO 0x13fd Initio Corporation vendor EMTEC 0x13fe Emtec vendor NOVATEL 0x1410 Novatel Wireless vendor MERLIN 0x1416 Merlin vendor REDOCTANE 0x1430 RedOctane vendor WISTRONNEWEB 0x1435 Wistron NeWeb vendor RADIOSHACK 0x1453 Radio Shack vendor FIC 0x1457 FIC / OpenMoko vendor HUAWEI3COM 0x1472 Huawei-3Com vendor ABOCOM2 0x1482 AboCom Systems vendor SILICOM 0x1485 Silicom vendor RALINK 0x148f Ralink Technology vendor IMAGINATION 0x149a Imagination Technologies vendor ATP 0x14af ATP Electronics vendor CONCEPTRONIC2 0x14b2 Conceptronic vendor SUPERTOP 0x14cd Super Top vendor PLANEX3 0x14ea Planex Communications vendor SILICONPORTALS 0x1527 Silicon Portals vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd. vendor JMICRON 0x152d JMicron vendor UBLOX 0x1546 U-blox vendor PNY 0x154b PNY vendor OWEN 0x1555 Owen vendor OQO 0x1557 OQO vendor UMEDIA 0x157e U-MEDIA Communications vendor FIBERLINE 0x1582 Fiberline vendor FREESCALE 0x15a2 Freescale Semiconductor, Inc. vendor AFATECH 0x15a4 Afatech Technologies, Inc. vendor SPARKLAN 0x15a9 SparkLAN vendor OLIMEX 0x15ba Olimex vendor SOUNDGRAPH 0x15c2 Soundgraph, Inc. vendor AMIT2 0x15c5 AMIT vendor TEXTECH 0x15ca Textech International Ltd. vendor SOHOWARE 0x15e8 SOHOware vendor UMAX 0x1606 UMAX Data Systems vendor INSIDEOUT 0x1608 Inside Out Networks vendor AMOI 0x1614 Amoi Electronics vendor GOODWAY 0x1631 Good Way Technology vendor ENTREGA 0x1645 Entrega vendor ACTIONTEC 0x1668 Actiontec Electronics vendor CLIPSAL 0x166a Clipsal vendor CISCOLINKSYS2 0x167b Cisco-Linksys vendor ATHEROS 0x168c Atheros Communications vendor GIGASET 0x1690 Gigaset vendor GLOBALSUN 0x16ab Global Sun Technology vendor ANYDATA 0x16d5 AnyDATA Corporation vendor JABLOTRON 0x16d6 Jablotron vendor CMOTECH 0x16d8 C-motech vendor WIENERPLEINBAUS 0x16dc WIENER Plein & Baus GmbH. vendor AXESSTEL 0x1726 Axesstel Co., Ltd. vendor LINKSYS4 0x1737 Linksys vendor SENAO 0x1740 Senao vendor ASUS2 0x1761 ASUS vendor SWEEX2 0x177f Sweex vendor METAGEEK 0x1781 MetaGeek vendor KAMSTRUP 0x17a8 Kamstrup A/S vendor DISPLAYLINK 0x17e9 DisplayLink vendor LENOVO 0x17ef Lenovo vendor WAVESENSE 0x17f4 WaveSense vendor VAISALA 0x1843 Vaisala vendor AMIT 0x18c5 AMIT vendor GOOGLE 0x18d1 Google vendor QCOM 0x18e8 Qcom vendor ELV 0x18ef ELV vendor LINKSYS3 0x1915 Linksys vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated vendor QUALCOMM3 0x19f5 Qualcomm, Inc. vendor BAYER 0x1a79 Bayer vendor WCH2 0x1a86 QinHeng Electronics vendor STELERA 0x1a8d Stelera Wireless vendor SEL 0x1adb Schweitzer Engineering Laboratories vendor CORSAIR 0x1b1c Corsair vendor MATRIXORBITAL 0x1b3d Matrix Orbital vendor OVISLINK 0x1b75 OvisLink vendor TML 0x1b91 The Mobility Lab vendor TCTMOBILE 0x1bbb TCT Mobile vendor ALTI2 0x1bc9 Alti-2 products vendor SUNPLUS 0x1bcf Sunplus Innovation Technology Inc. vendor WAGO 0x1be3 WAGO Kontakttechnik GmbH. vendor TELIT 0x1bc7 Telit vendor IONICS 0x1c0c Ionics PlugComputer vendor LONGCHEER 0x1c9e Longcheer Holdings, Ltd. vendor MPMAN 0x1cae MpMan vendor DRESDENELEKTRONIK 0x1cf1 dresden elektronik vendor NEOTEL 0x1d09 Neotel vendor DREAMLINK 0x1d34 Dream Link vendor PEGATRON 0x1d4d Pegatron vendor QISDA 0x1da5 Qisda vendor METAGEEK2 0x1dd5 MetaGeek vendor ALINK 0x1e0e Alink vendor AIRTIES 0x1eda AirTies vendor FESTO 0x1e29 Festo vendor LAKESHORE 0x1fb9 Lake Shore Cryotronics, Inc. vendor VERTEX 0x1fe7 Vertex Wireless Co., Ltd. vendor DLINK 0x2001 D-Link vendor PLANEX2 0x2019 Planex Communications vendor HAUPPAUGE2 0x2040 Hauppauge Computer Works vendor TLAYTECH 0x20b9 Tlay Tech vendor ENCORE 0x203d Encore vendor QIHARDWARE 0x20b7 QI-hardware vendor PARA 0x20b8 PARA Industrial vendor SIMTEC 0x20df Simtec Electronics vendor TRENDNET 0x20f4 TRENDnet vendor RTSYSTEMS 0x2100 RTSYSTEMS vendor VIALABS 0x2109 VIA Labs vendor ERICSSON 0x2282 Ericsson vendor MOTOROLA2 0x22b8 Motorola vendor WETELECOM 0x22de WeTelecom vendor WESTMOUNTAIN 0x2405 West Mountain Radio vendor TRIPPLITE 0x2478 Tripp-Lite vendor HIROSE 0x2631 Hirose Electric vendor NHJ 0x2770 NHJ vendor PLANEX 0x2c02 Planex Communications vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd vendor LINKINSTRUMENTS 0x3195 Link Instruments Inc. vendor AEI 0x3334 AEI vendor HANK 0x3353 Hank Connection vendor PQI 0x3538 PQI vendor DAISY 0x3579 Daisy Technology vendor NI 0x3923 National Instruments vendor MICRONET 0x3980 Micronet Communications vendor IODATA2 0x40bb I-O Data vendor IRIVER 0x4102 iRiver vendor DELL 0x413c Dell vendor WCH 0x4348 QinHeng Electronics vendor ACEECA 0x4766 Aceeca vendor FEIXUN 0x4855 FeiXun Communication vendor PAPOUCH 0x5050 Papouch products vendor AVERATEC 0x50c2 Averatec vendor SWEEX 0x5173 Sweex vendor PROLIFIC2 0x5372 Prolific Technologies vendor ONSPEC2 0x55aa OnSpec Electronic Inc. vendor ZINWELL 0x5a57 Zinwell vendor SITECOM 0x6189 Sitecom vendor ARKMICRO 0x6547 Arkmicro Technologies Inc. vendor 3COM2 0x6891 3Com vendor EDIMAX 0x7392 Edimax vendor INTEL 0x8086 Intel vendor INTEL2 0x8087 Intel vendor ALLWIN 0x8516 ALLWIN Tech vendor SITECOM2 0x9016 Sitecom vendor MOSCHIP 0x9710 MosChip Semiconductor vendor NETGEAR4 0x9846 Netgear vendor MARVELL 0x9e88 Marvell Technology Group Ltd. vendor 3COM3 0xa727 3Com vendor CACE 0xcace CACE Technologies vendor EVOLUTION 0xdeee Evolution Robotics products vendor DATAAPEX 0xdaae DataApex vendor HP2 0xf003 Hewlett Packard vendor LOGILINK 0xfc08 LogiLink vendor USRP 0xfffe GNU Radio USRP /* * List of known products. Grouped by vendor. */ /* 3Com products */ product 3COM HOMECONN 0x009d HomeConnect Camera product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter product 3COM 3C460 0x11f8 HomeConnect 3C460 product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro product 3COM 3C460B 0x4601 HomeConnect 3C460B product 3COM2 3CRUSB10075 0xa727 3CRUSB10075 product 3COM3 AR5523_1 0x6893 AR5523 product 3COM3 AR5523_2 0x6895 AR5523 product 3COM3 AR5523_3 0x6897 AR5523 product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro /* AboCom products */ product ABOCOM XX1 0x110c XX1 product ABOCOM XX2 0x200c XX2 product ABOCOM RT2770 0x2770 RT2770 product ABOCOM RT2870 0x2870 RT2870 product ABOCOM RT3070 0x3070 RT3070 product ABOCOM RT3071 0x3071 RT3071 product ABOCOM RT3072 0x3072 RT3072 product ABOCOM2 RT2870_1 0x3c09 RT2870 product ABOCOM URE450 0x4000 URE450 Ethernet Adapter product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter product ABOCOM XX4 0x4004 XX4 product ABOCOM XX5 0x4007 XX5 product ABOCOM XX6 0x400b XX6 product ABOCOM XX7 0x400c XX7 product ABOCOM RTL8151 0x401a RTL8151 product ABOCOM XX8 0x4102 XX8 product ABOCOM XX9 0x4104 XX9 product ABOCOM UF200 0x420a UF200 Ethernet product ABOCOM WL54 0x6001 WL54 product ABOCOM XX10 0xabc1 XX10 product ABOCOM BWU613 0xb000 BWU613 product ABOCOM HWU54DM 0xb21b HWU54DM product ABOCOM RT2573_2 0xb21c RT2573 product ABOCOM RT2573_3 0xb21d RT2573 product ABOCOM RT2573_4 0xb21e RT2573 product ABOCOM RTL8188CU_1 0x8188 RTL8188CU product ABOCOM RTL8188CU_2 0x8189 RTL8188CU product ABOCOM RTL8192CU 0x8178 RTL8192CU product ABOCOM RTL8188EU 0x8179 RTL8188EU product ABOCOM WUG2700 0xb21f WUG2700 /* Acton Research Corp. */ product ACTON SPECTRAPRO 0x0100 FTDI compatible adapter /* Accton products */ product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter product ACCTON 2664W 0x3501 2664W product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter product ACCTON SMCWUSBG_NF 0x4505 SMCWUSB-G (no firmware) product ACCTON SMCWUSBG 0x4506 SMCWUSB-G product ACCTON SMCWUSBTG2_NF 0x4507 SMCWUSBT-G2 (no firmware) product ACCTON SMCWUSBTG2 0x4508 SMCWUSBT-G2 product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter product ACCTON RT2870_2 0x6618 RT2870 product ACCTON RT3070 0x7511 RT3070 product ACCTON RT2770 0x7512 RT2770 product ACCTON RT2870_3 0x7522 RT2870 product ACCTON RT2870_5 0x8522 RT2870 product ACCTON RT3070_4 0xa512 RT3070 product ACCTON RT2870_4 0xa618 RT2870 product ACCTON RT3070_1 0xa701 RT3070 product ACCTON RT3070_2 0xa702 RT3070 product ACCTON RT2870_1 0xb522 RT2870 product ACCTON RT3070_3 0xc522 RT3070 product ACCTON RT3070_5 0xd522 RT3070 product ACCTON RTL8192SU 0xc512 RTL8192SU product ACCTON ZD1211B 0xe501 ZD1211B product ACCTON WN7512 0xf522 WN7512 /* Aceeca products */ product ACEECA MEZ1000 0x0001 MEZ1000 RDA /* Acer Communications & Multimedia (oemd by Surecom) */ product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter /* Acer Labs products */ product ACERLABS M5632 0x5632 USB 2.0 Data Link /* Acer Peripherals, Inc. products */ product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U product ACERP ACERSCAN_320U 0x2022 Acerscan 320U product ACERP ACERSCAN_640U 0x2040 Acerscan 640U product ACERP ACERSCAN_620U 0x2060 Acerscan 620U product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U product ACERP S81 0x4027 BenQ S81 phone product ACERP H10 0x4068 AWL400 Wireless Adapter product ACERP ATAPI 0x6003 ATA/ATAPI Adapter product ACERP AWL300 0x9000 AWL300 Wireless Adapter product ACERP AWL400 0x9001 AWL400 Wireless Adapter /* Acer Warp products */ product ACERW WARPLINK 0x0204 Warplink /* Actions products */ product ACTIONS MP4 0x1101 Actions MP4 Player /* Actiontec, Inc. products */ product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter /* ACTiSYS products */ product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR /* ActiveWire, Inc. products */ product ACTIVEWIRE IOBOARD 0x0100 I/O Board product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware /* Adaptec products */ product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN /* Addtron products */ product ADDTRON AWU120 0xff31 AWU-120 /* ADLINK Texhnology products */ product ADLINK ND6530 0x6530 ND-6530 USB-Serial /* ADMtek products */ product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet product ADMTEK PEGASUS 0x0986 AN986 Ethernet product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet /* ADDON products */ /* PNY OEMs these */ product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) /* Addonics products */ product ADDONICS2 CABLE_205 0xa001 Cable 205 /* ADS products */ product ADS UBS10BT 0x0008 UBS-10BT Ethernet product ADS UBS10BTX 0x0009 UBS-10BT Ethernet /* AEI products */ product AEI FASTETHERNET 0x1701 Fast Ethernet /* Afatech Technologies, Inc. */ product AFATECH AFATECH1336 0x1336 Flash Card Reader /* Agate Technologies products */ product AGATE QDRIVE 0x0378 Q-Drive /* AGFA products */ product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U product AGFA SNAPSCANE40 0x208d SnapScan e40 product AGFA SNAPSCANE50 0x208f SnapScan e50 product AGFA SNAPSCANE20 0x2091 SnapScan e20 product AGFA SNAPSCANE25 0x2095 SnapScan e25 product AGFA SNAPSCANE26 0x2097 SnapScan e26 product AGFA SNAPSCANE52 0x20fd SnapScan e52 /* Ain Communication Technology products */ product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter /* AIPTEK products */ product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega product AIPTEK2 PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3 product AIPTEK2 SUNPLUS_TECH 0x0c15 Sunplus Technology Inc. /* AirPlis products */ product AIRPLUS MCD650 0x3198 MCD650 modem /* AirPrime products */ product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card product AIRPRIME USB308 0x68A3 USB308 HSPA+ USB Modem product AIRPRIME AC313U 0x68aa Sierra Wireless AirCard 313U /* AirTies products */ product AIRTIES RT3070 0x2310 RT3070 /* AKS products */ product AKS USBHASP 0x0001 USB-HASP 0.06 /* Alcatel products */ product ALCATEL OT535 0x02df One Touch 535/735 /* Alcor Micro, Inc. products */ product ALCOR2 KBD_HUB 0x2802 Kbd Hub product ALCOR DUMMY 0x0000 Dummy product product ALCOR SDCR_6335 0x6335 SD/MMC Card Reader product ALCOR SDCR_6362 0x6362 SD/MMC Card Reader product ALCOR SDCR_6366 0x6366 SD/MMC Card Reader product ALCOR TRANSCEND 0x6387 Transcend JetFlash Drive product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub product ALCOR AU9814 0x9215 AU9814 Hub product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub product ALCOR AU9720 0x9720 USB2 - RS-232 product ALCOR AU6390 0x6390 AU6390 USB-IDE converter /* Alink products */ product ALINK DWM652U5 0xce16 DWM-652 product ALINK 3G 0x9000 3G modem product ALINK 3GU 0x9200 3G modem /* Altec Lansing products */ product ALTEC ADA70 0x0070 ADA70 Speakers product ALTEC ASC495 0xff05 ASC495 Speakers /* Alti-2 products */ product ALTI2 N3 0x6001 FTDI compatible adapter /* Allied Telesyn International products */ product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100 /* ALLWIN Tech products */ product ALLWIN RT2070 0x2070 RT2070 product ALLWIN RT2770 0x2770 RT2770 product ALLWIN RT2870 0x2870 RT2870 product ALLWIN RT3070 0x3070 RT3070 product ALLWIN RT3071 0x3071 RT3071 product ALLWIN RT3072 0x3072 RT3072 product ALLWIN RT3572 0x3572 RT3572 /* AlphaSmart, Inc. products */ product ALPHASMART DANA_KB 0xdbac AlphaSmart Dana Keyboard product ALPHASMART DANA_SYNC 0xdf00 AlphaSmart Dana HotSync /* Amoi products */ product AMOI H01 0x0800 H01 3G modem product AMOI H01A 0x7002 H01A 3G modem product AMOI H02 0x0802 H02 3G modem /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Ambit Microsystems products */ product AMBIT WLAN 0x0302 WLAN product AMBIT NTL_250 0x6098 NTL 250 cable modem /* Apacer products */ product APACER HT202 0xb113 USB 2.0 Flash Drive /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Amigo Technology products */ product AMIGO RT2870_1 0x9031 RT2870 product AMIGO RT2870_2 0x9041 RT2870 /* AMIT products */ product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO product AMIT CGWLUSB2GNR 0x0008 CG-WLUSB2GNR product AMIT RT2870_1 0x0012 RT2870 /* AMIT(2) products */ product AMIT2 RT2870 0x0008 RT2870 /* Analog Devices products */ product ANALOGDEVICES GNICE 0xf000 FTDI compatible adapter product ANALOGDEVICES GNICEPLUS 0xf001 FTDI compatible adapter /* Anchor products */ product ANCHOR SERIAL 0x2008 Serial product ANCHOR EZUSB 0x2131 EZUSB product ANCHOR EZLINK 0x2720 EZLINK /* AnyData products */ product ANYDATA ADU_620UW 0x6202 CDMA 2000 EV-DO USB Modem product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem /* AOX, Inc. products */ product AOX USB101 0x0008 Ethernet /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Apple Computer products */ product APPLE DUMMY 0x0000 Dummy product product APPLE IMAC_KBD 0x0201 USB iMac Keyboard product APPLE KBD 0x0202 USB Keyboard M2452 product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard /* MacbookAir, aka wellspring */ product APPLE WELLSPRING_ANSI 0x0223 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING_ISO 0x0224 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING_JIS 0x0225 Apple Internal Keyboard/Trackpad /* MacbookProPenryn, aka wellspring2 */ product APPLE WELLSPRING2_ANSI 0x0230 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING2_ISO 0x0231 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING2_JIS 0x0232 Apple Internal Keyboard/Trackpad /* Macbook5,1 (unibody), aka wellspring3 */ product APPLE WELLSPRING3_ANSI 0x0236 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING3_ISO 0x0237 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING3_JIS 0x0238 Apple Internal Keyboard/Trackpad /* MacbookAir3,2 (unibody), aka wellspring4 */ product APPLE WELLSPRING4_ANSI 0x023f Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4_ISO 0x0240 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4_JIS 0x0241 Apple Internal Keyboard/Trackpad /* MacbookAir3,1 (unibody), aka wellspring4 */ product APPLE WELLSPRING4A_ANSI 0x0242 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4A_ISO 0x0243 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4A_JIS 0x0244 Apple Internal Keyboard/Trackpad /* Macbook8 (unibody, March 2011) */ product APPLE WELLSPRING5_ANSI 0x0245 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5_ISO 0x0246 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5_JIS 0x0247 Apple Internal Keyboard/Trackpad /* MacbookAir4,1 (unibody, July 2011) */ product APPLE WELLSPRING6A_ANSI 0x0249 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6A_ISO 0x024a Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6A_JIS 0x024b Apple Internal Keyboard/Trackpad /* MacbookAir4,2 (unibody, July 2011) */ product APPLE WELLSPRING6_ANSI 0x024c Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6_ISO 0x024d Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6_JIS 0x024e Apple Internal Keyboard/Trackpad /* Macbook8,2 (unibody) */ product APPLE WELLSPRING5A_ANSI 0x0252 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5A_ISO 0x0253 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5A_JIS 0x0254 Apple Internal Keyboard/Trackpad /* MacbookPro10,1 (unibody, June 2012) */ product APPLE WELLSPRING7_ANSI 0x0262 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7_ISO 0x0263 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7_JIS 0x0264 Apple Internal Keyboard/Trackpad /* MacbookPro10,2 (unibody, October 2012) */ product APPLE WELLSPRING7A_ANSI 0x0259 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7A_ISO 0x025a Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7A_JIS 0x025b Apple Internal Keyboard/Trackpad /* MacbookAir6,2 (unibody, June 2013) */ product APPLE WELLSPRING8_ANSI 0x0290 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING8_ISO 0x0291 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING8_JIS 0x0292 Apple Internal Keyboard/Trackpad /* MacbookPro12,1 */ product APPLE WELLSPRING9_ANSI 0x0272 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING9_ISO 0x0273 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING9_JIS 0x0274 Apple Internal Keyboard/Trackpad product APPLE MOUSE 0x0301 Mouse M4848 product APPLE OPTMOUSE 0x0302 Optical mouse product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse product APPLE KBD_HUB 0x1001 Hub in Apple USB Keyboard product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard product APPLE SPEAKERS 0x1101 Speakers product APPLE IPOD 0x1201 iPod product APPLE IPOD2G 0x1202 iPod 2G product APPLE IPOD3G 0x1203 iPod 3G product APPLE IPOD_04 0x1204 iPod '04' product APPLE IPODMINI 0x1205 iPod Mini product APPLE IPOD_06 0x1206 iPod '06' product APPLE IPOD_07 0x1207 iPod '07' product APPLE IPOD_08 0x1208 iPod '08' product APPLE IPODVIDEO 0x1209 iPod Video product APPLE IPODNANO 0x120a iPod Nano product APPLE IPHONE 0x1290 iPhone product APPLE IPOD_TOUCH 0x1291 iPod Touch product APPLE IPHONE_3G 0x1292 iPhone 3G product APPLE IPHONE_3GS 0x1294 iPhone 3GS product APPLE IPHONE_4 0x1297 iPhone 4 product APPLE IPHONE_4S 0x12a0 iPhone 4S product APPLE IPHONE_5 0x12a8 iPhone 5 product APPLE IPAD 0x129a iPad product APPLE ETHERNET 0x1402 Ethernet A1277 /* Arkmicro Technologies */ product ARKMICRO ARK3116 0x0232 ARK3116 Serial /* Asahi Optical products */ product ASAHIOPTICAL OPTIO230 0x0004 Digital camera product ASAHIOPTICAL OPTIO330 0x0006 Digital camera /* Asante products */ product ASANTE EA 0x1427 Ethernet /* ASIX Electronics products */ product ASIX AX88172 0x1720 10/100 Ethernet product ASIX AX88178 0x1780 AX88178 product ASIX AX88178A 0x178a AX88178A USB 2.0 10/100/1000 Ethernet product ASIX AX88179 0x1790 AX88179 USB 3.0 10/100/1000 Ethernet product ASIX AX88772 0x7720 AX88772 product ASIX AX88772A 0x772a AX88772A USB 2.0 10/100 Ethernet product ASIX AX88772B 0x772b AX88772B USB 2.0 10/100 Ethernet product ASIX AX88772B_1 0x7e2b AX88772B USB 2.0 10/100 Ethernet /* ASUS products */ product ASUS2 USBN11 0x0b05 USB-N11 product ASUS RT2570 0x1706 RT2500USB Wireless Adapter product ASUS WL167G 0x1707 WL-167g Wireless Adapter product ASUS WL159G 0x170c WL-159g product ASUS A9T_WIFI 0x171b A9T wireless product ASUS P5B_WIFI 0x171d P5B wireless product ASUS RT2573_1 0x1723 RT2573 product ASUS RT2573_2 0x1724 RT2573 product ASUS LCM 0x1726 LCM display product ASUS RT2870_1 0x1731 RT2870 product ASUS RT2870_2 0x1732 RT2870 product ASUS RT2870_3 0x1742 RT2870 product ASUS RT2870_4 0x1760 RT2870 product ASUS RT2870_5 0x1761 RT2870 product ASUS USBN13 0x1784 USB-N13 product ASUS USBN10 0x1786 USB-N10 product ASUS RT3070_1 0x1790 RT3070 product ASUS RTL8192SU 0x1791 RTL8192SU product ASUS USB_N53 0x179d ASUS Black Diamond Dual Band USB-N53 product ASUS RTL8192CU 0x17ab RTL8192CU product ASUS USBN66 0x17ad USB-N66 product ASUS USBN10NANO 0x17ba USB-N10 Nano product ASUS USBAC51 0x17d1 USB-AC51 product ASUS A730W 0x4202 ASUS MyPal A730W product ASUS P535 0x420f ASUS P535 PDA product ASUS GMSC 0x422f ASUS Generic Mass Storage /* ATen products */ product ATEN UC1284 0x2001 Parallel printer product ATEN UC10T 0x2002 10Mbps Ethernet product ATEN UC110T 0x2007 UC-110T Ethernet product ATEN UC232A 0x2008 Serial product ATEN UC210T 0x2009 UC-210T Ethernet product ATEN DSB650C 0x4000 DSB-650C /* ATP Electronics products */ product ATP EUSB 0xaf01 ATP IG eUSB SSD /* Atheros Communications products */ product ATHEROS AR5523 0x0001 AR5523 product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_1 0x0001 AR5523 product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_2 0x0003 AR5523 product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware) product ATHEROS2 AR5523_3 0x0005 AR5523 product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware) product ATHEROS2 TG121N 0x1001 TG121N product ATHEROS2 WN821NV2 0x1002 WN821NV2 product ATHEROS2 3CRUSBN275 0x1010 3CRUSBN275 product ATHEROS2 WN612 0x1011 WN612 product ATHEROS2 AR9170 0x9170 AR9170 /* Atmel Comp. products */ product ATMEL STK541 0x2109 Zigbee Controller product ATMEL UHB124 0x3301 UHB124 hub product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter product ATMEL BW002 0x7605 BW002 Wireless Adapter product ATMEL WL1130USB 0x7613 WL-1130 USB product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter /* AuthenTec products */ product AUTHENTEC AES1610 0x1600 AES1610 Fingerprint Sensor /* Avision products */ product AVISION 1200U 0x0268 1200U scanner /* AVM products */ product AVM FRITZWLAN 0x8401 FRITZ!WLAN N /* Axesstel products */ product AXESSTEL DATAMODEM 0x1000 Data Modem /* AsureWave products */ product AZUREWAVE RT2870_1 0x3247 RT2870 product AZUREWAVE RT2870_2 0x3262 RT2870 product AZUREWAVE RT3070_1 0x3273 RT3070 product AZUREWAVE RT3070_2 0x3284 RT3070 product AZUREWAVE RT3070_3 0x3305 RT3070 product AZUREWAVE RTL8188CU 0x3357 RTL8188CU product AZUREWAVE RTL8188CE_1 0x3358 RTL8188CE product AZUREWAVE RTL8188CE_2 0x3359 RTL8188CE product AZUREWAVE RTL8192SU_1 0x3306 RTL8192SU product AZUREWAVE RTL8192SU_2 0x3309 RTL8192SU product AZUREWAVE RTL8192SU_3 0x3310 RTL8192SU product AZUREWAVE RTL8192SU_4 0x3311 RTL8192SU product AZUREWAVE RTL8192SU_5 0x3325 RTL8192SU /* Baltech products */ product BALTECH CARDREADER 0x9999 Card reader /* Bayer products */ product BAYER CONTOUR_CABLE 0x6001 FTDI compatible adapter /* B&B Electronics products */ product BBELECTRONICS USOTL4 0xAC01 RS-422/485 product BBELECTRONICS 232USB9M 0xac27 FTDI compatible adapter product BBELECTRONICS 485USB9F_2W 0xac25 FTDI compatible adapter product BBELECTRONICS 485USB9F_4W 0xac26 FTDI compatible adapter product BBELECTRONICS 485USBTB_2W 0xac33 FTDI compatible adapter product BBELECTRONICS 485USBTB_4W 0xac34 FTDI compatible adapter product BBELECTRONICS TTL3USB9M 0xac50 FTDI compatible adapter product BBELECTRONICS TTL5USB9M 0xac49 FTDI compatible adapter product BBELECTRONICS USO9ML2 0xac03 FTDI compatible adapter product BBELECTRONICS USO9ML2DR 0xac17 FTDI compatible adapter product BBELECTRONICS USO9ML2DR_2 0xac16 FTDI compatible adapter product BBELECTRONICS USOPTL4 0xac11 FTDI compatible adapter product BBELECTRONICS USOPTL4DR 0xac19 FTDI compatible adapter product BBELECTRONICS USOPTL4DR2 0xac18 FTDI compatible adapter product BBELECTRONICS USPTL4 0xac12 FTDI compatible adapter product BBELECTRONICS USTL4 0xac02 FTDI compatible adapter product BBELECTRONICS ZZ_PROG1_USB 0xba02 FTDI compatible adapter /* Belkin products */ /*product BELKIN F5U111 0x???? F5U111 Ethernet*/ product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth product BELKIN F5U103 0x0103 F5U103 Serial product BELKIN F5U109 0x0109 F5U109 Serial product BELKIN USB2SCSI 0x0115 USB to SCSI product BELKIN F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter product BELKIN USB2LAN 0x0121 USB to LAN product BELKIN F5U208 0x0208 F5U208 VideoBus II product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub product BELKIN F5U257 0x0257 F5U257 Serial product BELKIN F5U409 0x0409 F5U409 Serial product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS product BELKIN F5U120 0x1203 F5U120-PC Hub product BELKIN RTL8188CU 0x1102 RTL8188CU Wireless Adapter product BELKIN F9L1103 0x1103 F9L1103 Wireless Adapter product BELKIN RTL8192CU 0x2102 RTL8192CU Wireless Adapter product BELKIN F7D2102 0x2103 F7D2102 Wireless Adapter product BELKIN ZD1211B 0x4050 ZD1211B product BELKIN F5D5055 0x5055 F5D5055 product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter /* Also sold as 'Ativa 802.11g wireless card' */ product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter product BELKIN F5D7050E 0x705e F5D7050E Wireless Adapter product BELKIN RT2870_1 0x8053 RT2870 product BELKIN RT2870_2 0x805c RT2870 product BELKIN F5D8053V3 0x815c F5D8053 v3 product BELKIN RTL8192SU_1 0x815f RTL8192SU product BELKIN RTL8192SU_2 0x845a RTL8192SU product BELKIN RTL8192SU_3 0x945a RTL8192SU product BELKIN F5D8055 0x825a F5D8055 product BELKIN F5D8055V2 0x825b F5D8055 v2 product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter product BELKIN2 F5U002 0x0002 F5U002 Parallel printer product BELKIN F6D4050V1 0x935a F6D4050 v1 product BELKIN F6D4050V2 0x935b F6D4050 v2 /* Billionton products */ product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet product BILLIONTON USBLP100 0x0987 USB100LP product BILLIONTON USBEL100 0x0988 USB100EL product BILLIONTON USBE100 0x8511 USBE100 product BILLIONTON USB2AR 0x90ff USB2AR Ethernet /* Broadcom products */ product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle /* Brother Industries products */ product BROTHER HL1050 0x0002 HL-1050 laser printer product BROTHER MFC8600_9650 0x0100 MFC8600/9650 multifunction device /* Behavior Technology Computer products */ product BTC BTC6100 0x5550 6100C Keyboard product BTC BTC7932 0x6782 Keyboard with mouse port /* CACE Technologies products */ product CACE AIRPCAPNX 0x0300 AirPcap NX /* Canon, Inc. products */ product CANON N656U 0x2206 CanoScan N656U product CANON N1220U 0x2207 CanoScan N1220U product CANON D660U 0x2208 CanoScan D660U product CANON N676U 0x220d CanoScan N676U product CANON N1240U 0x220e CanoScan N1240U product CANON LIDE25 0x2220 CanoScan LIDE 25 product CANON S10 0x3041 PowerShot S10 product CANON S100 0x3045 PowerShot S100 product CANON S200 0x3065 PowerShot S200 product CANON REBELXT 0x30ef Digital Rebel XT /* CATC products */ product CATC NETMATE 0x000a Netmate Ethernet product CATC NETMATE2 0x000c Netmate2 Ethernet product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer product CATC ANDROMEDA 0x1237 Andromeda hub /* CASIO products */ product CASIO QV_DIGICAM 0x1001 QV DigiCam product CASIO EXS880 0x1105 Exilim EX-S880 product CASIO BE300 0x2002 BE-300 PDA product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB /* CCYU products */ product CCYU ED1064 0x2136 EasyDisk ED1064 /* Century products */ product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure product CENTURY EX35SW4_SB4 0x011f Century USB Disk Enclosure /* Cherry products */ product CHERRY MY3000KBD 0x0001 My3000 keyboard product CHERRY MY3000HUB 0x0003 My3000 hub product CHERRY CYBOARD 0x0004 CyBoard Keyboard /* Chic Technology products */ product CHIC MOUSE1 0x0001 mouse product CHIC CYPRESS 0x0003 Cypress USB Mouse /* Chicony products */ product CHICONY KB8933 0x0001 KB-8933 keyboard product CHICONY KU0325 0x0116 KU-0325 keyboard product CHICONY CNF7129 0xb071 Notebook Web Camera product CHICONY HDUVCCAM 0xb40a HD UVC WebCam product CHICONY RTL8188CUS_1 0xaff7 RTL8188CUS product CHICONY RTL8188CUS_2 0xaff8 RTL8188CUS product CHICONY RTL8188CUS_3 0xaff9 RTL8188CUS product CHICONY RTL8188CUS_4 0xaffa RTL8188CUS product CHICONY RTL8188CUS_5 0xaffa RTL8188CUS product CHICONY2 TWINKLECAM 0x600d TwinkleCam USB camera /* CH Products */ product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke /* Cisco-Linksys products */ product CISCOLINKSYS WUSB54AG 0x000c WUSB54AG Wireless Adapter product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter product CISCOLINKSYS USB200MV2 0x0018 USB200M v2 product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G product CISCOLINKSYS AE1000 0x002f AE1000 product CISCOLINKSYS USB3GIGV1 0x0041 USB3GIGV1 USB Ethernet Adapter product CISCOLINKSYS2 RT3070 0x4001 RT3070 product CISCOLINKSYS3 RT3070 0x0101 RT3070 /* Clipsal products */ product CLIPSAL 560884 0x0101 560884 C-Bus Audio Matrix Switch product CLIPSAL 5500PACA 0x0201 5500PACA C-Bus Pascal Automation Controller product CLIPSAL 5800PC 0x0301 5800PC C-Bus Wireless Interface product CLIPSAL 5500PCU 0x0303 5500PCU C-Bus Interface product CLIPSAL 5000CT2 0x0304 5000CT2 C-Bus Touch Screen product CLIPSAL C5000CT2 0x0305 C5000CT2 C-Bus Touch Screen product CLIPSAL L51xx 0x0401 L51xx C-Bus Dimmer /* CMOTECH products */ product CMOTECH CNU510 0x5141 CDMA Technologies USB modem product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem product CMOTECH CGU628 0x6006 CGU-628 product CMOTECH CDMA_MODEM1 0x6280 CDMA Technologies USB modem product CMOTECH DISK 0xf000 disk mode /* Compaq products */ product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC product COMPAQ PJB100 0x504a Personal Jukebox PJB100 product COMPAQ IPAQLINUX 0x505a iPAQ Linux /* Composite Corp products looks the same as "TANGTOP" */ product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor /* Conceptronic products */ product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN product CONCEPTRONIC C11U 0x7100 C11U product CONCEPTRONIC WL210 0x7110 WL-210 product CONCEPTRONIC AR5523_1 0x7801 AR5523 product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware) product CONCEPTRONIC AR5523_2 0x7811 AR5523 product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware) product CONCEPTRONIC2 RTL8192SU_1 0x3300 RTL8192SU product CONCEPTRONIC2 RTL8192SU_2 0x3301 RTL8192SU product CONCEPTRONIC2 RTL8192SU_3 0x3302 RTL8192SU product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN product CONCEPTRONIC2 C54RU2 0x3c22 C54RU product CONCEPTRONIC2 RT3070_1 0x3c08 RT3070 product CONCEPTRONIC2 RT3070_2 0x3c11 RT3070 product CONCEPTRONIC2 VIGORN61 0x3c25 VIGORN61 product CONCEPTRONIC2 RT2870_1 0x3c06 RT2870 product CONCEPTRONIC2 RT2870_2 0x3c07 RT2870 product CONCEPTRONIC2 RT2870_7 0x3c09 RT2870 product CONCEPTRONIC2 RT2870_8 0x3c12 RT2870 product CONCEPTRONIC2 RT2870_3 0x3c23 RT2870 product CONCEPTRONIC2 RT2870_4 0x3c25 RT2870 product CONCEPTRONIC2 RT2870_5 0x3c27 RT2870 product CONCEPTRONIC2 RT2870_6 0x3c28 RT2870 /* Connectix products */ product CONNECTIX QUICKCAM 0x0001 QuickCam /* Conect products */ product CONTEC COM1USBH 0x8311 FTDI compatible adapter /* Corega products */ product COREGA ETHER_USB_T 0x0001 Ether USB-T product COREGA FETHER_USB_TX 0x0004 FEther USB-TX product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11 product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS product COREGA WLANUSB 0x0012 Wireless LAN Stick-11 product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key product COREGA CGUSBRS232R 0x002a CG-USBRS232R product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX product COREGA RT2870_1 0x002f RT2870 product COREGA RT2870_2 0x003c RT2870 product COREGA RT2870_3 0x003f RT2870 product COREGA RT3070 0x0041 RT3070 product COREGA CGWLUSB300GNM 0x0042 CG-WLUSB300GNM product COREGA RTL8192SU 0x0047 RTL8192SU product COREGA RTL8192CU 0x0056 RTL8192CU product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11 product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC /* Corsair products */ product CORSAIR K60 0x0a60 Corsair Vengeance K60 keyboard product CORSAIR K70 0x1b09 Corsair Vengeance K70 keyboard product CORSAIR STRAFE 0x1b15 Cossair STRAFE Gaming keyboard /* Creative products */ product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG product CREATIVE NOMAD 0x4106 Nomad product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse /* Cambridge Silicon Radio Ltd. products */ product CSR BT_DONGLE 0x0001 Bluetooth USB dongle product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State /* Chipsbank Microelectronics Co., Ltd */ product CHIPSBANK USBMEMSTICK 0x6025 CBM2080 Flash drive controller product CHIPSBANK USBMEMSTICK1 0x6026 CBM1180 Flash drive controller /* CTX products */ product CTX EX1300 0x9999 Ex1300 hub /* Curitel products */ product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C) product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600) product CURITEL PC5740 0x3701 Broadband Wireless modem product CURITEL UM150 0x3711 EVDO modem product CURITEL UM175 0x3714 EVDO modem /* CyberPower products */ product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD /* CyberTAN Technology products */ product CYBERTAN TG54USB 0x1666 TG54USB product CYBERTAN RT2870 0x1828 RT2870 /* Cypress Semiconductor products */ product CYPRESS MOUSE 0x0001 mouse product CYPRESS THERMO 0x0002 thermometer product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy product CYPRESS KBDHUB 0x0101 Keyboard/Hub product CYPRESS FMRADIO 0x1002 FM Radio product CYPRESS IKARILASER 0x121f Ikari Laser SteelSeries ApS product CYPRESS USBRS232 0x5500 USB-RS232 Interface product CYPRESS SLIM_HUB 0x6560 Slim Hub product CYPRESS XX6830XX 0x6830 PATA Storage Device product CYPRESS SILVERSHIELD 0xfd13 Gembird Silver Shield PM /* Daisy Technology products */ product DAISY DMC 0x6901 USB MultiMedia Reader /* Dallas Semiconductor products */ product DALLAS J6502 0x4201 J-6502 speakers /* DataApex products */ product DATAAPEX MULTICOM 0xead6 MultiCom /* Dell products */ product DELL PORT 0x0058 Port Replicator product DELL AIO926 0x5115 Photo AIO Printer 926 product DELL BC02 0x8000 BC02 Bluetooth USB Adapter product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN product DELL U5700 0x8114 Dell 5700 3G product DELL U5500 0x8115 Dell 5500 3G product DELL U5505 0x8116 Dell 5505 3G product DELL U5700_2 0x8117 Dell 5700 3G product DELL U5510 0x8118 Dell 5510 3G product DELL U5700_3 0x8128 Dell 5700 3G product DELL U5700_4 0x8129 Dell 5700 3G product DELL U5720 0x8133 Dell 5720 3G product DELL U5720_2 0x8134 Dell 5720 3G product DELL U740 0x8135 Dell U740 CDMA product DELL U5520 0x8136 Dell 5520 3G product DELL U5520_2 0x8137 Dell 5520 3G product DELL U5520_3 0x8138 Dell 5520 3G product DELL U5730 0x8180 Dell 5730 3G product DELL U5730_2 0x8181 Dell 5730 3G product DELL U5730_3 0x8182 Dell 5730 3G product DELL DW700 0x9500 Dell DW700 GPS /* Delorme Paublishing products */ product DELORME EARTHMATE 0x0100 Earthmate GPS /* Desknote products */ product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B /* Diamond products */ product DIAMOND RIO500USB 0x0001 Rio 500 USB /* Dick Smith Electronics (really C-Net) products */ product DICKSMITH RT2573 0x9022 RT2573 product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F /* Digi International products */ product DIGI ACCELEPORT2 0x0002 AccelePort USB 2 product DIGI ACCELEPORT4 0x0004 AccelePort USB 4 product DIGI ACCELEPORT8 0x0008 AccelePort USB 8 /* Digianswer A/S products */ product DIGIANSWER ZIGBEE802154 0x000a ZigBee/802.15.4 MAC /* D-Link products */ /*product DLINK DSBS25 0x0100 DSB-S25 serial*/ product DLINK DUBE100 0x1a00 10/100 Ethernet product DLINK DUBE100C1 0x1a02 DUB-E100 rev C1 product DLINK DSB650TX4 0x200c 10/100 Ethernet product DLINK DWL120E 0x3200 DWL-120 rev E product DLINK DWA125D1 0x330f DWA-125 rev D1 product DLINK DWA123D1 0x3310 DWA-123 rev D1 product DLINK DWL122 0x3700 DWL-122 product DLINK DWLG120 0x3701 DWL-G120 product DLINK DWL120F 0x3702 DWL-120 rev F product DLINK DWLAG132 0x3a00 DWL-AG132 product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware) product DLINK DWLG132 0x3a02 DWL-G132 product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware) product DLINK DWLAG122 0x3a04 DWL-AG122 product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware) product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1 product DLINK RT2870 0x3c09 RT2870 product DLINK RT3072 0x3c0a RT3072 product DLINK DWA140B3 0x3c15 DWA-140 rev B3 product DLINK DWA160B2 0x3c1a DWA-160 rev B2 product DLINK DWA127 0x3c1b DWA-127 Wireless Adapter product DLINK DWA162 0x3c1f DWA-162 Wireless Adapter product DLINK DWA140D1 0x3c20 DWA-140 rev D1 product DLINK DSB650C 0x4000 10Mbps Ethernet product DLINK DSB650TX1 0x4001 10/100 Ethernet product DLINK DSB650TX 0x4002 10/100 Ethernet product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet product DLINK DSB650TX3 0x400b 10/100 Ethernet product DLINK DSB650TX2 0x4102 10/100 Ethernet product DLINK DUB1312 0x4a00 10/100/1000 Ethernet product DLINK DSB650 0xabc1 10/100 Ethernet product DLINK DUBH7 0xf103 DUB-H7 USB 2.0 7-Port Hub product DLINK DWR510_CD 0xa805 DWR-510 CD-ROM Mode product DLINK DWR510 0x7e12 DWR-510 product DLINK DWM157 0x7d02 DWM-157 product DLINK DWM157_CD 0xa707 DWM-157 CD-ROM Mode product DLINK RTL8188CU 0x3308 RTL8188CU product DLINK RTL8192CU_1 0x3307 RTL8192CU product DLINK RTL8192CU_2 0x3309 RTL8192CU product DLINK RTL8192CU_3 0x330a RTL8192CU product DLINK DWA131B 0x330d DWA-131 rev B product DLINK2 RTL8192SU_1 0x3300 RTL8192SU product DLINK2 RTL8192SU_2 0x3302 RTL8192SU product DLINK2 DWA131A1 0x3303 DWA-131 A1 product DLINK2 DWA160A2 0x3a09 DWA-160 A2 product DLINK2 DWA120 0x3a0c DWA-120 product DLINK2 DWA120_NF 0x3a0d DWA-120 (no firmware) product DLINK2 DWA130D1 0x3a0f DWA-130 D1 product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1 product DLINK2 WUA1340 0x3c04 WUA-1340 product DLINK2 DWA111 0x3c06 DWA-111 product DLINK2 RT2870_1 0x3c09 RT2870 product DLINK2 DWA110 0x3c07 DWA-110 product DLINK2 RT3072 0x3c0a RT3072 product DLINK2 RT3072_1 0x3c0b RT3072 product DLINK2 RT3070_1 0x3c0d RT3070 product DLINK2 RT3070_2 0x3c0e RT3070 product DLINK2 RT3070_3 0x3c0f RT3070 product DLINK2 DWA160A1 0x3c10 DWA-160 A1 product DLINK2 RT2870_2 0x3c11 RT2870 product DLINK2 DWA130 0x3c13 DWA-130 product DLINK2 RT3070_4 0x3c15 RT3070 product DLINK2 RT3070_5 0x3c16 RT3070 product DLINK3 DWM652 0x3e04 DWM-652 /* DisplayLink products */ product DISPLAYLINK LCD4300U 0x01ba LCD-4300U product DISPLAYLINK LCD8000U 0x01bb LCD-8000U product DISPLAYLINK LD220 0x0100 Samsung LD220 product DISPLAYLINK GUC2020 0x0059 IOGEAR DVI GUC2020 product DISPLAYLINK VCUD60 0x0136 Rextron DVI product DISPLAYLINK CONV 0x0138 StarTech CONV-USB2DVI product DISPLAYLINK DLDVI 0x0141 DisplayLink DVI product DISPLAYLINK VGA10 0x015a CMP-USBVGA10 product DISPLAYLINK WSDVI 0x0198 WS Tech DVI product DISPLAYLINK EC008 0x019b EasyCAP008 DVI product DISPLAYLINK HPDOCK 0x01d4 HP USB Docking product DISPLAYLINK NL571 0x01d7 HP USB DVI product DISPLAYLINK M01061 0x01e2 Lenovo DVI product DISPLAYLINK SWDVI 0x024c SUNWEIT DVI product DISPLAYLINK NBDOCK 0x0215 VideoHome NBdock1920 product DISPLAYLINK LUM70 0x02a9 Lilliput UM-70 product DISPLAYLINK UM7X0 0x401a nanovision MiMo product DISPLAYLINK LT1421 0x03e0 Lenovo ThinkVision LT1421 product DISPLAYLINK POLARIS2 0x0117 Polaris2 USB dock product DISPLAYLINK PLUGABLE 0x0377 Plugable docking station product DISPLAYLINK ITEC 0x02e9 i-tec USB 2.0 Docking Station /* DMI products */ product DMI CFSM_RW 0xa109 CF/SM Reader/Writer product DMI DISK 0x2bcf Generic Disk /* DrayTek products */ product DRAYTEK VIGOR550 0x0550 Vigor550 /* Dream Link products */ product DREAMLINK DL100B 0x0004 USB Webmail Notifier /* dresden elektronik products */ product DRESDENELEKTRONIK SENSORTERMINALBOARD 0x0001 SensorTerminalBoard product DRESDENELEKTRONIK WIRELESSHANDHELDTERMINAL 0x0004 Wireless Handheld Terminal product DRESDENELEKTRONIK DE_RFNODE 0x001c deRFnode product DRESDENELEKTRONIK LEVELSHIFTERSTICKLOWCOST 0x0022 Levelshifter Stick Low Cost /* DYMO */ product DYMO LABELMANAGERPNP 0x1001 DYMO LabelManager PnP /* Dynastream Innovations */ product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board product DYNASTREAM ANT2USB 0x1004 ANT2USB product DYNASTREAM ANTDEVBOARD2 0x1006 ANT dev board /* Edimax products */ product EDIMAX EW7318USG 0x7318 USB Wireless dongle product EDIMAX RTL8192SU_1 0x7611 RTL8192SU product EDIMAX RTL8192SU_2 0x7612 RTL8192SU product EDIMAX EW7622UMN 0x7622 EW-7622UMn product EDIMAX RT2870_1 0x7711 RT2870 product EDIMAX EW7717 0x7717 EW-7717 product EDIMAX EW7718 0x7718 EW-7718 product EDIMAX EW7733UND 0x7733 EW-7733UnD product EDIMAX EW7811UN 0x7811 EW-7811Un product EDIMAX RTL8192CU 0x7822 RTL8192CU /* eGalax Products */ product EGALAX TPANEL 0x0001 Touch Panel product EGALAX TPANEL2 0x0002 Touch Panel product EGALAX2 TPANEL 0x0001 Touch Panel /* EGO Products */ product EGO DUMMY 0x0000 Dummy Product product EGO M4U 0x1020 ESI M4U /* Eicon Networks */ product EICON DIVA852 0x4905 Diva 852 ISDN TA /* EIZO products */ product EIZO HUB 0x0000 hub product EIZO MONITOR 0x0001 monitor /* ELCON Systemtechnik products */ product ELCON PLAN 0x0002 Goldpfeil P-LAN /* Elecom products */ product ELECOM MOUSE29UO 0x0002 mouse 29UO product ELECOM LDUSBTX0 0x200c LD-USB/TX product ELECOM LDUSBTX1 0x4002 LD-USB/TX product ELECOM LDUSBLTX 0x4005 LD-USBL/TX product ELECOM WDC150SU2M 0x4008 WDC-150SU2M product ELECOM LDUSBTX2 0x400b LD-USB/TX product ELECOM LDUSB20 0x4010 LD-USB20 product ELECOM UCSGT 0x5003 UC-SGT product ELECOM UCSGT0 0x5004 UC-SGT product ELECOM LDUSBTX3 0xabc1 LD-USB/TX /* Elektor products */ product ELEKTOR FT323R 0x0005 FTDI compatible adapter /* Elsa products */ product ELSA MODEM1 0x2265 ELSA Modem Board product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet /* ELV products */ product ELV USBI2C 0xe00f USB-I2C interface /* EMS products */ product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter /* Emtec products */ product EMTEC RUF2PS 0x2240 Flash Drive /* Encore products */ product ENCORE RT3070_1 0x1480 RT3070 product ENCORE RT3070_2 0x14a1 RT3070 product ENCORE RT3070_3 0x14a9 RT3070 /* Entrega products */ product ENTREGA 1S 0x0001 1S serial product ENTREGA 2S 0x0002 2S serial product ENTREGA 1S25 0x0003 1S25 serial product ENTREGA 4S 0x0004 4S serial product ENTREGA E45 0x0005 E45 Ethernet product ENTREGA CENTRONICS 0x0006 Parallel Port product ENTREGA XX1 0x0008 Ethernet product ENTREGA 1S9 0x0093 1S9 serial product ENTREGA EZUSB 0x8000 EZ-USB /*product ENTREGA SERIAL 0x8001 DB25 Serial*/ product ENTREGA 2U4S 0x8004 2U4S serial/usb hub product ENTREGA XX2 0x8005 Ethernet /*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ /* Epson products */ product EPSON PRINTER1 0x0001 USB Printer product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac product EPSON PRINTER3 0x0003 ISD USB Smart Cable product EPSON PRINTER5 0x0005 USB Printer product EPSON 636 0x0101 Perfection 636U / 636Photo scanner product EPSON 610 0x0103 Perfection 610 scanner product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner product EPSON 1600 0x0107 Expression 1600 scanner product EPSON 1640 0x010a Perfection 1640SU scanner product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner product EPSON 640U 0x010c Perfection 640U scanner product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner product EPSON 1650 0x0110 Perfection 1650 scanner product EPSON GT9700F 0x0112 GT-9700F scanner product EPSON GT9300UF 0x011b GT-9300UF scanner product EPSON 3200 0x011c Perfection 3200 scanner product EPSON 1260 0x011d Perfection 1260 scanner product EPSON 1660 0x011e Perfection 1660 scanner product EPSON 1670 0x011f Perfection 1670 scanner product EPSON 1270 0x0120 Perfection 1270 scanner product EPSON 2480 0x0121 Perfection 2480 scanner product EPSON 3590 0x0122 Perfection 3590 scanner product EPSON 4990 0x012a Perfection 4990 Photo scanner product EPSON CRESSI_EDY 0x0521 Cressi Edy diving computer product EPSON N2ITION3 0x0522 Zeagle N2iTion3 diving computer product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader product EPSON CX5400 0x0808 CX5400 scanner product EPSON 3500 0x080e CX-3500/3600/3650 MFP product EPSON RX425 0x080f Stylus Photo RX425 scanner product EPSON DX3800 0x0818 CX3700/CX3800/DX38x0 MFP scanner product EPSON 4800 0x0819 CX4700/CX4800/DX48x0 MFP scanner product EPSON 4200 0x0820 CX4100/CX4200/DX4200 MFP scanner product EPSON 5000 0x082b CX4900/CX5000/DX50x0 MFP scanner product EPSON 6000 0x082e CX5900/CX6000/DX60x0 MFP scanner product EPSON DX4000 0x082f DX4000 MFP scanner product EPSON DX7400 0x0838 CX7300/CX7400/DX7400 MFP scanner product EPSON DX8400 0x0839 CX8300/CX8400/DX8400 MFP scanner product EPSON SX100 0x0841 SX100/NX100 MFP scanner product EPSON NX300 0x0848 NX300 MFP scanner product EPSON SX200 0x0849 SX200/SX205 MFP scanner product EPSON SX400 0x084a SX400/NX400/TX400 MFP scanner /* e-TEK Labs products */ product ETEK 1COM 0x8007 Serial /* Evolution products */ product EVOLUTION ER1 0x0300 FTDI compatible adapter product EVOLUTION HYBRID 0x0302 FTDI compatible adapter product EVOLUTION RCM4 0x0303 FTDI compatible adapter /* Extended Systems products */ product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA /* Falcom products */ product FALCOM TWIST 0x0001 USB GSM/GPRS Modem product FALCOM SAMBA 0x0005 FTDI compatible adapter /* FEIYA products */ product FEIYA DUMMY 0x0000 Dummy product product FEIYA 5IN1 0x1132 5-in-1 Card Reader product FEIYA ELANGO 0x6200 MicroSDHC Card Reader product FEIYA AC110 0x6300 AC-110 Card Reader /* FeiXun Communication products */ product FEIXUN RTL8188CU 0x0090 RTL8188CU product FEIXUN RTL8192CU 0x0091 RTL8192CU /* Festo */ product FESTO CPX_USB 0x0102 CPX-USB product FESTO CMSP 0x0501 CMSP /* Fiberline */ product FIBERLINE WL430U 0x6003 WL-430U /* FIC / OpenMoko */ product FIC NEO1973_DEBUG 0x5118 FTDI compatible adapter /* Fossil, Inc products */ product FOSSIL WRISTPDA 0x0002 Wrist PDA /* Foxconn products */ product FOXCONN TCOM_TC_300 0xe000 T-Com TC 300 product FOXCONN PIRELLI_DP_L10 0xe003 Pirelli DP-L10 /* Freecom products */ product FREECOM DVD 0xfc01 DVD drive product FREECOM HDD 0xfc05 Classic SL Hard Drive /* Fujitsu Siemens Computers products */ product FSC E5400 0x1009 PrismGT USB 2.0 WLAN /* Future Technology Devices products */ product FTDI SCX8_USB_PHOENIX 0x5259 SCx8 USB Phoenix interface product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial product FTDI SERIAL_8U232AM4 0x6004 8U232AM Serial product FTDI SERIAL_232RL 0x6006 FT232RL Serial product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial product FTDI 232H 0x6014 FTDI compatible adapter product FTDI 232EX 0x6015 FTDI compatible adapter product FTDI SERIAL_2232D 0x9e90 FT2232D Dual port Serial product FTDI SERIAL_4232H 0x6011 FT4232H Quad port Serial product FTDI XDS100V2 0xa6d0 TI XDS100V1/V2 and early Beaglebones product FTDI XDS100V3 0xa6d1 TI XDS100V3 product FTDI KTLINK 0xbbe2 KT-LINK Embedded Hackers Multitool product FTDI TURTELIZER2 0xbdc8 egnite Turtelizer 2 JTAG/RS232 Adapter /* Gude Analog- und Digitalsysteme products also uses FTDI's id: */ product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal product FTDI GAMMASCOUT 0xd678 Gamma-Scout product FTDI KBS 0xe6c8 Pyramid KBS USB LCD product FTDI EISCOU 0xe888 Expert ISDN Control USB product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II product FTDI PCMSFU 0xe88b Precision Clock MSF USB product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG product FTDI MAXSTREAM 0xee18 Maxstream PKG-U product FTDI USB_UIRT 0xf850 USB-UIRT product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3 product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5 product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation /* Commerzielle und Technische Informationssysteme GmbH products */ product FTDI CTI_USB_NANO_485 0xf60b CTI USB-Nano 485 product FTDI CTI_USB_MINI_485 0xf608 CTI USB-Mini 485 /* Other products */ product FTDI 232RL 0xfbfa FTDI compatible adapter product FTDI 4N_GALAXY_DE_1 0xf3c0 FTDI compatible adapter product FTDI 4N_GALAXY_DE_2 0xf3c1 FTDI compatible adapter product FTDI 4N_GALAXY_DE_3 0xf3c2 FTDI compatible adapter product FTDI 8U232AM_ALT 0x6006 FTDI compatible adapter product FTDI ACCESSO 0xfad0 FTDI compatible adapter product FTDI ACG_HFDUAL 0xdd20 FTDI compatible adapter product FTDI ACTIVE_ROBOTS 0xe548 FTDI compatible adapter product FTDI ACTZWAVE 0xf2d0 FTDI compatible adapter product FTDI AMC232 0xff00 FTDI compatible adapter product FTDI ARTEMIS 0xdf28 FTDI compatible adapter product FTDI ASK_RDR400 0xc991 FTDI compatible adapter product FTDI ATIK_ATK16 0xdf30 FTDI compatible adapter product FTDI ATIK_ATK16C 0xdf32 FTDI compatible adapter product FTDI ATIK_ATK16HR 0xdf31 FTDI compatible adapter product FTDI ATIK_ATK16HRC 0xdf33 FTDI compatible adapter product FTDI ATIK_ATK16IC 0xdf35 FTDI compatible adapter product FTDI BCS_SE923 0xfb99 FTDI compatible adapter product FTDI CANDAPTER 0x9f80 FTDI compatible adapter product FTDI CANUSB 0xffa8 FTDI compatible adapter product FTDI CCSICDU20_0 0xf9d0 FTDI compatible adapter product FTDI CCSICDU40_1 0xf9d1 FTDI compatible adapter product FTDI CCSICDU64_4 0xf9d4 FTDI compatible adapter product FTDI CCSLOAD_N_GO_3 0xf9d3 FTDI compatible adapter product FTDI CCSMACHX_2 0xf9d2 FTDI compatible adapter product FTDI CCSPRIME8_5 0xf9d5 FTDI compatible adapter product FTDI CHAMSYS_24_MASTER_WING 0xdaf8 FTDI compatible adapter product FTDI CHAMSYS_MAXI_WING 0xdafd FTDI compatible adapter product FTDI CHAMSYS_MEDIA_WING 0xdafe FTDI compatible adapter product FTDI CHAMSYS_MIDI_TIMECODE 0xdafb FTDI compatible adapter product FTDI CHAMSYS_MINI_WING 0xdafc FTDI compatible adapter product FTDI CHAMSYS_PC_WING 0xdaf9 FTDI compatible adapter product FTDI CHAMSYS_USB_DMX 0xdafa FTDI compatible adapter product FTDI CHAMSYS_WING 0xdaff FTDI compatible adapter product FTDI COM4SM 0xd578 FTDI compatible adapter product FTDI CONVERTER_0 0xd388 FTDI compatible adapter product FTDI CONVERTER_1 0xd389 FTDI compatible adapter product FTDI CONVERTER_2 0xd38a FTDI compatible adapter product FTDI CONVERTER_3 0xd38b FTDI compatible adapter product FTDI CONVERTER_4 0xd38c FTDI compatible adapter product FTDI CONVERTER_5 0xd38d FTDI compatible adapter product FTDI CONVERTER_6 0xd38e FTDI compatible adapter product FTDI CONVERTER_7 0xd38f FTDI compatible adapter product FTDI DMX4ALL 0xc850 FTDI compatible adapter product FTDI DOMINTELL_DGQG 0xef50 FTDI compatible adapter product FTDI DOMINTELL_DUSB 0xef51 FTDI compatible adapter product FTDI DOTEC 0x9868 FTDI compatible adapter product FTDI ECLO_COM_1WIRE 0xea90 FTDI compatible adapter product FTDI ECO_PRO_CDS 0xe520 FTDI compatible adapter product FTDI ELSTER_UNICOM 0xe700 FTDI compatible adapter product FTDI ELV_ALC8500 0xf06e FTDI compatible adapter product FTDI ELV_CLI7000 0xfb59 FTDI compatible adapter product FTDI ELV_CSI8 0xe0f0 FTDI compatible adapter product FTDI ELV_EC3000 0xe006 FTDI compatible adapter product FTDI ELV_EM1000DL 0xe0f1 FTDI compatible adapter product FTDI ELV_EM1010PC 0xe0ef FTDI compatible adapter product FTDI ELV_FEM 0xe00a FTDI compatible adapter product FTDI ELV_FHZ1000PC 0xf06f FTDI compatible adapter product FTDI ELV_FHZ1300PC 0xe0e8 FTDI compatible adapter product FTDI ELV_FM3RX 0xe0ed FTDI compatible adapter product FTDI ELV_FS20SIG 0xe0f4 FTDI compatible adapter product FTDI ELV_HS485 0xe0ea FTDI compatible adapter product FTDI ELV_KL100 0xe002 FTDI compatible adapter product FTDI ELV_MSM1 0xe001 FTDI compatible adapter product FTDI ELV_PCD200 0xf06c FTDI compatible adapter product FTDI ELV_PCK100 0xe0f2 FTDI compatible adapter product FTDI ELV_PPS7330 0xfb5c FTDI compatible adapter product FTDI ELV_RFP500 0xe0f3 FTDI compatible adapter product FTDI ELV_T1100 0xf06b FTDI compatible adapter product FTDI ELV_TFD128 0xe0ec FTDI compatible adapter product FTDI ELV_TFM100 0xfb5d FTDI compatible adapter product FTDI ELV_TWS550 0xe009 FTDI compatible adapter product FTDI ELV_UAD8 0xf068 FTDI compatible adapter product FTDI ELV_UDA7 0xf069 FTDI compatible adapter product FTDI ELV_UDF77 0xfb5e FTDI compatible adapter product FTDI ELV_UIO88 0xfb5f FTDI compatible adapter product FTDI ELV_ULA200 0xf06d FTDI compatible adapter product FTDI ELV_UM100 0xfb5a FTDI compatible adapter product FTDI ELV_UMS100 0xe0eb FTDI compatible adapter product FTDI ELV_UO100 0xfb5b FTDI compatible adapter product FTDI ELV_UR100 0xfb58 FTDI compatible adapter product FTDI ELV_USI2 0xf06a FTDI compatible adapter product FTDI ELV_USR 0xe000 FTDI compatible adapter product FTDI ELV_UTP8 0xe0f5 FTDI compatible adapter product FTDI ELV_WS300PC 0xe0f6 FTDI compatible adapter product FTDI ELV_WS444PC 0xe0f7 FTDI compatible adapter product FTDI ELV_WS500 0xe0e9 FTDI compatible adapter product FTDI ELV_WS550 0xe004 FTDI compatible adapter product FTDI ELV_WS777 0xe0ee FTDI compatible adapter product FTDI ELV_WS888 0xe008 FTDI compatible adapter product FTDI FUTURE_0 0xf44a FTDI compatible adapter product FTDI FUTURE_1 0xf44b FTDI compatible adapter product FTDI FUTURE_2 0xf44c FTDI compatible adapter product FTDI GENERIC 0x9378 FTDI compatible adapter product FTDI GUDEADS_E808 0xe808 FTDI compatible adapter product FTDI GUDEADS_E809 0xe809 FTDI compatible adapter product FTDI GUDEADS_E80A 0xe80a FTDI compatible adapter product FTDI GUDEADS_E80B 0xe80b FTDI compatible adapter product FTDI GUDEADS_E80C 0xe80c FTDI compatible adapter product FTDI GUDEADS_E80D 0xe80d FTDI compatible adapter product FTDI GUDEADS_E80E 0xe80e FTDI compatible adapter product FTDI GUDEADS_E80F 0xe80f FTDI compatible adapter product FTDI GUDEADS_E88D 0xe88d FTDI compatible adapter product FTDI GUDEADS_E88E 0xe88e FTDI compatible adapter product FTDI GUDEADS_E88F 0xe88f FTDI compatible adapter product FTDI HD_RADIO 0x937c FTDI compatible adapter product FTDI HO720 0xed72 FTDI compatible adapter product FTDI HO730 0xed73 FTDI compatible adapter product FTDI HO820 0xed74 FTDI compatible adapter product FTDI HO870 0xed71 FTDI compatible adapter product FTDI IBS_APP70 0xff3d FTDI compatible adapter product FTDI IBS_PCMCIA 0xff3a FTDI compatible adapter product FTDI IBS_PEDO 0xff3e FTDI compatible adapter product FTDI IBS_PICPRO 0xff39 FTDI compatible adapter product FTDI IBS_PK1 0xff3b FTDI compatible adapter product FTDI IBS_PROD 0xff3f FTDI compatible adapter product FTDI IBS_RS232MON 0xff3c FTDI compatible adapter product FTDI IBS_US485 0xff38 FTDI compatible adapter product FTDI IPLUS 0xd070 FTDI compatible adapter product FTDI IPLUS2 0xd071 FTDI compatible adapter product FTDI IRTRANS 0xfc60 FTDI compatible adapter product FTDI LENZ_LIUSB 0xd780 FTDI compatible adapter product FTDI LM3S_DEVEL_BOARD 0xbcd8 FTDI compatible adapter product FTDI LM3S_EVAL_BOARD 0xbcd9 FTDI compatible adapter product FTDI LM3S_ICDI_B_BOARD 0xbcda FTDI compatible adapter product FTDI MASTERDEVEL2 0xf449 FTDI compatible adapter product FTDI MHAM_DB9 0xeeed FTDI compatible adapter product FTDI MHAM_IC 0xeeec FTDI compatible adapter product FTDI MHAM_KW 0xeee8 FTDI compatible adapter product FTDI MHAM_RS232 0xeeee FTDI compatible adapter product FTDI MHAM_Y6 0xeeea FTDI compatible adapter product FTDI MHAM_Y8 0xeeeb FTDI compatible adapter product FTDI MHAM_Y9 0xeeef FTDI compatible adapter product FTDI MHAM_YS 0xeee9 FTDI compatible adapter product FTDI MICRO_CHAMELEON 0xcaa0 FTDI compatible adapter product FTDI MTXORB_5 0xfa05 FTDI compatible adapter product FTDI MTXORB_6 0xfa06 FTDI compatible adapter product FTDI NXTCAM 0xabb8 FTDI compatible adapter product FTDI OCEANIC 0xf460 FTDI compatible adapter product FTDI OOCDLINK 0xbaf8 FTDI compatible adapter product FTDI OPENDCC 0xbfd8 FTDI compatible adapter product FTDI OPENDCC_GATEWAY 0xbfdb FTDI compatible adapter product FTDI OPENDCC_GBM 0xbfdc FTDI compatible adapter product FTDI OPENDCC_SNIFFER 0xbfd9 FTDI compatible adapter product FTDI OPENDCC_THROTTLE 0xbfda FTDI compatible adapter product FTDI PCDJ_DAC2 0xfa88 FTDI compatible adapter product FTDI PERLE_ULTRAPORT 0xf0c0 FTDI compatible adapter product FTDI PHI_FISCO 0xe40b FTDI compatible adapter product FTDI PIEGROUP 0xf208 FTDI compatible adapter product FTDI PROPOX_JTAGCABLEII 0xd738 FTDI compatible adapter product FTDI R2000KU_TRUE_RNG 0xfb80 FTDI compatible adapter product FTDI R2X0 0xfc71 FTDI compatible adapter product FTDI RELAIS 0xfa10 FTDI compatible adapter product FTDI REU_TINY 0xed22 FTDI compatible adapter product FTDI RMP200 0xe729 FTDI compatible adapter product FTDI RM_CANVIEW 0xfd60 FTDI compatible adapter product FTDI RRCIRKITS_LOCOBUFFER 0xc7d0 FTDI compatible adapter product FTDI SCIENCESCOPE_HS_LOGBOOK 0xff1d FTDI compatible adapter product FTDI SCIENCESCOPE_LOGBOOKML 0xff18 FTDI compatible adapter product FTDI SCIENCESCOPE_LS_LOGBOOK 0xff1c FTDI compatible adapter product FTDI SCS_DEVICE_0 0xd010 FTDI compatible adapter product FTDI SCS_DEVICE_1 0xd011 FTDI compatible adapter product FTDI SCS_DEVICE_2 0xd012 FTDI compatible adapter product FTDI SCS_DEVICE_3 0xd013 FTDI compatible adapter product FTDI SCS_DEVICE_4 0xd014 FTDI compatible adapter product FTDI SCS_DEVICE_5 0xd015 FTDI compatible adapter product FTDI SCS_DEVICE_6 0xd016 FTDI compatible adapter product FTDI SCS_DEVICE_7 0xd017 FTDI compatible adapter product FTDI SDMUSBQSS 0xf448 FTDI compatible adapter product FTDI SIGNALYZER_SH2 0xbca2 FTDI compatible adapter product FTDI SIGNALYZER_SH4 0xbca4 FTDI compatible adapter product FTDI SIGNALYZER_SLITE 0xbca1 FTDI compatible adapter product FTDI SIGNALYZER_ST 0xbca0 FTDI compatible adapter product FTDI SPECIAL_1 0xfc70 FTDI compatible adapter product FTDI SPECIAL_3 0xfc72 FTDI compatible adapter product FTDI SPECIAL_4 0xfc73 FTDI compatible adapter product FTDI SPROG_II 0xf0c8 FTDI compatible adapter product FTDI SR_RADIO 0x9379 FTDI compatible adapter product FTDI SUUNTO_SPORTS 0xf680 FTDI compatible adapter product FTDI TAVIR_STK500 0xfa33 FTDI compatible adapter product FTDI TERATRONIK_D2XX 0xec89 FTDI compatible adapter product FTDI TERATRONIK_VCP 0xec88 FTDI compatible adapter product FTDI THORLABS 0xfaf0 FTDI compatible adapter product FTDI TNC_X 0xebe0 FTDI compatible adapter product FTDI TTUSB 0xff20 FTDI compatible adapter product FTDI USBX_707 0xf857 FTDI compatible adapter product FTDI USINT_CAT 0xb810 FTDI compatible adapter product FTDI USINT_RS232 0xb812 FTDI compatible adapter product FTDI USINT_WKEY 0xb811 FTDI compatible adapter product FTDI VARDAAN 0xf070 FTDI compatible adapter product FTDI VNHCPCUSB_D 0xfe38 FTDI compatible adapter product FTDI WESTREX_MODEL_777 0xdc00 FTDI compatible adapter product FTDI WESTREX_MODEL_8900F 0xdc01 FTDI compatible adapter product FTDI XF_547 0xfc0a FTDI compatible adapter product FTDI XF_640 0xfc0e FTDI compatible adapter product FTDI XF_642 0xfc0f FTDI compatible adapter product FTDI XM_RADIO 0x937a FTDI compatible adapter product FTDI YEI_SERVOCENTER31 0xe050 FTDI compatible adapter /* Fuji photo products */ product FUJIPHOTO MASS0100 0x0100 Mass Storage /* Fujitsu protducts */ product FUJITSU AH_F401U 0x105b AH-F401U Air H device /* Fujitsu-Siemens protducts */ product FUJITSUSIEMENS SCR 0x0009 Fujitsu-Siemens SCR USB Reader /* Garmin products */ product GARMIN IQUE_3600 0x0004 iQue 3600 /* Gemalto products */ product GEMALTO PROXPU 0x5501 Prox-PU/CU RFID Card Reader /* General Instruments (Motorola) products */ product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem /* Genesys Logic products */ product GENESYS GL620USB 0x0501 GL620USB Host-Host interface product GENESYS GL650 0x0604 GL650 HUB product GENESYS GL606 0x0606 USB 2.0 HUB product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2 product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader /* GIGABYTE products */ product GIGABYTE GN54G 0x8001 GN-54G product GIGABYTE GNBR402W 0x8002 GN-BR402W product GIGABYTE GNWLBM101 0x8003 GN-WLBM101 product GIGABYTE GNWBKG 0x8007 GN-WBKG product GIGABYTE GNWB01GS 0x8008 GN-WB01GS product GIGABYTE GNWI05GS 0x800a GN-WI05GS /* Gigaset products */ product GIGASET WLAN 0x0701 WLAN product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware) product GIGASET AR5523 0x0712 AR5523 product GIGASET AR5523_NF 0x0713 AR5523 (no firmware) product GIGASET RT2573 0x0722 RT2573 product GIGASET RT3070_1 0x0740 RT3070 product GIGASET RT3070_2 0x0744 RT3070 product GIGABYTE RT2870_1 0x800b RT2870 product GIGABYTE GNWB31N 0x800c GN-WB31N product GIGABYTE GNWB32L 0x800d GN-WB32L /* Global Sun Technology product */ product GLOBALSUN AR5523_1 0x7801 AR5523 product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware) product GLOBALSUN AR5523_2 0x7811 AR5523 product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware) /* Globespan products */ product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* G.Mate, Inc products */ product GMATE YP3X00 0x1001 YP3X00 PDA /* GN Otometrics */ product GNOTOMETRICS USB 0x0010 FTDI compatible adapter /* GoHubs products */ product GOHUBS GOCOM232 0x1001 GoCOM232 Serial /* Good Way Technology products */ product GOODWAY GWUSB2E 0x6200 GWUSB2E product GOODWAY RT2573 0xc019 RT2573 /* Google products */ product GOOGLE NEXUSONE 0x4e11 Nexus One /* Gravis products */ product GRAVIS GAMEPADPRO 0x4001 GamePad Pro /* GREENHOUSE products */ product GREENHOUSE KANA21 0x0001 CF-writer with MP3 /* Griffin Technology */ product GRIFFIN IMATE 0x0405 iMate, ADB Adapter /* Guillemot Corporation */ product GUILLEMOT DALEADER 0xa300 DA Leader product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP product GUILLEMOT HWNU300 0xe030 HWNU-300 product GUILLEMOT HWNUM300 0xe031 HWNUm-300 product GUILLEMOT HWGUN54 0xe032 HWGUn-54 product GUILLEMOT HWNUP150 0xe033 HWNUP-150 /* Hagiwara products */ product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader product HAGIWARA FG 0x0005 FlashGate /* HAL Corporation products */ product HAL IMR001 0x0011 Crossam2+USB IR commander /* Handspring, Inc. */ product HANDSPRING VISOR 0x0100 Handspring Visor product HANDSPRING TREO 0x0200 Handspring Treo product HANDSPRING TREO600 0x0300 Handspring Treo 600 /* Hauppauge Computer Works */ product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM product HAUPPAUGE2 NOVAT500 0x9580 NovaT 500Stick /* Hawking Technologies products */ product HAWKING RT2870_1 0x0001 RT2870 product HAWKING RT2870_2 0x0003 RT2870 product HAWKING HWUN2 0x0009 HWUN2 product HAWKING RT3070 0x000b RT3070 product HAWKING RTL8192CU 0x0019 RTL8192CU product HAWKING UF100 0x400c 10/100 USB Ethernet product HAWKING RTL8192SU_1 0x0015 RTL8192SU product HAWKING RTL8192SU_2 0x0016 RTL8192SU /* HID Global GmbH products */ product HIDGLOBAL CM2020 0x0596 Omnikey Cardman 2020 product HIDGLOBAL CM6020 0x1784 Omnikey Cardman 6020 /* Hitachi, Ltd. products */ product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface /* Holtek products */ product HOLTEK F85 0xa030 Holtek USB gaming keyboard /* HP products */ product HP 895C 0x0004 DeskJet 895C product HP 4100C 0x0101 Scanjet 4100C product HP S20 0x0102 Photosmart S20 product HP 880C 0x0104 DeskJet 880C product HP 4200C 0x0105 ScanJet 4200C product HP CDWRITERPLUS 0x0107 CD-Writer Plus product HP KBDHUB 0x010c Multimedia Keyboard Hub product HP G55XI 0x0111 OfficeJet G55xi product HP HN210W 0x011c HN210W 802.11b WLAN product HP 49GPLUS 0x0121 49g+ graphing calculator product HP 6200C 0x0201 ScanJet 6200C product HP S20b 0x0202 PhotoSmart S20 product HP 815C 0x0204 DeskJet 815C product HP 3300C 0x0205 ScanJet 3300C product HP CDW8200 0x0207 CD-Writer Plus 8200e product HP MMKEYB 0x020c Multimedia keyboard product HP 1220C 0x0212 DeskJet 1220C product HP UN2420_QDL 0x241d UN2420 QDL Firmware Loader product HP UN2420 0x251d UN2420 WWAN/GPS Module product HP 810C 0x0304 DeskJet 810C/812C product HP 4300C 0x0305 Scanjet 4300C product HP CDW4E 0x0307 CD-Writer+ CD-4e product HP G85XI 0x0311 OfficeJet G85xi product HP 1200 0x0317 LaserJet 1200 product HP 5200C 0x0401 Scanjet 5200C product HP 830C 0x0404 DeskJet 830C product HP 3400CSE 0x0405 ScanJet 3400cse product HP 6300C 0x0601 Scanjet 6300C product HP 840C 0x0604 DeskJet 840c product HP 2200C 0x0605 ScanJet 2200C product HP 5300C 0x0701 Scanjet 5300C product HP 4400C 0x0705 Scanjet 4400C product HP 4470C 0x0805 Scanjet 4470C product HP 82x0C 0x0b01 Scanjet 82x0C product HP 2300D 0x0b17 Laserjet 2300d product HP 970CSE 0x1004 Deskjet 970Cse product HP 5400C 0x1005 Scanjet 5400C product HP 2215 0x1016 iPAQ 22xx/Jornada 548 product HP 568J 0x1116 Jornada 568 product HP 930C 0x1204 DeskJet 930c product HP3 RTL8188CU 0x1629 RTL8188CU product HP P2000U 0x1801 Inkjet P-2000U product HP HS2300 0x1e1d HS2300 HSDPA (aka MC8775) product HP 640C 0x2004 DeskJet 640c product HP 4670V 0x3005 ScanJet 4670v product HP P1100 0x3102 Photosmart P1100 product HP LD220 0x3524 LD220 POS Display product HP OJ4215 0x3d11 OfficeJet 4215 product HP HN210E 0x811c Ethernet HN210E product HP2 C500 0x6002 PhotoSmart C500 product HP EV2200 0x1b1d ev2200 HSDPA (aka MC5720) product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775) /* HTC products */ product HTC WINMOBILE 0x00ce HTC USB Sync product HTC PPC6700MODEM 0x00cf PPC6700 Modem product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync product HTC WIZARD 0x0bce HTC Wizard USB Sync product HTC LEGENDSYNC 0x0c97 HTC Legend USB Sync product HTC LEGEND 0x0ff9 HTC Legend product HTC LEGENDINTERNET 0x0ffe HTC Legend Internet Sharing /* HUAWEI products */ product HUAWEI MOBILE 0x1001 Huawei Mobile product HUAWEI E220 0x1003 HSDPA modem product HUAWEI E220BIS 0x1004 HSDPA modem product HUAWEI E1401 0x1401 3G modem product HUAWEI E1402 0x1402 3G modem product HUAWEI E1403 0x1403 3G modem product HUAWEI E1404 0x1404 3G modem product HUAWEI E1405 0x1405 3G modem product HUAWEI E1406 0x1406 3G modem product HUAWEI E1407 0x1407 3G modem product HUAWEI E1408 0x1408 3G modem product HUAWEI E1409 0x1409 3G modem product HUAWEI E140A 0x140a 3G modem product HUAWEI E140B 0x140b 3G modem product HUAWEI E180V 0x140c E180V product HUAWEI E140D 0x140d 3G modem product HUAWEI E140E 0x140e 3G modem product HUAWEI E140F 0x140f 3G modem product HUAWEI E1410 0x1410 3G modem product HUAWEI E1411 0x1411 3G modem product HUAWEI E1412 0x1412 3G modem product HUAWEI E1413 0x1413 3G modem product HUAWEI E1414 0x1414 3G modem product HUAWEI E1415 0x1415 3G modem product HUAWEI E1416 0x1416 3G modem product HUAWEI E1417 0x1417 3G modem product HUAWEI E1418 0x1418 3G modem product HUAWEI E1419 0x1419 3G modem product HUAWEI E141A 0x141a 3G modem product HUAWEI E141B 0x141b 3G modem product HUAWEI E141C 0x141c 3G modem product HUAWEI E141D 0x141d 3G modem product HUAWEI E141E 0x141e 3G modem product HUAWEI E141F 0x141f 3G modem product HUAWEI E1420 0x1420 3G modem product HUAWEI E1421 0x1421 3G modem product HUAWEI E1422 0x1422 3G modem product HUAWEI E1423 0x1423 3G modem product HUAWEI E1424 0x1424 3G modem product HUAWEI E1425 0x1425 3G modem product HUAWEI E1426 0x1426 3G modem product HUAWEI E1427 0x1427 3G modem product HUAWEI E1428 0x1428 3G modem product HUAWEI E1429 0x1429 3G modem product HUAWEI E142A 0x142a 3G modem product HUAWEI E142B 0x142b 3G modem product HUAWEI E142C 0x142c 3G modem product HUAWEI E142D 0x142d 3G modem product HUAWEI E142E 0x142e 3G modem product HUAWEI E142F 0x142f 3G modem product HUAWEI E1430 0x1430 3G modem product HUAWEI E1431 0x1431 3G modem product HUAWEI E1432 0x1432 3G modem product HUAWEI E1433 0x1433 3G modem product HUAWEI E1434 0x1434 3G modem product HUAWEI E1435 0x1435 3G modem product HUAWEI E1436 0x1436 3G modem product HUAWEI E1437 0x1437 3G modem product HUAWEI E1438 0x1438 3G modem product HUAWEI E1439 0x1439 3G modem product HUAWEI E143A 0x143a 3G modem product HUAWEI E143B 0x143b 3G modem product HUAWEI E143C 0x143c 3G modem product HUAWEI E143D 0x143d 3G modem product HUAWEI E143E 0x143e 3G modem product HUAWEI E143F 0x143f 3G modem product HUAWEI E1752 0x1446 3G modem product HUAWEI K4505 0x1464 3G modem product HUAWEI K3765 0x1465 3G modem product HUAWEI E1820 0x14ac E1820 HSPA+ USB Slider product HUAWEI K3770 0x14c9 3G modem product HUAWEI K3772 0x14cf K3772 product HUAWEI K3770_INIT 0x14d1 K3770 Initial product HUAWEI E3131_INIT 0x14fe 3G modem initial product HUAWEI E392 0x1505 LTE modem product HUAWEI E3131 0x1506 3G modem product HUAWEI K3765_INIT 0x1520 K3765 Initial product HUAWEI K4505_INIT 0x1521 K4505 Initial product HUAWEI K3772_INIT 0x1526 K3772 Initial product HUAWEI E3272_INIT 0x155b LTE modem initial product HUAWEI ME909U 0x1573 LTE modem product HUAWEI R215_INIT 0x1582 LTE modem initial product HUAWEI R215 0x1588 LTE modem product HUAWEI ETS2055 0x1803 CDMA modem product HUAWEI E173 0x1c05 3G modem product HUAWEI E173_INIT 0x1c0b 3G modem initial product HUAWEI E3272 0x1c1e LTE modem /* HUAWEI 3com products */ product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g /* IBM Corporation */ product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive /* Icom products */ product ICOM SP1 0x0004 FTDI compatible adapter product ICOM OPC_U_UC 0x0018 FTDI compatible adapter product ICOM RP2C1 0x0009 FTDI compatible adapter product ICOM RP2C2 0x000a FTDI compatible adapter product ICOM RP2D 0x000b FTDI compatible adapter product ICOM RP2KVR 0x0013 FTDI compatible adapter product ICOM RP2KVT 0x0012 FTDI compatible adapter product ICOM RP2VR 0x000d FTDI compatible adapter product ICOM RP2VT 0x000c FTDI compatible adapter product ICOM RP4KVR 0x0011 FTDI compatible adapter product ICOM RP4KVT 0x0010 FTDI compatible adapter /* ID-tech products */ product IDTECH IDT1221U 0x0300 FTDI compatible adapter /* Imagination Technologies products */ product IMAGINATION DBX1 0x2107 DBX1 DSP core /* Initio Corporation products */ product INITIO DUMMY 0x0000 Dummy product product INITIO INIC_1610P 0x1e40 USB to SATA Bridge /* Inside Out Networks products */ product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports /* In-System products */ product INSYSTEM F5U002 0x0002 Parallel printer product INSYSTEM ATAPI 0x0031 ATAPI Adapter product INSYSTEM ISD110 0x0200 IDE Adapter ISD110 product INSYSTEM ISD105 0x0202 IDE Adapter ISD105 product INSYSTEM USBCABLE 0x081a USB cable product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2 /* Intel products */ product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera product INTEL TESTBOARD 0x9890 82930 test board product INTEL2 IRMH 0x0020 Integrated Rate Matching Hub product INTEL2 IRMH2 0x0024 Integrated Rate Matching Hub product INTEL2 IRMH3 0x8000 Integrated Rate Matching Hub product INTEL2 IRMH4 0x8008 Integrated Rate Matching Hub /* Interbiometric products */ product INTERBIOMETRICS IOBOARD 0x1002 FTDI compatible adapter product INTERBIOMETRICS MINI_IOBOARD 0x1006 FTDI compatible adapter /* Intersil products */ product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN /* Interpid Control Systems products */ product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface /* I/O DATA products */ product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2 product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8 product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card product IODATA USBSDRW 0x031e USB-SDRW SD-card product IODATA USBETT 0x0901 USB ETT product IODATA USBETTX 0x0904 USB ETTX product IODATA USBETTXS 0x0913 USB ETTX product IODATA USBWNB11A 0x0919 USB WN-B11 product IODATA USBWNB11 0x0922 USB Airport WN-B11 product IODATA ETGUS2 0x0930 ETG-US2 product IODATA WNGDNUS2 0x093f WN-GDN/US2 product IODATA RT3072_1 0x0944 RT3072 product IODATA RT3072_2 0x0945 RT3072 product IODATA RT3072_3 0x0947 RT3072 product IODATA RT3072_4 0x0948 RT3072 product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1 product IODATA USBRSAQ5 0x0a0e Serial USB-RSAQ5 product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC /* Iomega products */ product IOMEGA ZIP100 0x0001 Zip 100 product IOMEGA ZIP250 0x0030 Zip 250 /* Ionic products */ product IONICS PLUGCOMPUTER 0x0102 FTDI compatible adapter /* Integrated System Solution Corp. products */ product ISSC ISSCBTA 0x1001 Bluetooth USB Adapter /* iTegno products */ product ITEGNO WM1080A 0x1080 WM1080A GSM/GPRS modem product ITEGNO WM2080A 0x2080 WM2080A CDMA modem /* Ituner networks products */ product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20 product ITUNERNET USBLCD4X20 0xc001 USB-LCD 4x20 /* Jablotron products */ product JABLOTRON PC60B 0x0001 PC-60B /* Jaton products */ product JATON EDA 0x5704 Ethernet /* Jeti products */ product JETI SPC1201 0x04b2 FTDI compatible adapter /* JMicron products */ product JMICRON JM20336 0x2336 USB to SATA Bridge product JMICRON JM20337 0x2338 USB to ATA/ATAPI Bridge /* JVC products */ product JVC GR_DX95 0x000a GR-DX95 product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet /* JRC products */ product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V /* Kamstrrup products */ product KAMSTRUP OPTICALEYE 0x0001 Optical Eye/3-wire product KAMSTRUP MBUS_250D 0x0005 M-Bus Master MultiPort 250D /* Kawatsu products */ product KAWATSU MH4000P 0x0003 MiniHub 4000P /* Keisokugiken Corp. products */ product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ /* Kensington products */ product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball product KENSINGTON TURBOBALL 0x1005 TurboBall /* Keyspan products */ product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware) product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware) product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware) product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware) product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware) product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware) product KEYSPAN USA19 0x0107 USA-19 serial Adapter product KEYSPAN USA19W 0x0108 USA-19W serial Adapter product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware) product KEYSPAN USA49W 0x010a USA-49W serial Adapter product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware) product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware) product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter product KEYSPAN USA28 0x010f USA-28 serial Adapter product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter product KEYSPAN USA18 0x0111 USA-18 serial Adapter product KEYSPAN USA18X 0x0112 USA-18X serial Adapter product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware) product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware) product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter product KEYSPAN UIA10 0x0201 UIA-10 remote control product KEYSPAN UIA11 0x0202 UIA-11 remote control /* Kingston products */ product KINGSTON XX1 0x0008 Ethernet product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet product KINGSTON HYPERX3_0 0x162b DT HyperX 3.0 /* Kawasaki products */ product KLSI DUH3E10BT 0x0008 USB Ethernet product KLSI DUH3E10BTN 0x0009 USB Ethernet /* Kobil products */ product KOBIL CONV_B1 0x2020 FTDI compatible adapter product KOBIL CONV_KAAN 0x2021 FTDI compatible adapter /* Kodak products */ product KODAK DC220 0x0100 Digital Science DC220 product KODAK DC260 0x0110 Digital Science DC260 product KODAK DC265 0x0111 Digital Science DC265 product KODAK DC290 0x0112 Digital Science DC290 product KODAK DC240 0x0120 Digital Science DC240 product KODAK DC280 0x0130 Digital Science DC280 /* Kontron AG products */ product KONTRON DM9601 0x8101 USB Ethernet product KONTRON JP1082 0x9700 USB Ethernet /* Konica Corp. Products */ product KONICA CAMERA 0x0720 Digital Color Camera /* KYE products */ product KYE NICHE 0x0001 Niche mouse product KYE NETSCROLL 0x0003 Genius NetScroll mouse product KYE FLIGHT2000 0x1004 Flight 2000 joystick product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner /* Kyocera products */ product KYOCERA FINECAM_S3X 0x0100 Finecam S3x product KYOCERA FINECAM_S4 0x0101 Finecam S4 product KYOCERA FINECAM_S5 0x0103 Finecam S5 product KYOCERA FINECAM_L3 0x0105 Finecam L3 product KYOCERA AHK3001V 0x0203 AH-K3001V product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM product KYOCERA2 KPC680 0x180a Qualcomm Kyocera CDMA Technologies MSM /* LaCie products */ product LACIE HD 0xa601 Hard Disk product LACIE CDRW 0xa602 CD R/W /* Lake Shore Cryotronics products */ product LAKESHORE 121 0x0100 121 Current Source product LAKESHORE 218A 0x0200 218A Temperature Monitor product LAKESHORE 219 0x0201 219 Temperature Monitor product LAKESHORE 233 0x0202 233 Temperature Transmitter product LAKESHORE 235 0x0203 235 Temperature Transmitter product LAKESHORE 335 0x0300 335 Temperature Controller product LAKESHORE 336 0x0301 336 Temperature Controller product LAKESHORE 350 0x0302 350 Temperature Controller product LAKESHORE 371 0x0303 371 AC Bridge product LAKESHORE 411 0x0400 411 Handheld Gaussmeter product LAKESHORE 425 0x0401 425 Gaussmeter product LAKESHORE 455A 0x0402 455A DSP Gaussmeter product LAKESHORE 475A 0x0403 475A DSP Gaussmeter product LAKESHORE 465 0x0404 465 Gaussmeter product LAKESHORE 625A 0x0600 625A Magnet PSU product LAKESHORE 642A 0x0601 642A Magnet PSU product LAKESHORE 648 0x0602 648 Magnet PSU product LAKESHORE 737 0x0700 737 VSM Controller product LAKESHORE 776 0x0701 776 Matrix Switch /* Larsen and Brusgaard products */ product LARSENBRUSGAARD ALTITRACK 0x0001 FTDI compatible adapter /* Leadtek products */ product LEADTEK 9531 0x2101 9531 GPS /* Lenovo products */ product LENOVO GIGALAN 0x304b USB 3.0 Ethernet product LENOVO ETHERNET 0x7203 USB 2.0 Ethernet /* Lexar products */ product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader product LEXAR CF_READER 0xb002 USB CF Reader product LEXAR JUMPDRIVE 0xa833 USB Jumpdrive Flash Drive /* Lexmark products */ product LEXMARK S2450 0x0009 Optra S 2450 /* Liebert products */ product LIEBERT POWERSURE_PXT 0xffff PowerSure Personal XT product LIEBERT2 PSI1000 0x0004 UPS PSI 1000 FW:08 /* Link Instruments Inc. products */ product LINKINSTRUMENTS MSO19 0xf190 Link Instruments MSO-19 product LINKINSTRUMENTS MSO28 0xf280 Link Instruments MSO-28 product LINKINSTRUMENTS MSO28_2 0xf281 Link Instruments MSO-28 /* Linksys products */ product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2 product LINKSYS USB10TX1 0x200c USB10TX product LINKSYS USB10T 0x2202 USB10T Ethernet product LINKSYS USB100TX 0x2203 USB100TX Ethernet product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA product LINKSYS USB10TA 0x2206 USB10TA Ethernet product LINKSYS USB10TX2 0x400b USB10TX product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter product LINKSYS4 USB1000 0x0039 USB1000 product LINKSYS4 WUSB100 0x0070 WUSB100 product LINKSYS4 WUSB600N 0x0071 WUSB600N product LINKSYS4 WUSB54GCV2 0x0073 WUSB54GC v2 product LINKSYS4 WUSB54GCV3 0x0077 WUSB54GC v3 product LINKSYS4 RT3070 0x0078 RT3070 product LINKSYS4 WUSB600NV2 0x0079 WUSB600N v2 /* Logilink products */ product LOGILINK DUMMY 0x0000 Dummy product product LOGILINK U2M 0x0101 LogiLink USB MIDI Cable /* Logitech products */ product LOGITECH M2452 0x0203 M2452 keyboard product LOGITECH M4848 0x0301 M4848 mouse product LOGITECH PAGESCAN 0x040f PageScan product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro product LOGITECH WEBCAMC100 0X0817 Webcam C100 product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express product LOGITECH QUICKCAM 0x0850 QuickCam product LOGITECH QUICKCAMPRO3 0x0990 QuickCam Pro 9000 product LOGITECH N43 0xc000 N43 product LOGITECH N48 0xc001 N48 mouse product LOGITECH MBA47 0xc002 M-BA47 mouse product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse product LOGITECH BD58 0xc00c BD58 mouse product LOGITECH UN58A 0xc030 iFeel Mouse product LOGITECH UN53B 0xc032 iFeel MouseMan product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme product LOGITECH WMRPAD 0xc20a WingMan RumblePad product LOGITECH WMJOY 0xc281 WingMan Force joystick product LOGITECH BB13 0xc401 USB-PS/2 Trackball product LOGITECH RK53 0xc501 Cordless mouse product LOGITECH RB6 0xc503 Cordless keyboard product LOGITECH MX700 0xc506 Cordless optical mouse product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro /* Logitec Corp. products */ product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2 product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2 product LOGITEC LAN_GTJU2A 0x0160 LAN-GTJ/U2A Ethernet product LOGITEC RT2870_1 0x0162 RT2870 product LOGITEC RT2870_2 0x0163 RT2870 product LOGITEC RT2870_3 0x0164 RT2870 product LOGITEC LANW300NU2 0x0166 LAN-W300N/U2 product LOGITEC LANW150NU2 0x0168 LAN-W150N/U2 product LOGITEC LANW300NU2S 0x0169 LAN-W300N/U2S /* Longcheer Holdings, Ltd. products */ product LONGCHEER WM66 0x6061 Longcheer WM66 HSDPA product LONGCHEER W14 0x9603 Mobilcom W14 product LONGCHEER DISK 0xf000 Driver disk product LONGCHEER XSSTICK 0x9605 4G Systems XSStick P14 /* Lucent products */ product LUCENT EVALKIT 0x1001 USS-720 evaluation kit /* Luwen products */ product LUWEN EASYDISK 0x0005 EasyDisc /* Macally products */ product MACALLY MOUSE1 0x0101 mouse /* Mag-Tek products */ product MAGTEK USBSWIPE 0x0002 USB Mag Stripe Swipe Reader /* Marvell Technology Group, Ltd. products */ product MARVELL SHEEVAPLUG 0x9e8f SheevaPlug serial interface /* Matrix Orbital products */ product MATRIXORBITAL FTDI_RANGE_0100 0x0100 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0101 0x0101 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0102 0x0102 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0103 0x0103 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0104 0x0104 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0105 0x0105 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0106 0x0106 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0107 0x0107 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0108 0x0108 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0109 0x0109 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010A 0x010a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010B 0x010b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010C 0x010c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010D 0x010d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010E 0x010e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010F 0x010f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0110 0x0110 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0111 0x0111 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0112 0x0112 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0113 0x0113 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0114 0x0114 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0115 0x0115 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0116 0x0116 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0117 0x0117 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0118 0x0118 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0119 0x0119 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011A 0x011a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011B 0x011b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011C 0x011c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011D 0x011d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011E 0x011e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011F 0x011f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0120 0x0120 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0121 0x0121 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0122 0x0122 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0123 0x0123 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0124 0x0124 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0125 0x0125 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0126 0x0126 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0128 0x0128 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0129 0x0129 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012A 0x012a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012B 0x012b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012D 0x012d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012E 0x012e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012F 0x012f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0130 0x0130 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0131 0x0131 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0132 0x0132 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0133 0x0133 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0134 0x0134 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0135 0x0135 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0136 0x0136 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0137 0x0137 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0138 0x0138 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0139 0x0139 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013A 0x013a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013B 0x013b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013C 0x013c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013D 0x013d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013E 0x013e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013F 0x013f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0140 0x0140 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0141 0x0141 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0142 0x0142 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0143 0x0143 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0144 0x0144 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0145 0x0145 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0146 0x0146 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0147 0x0147 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0148 0x0148 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0149 0x0149 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014A 0x014a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014B 0x014b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014C 0x014c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014D 0x014d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014E 0x014e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014F 0x014f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0150 0x0150 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0151 0x0151 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0152 0x0152 FTDI compatible adapter product MATRIXORBITAL MOUA 0x0153 Martrix Orbital MOU-Axxxx LCD displays product MATRIXORBITAL FTDI_RANGE_0159 0x0159 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015A 0x015a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015B 0x015b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015C 0x015c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015D 0x015d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015E 0x015e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015F 0x015f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0160 0x0160 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0161 0x0161 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0162 0x0162 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0163 0x0163 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0164 0x0164 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0165 0x0165 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0166 0x0166 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0167 0x0167 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0168 0x0168 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0169 0x0169 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016A 0x016a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016B 0x016b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016C 0x016c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016D 0x016d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016E 0x016e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016F 0x016f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0170 0x0170 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0171 0x0171 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0172 0x0172 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0173 0x0173 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0174 0x0174 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0175 0x0175 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0176 0x0176 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0177 0x0177 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0178 0x0178 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0179 0x0179 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017A 0x017a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017B 0x017b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017C 0x017c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017D 0x017d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017E 0x017e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017F 0x017f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0180 0x0180 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0181 0x0181 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0182 0x0182 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0183 0x0183 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0184 0x0184 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0185 0x0185 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0186 0x0186 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0187 0x0187 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0188 0x0188 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0189 0x0189 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018A 0x018a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018B 0x018b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018C 0x018c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018D 0x018d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018E 0x018e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018F 0x018f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0190 0x0190 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0191 0x0191 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0192 0x0192 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0193 0x0193 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0194 0x0194 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0195 0x0195 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0196 0x0196 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0197 0x0197 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0198 0x0198 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0199 0x0199 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019A 0x019a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019B 0x019b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019C 0x019c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019D 0x019d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019E 0x019e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019F 0x019f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A0 0x01a0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A1 0x01a1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A2 0x01a2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A3 0x01a3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A4 0x01a4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A5 0x01a5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A6 0x01a6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A7 0x01a7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A8 0x01a8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A9 0x01a9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AA 0x01aa FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AB 0x01ab FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AC 0x01ac FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AD 0x01ad FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AE 0x01ae FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AF 0x01af FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B0 0x01b0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B1 0x01b1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B2 0x01b2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B3 0x01b3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B4 0x01b4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B5 0x01b5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B6 0x01b6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B7 0x01b7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B8 0x01b8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B9 0x01b9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BA 0x01ba FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BB 0x01bb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BC 0x01bc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BD 0x01bd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BE 0x01be FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BF 0x01bf FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C0 0x01c0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C1 0x01c1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C2 0x01c2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C3 0x01c3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C4 0x01c4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C5 0x01c5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C6 0x01c6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C7 0x01c7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C8 0x01c8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C9 0x01c9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CA 0x01ca FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CB 0x01cb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CC 0x01cc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CD 0x01cd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CE 0x01ce FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CF 0x01cf FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D0 0x01d0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D1 0x01d1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D2 0x01d2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D3 0x01d3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D4 0x01d4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D5 0x01d5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D6 0x01d6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D7 0x01d7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D8 0x01d8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D9 0x01d9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DA 0x01da FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DB 0x01db FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DC 0x01dc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DD 0x01dd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DE 0x01de FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DF 0x01df FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E0 0x01e0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E1 0x01e1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E2 0x01e2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E3 0x01e3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E4 0x01e4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E5 0x01e5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E6 0x01e6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E7 0x01e7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E8 0x01e8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E9 0x01e9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EA 0x01ea FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EB 0x01eb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EC 0x01ec FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01ED 0x01ed FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EE 0x01ee FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EF 0x01ef FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F0 0x01f0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F1 0x01f1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F2 0x01f2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F3 0x01f3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F4 0x01f4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F5 0x01f5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F6 0x01f6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F7 0x01f7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F8 0x01f8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F9 0x01f9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FA 0x01fa FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FB 0x01fb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FC 0x01fc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FD 0x01fd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FE 0x01fe FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FF 0x01ff FTDI compatible adapter /* MCT Corp. */ product MCT HUB0100 0x0100 Hub product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub product MCT USB232 0x0210 USB-232 Interface product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products /* Medeli */ product MEDELI DD305 0x5011 DD305 Digital Drum Set /* MediaTek, Inc. */ product MEDIATEK MTK3329 0x3329 MTK II GPS Receiver /* Meizu Electronics */ product MEIZU M6_SL 0x0140 MiniPlayer M6 (SL) /* Melco, Inc products */ product MELCO LUATX1 0x0001 LUA-TX Ethernet product MELCO LUATX5 0x0005 LUA-TX Ethernet product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet product MELCO LUAKTX 0x0012 LUA-KTX Ethernet product MELCO DUBPXXG 0x001c DUB-PxxG product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN product MELCO KG54 0x0066 WLI-U2-KG54 WLAN product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN product MELCO LUA3U2AGT 0x006e LUA3-U2-AGT product MELCO NINWIFI 0x008b Nintendo Wi-Fi product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation product MELCO SG54HP 0x00d8 WLI-U2-SG54HP product MELCO G54HP 0x00d9 WLI-U2-G54HP product MELCO KG54L 0x00da WLI-U2-KG54L product MELCO WLIUCG300N 0x00e8 WLI-UC-G300N product MELCO SG54HG 0x00f4 WLI-U2-SG54HG product MELCO WLRUCG 0x0116 WLR-UC-G product MELCO WLRUCGAOSS 0x0119 WLR-UC-G-AOSS product MELCO WLIUCAG300N 0x012e WLI-UC-AG300N product MELCO WLIUCG 0x0137 WLI-UC-G product MELCO WLIUCG300HP 0x0148 WLI-UC-G300HP product MELCO RT2870_2 0x0150 RT2870 product MELCO WLIUCGN 0x015d WLI-UC-GN product MELCO WLIUCG301N 0x016f WLI-UC-G301N product MELCO WLIUCGNM 0x01a2 WLI-UC-GNM product MELCO WLIUCG300HPV1 0x01a8 WLI-UC-G300HP-V1 product MELCO WLIUCGNM2 0x01ee WLI-UC-GNM2 /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* MetaGeek products */ product METAGEEK TELLSTICK 0x0c30 FTDI compatible adapter product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x product METAGEEK2 WISPYDBX 0x5000 MetaGeek Wi-Spy DBx /* Metricom products */ product METRICOM RICOCHET_GS 0x0001 Ricochet GS /* MGE UPS Systems */ product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1 product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2 /* MEI products */ product MEI CASHFLOW_SC 0x1100 Cashflow-SC Cash Acceptor product MEI S2000 0x1101 Series 2000 Combo Acceptor /* Microdia / Sonix Techonology Co., Ltd. products */ product CHICONY2 YUREX 0x1010 YUREX product CHICONY2 CAM_1 0x62c0 CAM_1 product CHICONY2 TEMPER 0x7401 TEMPer sensor /* Micro Star International products */ product MSI BT_DONGLE 0x1967 Bluetooth USB dongle product MSI RT3070_1 0x3820 RT3070 product MSI RT3070_2 0x3821 RT3070 product MSI RT3070_8 0x3822 RT3070 product MSI RT3070_3 0x3870 RT3070 product MSI RT3070_9 0x3871 RT3070 product MSI UB11B 0x6823 UB11B product MSI RT2570 0x6861 RT2570 product MSI RT2570_2 0x6865 RT2570 product MSI RT2570_3 0x6869 RT2570 product MSI RT2573_1 0x6874 RT2573 product MSI RT2573_2 0x6877 RT2573 product MSI RT3070_4 0x6899 RT3070 product MSI RT3070_5 0x821a RT3070 product MSI RT3070_10 0x822a RT3070 product MSI RT3070_6 0x870a RT3070 product MSI RT3070_11 0x871a RT3070 product MSI RT3070_7 0x899a RT3070 product MSI RT2573_3 0xa861 RT2573 product MSI RT2573_4 0xa874 RT2573 /* Micron products */ product MICRON REALSSD 0x0655 Real SSD eUSB /* Microsoft products */ product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite product MICROSOFT DDS80 0x0014 Digital Sound System 80 product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel product MICROSOFT INETPRO 0x001c Internet Keyboard Pro product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro product MICROSOFT INTELLIMOUSE5 0x0039 IntelliMouse 1.1 5-Button Mouse product MICROSOFT WHEELMOUSE 0x0040 Wheel Mouse Optical product MICROSOFT MN510 0x006e MN510 Wireless product MICROSOFT 700WX 0x0079 Palm 700WX product MICROSOFT MN110 0x007a 10/100 USB NIC product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023) product MICROSOFT COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043) product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049) product MICROSOFT NATURAL4000 0x00db Natural Ergonomic Keyboard 4000 product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056) product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN /* Microtech products */ product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25 product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50 product MICROTECH DPCM 0x0006 USB CameraMate product MICROTECH FREECOM 0xfc01 Freecom USB-IDE /* Microtek products */ product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner product MICROTEK X6U 0x0099 ScanMaker X6 - X6U product MICROTEK C6 0x009a Phantom C6 scanner product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner product MICROTEK V6USL 0x00a3 ScanMaker V6USL product MICROTEK V6USL2 0x80a3 ScanMaker V6USL product MICROTEK V6UL 0x80ac ScanMaker V6UL /* Microtune, Inc. products */ product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle /* Midiman products */ product MAUDIO MIDISPORT2X2 0x1001 Midisport 2x2 product MAUDIO FASTTRACKULTRA 0x2080 Fast Track Ultra product MAUDIO FASTTRACKULTRA8R 0x2081 Fast Track Ultra 8R /* MindsAtWork products */ product MINDSATWORK WALLET 0x0001 Digital Wallet /* Minolta Co., Ltd. */ product MINOLTA 2300 0x4001 Dimage 2300 product MINOLTA S304 0x4007 Dimage S304 product MINOLTA X 0x4009 Dimage X product MINOLTA 5400 0x400e Dimage 5400 product MINOLTA F300 0x4011 Dimage F300 product MINOLTA E223 0x4017 Dimage E223 /* Mitsumi products */ product MITSUMI CDRRW 0x0000 CD-R/RW Drive product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle product MITSUMI FDD 0x6901 USB FDD /* Mobile Action products */ product MOBILEACTION MA620 0x0620 MA-620 Infrared Adapter /* Mobility products */ product MOBILITY USB_SERIAL 0x0202 FTDI compatible adapter product MOBILITY EA 0x0204 Ethernet product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet /* MosChip products */ product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter product MOSCHIP MCS7730 0x7730 MCS7730 Ethernet product MOSCHIP MCS7820 0x7820 MCS7820 Serial Port Adapter product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet product MOSCHIP MCS7832 0x7832 MCS7832 Ethernet product MOSCHIP MCS7840 0x7840 MCS7840 Serial Port Adapter /* Motorola products */ product MOTOROLA MC141555 0x1555 MC141555 hub controller product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem product MOTOROLA2 T720C 0x2822 T720c product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones product MOTOROLA2 E398 0x4810 E398 Mobile Phone product MOTOROLA2 USBLAN 0x600c USBLAN product MOTOROLA2 USBLAN2 0x6027 USBLAN product MOTOROLA2 MB886 0x710f MB886 Mobile Phone (Atria HD) product MOTOROLA4 RT2770 0x9031 RT2770 product MOTOROLA4 RT3070 0x9032 RT3070 /* MpMan products */ product MPMAN MPF400_2 0x25a8 MPF400 Music Player 2Go product MPMAN MPF400_1 0x36d0 MPF400 Music Player 1Go /* MultiTech products */ product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem /* Mustek products */ product MUSTEK 1200CU 0x0001 1200 CU scanner product MUSTEK 600CU 0x0002 600 CU scanner product MUSTEK 1200USB 0x0003 1200 USB scanner product MUSTEK 1200UB 0x0006 1200 UB scanner product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner product MUSTEK BEARPAW2400TA 0x0218 BearPaw 2400TA scanner product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner product MUSTEK 600USB 0x0873 600 USB scanner product MUSTEK MDC800 0xa800 MDC-800 digital camera /* M-Systems products */ product MSYSTEMS DISKONKEY 0x0010 DiskOnKey product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey /* Myson products */ product MYSON HEDEN_8813 0x8813 USB-IDE product MYSON HEDEN 0x8818 USB-IDE product MYSON HUBREADER 0x8819 COMBO Card reader with USB HUB product MYSON STARREADER 0x9920 USB flash card adapter /* National Semiconductor */ product NATIONAL BEARPAW1200 0x1000 BearPaw 1200 product NATIONAL BEARPAW2400 0x1001 BearPaw 2400 /* NEC products */ product NEC HUB_0050 0x0050 USB 2.0 7-Port Hub product NEC HUB_005A 0x005a USB 2.0 4-Port Hub product NEC WL300NUG 0x0249 WL300NU-G product NEC HUB 0x55aa hub product NEC HUB_B 0x55ab hub /* NEODIO products */ product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller product NEODIO ND5010 0x5010 Multi-format Flash Controller /* Neotel products */ product NEOTEL PRIME 0x4000 Prime USB modem /* Netac products */ product NETAC CF_CARD 0x1060 USB-CF-Card product NETAC ONLYDISK 0x0003 OnlyDisk /* NetChip Technology Products */ product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect product NETCHIP CLIK_40 0xa140 USB Clik! 40 product NETCHIP GADGETZERO 0xa4a0 Linux Gadget Zero product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x product NETCHIP POCKETBOOK 0xa4a5 PocketBook /* Netgear products */ product NETGEAR EA101 0x1001 Ethernet product NETGEAR EA101X 0x1002 Ethernet product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1 product NETGEAR FA120 0x1040 USB 2.0 Ethernet product NETGEAR M4100 0x1100 M4100/M5300/M7100 series switch product NETGEAR WG111V1_2 0x4240 PrismGT USB 2.0 WLAN product NETGEAR WG111V3 0x4260 WG111v3 product NETGEAR WG111U 0x4300 WG111U product NETGEAR WG111U_NF 0x4301 WG111U (no firmware) product NETGEAR WG111V2 0x6a00 WG111V2 product NETGEAR WN111V2 0x9001 WN111V2 product NETGEAR WNDA3100 0x9010 WNDA3100 product NETGEAR WNDA4100 0x9012 WNDA4100 product NETGEAR WNDA3200 0x9018 WNDA3200 product NETGEAR RTL8192CU 0x9021 RTL8192CU product NETGEAR WNA1000 0x9040 WNA1000 product NETGEAR WNA1000M 0x9041 WNA1000M product NETGEAR2 MA101 0x4100 MA101 product NETGEAR2 MA101B 0x4102 MA101 Rev B product NETGEAR3 WG111T 0x4250 WG111T product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware) product NETGEAR3 WPN111 0x5f00 WPN111 product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware) product NETGEAR3 WPN111_2 0x5f02 WPN111 product NETGEAR4 RTL8188CU 0x9041 RTL8188CU /* NetIndex products */ product NETINDEX WS002IN 0x2001 Willcom WS002IN /* NEWlink */ product NEWLINK USB2IDEBRIDGE 0x00ff USB 2.0 Hard Drive Enclosure /* Nikon products */ product NIKON E990 0x0102 Digital Camera E990 product NIKON LS40 0x4000 CoolScan LS40 ED product NIKON D300 0x041a Digital Camera D300 /* NovaTech Products */ product NOVATECH NV902 0x9020 NovaTech NV-902W product NOVATECH RT2573 0x9021 RT2573 product NOVATECH RTL8188CU 0x9071 RTL8188CU /* Nokia products */ product NOKIA N958GB 0x0070 Nokia N95 8GBc product NOKIA2 CA42 0x1234 CA-42 cable /* Novatel Wireless products */ product NOVATEL V640 0x1100 Merlin V620 product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA product NOVATEL V620 0x1110 Merlin V620 product NOVATEL V740 0x1120 Merlin V740 product NOVATEL V720 0x1130 Merlin V720 product NOVATEL U740 0x1400 Merlin U740 product NOVATEL U740_2 0x1410 Merlin U740 product NOVATEL U870 0x1420 Merlin U870 product NOVATEL XU870 0x1430 Merlin XU870 product NOVATEL X950D 0x1450 Merlin X950D product NOVATEL ES620 0x2100 Expedite ES620 product NOVATEL E725 0x2120 Expedite E725 product NOVATEL ES620_2 0x2130 Expedite ES620 product NOVATEL ES620 0x2100 ES620 CDMA product NOVATEL U720 0x2110 Merlin U720 product NOVATEL EU730 0x2400 Expedite EU730 product NOVATEL EU740 0x2410 Expedite EU740 product NOVATEL EU870D 0x2420 Expedite EU870D product NOVATEL U727 0x4100 Merlin U727 CDMA product NOVATEL MC950D 0x4400 Novatel MC950D HSUPA product NOVATEL MC990D 0x7001 Novatel MC990D product NOVATEL ZEROCD 0x5010 Novatel ZeroCD product NOVATEL MIFI2200V 0x5020 Novatel MiFi 2200 CDMA Virgin Mobile product NOVATEL ZEROCD2 0x5030 Novatel ZeroCD product NOVATEL MIFI2200 0x5041 Novatel MiFi 2200 CDMA product NOVATEL U727_2 0x5100 Merlin U727 CDMA product NOVATEL U760 0x6000 Novatel U760 product NOVATEL MC760 0x6002 Novatel MC760 product NOVATEL MC547 0x7042 Novatel MC547 product NOVATEL MC679 0x7031 Novatel MC679 product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* O2Micro products */ product O2MICRO OZ776_HUB 0x7761 OZ776 hub product O2MICRO OZ776_CCID_SC 0x7772 OZ776 CCID SC Reader /* Olimex products */ product OLIMEX ARM_USB_OCD 0x0003 FTDI compatible adapter product OLIMEX ARM_USB_OCD_H 0x002b FTDI compatible adapter /* Olympus products */ product OLYMPUS C1 0x0102 C-1 Digital Camera product OLYMPUS C700 0x0105 C-700 Ultra Zoom /* OmniVision Technologies, Inc. products */ product OMNIVISION OV511 0x0511 OV511 Camera product OMNIVISION OV511PLUS 0xa511 OV511+ Camera /* OnSpec Electronic, Inc. */ product ONSPEC SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer product ONSPEC READER 0xa003 Datafab-based Reader product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1) product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55 /* Option products */ product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard product OPTION GT3G 0x6000 GlobeTrotter 3G datacard product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard product OPTION GTICON322 0xd033 GlobeTrotter Icon322 storage product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem product OPTION GTMAX72 0x6711 GlobeTrotter Max 7.2 HSDPA product OPTION GTHSDPA 0x6971 GlobeTrotter HSDPA product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA product OPTION GTMAXHSUPAE 0x6901 GlobeTrotter HSUPA PCIe product OPTION GTMAX380HSUPAE 0x7211 GlobeTrotter 380HSUPA PCIe product OPTION GT3G_1 0x6050 3G modem product OPTION GT3G_2 0x6100 3G modem product OPTION GT3G_3 0x6150 3G modem product OPTION GT3G_4 0x6200 3G modem product OPTION GT3G_5 0x6250 3G modem product OPTION GT3G_6 0x6350 3G modem product OPTION E6500 0x6500 3G modem product OPTION E6501 0x6501 3G modem product OPTION E6601 0x6601 3G modem product OPTION E6721 0x6721 3G modem product OPTION E6741 0x6741 3G modem product OPTION E6761 0x6761 3G modem product OPTION E6800 0x6800 3G modem product OPTION E7021 0x7021 3G modem product OPTION E7041 0x7041 3G modem product OPTION E7061 0x7061 3G modem product OPTION E7100 0x7100 3G modem product OPTION GTM380 0x7201 3G modem product OPTION GE40X 0x7601 Globetrotter HSUPA product OPTION GSICON72 0x6911 GlobeSurfer iCON product OPTION GSICONHSUPA 0x7251 Globetrotter HSUPA product OPTION ICON401 0x7401 GlobeSurfer iCON 401 product OPTION GTHSUPA 0x7011 Globetrotter HSUPA product OPTION GMT382 0x7501 Globetrotter HSUPA product OPTION GE40X_1 0x7301 Globetrotter HSUPA product OPTION GE40X_2 0x7361 Globetrotter HSUPA product OPTION GE40X_3 0x7381 Globetrotter HSUPA product OPTION GTM661W 0x9000 GTM661W product OPTION ICONEDGE 0xc031 GlobeSurfer iCON EDGE product OPTION MODHSXPA 0xd013 Globetrotter HSUPA product OPTION ICON321 0xd031 Globetrotter HSUPA product OPTION ICON505 0xd055 Globetrotter iCON 505 product OPTION ICON452 0x7901 Globetrotter iCON 452 /* Optoelectronics Co., Ltd */ product OPTO BARCODE 0x0001 Barcode Reader product OPTO OPTICONCODE 0x0009 Opticon Code Reader product OPTO BARCODE_1 0xa002 Barcode Reader product OPTO CRD7734 0xc000 USB Cradle CRD-7734-RU product OPTO CRD7734_1 0xc001 USB Cradle CRD-7734-RU /* OvisLink product */ product OVISLINK RT3072 0x3072 RT3072 /* OQO */ product OQO WIFI01 0x0002 model 01 WiFi interface product OQO BT01 0x0003 model 01 Bluetooth interface product OQO ETHER01PLUS 0x7720 model 01+ Ethernet product OQO ETHER01 0x8150 model 01 Ethernet interface /* Ours Technology Inc. */ product OTI DKU5 0x6858 DKU-5 Serial /* Owen.ru products */ product OWEN AC4 0x0004 AC4 USB-RS485 converter /* OWL producs */ product OWL CM_160 0xca05 OWL CM-160 power monitor /* Palm Computing, Inc. product */ product PALM SERIAL 0x0080 USB Serial product PALM M500 0x0001 Palm m500 product PALM M505 0x0002 Palm m505 product PALM M515 0x0003 Palm m515 product PALM I705 0x0020 Palm i705 product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z product PALM M125 0x0040 Palm m125 product PALM M130 0x0050 Palm m130 product PALM TUNGSTEN_T 0x0060 Palm Tungsten T product PALM ZIRE31 0x0061 Palm Zire 31 product PALM ZIRE 0x0070 Palm Zire /* Panasonic products */ product PANASONIC LS120CAM 0x0901 LS-120 Camera product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW product PANASONIC SDCAAE 0x1b00 MultiMediaCard product PANASONIC TYTP50P6S 0x3900 TY-TP50P6-S 50in Touch Panel /* Papouch products */ product PAPOUCH AD4USB 0x8003 FTDI compatible adapter product PAPOUCH AP485 0x0101 FTDI compatible adapter product PAPOUCH AP485_2 0x0104 FTDI compatible adapter product PAPOUCH DRAK5 0x0700 FTDI compatible adapter product PAPOUCH DRAK6 0x1000 FTDI compatible adapter product PAPOUCH GMSR 0x8005 FTDI compatible adapter product PAPOUCH GMUX 0x8004 FTDI compatible adapter product PAPOUCH IRAMP 0x0500 FTDI compatible adapter product PAPOUCH LEC 0x0300 FTDI compatible adapter product PAPOUCH MU 0x8001 FTDI compatible adapter product PAPOUCH QUIDO10X1 0x0b00 FTDI compatible adapter product PAPOUCH QUIDO2X16 0x0e00 FTDI compatible adapter product PAPOUCH QUIDO2X2 0x0a00 FTDI compatible adapter product PAPOUCH QUIDO30X3 0x0c00 FTDI compatible adapter product PAPOUCH QUIDO3X32 0x0f00 FTDI compatible adapter product PAPOUCH QUIDO4X4 0x0900 FTDI compatible adapter product PAPOUCH QUIDO60X3 0x0d00 FTDI compatible adapter product PAPOUCH QUIDO8X8 0x0800 FTDI compatible adapter product PAPOUCH SB232 0x0301 FTDI compatible adapter product PAPOUCH SB422 0x0102 FTDI compatible adapter product PAPOUCH SB422_2 0x0105 FTDI compatible adapter product PAPOUCH SB485 0x0100 FTDI compatible adapter product PAPOUCH SB485C 0x0107 FTDI compatible adapter product PAPOUCH SB485S 0x0106 FTDI compatible adapter product PAPOUCH SB485_2 0x0103 FTDI compatible adapter product PAPOUCH SIMUKEY 0x8002 FTDI compatible adapter product PAPOUCH TMU 0x0400 FTDI compatible adapter product PAPOUCH UPSUSB 0x8000 FTDI compatible adapter /* PARA Industrial products */ product PARA RT3070 0x8888 RT3070 /* Simtec Electronics products */ product SIMTEC ENTROPYKEY 0x0001 Entropy Key /* Pegatron products */ product PEGATRON RT2870 0x0002 RT2870 product PEGATRON RT3070 0x000c RT3070 product PEGATRON RT3070_2 0x000e RT3070 product PEGATRON RT3070_3 0x0010 RT3070 /* Peracom products */ product PERACOM SERIAL1 0x0001 Serial product PERACOM ENET 0x0002 Ethernet product PERACOM ENET3 0x0003 At Home Ethernet product PERACOM ENET2 0x0005 Ethernet /* Philips products */ product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System product PHILIPS HUB 0x0201 hub product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System product PHILIPS ACE1001 0x066a AKTAKOM ACE-1001 cable product PHILIPS SPE3030CC 0x083a USB 2.0 External Disk product PHILIPS SNU5600 0x1236 SNU5600 product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player product PHILIPS RT2870 0x200f RT2870 /* Philips Semiconductor products */ product PHILIPSSEMI HUB1122 0x1122 HUB /* Megatec */ product MEGATEC UPS 0x5161 Phoenixtec protocol based UPS /* P.I. Engineering products */ product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter /* Planex Communications products */ product PLANEX GW_US11H 0x14ea GW-US11H WLAN product PLANEX2 RTL8188CUS 0x1201 RTL8188CUS product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN product PLANEX2 GW_US300 0x5304 GW-US300 product PLANEX2 RTL8188CU_1 0xab2a RTL8188CU product PLANEX2 RTL8188CU_2 0xed17 RTL8188CU product PLANEX2 RTL8188CU_3 0x4902 RTL8188CU product PLANEX2 RTL8188CU_4 0xab2e RTL8188CU product PLANEX2 RTL8192CU 0xab2b RTL8192CU product PLANEX2 GWUS54HP 0xab01 GW-US54HP product PLANEX2 GWUS300MINIS 0xab24 GW-US300MiniS product PLANEX2 RT3070 0xab25 RT3070 product PLANEX2 MZKUE150N 0xab2f MZK-UE150N product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2 product PLANEX2 GWUS54SG 0xc002 GW-US54SG product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL product PLANEX2 GWUS54GD 0xed01 GW-US54GD product PLANEX2 GWUSMM 0xed02 GW-USMM product PLANEX2 RT2870 0xed06 RT2870 product PLANEX2 GWUSMICRON 0xed14 GW-USMicroN product PLANEX2 GWUSVALUEEZ 0xed17 GW-USValue-EZ product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ product PLANEX3 GU1000T 0xab11 GU-1000T product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini product PLANEX2 GWUSNANO 0xab28 GW-USNano /* Plextor Corp. */ product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U /* PLX products */ product PLX TESTBOARD 0x9060 test board product PLX CA42 0xac70 CA-42 /* PNY products */ product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive /* PortGear products */ product PORTGEAR EA8 0x0008 Ethernet product PORTGEAR EA9 0x0009 Ethernet /* Portsmith products */ product PORTSMITH EEA 0x3003 Express Ethernet /* Posiflex products */ product POSIFLEX PP7000 0x0300 FTDI compatible adapter /* Primax products */ product PRIMAX G2X300 0x0300 G2-200 scanner product PRIMAX G2E300 0x0301 G2E-300 scanner product PRIMAX G2300 0x0302 G2-300 scanner product PRIMAX G2E3002 0x0303 G2E-300 scanner product PRIMAX 9600 0x0340 Colorado USB 9600 scanner product PRIMAX 600U 0x0341 Colorado 600u scanner product PRIMAX 6200 0x0345 Visioneer 6200 scanner product PRIMAX 19200 0x0360 Colorado USB 19200 scanner product PRIMAX 1200U 0x0361 Colorado 1200u scanner product PRIMAX G600 0x0380 G2-600 scanner product PRIMAX 636I 0x0381 ReadyScan 636i product PRIMAX G2600 0x0382 G2-600 scanner product PRIMAX G2E600 0x0383 G2E-600 scanner product PRIMAX COMFORT 0x4d01 Comfort product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1 product PRIMAX HP_RH304AA 0x4d17 HP RH304AA mouse /* Prolific products */ product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface product PROLIFIC MOTOROLA 0x0307 Motorola Cable product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2) product PROLIFIC ALLTRONIX_GPRS 0x0609 Alltronix ACM003U00 modem product PROLIFIC ALDIGA_AL11U 0x0611 AlDiga AL-11U modem product PROLIFIC MICROMAX_610U 0x0612 Micromax 610U product PROLIFIC DCU11 0x1234 DCU-11 Phone Cable product PROLIFIC UIC_MSR206 0x206a UIC MSR206 Card Reader product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A) product PROLIFIC PL2305 0x2305 Parallel printer product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface product PROLIFIC PL2506 0x2506 PL2506 USB to IDE Bridge product PROLIFIC HCR331 0x331a HCR331 Hybrid Card Reader product PROLIFIC PHAROS 0xaaa0 Prolific Pharos product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3) product PROLIFIC2 PL2303 0x2303 PL2303 Serial Adapter /* Putercom products */ product PUTERCOM UPA100 0x047e USB-1284 BRIDGE /* Qcom products */ product QCOM RT2573 0x6196 RT2573 product QCOM RT2573_2 0x6229 RT2573 product QCOM RT2573_3 0x6238 RT2573 product QCOM RT2870 0x6259 RT2870 /* QI-hardware */ product QIHARDWARE JTAGSERIAL 0x0713 FTDI compatible adapter /* Qisda products */ product QISDA H21_1 0x4512 3G modem product QISDA H21_2 0x4523 3G modem product QISDA H20_1 0x4515 3G modem product QISDA H20_2 0x4519 3G modem /* Qualcomm products */ product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone product QUALCOMM NTT_L02C_MODEM 0x618f NTT DOCOMO L-02C product QUALCOMM NTT_L02C_STORAGE 0x61dd NTT DOCOMO L-02C product QUALCOMM2 MF330 0x6613 MF330 product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem product QUALCOMM2 AC8700 0x6000 AC8700 product QUALCOMM2 VW110L 0x1000 Vertex Wireless 110L modem product QUALCOMM2 SIM5218 0x9000 SIM5218 product QUALCOMM2 WM620 0x9002 Neoway WM620 product QUALCOMM2 GOBI2000_QDL 0x9204 Qualcomm Gobi 2000 QDL product QUALCOMM2 GOBI2000 0x9205 Qualcomm Gobi 2000 modem product QUALCOMM2 VT80N 0x6500 Venus VT80N product QUALCOMM3 VFAST2 0x9909 Venus Fast2 modem product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem product QUALCOMMINC E0002 0x0002 3G modem product QUALCOMMINC E0003 0x0003 3G modem product QUALCOMMINC E0004 0x0004 3G modem product QUALCOMMINC E0005 0x0005 3G modem product QUALCOMMINC E0006 0x0006 3G modem product QUALCOMMINC E0007 0x0007 3G modem product QUALCOMMINC E0008 0x0008 3G modem product QUALCOMMINC E0009 0x0009 3G modem product QUALCOMMINC E000A 0x000a 3G modem product QUALCOMMINC E000B 0x000b 3G modem product QUALCOMMINC E000C 0x000c 3G modem product QUALCOMMINC E000D 0x000d 3G modem product QUALCOMMINC E000E 0x000e 3G modem product QUALCOMMINC E000F 0x000f 3G modem product QUALCOMMINC E0010 0x0010 3G modem product QUALCOMMINC E0011 0x0011 3G modem product QUALCOMMINC E0012 0x0012 3G modem product QUALCOMMINC E0013 0x0013 3G modem product QUALCOMMINC E0014 0x0014 3G modem product QUALCOMMINC MF628 0x0015 3G modem product QUALCOMMINC MF633R 0x0016 ZTE WCDMA modem product QUALCOMMINC E0017 0x0017 3G modem product QUALCOMMINC E0018 0x0018 3G modem product QUALCOMMINC E0019 0x0019 3G modem product QUALCOMMINC E0020 0x0020 3G modem product QUALCOMMINC E0021 0x0021 3G modem product QUALCOMMINC E0022 0x0022 3G modem product QUALCOMMINC E0023 0x0023 3G modem product QUALCOMMINC E0024 0x0024 3G modem product QUALCOMMINC E0025 0x0025 3G modem product QUALCOMMINC E0026 0x0026 3G modem product QUALCOMMINC E0027 0x0027 3G modem product QUALCOMMINC E0028 0x0028 3G modem product QUALCOMMINC E0029 0x0029 3G modem product QUALCOMMINC E0030 0x0030 3G modem product QUALCOMMINC MF626 0x0031 3G modem product QUALCOMMINC E0032 0x0032 3G modem product QUALCOMMINC E0033 0x0033 3G modem product QUALCOMMINC E0037 0x0037 3G modem product QUALCOMMINC E0039 0x0039 3G modem product QUALCOMMINC E0042 0x0042 3G modem product QUALCOMMINC E0043 0x0043 3G modem product QUALCOMMINC E0048 0x0048 3G modem product QUALCOMMINC E0049 0x0049 3G modem product QUALCOMMINC E0051 0x0051 3G modem product QUALCOMMINC E0052 0x0052 3G modem product QUALCOMMINC ZTE_STOR2 0x0053 USB ZTE Storage product QUALCOMMINC E0054 0x0054 3G modem product QUALCOMMINC E0055 0x0055 3G modem product QUALCOMMINC E0057 0x0057 3G modem product QUALCOMMINC E0058 0x0058 3G modem product QUALCOMMINC E0059 0x0059 3G modem product QUALCOMMINC E0060 0x0060 3G modem product QUALCOMMINC E0061 0x0061 3G modem product QUALCOMMINC E0062 0x0062 3G modem product QUALCOMMINC E0063 0x0063 3G modem product QUALCOMMINC E0064 0x0064 3G modem product QUALCOMMINC E0066 0x0066 3G modem product QUALCOMMINC E0069 0x0069 3G modem product QUALCOMMINC E0070 0x0070 3G modem product QUALCOMMINC E0073 0x0073 3G modem product QUALCOMMINC E0076 0x0076 3G modem product QUALCOMMINC E0078 0x0078 3G modem product QUALCOMMINC E0082 0x0082 3G modem product QUALCOMMINC E0086 0x0086 3G modem product QUALCOMMINC MF112 0x0103 3G modem product QUALCOMMINC SURFSTICK 0x0117 1&1 Surf Stick product QUALCOMMINC K3772_Z_INIT 0x1179 K3772-Z Initial product QUALCOMMINC K3772_Z 0x1181 K3772-Z product QUALCOMMINC ZTE_MF730M 0x1420 3G modem product QUALCOMMINC MF195E_INIT 0x1514 MF195E initial product QUALCOMMINC MF195E 0x1516 MF195E product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage product QUALCOMMINC E2002 0x2002 3G modem product QUALCOMMINC E2003 0x2003 3G modem product QUALCOMMINC AC682 0xffdd CDMA 1xEVDO USB modem product QUALCOMMINC AC682_INIT 0xffde CDMA 1xEVDO USB modem (initial) product QUALCOMMINC AC8710 0xfff1 3G modem product QUALCOMMINC AC2726 0xfff5 3G modem product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB modem /* Quanta products */ product QUANTA RW6815_1 0x00ce HP iPAQ rw6815 product QUANTA RT3070 0x0304 RT3070 product QUANTA Q101_STOR 0x1000 USB Q101 Storage product QUANTA Q101 0xea02 HSDPA modem product QUANTA Q111 0xea03 HSDPA modem product QUANTA GLX 0xea04 HSDPA modem product QUANTA GKE 0xea05 HSDPA modem product QUANTA GLE 0xea06 HSDPA modem product QUANTA RW6815R 0xf003 HP iPAQ rw6815 RNDIS /* Qtronix products */ product QTRONIX 980N 0x2011 Scorpion-980N keyboard /* Quickshot products */ product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad /* Radio Shack */ product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable /* Rainbow Technologies products */ product RAINBOW IKEY2000 0x1200 i-Key 2000 /* Ralink Technology products */ product RALINK RT2570 0x1706 RT2500USB Wireless Adapter product RALINK RT2070 0x2070 RT2070 product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter product RALINK RT2573 0x2573 RT2501USB Wireless Adapter product RALINK RT2671 0x2671 RT2601USB Wireless Adapter product RALINK RT2770 0x2770 RT2770 product RALINK RT2870 0x2870 RT2870 product RALINK RT_STOR 0x2878 USB Storage product RALINK RT3070 0x3070 RT3070 product RALINK RT3071 0x3071 RT3071 product RALINK RT3072 0x3072 RT3072 product RALINK RT3370 0x3370 RT3370 product RALINK RT3572 0x3572 RT3572 product RALINK RT3573 0x3573 RT3573 product RALINK RT5370 0x5370 RT5370 product RALINK RT5572 0x5572 RT5572 product RALINK RT8070 0x8070 RT8070 product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter /* RATOC Systems products */ product RATOC REXUSB60 0xb000 USB serial adapter REX-USB60 product RATOC REXUSB60F 0xb020 USB serial adapter REX-USB60F /* Realtek products */ /* Green House and CompUSA OEM this part */ product REALTEK DUMMY 0x0000 Dummy product product REALTEK USB20CRW 0x0158 USB20CRW Card Reader product REALTEK RTL8188ETV 0x0179 RTL8188ETV product REALTEK RTL8188CTV 0x018a RTL8188CTV product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet product REALTEK RTL8152 0x8152 RTL8152 USB Ethernet product REALTEK RTL8153 0x8153 RTL8153 USB Ethernet product REALTEK RTL8188CE_0 0x8170 RTL8188CE product REALTEK RTL8171 0x8171 RTL8171 product REALTEK RTL8172 0x8172 RTL8172 product REALTEK RTL8173 0x8173 RTL8173 product REALTEK RTL8174 0x8174 RTL8174 product REALTEK RTL8188CU_0 0x8176 RTL8188CU product REALTEK RTL8188EU 0x8179 RTL8188EU product REALTEK RTL8188CE_1 0x817e RTL8188CE product REALTEK RTL8188CU_1 0x817a RTL8188CU product REALTEK RTL8188CU_2 0x817b RTL8188CU product REALTEK RTL8187 0x8187 RTL8187 Wireless Adapter product REALTEK RTL8187B_0 0x8189 RTL8187B Wireless Adapter product REALTEK RTL8188CU_3 0x8191 RTL8188CU product REALTEK RTL8196EU 0x8196 RTL8196EU product REALTEK RTL8187B_1 0x8197 RTL8187B Wireless Adapter product REALTEK RTL8187B_2 0x8198 RTL8187B Wireless Adapter product REALTEK RTL8188CUS 0x818a RTL8188CUS product REALTEK RTL8188CU_COMBO 0x8754 RTL8188CU product REALTEK RTL8191CU 0x8177 RTL8191CU product REALTEK RTL8192CU 0x8178 RTL8192CU product REALTEK RTL8192CE 0x817c RTL8192CE product REALTEK RTL8188RU_1 0x817d RTL8188RU product REALTEK RTL8188RU_3 0x817f RTL8188RU product REALTEK RTL8712 0x8712 RTL8712 product REALTEK RTL8713 0x8712 RTL8713 product REALTEK RTL8188RU_2 0x317f RTL8188RU product REALTEK RTL8192SU 0xc512 RTL8192SU /* RedOctane products */ product REDOCTANE DUMMY 0x0000 Dummy product product REDOCTANE GHMIDI 0x474b GH MIDI INTERFACE /* Renesas products */ product RENESAS RX610 0x0053 RX610 RX-Stick /* Ricoh products */ product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera product RICOH VGPVCC7 0x183a VGP-VCC7 Camera product RICOH VGPVCC8 0x183b VGP-VCC8 Camera /* Reiner-SCT products */ product REINERSCT CYBERJACK_ECOM 0x0100 e-com cyberJack /* Roland products */ product ROLAND UA100 0x0000 UA-100 Audio I/F product ROLAND UM4 0x0002 UM-4 MIDI I/F product ROLAND SC8850 0x0003 SC-8850 MIDI Synth product ROLAND U8 0x0004 U-8 Audio I/F product ROLAND UM2 0x0005 UM-2 MIDI I/F product ROLAND SC8820 0x0007 SC-8820 MIDI Synth product ROLAND PC300 0x0008 PC-300 MIDI Keyboard product ROLAND UM1 0x0009 UM-1 MIDI I/F product ROLAND SK500 0x000b SK-500 MIDI Keyboard product ROLAND SCD70 0x000c SC-D70 MIDI Synth product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native) product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic) product ROLAND SD90 0x0016 SD-90 MIDI Synth product ROLAND UM550 0x0023 UM-550 MIDI I/F product ROLAND SD20 0x0027 SD-20 MIDI Synth product ROLAND SD80 0x0029 SD-80 MIDI Synth product ROLAND UA700 0x002b UA-700 Audio I/F /* Rockfire products */ product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB /* RATOC Systems products */ product RATOC REXUSB60 0xb000 REX-USB60 product RATOC REXUSB60F 0xb020 REX-USB60F /* RT system products */ product RTSYSTEMS CT29B 0x9e54 FTDI compatible adapter product RTSYSTEMS SERIAL_VX7 0x9e52 FTDI compatible adapter /* Sagem products */ product SAGEM USBSERIAL 0x0027 USB-Serial Controller product SAGEM XG760A 0x004a XG-760A product SAGEM XG76NA 0x0062 XG-76NA /* Samsung products */ product SAMSUNG WIS09ABGN 0x2018 WIS09ABGN Wireless LAN adapter product SAMSUNG ML6060 0x3008 ML-6060 laser printer product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player product SAMSUNG YP_U4 0x5092 YP-U4 MP3 Player product SAMSUNG I500 0x6601 I500 Palm USB Phone product SAMSUNG I330 0x8001 I330 phone cradle product SAMSUNG2 RT2870_1 0x2018 RT2870 /* Samsung Techwin products */ product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410 /* SanDisk products */ product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a product SANDISK SDDR31 0x0002 ImageMate SDDR-31 product SANDISK SDDR05 0x0005 ImageMate SDDR-05 product SANDISK SDDR12 0x0100 ImageMate SDDR-12 product SANDISK SDDR09 0x0200 ImageMate SDDR-09 product SANDISK SDDR75 0x0810 ImageMate SDDR-75 product SANDISK SDCZ2_128 0x7100 Cruzer Mini 128MB product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB product SANDISK IMAGEMATE_SDDR289 0xb6ba ImageMate SDDR-289 /* Sanwa Electric Instrument Co., Ltd. products */ product SANWA KB_USB2 0x0701 KB-USB2 multimeter cable /* Sanyo Electric products */ product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone /* ScanLogic products */ product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner /* Schweitzer Engineering Laboratories products */ product SEL C662 0x0001 C662 Cable /* Sealevel products */ product SEALEVEL 2101 0x2101 FTDI compatible adapter product SEALEVEL 2102 0x2102 FTDI compatible adapter product SEALEVEL 2103 0x2103 FTDI compatible adapter product SEALEVEL 2104 0x2104 FTDI compatible adapter product SEALEVEL 2106 0x9020 FTDI compatible adapter product SEALEVEL 2201_1 0x2211 FTDI compatible adapter product SEALEVEL 2201_2 0x2221 FTDI compatible adapter product SEALEVEL 2202_1 0x2212 FTDI compatible adapter product SEALEVEL 2202_2 0x2222 FTDI compatible adapter product SEALEVEL 2203_1 0x2213 FTDI compatible adapter product SEALEVEL 2203_2 0x2223 FTDI compatible adapter product SEALEVEL 2401_1 0x2411 FTDI compatible adapter product SEALEVEL 2401_2 0x2421 FTDI compatible adapter product SEALEVEL 2401_3 0x2431 FTDI compatible adapter product SEALEVEL 2401_4 0x2441 FTDI compatible adapter product SEALEVEL 2402_1 0x2412 FTDI compatible adapter product SEALEVEL 2402_2 0x2422 FTDI compatible adapter product SEALEVEL 2402_3 0x2432 FTDI compatible adapter product SEALEVEL 2402_4 0x2442 FTDI compatible adapter product SEALEVEL 2403_1 0x2413 FTDI compatible adapter product SEALEVEL 2403_2 0x2423 FTDI compatible adapter product SEALEVEL 2403_3 0x2433 FTDI compatible adapter product SEALEVEL 2403_4 0x2443 FTDI compatible adapter product SEALEVEL 2801_1 0x2811 FTDI compatible adapter product SEALEVEL 2801_2 0x2821 FTDI compatible adapter product SEALEVEL 2801_3 0x2831 FTDI compatible adapter product SEALEVEL 2801_4 0x2841 FTDI compatible adapter product SEALEVEL 2801_5 0x2851 FTDI compatible adapter product SEALEVEL 2801_6 0x2861 FTDI compatible adapter product SEALEVEL 2801_7 0x2871 FTDI compatible adapter product SEALEVEL 2801_8 0x2881 FTDI compatible adapter product SEALEVEL 2802_1 0x2812 FTDI compatible adapter product SEALEVEL 2802_2 0x2822 FTDI compatible adapter product SEALEVEL 2802_3 0x2832 FTDI compatible adapter product SEALEVEL 2802_4 0x2842 FTDI compatible adapter product SEALEVEL 2802_5 0x2852 FTDI compatible adapter product SEALEVEL 2802_6 0x2862 FTDI compatible adapter product SEALEVEL 2802_7 0x2872 FTDI compatible adapter product SEALEVEL 2802_8 0x2882 FTDI compatible adapter product SEALEVEL 2803_1 0x2813 FTDI compatible adapter product SEALEVEL 2803_2 0x2823 FTDI compatible adapter product SEALEVEL 2803_3 0x2833 FTDI compatible adapter product SEALEVEL 2803_4 0x2843 FTDI compatible adapter product SEALEVEL 2803_5 0x2853 FTDI compatible adapter product SEALEVEL 2803_6 0x2863 FTDI compatible adapter product SEALEVEL 2803_7 0x2873 FTDI compatible adapter product SEALEVEL 2803_8 0x2883 FTDI compatible adapter /* Senao products */ product SENAO RT2870_3 0x0605 RT2870 product SENAO RT2870_4 0x0615 RT2870 product SENAO NUB8301 0x2000 NUB-8301 product SENAO RT2870_1 0x9701 RT2870 product SENAO RT2870_2 0x9702 RT2870 product SENAO RT3070 0x9703 RT3070 product SENAO RT3071 0x9705 RT3071 product SENAO RT3072_1 0x9706 RT3072 product SENAO RT3072_2 0x9707 RT3072 product SENAO RT3072_3 0x9708 RT3072 product SENAO RT3072_4 0x9709 RT3072 product SENAO RT3072_5 0x9801 RT3072 product SENAO RTL8192SU_1 0x9603 RTL8192SU product SENAO RTL8192SU_2 0x9605 RTL8192SU /* ShanTou products */ product SHANTOU ST268 0x0268 ST268 product SHANTOU DM9601 0x9601 DM 9601 product SHANTOU ADM8515 0x8515 ADM8515 /* Shark products */ product SHARK PA 0x0400 Pocket Adapter /* Sharp products */ product SHARP SL5500 0x8004 Zaurus SL-5500 PDA product SHARP SLA300 0x8005 Zaurus SL-A300 PDA product SHARP SL5600 0x8006 Zaurus SL-5600 PDA product SHARP SLC700 0x8007 Zaurus SL-C700 PDA product SHARP SLC750 0x9031 Zaurus SL-C750 PDA product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone product SHARP WZERO3ADES 0x91ac Advanced W-ZERO3 ES Smartphone product SHARP WILLCOM03 0x9242 WILLCOM03 /* Shuttle Technology products */ product SHUTTLE EUSB 0x0001 E-USB Bridge product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge product SHUTTLE SDDR09 0x0003 ImageMate SDDR09 product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter product SHUTTLE HIFD 0x0007 Sony Hifd product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter product SHUTTLE CF 0x000a eUSB CompactFlash Adapter product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge product SHUTTLE CDRW 0x0101 CD-RW Device product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader /* Siemens products */ product SIEMENS SPEEDSTREAM 0x1001 SpeedStream product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022 product SIEMENS2 WLL013 0x001b WLL013 product SIEMENS2 ES75 0x0034 GSM module MC35 product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter product SIEMENS3 SX1 0x0001 SX1 product SIEMENS3 X65 0x0003 X65 product SIEMENS3 X75 0x0004 X75 product SIEMENS3 EF81 0x0005 EF81 /* Sierra Wireless products */ product SIERRA EM5625 0x0017 EM5625 product SIERRA MC5720_2 0x0018 MC5720 product SIERRA MC5725 0x0020 MC5725 product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E product SIERRA EM5725 0x0022 EM5725 product SIERRA C597 0x0023 Sierra Wireless Compass 597 product SIERRA MC5727 0x0024 MC5727 product SIERRA T598 0x0025 T598 product SIERRA T11 0x0026 T11 product SIERRA AC402 0x0027 AC402 product SIERRA MC5728 0x0028 MC5728 product SIERRA E0029 0x0029 E0029 product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA MC5720 0x0218 MC5720 Wireless Modem product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275 product SIERRA MC5727_2 0x0224 MC5727 product SIERRA MC8755_2 0x6802 MC8755 product SIERRA MC8765 0x6803 MC8765 product SIERRA MC8755 0x6804 MC8755 product SIERRA MC8765_2 0x6805 MC8765 product SIERRA MC8755_4 0x6808 MC8755 product SIERRA MC8765_3 0x6809 MC8765 product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem product SIERRA MC8755_3 0x6813 MC8755 HSDPA product SIERRA MC8775_2 0x6815 MC8775 product SIERRA MC8775 0x6816 MC8775 product SIERRA AC875 0x6820 Sierra Wireless AirCard 875 product SIERRA AC875U_2 0x6821 AC875U product SIERRA AC875E 0x6822 AC875E product SIERRA MC8780 0x6832 MC8780 product SIERRA MC8781 0x6833 MC8781 product SIERRA MC8780_2 0x6834 MC8780 product SIERRA MC8781_2 0x6835 MC8781 product SIERRA MC8780_3 0x6838 MC8780 product SIERRA MC8781_3 0x6839 MC8781 product SIERRA MC8785 0x683A MC8785 product SIERRA MC8785_2 0x683B MC8785 product SIERRA MC8790 0x683C MC8790 product SIERRA MC8791 0x683D MC8791 product SIERRA MC8792 0x683E MC8792 product SIERRA AC880 0x6850 Sierra Wireless AirCard 880 product SIERRA AC881 0x6851 Sierra Wireless AirCard 881 product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U product SIERRA AC885E 0x6859 AC885E product SIERRA AC885E_2 0x685A AC885E product SIERRA AC885U 0x6880 Sierra Wireless AirCard 885U product SIERRA C888 0x6890 C888 product SIERRA C22 0x6891 C22 product SIERRA E6892 0x6892 E6892 product SIERRA E6893 0x6893 E6893 product SIERRA MC8700 0x68A3 MC8700 product SIERRA MC7354 0x68C0 MC7354 product SIERRA MC7355 0x9041 MC7355 product SIERRA AC313U 0x68aa Sierra Wireless AirCard 313U product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer /* Sigmatel products */ product SIGMATEL WBT_3052 0x4200 WBT-3052 IrDA/USB Bridge product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player /* SIIG products */ /* Also: Omnidirectional Control Technology products */ product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader product SIIG WINTERREADER 0x0330 WINTERREADER Reader product SIIG2 DK201 0x0103 FTDI compatible adapter product SIIG2 USBTOETHER 0x0109 USB TO Ethernet product SIIG2 US2308 0x0421 Serial /* Silicom products */ product SILICOM U2E 0x0001 U2E product SILICOM GPE 0x0002 Psion Gold Port Ethernet /* SI Labs */ product SILABS VSTABI 0x0f91 VStabi Controller product SILABS ARKHAM_DS101_M 0x1101 Arkham DS101 Monitor product SILABS ARKHAM_DS101_A 0x1601 Arkham DS101 Adapter product SILABS BSM7DUSB 0x800a SPORTident BSM7-D USB product SILABS POLOLU 0x803b Pololu Serial product SILABS CYGNAL_DEBUG 0x8044 Cygnal Debug Adapter product SILABS SB_PARAMOUNT_ME 0x8043 Software Bisque Paramount ME product SILABS SAEL 0x8053 SA-EL USB product SILABS GSM2228 0x8054 Enfora GSM2228 USB product SILABS ARGUSISP 0x8066 Argussoft ISP product SILABS IMS_USB_RS422 0x806f IMS USB-RS422 product SILABS CRUMB128 0x807a Crumb128 board product SILABS OPTRIS_MSPRO 0x80c4 Optris MSpro LT Thermometer product SILABS DEGREE 0x80ca Degree Controls Inc product SILABS TRACIENT 0x80dd Tracient RFID product SILABS TRAQMATE 0x80ed Track Systems Traqmate product SILABS SUUNTO 0x80f6 Suunto Sports Instrument product SILABS ARYGON_MIFARE 0x8115 Arygon Mifare RFID reader product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile product SILABS TAMSMASTER 0x813f Tams Master Easy Control product SILABS WMRBATT 0x814a WMR RIGblaster Plug&Play product SILABS WMRRIGBLASTER 0x814a WMR RIGblaster Plug&Play product SILABS WMRRIGTALK 0x814b WMR RIGtalk RT1 product SILABS B_G_H3000 0x8156 B&G H3000 Data Cable product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM product SILABS HAMLINKUSB 0x815f Timewave HamLinkUSB product SILABS AVIT_USB_TTL 0x818b AVIT Research USB-TTL product SILABS MJS_TOSLINK 0x819f MJS USB-TOSLINK product SILABS WAVIT 0x81a6 ThinkOptics WavIt product SILABS MULTIPLEX_RC 0x81a9 Multiplex RC adapter product SILABS MSD_DASHHAWK 0x81ac MSD DashHawk product SILABS INSYS_MODEM 0x81ad INSYS Modem product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN product SILABS AEROCOMM 0x81e7 Aerocomm Radio product SILABS ZEPHYR_BIO 0x81e8 Zephyr Bioharness product SILABS EMS_C1007 0x81f2 EMS C1007 HF RFID controller product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1 product SILABS C2_EDGE_MODEM 0x822b Commander 2 EDGE(GSM) Modem product SILABS CYGNAL_GPS 0x826b Cygnal Fasttrax GPS product SILABS TELEGESIS_ETRX2 0x8293 Telegesis ETRX2USB product SILABS PROCYON_AVS 0x82f9 Procyon AVS product SILABS MC35PU 0x8341 MC35pu product SILABS CYGNAL 0x8382 Cygnal product SILABS AMBER_AMB2560 0x83a8 Amber Wireless AMB2560 product SILABS DEKTEK_DTAPLUS 0x83d8 DekTec DTA Plus VHF/UHF Booster product SILABS KYOCERA_GPS 0x8411 Kyocera GPS product SILABS IRZ_SG10 0x8418 IRZ SG-10 GSM/GPRS Modem product SILABS BEI_VCP 0x846e BEI USB Sensor (VCP) product SILABS BALLUFF_RFID 0x8477 Balluff RFID reader product SILABS AC_SERV_IBUS 0x85ea AC-Services IBUS Interface product SILABS AC_SERV_CIS 0x85eb AC-Services CIS-IBUS product SILABS V_PREON32 0x85f8 Virtenio Preon32 product SILABS AC_SERV_CAN 0x8664 AC-Services CAN Interface product SILABS AC_SERV_OBD 0x8665 AC-Services OBD Interface product SILABS MMB_ZIGBEE 0x88a4 MMB Networks ZigBee product SILABS INGENI_ZIGBEE 0x88a5 Planet Innovation Ingeni ZigBee product SILABS CP2102 0xea60 SILABS USB UART product SILABS CP210X_2 0xea61 CP210x Serial product SILABS CP210X_3 0xea70 CP210x Serial product SILABS CP210X_4 0xea80 CP210x Serial product SILABS INFINITY_MIC 0xea71 Infinity GPS-MIC-1 Radio Monophone product SILABS USBSCOPE50 0xf001 USBscope50 product SILABS USBWAVE12 0xf002 USBwave12 product SILABS USBPULSE100 0xf003 USBpulse100 product SILABS USBCOUNT50 0xf004 USBcount50 product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone product SILABS3 GPRS_MODEM 0xea61 GPRS Modem product SILABS4 100EU_MODEM 0xea61 GPRS Modem 100EU /* Silicon Portals Inc. */ product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware) product SILICONPORTALS YAPPHONE 0x0201 YAP Phone /* Sirius Technologies products */ product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB /* Sitecom products */ product SITECOM LN029 0x182d USB 2.0 Ethernet product SITECOM SERIAL 0x2068 USB to serial cable (v2) product SITECOM2 WL022 0x182d WL-022 /* Sitecom Europe products */ product SITECOMEU RT2870_1 0x0017 RT2870 product SITECOMEU WL168V1 0x000d WL-168 v1 product SITECOMEU LN030 0x0021 MCS7830 product SITECOMEU WL168V4 0x0028 WL-168 v4 product SITECOMEU RT2870_2 0x002b RT2870 product SITECOMEU RT2870_3 0x002c RT2870 product SITECOMEU RT2870_4 0x002d RT2870 product SITECOMEU RT2770 0x0039 RT2770 product SITECOMEU RT3070_2 0x003b RT3070 product SITECOMEU RT3070_3 0x003c RT3070 product SITECOMEU RT3070_4 0x003d RT3070 product SITECOMEU RT3070 0x003e RT3070 product SITECOMEU WL608 0x003f WL-608 product SITECOMEU RT3071 0x0040 RT3071 product SITECOMEU RT3072_1 0x0041 RT3072 product SITECOMEU RT3072_2 0x0042 RT3072 product SITECOMEU WL353 0x0045 WL-353 product SITECOMEU RT3072_3 0x0047 RT3072 product SITECOMEU RT3072_4 0x0048 RT3072 product SITECOMEU RT3072_5 0x004a RT3072 product SITECOMEU WL349V1 0x004b WL-349 v1 product SITECOMEU RT3072_6 0x004d RT3072 product SITECOMEU RTL8188CU_1 0x0052 RTL8188CU product SITECOMEU RTL8188CU_2 0x005c RTL8188CU product SITECOMEU RTL8192CU 0x0061 RTL8192CU product SITECOMEU LN032 0x0072 LN-032 product SITECOMEU LN031 0x0056 LN-031 product SITECOMEU LN028 0x061c LN-028 product SITECOMEU WL113 0x9071 WL-113 product SITECOMEU ZD1211B 0x9075 ZD1211B product SITECOMEU WL172 0x90ac WL-172 product SITECOMEU WL113R2 0x9712 WL-113 rev 2 /* Skanhex Technology products */ product SKANHEX MD_7425 0x410a MD 7425 Camera product SKANHEX SX_520Z 0x5200 SX 520z Camera /* Smart Technologies products */ product SMART PL2303 0x2303 Serial adapter /* SmartBridges products */ product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet /* SMC products */ product SMC 2102USB 0x0100 10Mbps Ethernet product SMC 2202USB 0x0200 10/100 Ethernet product SMC 2206USB 0x0201 EZ Connect USB Ethernet product SMC 2862WG 0xee13 EZ Connect Wireless Adapter product SMC2 2020HUB 0x2020 USB Hub product SMC2 2514HUB 0x2514 USB Hub product SMC3 2662WUSB 0xa002 2662W-AR Wireless product SMC2 LAN9500_ETH 0x9500 USB/Ethernet product SMC2 LAN9505_ETH 0x9505 USB/Ethernet product SMC2 LAN9530_ETH 0x9530 USB/Ethernet product SMC2 LAN9730_ETH 0x9730 USB/Ethernet product SMC2 LAN9500_SAL10 0x9900 USB/Ethernet product SMC2 LAN9505_SAL10 0x9901 USB/Ethernet product SMC2 LAN9500A_SAL10 0x9902 USB/Ethernet product SMC2 LAN9505A_SAL10 0x9903 USB/Ethernet product SMC2 LAN9514_SAL10 0x9904 USB/Ethernet product SMC2 LAN9500A_HAL 0x9905 USB/Ethernet product SMC2 LAN9505A_HAL 0x9906 USB/Ethernet product SMC2 LAN9500_ETH_2 0x9907 USB/Ethernet product SMC2 LAN9500A_ETH_2 0x9908 USB/Ethernet product SMC2 LAN9514_ETH_2 0x9909 USB/Ethernet product SMC2 LAN9500A_ETH 0x9e00 USB/Ethernet product SMC2 LAN9505A_ETH 0x9e01 USB/Ethernet product SMC2 LAN89530_ETH 0x9e08 USB/Ethernet product SMC2 LAN9514_ETH 0xec00 USB/Ethernet /* SOHOware products */ product SOHOWARE NUB100 0x9100 10/100 USB Ethernet product SOHOWARE NUB110 0x9110 10/100 USB Ethernet /* SOLID YEAR products */ product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard /* SONY products */ product SONY DSC 0x0010 DSC cameras product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7 product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2 product SONY MSACUS1 0x002d Memorystick MSAC-US1 product SONY HANDYCAM 0x002e Handycam product SONY MSC 0x0032 MSC memory stick slot product SONY CLIE_35 0x0038 Sony Clie v3.5 product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick product SONY CLIE_40 0x0066 Sony Clie v4.0 product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03 product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot product SONY CLIE_S360 0x0095 Sony Clie s360 product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot product SONY CLIE_41 0x009a Sony Clie v4.1 product SONY CLIE_NX60 0x00da Sony Clie nx60 product SONY CLIE_TH55 0x0144 Sony Clie th55 product SONY CLIE_TJ37 0x0169 Sony Clie tj37 product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1 product SONY QN3 0x0437 Sony QN3 CMD-Jxx phone cable /* Sony Ericsson products */ product SONYERICSSON DCU10 0x0528 DCU-10 Phone Data Cable product SONYERICSSON DATAPILOT 0x2003 Datapilot Phone Cable /* SOURCENEXT products */ product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8 product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger /* SparkLAN products */ product SPARKLAN RT2573 0x0004 RT2573 product SPARKLAN RT2870_1 0x0006 RT2870 product SPARKLAN RT3070 0x0010 RT3070 /* Soundgraph products */ product SOUNDGRAPH IMON_VFD 0x0044 Antec Veris Elite VFD Panel, Knob, and Remote product SOUNDGRAPH SSTONE_LC16 0xffdc Silverstone LC16 VFD Panel, Knob, and Remote /* Speed Dragon Multimedia products */ product SPEEDDRAGON MS3303H 0x110b MS3303H Serial /* Sphairon Access Systems GmbH products */ product SPHAIRON UB801R 0x0110 UB801R /* Stelera Wireless products */ product STELERA ZEROCD 0x1000 Zerocd Installer product STELERA C105 0x1002 Stelera/Bandrish C105 USB product STELERA E1003 0x1003 3G modem product STELERA E1004 0x1004 3G modem product STELERA E1005 0x1005 3G modem product STELERA E1006 0x1006 3G modem product STELERA E1007 0x1007 3G modem product STELERA E1008 0x1008 3G modem product STELERA E1009 0x1009 3G modem product STELERA E100A 0x100a 3G modem product STELERA E100B 0x100b 3G modem product STELERA E100C 0x100c 3G modem product STELERA E100D 0x100d 3G modem product STELERA E100E 0x100e 3G modem product STELERA E100F 0x100f 3G modem product STELERA E1010 0x1010 3G modem product STELERA E1011 0x1011 3G modem product STELERA E1012 0x1012 3G modem /* STMicroelectronics products */ product STMICRO BIOCPU 0x2016 Biometric Coprocessor product STMICRO COMMUNICATOR 0x7554 USB Communicator product STMICRO ST72682 0xfada USB 2.0 Flash drive controller /* STSN products */ product STSN STSN0001 0x0001 Internet Access Device /* SUN Corporation products */ product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2 product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1 product SUNTAC VS10U 0x0009 SUNTAC Slipper U product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3 product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4 /* Sun Microsystems products */ product SUN KEYBOARD_TYPE_6 0x0005 Type 6 USB keyboard product SUN KEYBOARD_TYPE_7 0x00a2 Type 7 USB keyboard /* XXX The above is a North American PC style keyboard possibly */ product SUN MOUSE 0x0100 Type 6 USB mouse product SUN KBD_HUB 0x100e Kbd Hub /* Sunplus Innovation Technology Inc. products */ product SUNPLUS USBMOUSE 0x0007 USB Optical Mouse /* Super Top products */ product SUPERTOP IDE 0x6600 USB-IDE product SUPERTOP FLASHDRIVE 0x121c extrememory Snippy /* Syntech products */ product SYNTECH CPT8001C 0x0001 CPT-8001C Barcode scanner product SYNTECH CYPHERLAB100 0x1000 CipherLab USB Barcode Scanner /* Teclast products */ product TECLAST TLC300 0x3203 USB Media Player /* Testo products */ product TESTO USB_INTERFACE 0x0001 FTDI compatible adapter /* TexTech products */ product TEXTECH DUMMY 0x0000 Dummy product product TEXTECH U2M_1 0x0101 Textech USB MIDI cable product TEXTECH U2M_2 0x1806 Textech USB MIDI cable /* The Mobility Lab products */ product TML USB_SERIAL 0x0064 FTDI compatible adapter /* Thurlby Thandar Instrument products */ product TTI QL355P 0x03e8 FTDI compatible adapter /* Supra products */ product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem product DIAMOND2 RIO600USB 0x5001 Rio 600 USB product DIAMOND2 RIO800USB 0x5002 Rio 800 USB /* Surecom Technology products */ product SURECOM EP9001G2A 0x11f2 EP-9001-G rev 2A product SURECOM RT2570 0x11f3 RT2570 product SURECOM RT2573 0x31f3 RT2573 /* Sweex products */ product SWEEX ZD1211 0x1809 ZD1211 product SWEEX2 LW153 0x0153 LW153 product SWEEX2 LW154 0x0154 LW154 product SWEEX2 LW303 0x0302 LW303 product SWEEX2 LW313 0x0313 LW313 /* System TALKS, Inc. */ product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL /* Tapwave products */ product TAPWAVE ZODIAC 0x0100 Zodiac /* Taugagreining products */ product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB) /* TCTMobile products */ product TCTMOBILE X060S 0x0000 X060S 3G modem product TCTMOBILE X080S 0xf000 X080S 3G modem /* TDK products */ product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664 product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464 product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400 product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400 product TDK BT_DONGLE 0x0309 Bluetooth USB dongle /* TEAC products */ product TEAC FD05PUB 0x0000 FD-05PUB floppy /* Tekram Technology products */ product TEKRAM QUICKWLAN 0x1630 QuickWLAN product TEKRAM ZD1211_1 0x5630 ZD1211 product TEKRAM ZD1211_2 0x6630 ZD1211 /* Telex Communications products */ product TELEX MIC1 0x0001 Enhanced USB Microphone /* Telit products */ product TELIT UC864E 0x1003 UC864E 3G modem product TELIT UC864G 0x1004 UC864G 3G modem /* Ten X Technology, Inc. */ product TENX UAUDIO0 0xf211 USB audio headset /* Texas Intel products */ product TI UTUSB41 0x1446 UT-USB41 hub product TI TUSB2046 0x2046 TUSB2046 hub /* Thrustmaster products */ product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad /* TLayTech products */ product TLAYTECH TEU800 0x1682 TEU800 3G modem /* Topre Corporation products */ product TOPRE HHKB 0x0100 HHKB Professional /* Toshiba Corporation products */ product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740 product TOSHIBA RT3070 0x0a07 RT3070 product TOSHIBA G450 0x0d45 G450 modem product TOSHIBA HSDPA 0x1302 G450 modem product TOSHIBA TRANSMEMORY 0x6545 USB ThumbDrive /* Trek Technology products */ product TREK THUMBDRIVE 0x1111 ThumbDrive product TREK MEMKEY 0x8888 IBM USB Memory Key product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB /* TRENDnet products */ product TRENDNET RTL8192CU 0x624d RTL8192CU product TRENDNET TEW646UBH 0x646b TEW-646UBH product TRENDNET RTL8188CU 0x648b RTL8188CU /* Tripp-Lite products */ product TRIPPLITE U209 0x2008 Serial /* Trumpion products */ product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller product TRUMPION C3310 0x1100 Comotron C3310 MP3 player product TRUMPION MP3 0x1200 MP3 player /* TwinMOS */ product TWINMOS G240 0xa006 G240 product TWINMOS MDIV 0x1325 Memory Disk IV /* Ubiquam products */ product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520) /* Ultima products */ product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner /* UMAX products */ product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner product UMAX ASTRA3400 0x0060 Astra 3400 Scanner /* U-MEDIA Communications products */ product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware) product UMEDIA TEW429UB_A 0x300a TEW-429UB_A product UMEDIA TEW429UB 0x300b TEW-429UB product UMEDIA TEW429UBC1 0x300d TEW-429UB C1 product UMEDIA RT2870_1 0x300e RT2870 product UMEDIA ALL0298V2 0x3204 ALL0298 v2 product UMEDIA AR5523_2 0x3205 AR5523 product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware) /* Universal Access products */ product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter /* Unknown products */ product UNKNOWN4 NF_RIC 0x0001 FTDI compatible adapter /* USI products */ product USI MC60 0x10c5 MC60 Serial /* U.S. Robotics products */ product USR USR5422 0x0118 USR5422 WLAN product USR USR5423 0x0121 USR5423 WLAN /* VIA Technologies products */ product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge /* VIA Labs */ product VIALABS USB30SATABRIDGE 0x0700 USB 3.0 SATA Bridge /* Vaisala products */ product VAISALA CABLE 0x0200 USB Interface cable /* Vertex products */ product VERTEX VW110L 0x0100 Vertex VW110L modem /* VidzMedia products */ product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H /* Vision products */ product VISION VC6452V002 0x0002 CPiA Camera /* Visioneer products */ product VISIONEER 7600 0x0211 OneTouch 7600 product VISIONEER 5300 0x0221 OneTouch 5300 product VISIONEER 3000 0x0224 Scanport 3000 product VISIONEER 6100 0x0231 OneTouch 6100 product VISIONEER 6200 0x0311 OneTouch 6200 product VISIONEER 8100 0x0321 OneTouch 8100 product VISIONEER 8600 0x0331 OneTouch 8600 /* Vivitar products */ product VIVITAR 35XX 0x0003 Vivicam 35Xx /* VTech products */ product VTECH RT2570 0x3012 RT2570 product VTECH ZD1211B 0x3014 ZD1211B /* Wacom products */ product WACOM CT0405U 0x0000 CT-0405-U Tablet product WACOM GRAPHIRE 0x0010 Graphire product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 product WACOM INTUOSA5 0x0021 Intuos A5 product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet /* WAGO Kontakttechnik GmbH products */ product WAGO SERVICECABLE 0x07a6 USB Service Cable 750-923 /* WaveSense products */ product WAVESENSE JAZZ 0xaaaa Jazz blood glucose meter /* WCH products */ product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge product WCH2 DUMMY 0x0000 Dummy product product WCH2 CH341SER_2 0x5523 CH341/CH340 USB-Serial Bridge product WCH2 CH341SER 0x7523 CH341/CH340 USB-Serial Bridge product WCH2 U2M 0X752d CH345 USB2.0-MIDI /* West Mountain Radio products */ product WESTMOUNTAIN RIGBLASTER_ADVANTAGE 0x0003 RIGblaster Advantage /* Western Digital products */ product WESTERN COMBO 0x0200 Firewire USB Combo product WESTERN EXTHDD 0x0400 External HDD product WESTERN HUB 0x0500 USB HUB product WESTERN MYBOOK 0x0901 MyBook External HDD product WESTERN MYPASSPORT_00 0x0704 MyPassport External HDD product WESTERN MYPASSPORT_11 0x0741 MyPassport External HDD product WESTERN MYPASSPORT_01 0x0746 MyPassport External HDD product WESTERN MYPASSPORT_02 0x0748 MyPassport External HDD product WESTERN MYPASSPORT_03 0x074A MyPassport External HDD product WESTERN MYPASSPORT_04 0x074C MyPassport External HDD product WESTERN MYPASSPORT_05 0x074E MyPassport External HDD product WESTERN MYPASSPORT_06 0x07A6 MyPassport External HDD product WESTERN MYPASSPORT_07 0x07A8 MyPassport External HDD product WESTERN MYPASSPORT_08 0x07AA MyPassport External HDD product WESTERN MYPASSPORT_09 0x07AC MyPassport External HDD product WESTERN MYPASSPORT_10 0x07AE MyPassport External HDD product WESTERN MYPASSPORTES_00 0x070A MyPassport Essential External HDD product WESTERN MYPASSPORTES_01 0x071A MyPassport Essential External HDD product WESTERN MYPASSPORTES_02 0x0730 MyPassport Essential External HDD product WESTERN MYPASSPORTES_03 0x0732 MyPassport Essential External HDD product WESTERN MYPASSPORTES_04 0x0740 MyPassport Essential External HDD product WESTERN MYPASSPORTES_05 0x0742 MyPassport Essential External HDD product WESTERN MYPASSPORTES_06 0x0750 MyPassport Essential External HDD product WESTERN MYPASSPORTES_07 0x0752 MyPassport Essential External HDD product WESTERN MYPASSPORTES_08 0x07A0 MyPassport Essential External HDD product WESTERN MYPASSPORTES_09 0x07A2 MyPassport Essential External HDD /* WeTelecom products */ product WETELECOM WM_D200 0x6801 WM-D200 /* WIENER Plein & Baus GmbH products */ product WIENERPLEINBAUS PL512 0x0010 PL512 PSU product WIENERPLEINBAUS RCM 0x0011 RCM Remote Control product WIENERPLEINBAUS MPOD 0x0012 MPOD PSU product WIENERPLEINBAUS CML 0x0015 CML Data Logger /* Windbond Electronics */ product WINBOND UH104 0x5518 4-port USB Hub /* WinMaxGroup products */ product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C /* Wistron NeWeb products */ product WISTRONNEWEB WNC0600 0x0326 WNC-0600USB product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN product WISTRONNEWEB UR055G 0x0711 UR055G product WISTRONNEWEB O8494 0x0804 ORiNOCO 802.11n product WISTRONNEWEB AR5523_1 0x0826 AR5523 product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware) product WISTRONNEWEB AR5523_2 0x082a AR5523 product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware) /* Xerox products */ product XEROX WCM15 0xffef WorkCenter M15 /* Xirlink products */ product XIRLINK PCCAM 0x8080 IBM PC Camera /* Xyratex products */ product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* Yamaha products */ product YAMAHA UX256 0x1000 UX256 MIDI I/F product YAMAHA UX96 0x1008 UX96 MIDI I/F product YAMAHA RPU200 0x3104 RP-U200 product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router /* Yano products */ product YANO U640MO 0x0101 U640MO-03 product YANO FW800HD 0x05fc METALWEAR-HDD /* Y.C. Cable products */ product YCCABLE PL2303 0x0fba PL2303 Serial /* Y-E Data products */ product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U /* Yiso Wireless Co. products */ product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card /* Z-Com products */ product ZCOM M4Y750 0x0001 M4Y-750 product ZCOM XI725 0x0002 XI-725/726 product ZCOM XI735 0x0005 XI-735 product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN product ZCOM ZD1211 0x0011 ZD1211 product ZCOM AR5523 0x0012 AR5523 product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware) product ZCOM XM142 0x0015 XM-142 product ZCOM ZD1211B 0x001a ZD1211B product ZCOM RT2870_1 0x0022 RT2870 product ZCOM UB81 0x0023 UB81 product ZCOM RT2870_2 0x0025 RT2870 product ZCOM UB82 0x0026 UB82 /* Zinwell products */ product ZINWELL RT2570 0x0260 RT2570 product ZINWELL RT2870_1 0x0280 RT2870 product ZINWELL RT2870_2 0x0282 RT2870 product ZINWELL RT3072_1 0x0283 RT3072 product ZINWELL RT3072_2 0x0284 RT3072 product ZINWELL RT3070 0x5257 RT3070 /* Zoom Telephonics, Inc. products */ product ZOOM 2986L 0x9700 2986L Fax modem /* Zoran Microelectronics products */ product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC /* Zydas Technology Corporation products */ product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg product ZYDAS ZD1211B 0x1215 ZD1211B product ZYDAS ZD1221 0x1221 ZD1221 /* ZyXEL Communication Co. products */ product ZYXEL OMNI56K 0x1500 Omni 56K Plus product ZYXEL 980N 0x2011 Scorpion-980N keyboard product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220 product ZYXEL G200V2 0x3407 G-200 v2 product ZYXEL AG225H 0x3409 AG-225H product ZYXEL M202 0x340a M-202 product ZYXEL G220V2 0x340f G-220 v2 product ZYXEL G202 0x3410 G-202 product ZYXEL RT2870_1 0x3416 RT2870 product ZYXEL NWD271N 0x3417 NWD-271N product ZYXEL NWD211AN 0x3418 NWD-211AN product ZYXEL RT2870_2 0x341a RT2870 product ZYXEL RT3070 0x341e NWD2105 product ZYXEL RTL8192CU 0x341f RTL8192CU product ZYXEL NWD2705 0x3421 NWD2705 Index: head/sys/dev/usb/usbdi.h =================================================================== --- head/sys/dev/usb/usbdi.h (revision 298931) +++ head/sys/dev/usb/usbdi.h (revision 298932) @@ -1,657 +1,657 @@ /*- * Copyright (c) 2009 Andrew Thompson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _USB_USBDI_H_ #define _USB_USBDI_H_ struct usb_fifo; struct usb_xfer; struct usb_device; struct usb_attach_arg; struct usb_interface; struct usb_endpoint; struct usb_page_cache; struct usb_page_search; struct usb_process; struct usb_proc_msg; struct usb_mbuf; struct usb_fs_privdata; struct mbuf; typedef enum { /* keep in sync with usb_errstr_table */ USB_ERR_NORMAL_COMPLETION = 0, USB_ERR_PENDING_REQUESTS, /* 1 */ USB_ERR_NOT_STARTED, /* 2 */ USB_ERR_INVAL, /* 3 */ USB_ERR_NOMEM, /* 4 */ USB_ERR_CANCELLED, /* 5 */ USB_ERR_BAD_ADDRESS, /* 6 */ USB_ERR_BAD_BUFSIZE, /* 7 */ USB_ERR_BAD_FLAG, /* 8 */ USB_ERR_NO_CALLBACK, /* 9 */ USB_ERR_IN_USE, /* 10 */ USB_ERR_NO_ADDR, /* 11 */ USB_ERR_NO_PIPE, /* 12 */ USB_ERR_ZERO_NFRAMES, /* 13 */ USB_ERR_ZERO_MAXP, /* 14 */ USB_ERR_SET_ADDR_FAILED, /* 15 */ USB_ERR_NO_POWER, /* 16 */ USB_ERR_TOO_DEEP, /* 17 */ USB_ERR_IOERROR, /* 18 */ USB_ERR_NOT_CONFIGURED, /* 19 */ USB_ERR_TIMEOUT, /* 20 */ USB_ERR_SHORT_XFER, /* 21 */ USB_ERR_STALLED, /* 22 */ USB_ERR_INTERRUPTED, /* 23 */ USB_ERR_DMA_LOAD_FAILED, /* 24 */ USB_ERR_BAD_CONTEXT, /* 25 */ USB_ERR_NO_ROOT_HUB, /* 26 */ USB_ERR_NO_INTR_THREAD, /* 27 */ USB_ERR_NOT_LOCKED, /* 28 */ USB_ERR_MAX } usb_error_t; /* * Flags for transfers */ #define USB_FORCE_SHORT_XFER 0x0001 /* force a short transmit last */ #define USB_SHORT_XFER_OK 0x0004 /* allow short reads */ #define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */ #define USB_USER_DATA_PTR 0x0020 /* internal flag */ #define USB_MULTI_SHORT_OK 0x0040 /* allow multiple short frames */ #define USB_MANUAL_STATUS 0x0080 /* manual ctrl status */ #define USB_NO_TIMEOUT 0 #define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ #if defined(_KERNEL) /* typedefs */ typedef void (usb_callback_t)(struct usb_xfer *, usb_error_t); typedef void (usb_proc_callback_t)(struct usb_proc_msg *); typedef usb_error_t (usb_handle_req_t)(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); typedef int (usb_fifo_open_t)(struct usb_fifo *fifo, int fflags); typedef void (usb_fifo_close_t)(struct usb_fifo *fifo, int fflags); typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags); typedef void (usb_fifo_cmd_t)(struct usb_fifo *fifo); typedef void (usb_fifo_filter_t)(struct usb_fifo *fifo, struct usb_mbuf *m); /* USB events */ #ifndef USB_GLOBAL_INCLUDE_FILE #include #endif typedef void (*usb_dev_configured_t)(void *, struct usb_device *, struct usb_attach_arg *); EVENTHANDLER_DECLARE(usb_dev_configured, usb_dev_configured_t); /* * The following macros are used used to convert milliseconds into * HZ. We use 1024 instead of 1000 milliseconds per second to save a * full division. */ #define USB_MS_HZ 1024 #define USB_MS_TO_TICKS(ms) \ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ) /* * Common queue structure for USB transfers. */ struct usb_xfer_queue { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *curr; /* current USB transfer processed */ void (*command) (struct usb_xfer_queue *pq); uint8_t recurse_1:1; uint8_t recurse_2:1; uint8_t recurse_3:1; uint8_t reserved:5; }; /* * The following structure defines an USB endpoint * USB endpoint. */ struct usb_endpoint { /* queue of USB transfers */ struct usb_xfer_queue endpoint_q[USB_MAX_EP_STREAMS]; struct usb_endpoint_descriptor *edesc; struct usb_endpoint_ss_comp_descriptor *ecomp; const struct usb_pipe_methods *methods; /* set by HC driver */ uint16_t isoc_next; uint8_t toggle_next:1; /* next data toggle value */ uint8_t is_stalled:1; /* set if endpoint is stalled */ uint8_t is_synced:1; /* set if we a synchronised */ uint8_t unused:5; uint8_t iface_index; /* not used by "default endpoint" */ uint8_t refcount_alloc; /* allocation refcount */ uint8_t refcount_bw; /* bandwidth refcount */ #define USB_EP_REF_MAX 0x3f /* High-Speed resource allocation (valid if "refcount_bw" > 0) */ uint8_t usb_smask; /* USB start mask */ uint8_t usb_cmask; /* USB complete mask */ uint8_t usb_uframe; /* USB microframe */ /* USB endpoint mode, see USB_EP_MODE_XXX */ uint8_t ep_mode; }; /* * The following structure defines an USB interface. */ struct usb_interface { struct usb_interface_descriptor *idesc; device_t subdev; uint8_t alt_index; uint8_t parent_iface_index; /* Linux compat */ struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; struct usb_device *linux_udev; void *bsd_priv_sc; /* device specific information */ char *pnpinfo; /* additional PnP-info for this interface */ uint8_t num_altsetting; /* number of alternate settings */ uint8_t bsd_iface_index; }; /* * The following structure defines a set of USB transfer flags. */ struct usb_xfer_flags { uint8_t force_short_xfer:1; /* force a short transmit transfer * last */ uint8_t short_xfer_ok:1; /* allow short receive transfers */ uint8_t short_frames_ok:1; /* allow short frames */ uint8_t pipe_bof:1; /* block pipe on failure */ uint8_t proxy_buffer:1; /* makes buffer size a factor of * "max_frame_size" */ uint8_t ext_buffer:1; /* uses external DMA buffer */ uint8_t manual_status:1; /* non automatic status stage on * control transfers */ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can * be ignored */ uint8_t stall_pipe:1; /* set if the endpoint belonging to * this USB transfer should be stalled * before starting this transfer! */ uint8_t pre_scale_frames:1; /* "usb_config->frames" is * assumed to give the * buffering time in * milliseconds and is * converted into the nearest * number of frames when the * USB transfer is setup. This * option only has effect for * ISOCHRONOUS transfers. */ }; /* * The following structure define an USB configuration, that basically * is used when setting up an USB transfer. */ struct usb_config { usb_callback_t *callback; /* USB transfer callback */ usb_frlength_t bufsize; /* total pipe buffer size in bytes */ usb_frcount_t frames; /* maximum number of USB frames */ usb_timeout_t interval; /* interval in milliseconds */ #define USB_DEFAULT_INTERVAL 0 usb_timeout_t timeout; /* transfer timeout in milliseconds */ struct usb_xfer_flags flags; /* transfer flags */ usb_stream_t stream_id; /* USB3.0 specific */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t type; /* pipe type */ uint8_t endpoint; /* pipe number */ uint8_t direction; /* pipe direction */ uint8_t ep_index; /* pipe index match to use */ uint8_t if_index; /* "ifaces" index to use */ }; /* * Use these macro when defining USB device ID arrays if you want to * have your driver module automatically loaded in host, device or - * both modes respectivly: + * both modes respectively: */ #if USB_HAVE_ID_SECTION #define STRUCT_USB_HOST_ID \ struct usb_device_id __section("usb_host_id") #define STRUCT_USB_DEVICE_ID \ struct usb_device_id __section("usb_device_id") #define STRUCT_USB_DUAL_ID \ struct usb_device_id __section("usb_dual_id") #else #define STRUCT_USB_HOST_ID \ struct usb_device_id #define STRUCT_USB_DEVICE_ID \ struct usb_device_id #define STRUCT_USB_DUAL_ID \ struct usb_device_id #endif /* USB_HAVE_ID_SECTION */ /* * The following structure is used when looking up an USB driver for * an USB device. It is inspired by the Linux structure called * "usb_device_id". */ struct usb_device_id { /* Select which fields to match against */ #if BYTE_ORDER == LITTLE_ENDIAN uint16_t match_flag_vendor:1, match_flag_product:1, match_flag_dev_lo:1, match_flag_dev_hi:1, match_flag_dev_class:1, match_flag_dev_subclass:1, match_flag_dev_protocol:1, match_flag_int_class:1, match_flag_int_subclass:1, match_flag_int_protocol:1, match_flag_unused:6; #else uint16_t match_flag_unused:6, match_flag_int_protocol:1, match_flag_int_subclass:1, match_flag_int_class:1, match_flag_dev_protocol:1, match_flag_dev_subclass:1, match_flag_dev_class:1, match_flag_dev_hi:1, match_flag_dev_lo:1, match_flag_product:1, match_flag_vendor:1; #endif /* Used for product specific matches; the BCD range is inclusive */ uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice_lo; uint16_t bcdDevice_hi; /* Used for device class matches */ uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; /* Used for interface class matches */ uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; #if USB_HAVE_COMPAT_LINUX /* which fields to match against */ uint16_t match_flags; #define USB_DEVICE_ID_MATCH_VENDOR 0x0001 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #endif /* Hook for driver specific information */ unsigned long driver_info; } __aligned(32); #define USB_STD_PNP_INFO "M16:mask;U16:vendor;U16:product;L16:product;G16:product;" \ "U8:devclass;U8:devsubclass;U8:devprotocol;" \ "U8:intclass;U8:intsubclass;U8:intprotocol;" #define USB_STD_PNP_HOST_INFO USB_STD_PNP_INFO "T:mode=host;" #define USB_STD_PNP_DEVICE_INFO USB_STD_PNP_INFO "T:mode=device;" #define USB_PNP_HOST_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_HOST_INFO, usb, table, table, sizeof(table[0]), \ sizeof(table) / sizeof(table[0])) #define USB_PNP_DEVICE_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_DEVICE_INFO, usb, table, table, sizeof(table[0]), \ sizeof(table) / sizeof(table[0])) #define USB_PNP_DUAL_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_INFO, usb, table, table, sizeof(table[0]), \ sizeof(table) / sizeof(table[0])) /* check that the size of the structure above is correct */ extern char usb_device_id_assert[(sizeof(struct usb_device_id) == 32) ? 1 : -1]; #define USB_VENDOR(vend) \ .match_flag_vendor = 1, .idVendor = (vend) #define USB_PRODUCT(prod) \ .match_flag_product = 1, .idProduct = (prod) #define USB_VP(vend,prod) \ USB_VENDOR(vend), USB_PRODUCT(prod) #define USB_VPI(vend,prod,info) \ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info) #define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo) #define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi) #define USB_DEV_CLASS(dc) \ .match_flag_dev_class = 1, .bDeviceClass = (dc) #define USB_DEV_SUBCLASS(dsc) \ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc) #define USB_DEV_PROTOCOL(dp) \ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp) #define USB_IFACE_CLASS(ic) \ .match_flag_int_class = 1, .bInterfaceClass = (ic) #define USB_IFACE_SUBCLASS(isc) \ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc) #define USB_IFACE_PROTOCOL(ip) \ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip) #define USB_IF_CSI(class,subclass,info) \ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info) #define USB_DRIVER_INFO(n) \ .driver_info = (n) #define USB_GET_DRIVER_INFO(did) \ (did)->driver_info /* * The following structure keeps information that is used to match * against an array of "usb_device_id" elements. */ struct usbd_lookup_info { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t bIfaceIndex; uint8_t bIfaceNum; uint8_t bConfigIndex; uint8_t bConfigNum; }; /* Structure used by probe and attach */ struct usb_attach_arg { struct usbd_lookup_info info; device_t temp_dev; /* for internal use */ unsigned long driver_info; /* for internal use */ void *driver_ivar; struct usb_device *device; /* current device */ struct usb_interface *iface; /* current interface */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t port; uint8_t dev_state; #define UAA_DEV_READY 0 #define UAA_DEV_DISABLED 1 #define UAA_DEV_EJECTING 2 }; /* * The following is a wrapper for the callout structure to ease * porting the code to other platforms. */ struct usb_callout { struct callout co; }; #define usb_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) #define usb_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d) #define usb_callout_stop(c) callout_stop(&(c)->co) #define usb_callout_drain(c) callout_drain(&(c)->co) #define usb_callout_pending(c) callout_pending(&(c)->co) /* USB transfer states */ #define USB_ST_SETUP 0 #define USB_ST_TRANSFERRED 1 #define USB_ST_ERROR 2 /* USB handle request states */ #define USB_HR_NOT_COMPLETE 0 #define USB_HR_COMPLETE_OK 1 #define USB_HR_COMPLETE_ERR 2 /* * The following macro will return the current state of an USB * transfer like defined by the "USB_ST_XXX" enums. */ #define USB_GET_STATE(xfer) (usbd_xfer_state(xfer)) /* * The following structure defines the USB process message header. */ struct usb_proc_msg { TAILQ_ENTRY(usb_proc_msg) pm_qentry; usb_proc_callback_t *pm_callback; usb_size_t pm_num; }; #define USB_FIFO_TX 0 #define USB_FIFO_RX 1 /* * Locking note for the following functions. All the * "usb_fifo_cmd_t" and "usb_fifo_filter_t" functions are called * locked. The others are called unlocked. */ struct usb_fifo_methods { usb_fifo_open_t *f_open; usb_fifo_close_t *f_close; usb_fifo_ioctl_t *f_ioctl; /* * NOTE: The post-ioctl callback is called after the USB reference * gets locked in the IOCTL handler: */ usb_fifo_ioctl_t *f_ioctl_post; usb_fifo_cmd_t *f_start_read; usb_fifo_cmd_t *f_stop_read; usb_fifo_cmd_t *f_start_write; usb_fifo_cmd_t *f_stop_write; usb_fifo_filter_t *f_filter_read; usb_fifo_filter_t *f_filter_write; const char *basename[4]; const char *postfix[4]; }; struct usb_fifo_sc { struct usb_fifo *fp[2]; struct usb_fs_privdata *dev; }; const char *usbd_errstr(usb_error_t error); void *usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); struct usb_config_descriptor *usbd_get_config_descriptor( struct usb_device *udev); struct usb_device_descriptor *usbd_get_device_descriptor( struct usb_device *udev); struct usb_interface *usbd_get_iface(struct usb_device *udev, uint8_t iface_index); struct usb_interface_descriptor *usbd_get_interface_descriptor( struct usb_interface *iface); struct usb_endpoint *usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup); struct usb_endpoint *usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val); usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count); enum usb_hc_mode usbd_get_mode(struct usb_device *udev); enum usb_dev_speed usbd_get_speed(struct usb_device *udev); void device_set_usb_desc(device_t dev); void usb_pause_mtx(struct mtx *mtx, int _ticks); usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo); usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk); usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode); uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep); const struct usb_device_id *usbd_lookup_id_by_info( const struct usb_device_id *id, usb_size_t sizeof_id, const struct usbd_lookup_info *info); int usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, struct usb_attach_arg *uaa); usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout); #define usbd_do_request(u,m,r,d) \ usbd_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT) uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2); uint8_t usbd_get_interface_altindex(struct usb_interface *iface); usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index); uint32_t usbd_get_isoc_fps(struct usb_device *udev); usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **pxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx); void usbd_transfer_submit(struct usb_xfer *xfer); void usbd_transfer_clear_stall(struct usb_xfer *xfer); void usbd_transfer_drain(struct usb_xfer *xfer); uint8_t usbd_transfer_pending(struct usb_xfer *xfer); void usbd_transfer_start(struct usb_xfer *xfer); void usbd_transfer_stop(struct usb_xfer *xfer); void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup); void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max); void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index); uint8_t usbd_get_bus_index(struct usb_device *udev); uint8_t usbd_get_device_index(struct usb_device *udev); void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_device_attached(struct usb_device *udev); usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes); struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_get_frame_buffer(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_softc(struct usb_xfer *xfer); void *usbd_xfer_get_priv(struct usb_xfer *xfer); void usbd_xfer_set_priv(struct usb_xfer *xfer, void *); void usbd_xfer_set_interval(struct usb_xfer *xfer, int); uint8_t usbd_xfer_state(struct usb_xfer *xfer); void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len); void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len); void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex); usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer); usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer); uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len); void usbd_xfer_set_timeout(struct usb_xfer *xfer, int timeout); void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n); void usbd_xfer_set_stall(struct usb_xfer *xfer); int usbd_xfer_is_stalled(struct usb_xfer *xfer); void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag); void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag); uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer); uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer); void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); int usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); int usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); void usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res); void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len); void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len); void usbd_start_re_enumerate(struct usb_device *udev); usb_error_t usbd_start_set_config(struct usb_device *, uint8_t); int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode); void usb_fifo_detach(struct usb_fifo_sc *f_sc); int usb_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize, uint16_t nbuf); void usb_fifo_free_buffer(struct usb_fifo *f); uint32_t usb_fifo_put_bytes_max(struct usb_fifo *fifo); void usb_fifo_put_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what); void usb_fifo_put_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, uint8_t what); uint8_t usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len); void usb_fifo_put_data_error(struct usb_fifo *fifo); uint8_t usb_fifo_get_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, usb_size_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen); void usb_fifo_reset(struct usb_fifo *f); void usb_fifo_wakeup(struct usb_fifo *f); void usb_fifo_get_data_error(struct usb_fifo *fifo); void *usb_fifo_softc(struct usb_fifo *fifo); void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t); void usb_fifo_set_write_defrag(struct usb_fifo *, uint8_t); void usb_fifo_free(struct usb_fifo *f); #endif /* _KERNEL */ #endif /* _USB_USBDI_H_ */ Index: head/sys/dev/usb/wlan/if_run.c =================================================================== --- head/sys/dev/usb/wlan/if_run.c (revision 298931) +++ head/sys/dev/usb/wlan/if_run.c (revision 298932) @@ -1,6239 +1,6239 @@ /*- * Copyright (c) 2008,2010 Damien Bergamini * ported to FreeBSD by Akinori Furukoshi * USB Consulting, Hans Petter Selasky * Copyright (c) 2013-2014 Kevin Lo * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2700U/RT2800U/RT3000U/RT3900E chipset driver. * http://www.ralinktech.com/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR run_debug #include #include #include #include #ifdef USB_DEBUG #define RUN_DEBUG #endif #ifdef RUN_DEBUG int run_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW, 0, "USB run"); SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0, "run debug level"); #endif #define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) /* * Because of LOR in run_key_delete(), use atomic instead. * '& RUN_CMDQ_MASQ' is to loop cmdq[]. */ #define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ) static const STRUCT_USB_HOST_ID run_devs[] = { #define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } #define RUN_DEV_EJECT(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) } #define RUN_EJECT 1 RUN_DEV(ABOCOM, RT2770), RUN_DEV(ABOCOM, RT2870), RUN_DEV(ABOCOM, RT3070), RUN_DEV(ABOCOM, RT3071), RUN_DEV(ABOCOM, RT3072), RUN_DEV(ABOCOM2, RT2870_1), RUN_DEV(ACCTON, RT2770), RUN_DEV(ACCTON, RT2870_1), RUN_DEV(ACCTON, RT2870_2), RUN_DEV(ACCTON, RT2870_3), RUN_DEV(ACCTON, RT2870_4), RUN_DEV(ACCTON, RT2870_5), RUN_DEV(ACCTON, RT3070), RUN_DEV(ACCTON, RT3070_1), RUN_DEV(ACCTON, RT3070_2), RUN_DEV(ACCTON, RT3070_3), RUN_DEV(ACCTON, RT3070_4), RUN_DEV(ACCTON, RT3070_5), RUN_DEV(AIRTIES, RT3070), RUN_DEV(ALLWIN, RT2070), RUN_DEV(ALLWIN, RT2770), RUN_DEV(ALLWIN, RT2870), RUN_DEV(ALLWIN, RT3070), RUN_DEV(ALLWIN, RT3071), RUN_DEV(ALLWIN, RT3072), RUN_DEV(ALLWIN, RT3572), RUN_DEV(AMIGO, RT2870_1), RUN_DEV(AMIGO, RT2870_2), RUN_DEV(AMIT, CGWLUSB2GNR), RUN_DEV(AMIT, RT2870_1), RUN_DEV(AMIT2, RT2870), RUN_DEV(ASUS, RT2870_1), RUN_DEV(ASUS, RT2870_2), RUN_DEV(ASUS, RT2870_3), RUN_DEV(ASUS, RT2870_4), RUN_DEV(ASUS, RT2870_5), RUN_DEV(ASUS, USBN13), RUN_DEV(ASUS, RT3070_1), RUN_DEV(ASUS, USBN66), RUN_DEV(ASUS, USB_N53), RUN_DEV(ASUS2, USBN11), RUN_DEV(AZUREWAVE, RT2870_1), RUN_DEV(AZUREWAVE, RT2870_2), RUN_DEV(AZUREWAVE, RT3070_1), RUN_DEV(AZUREWAVE, RT3070_2), RUN_DEV(AZUREWAVE, RT3070_3), RUN_DEV(BELKIN, F9L1103), RUN_DEV(BELKIN, F5D8053V3), RUN_DEV(BELKIN, F5D8055), RUN_DEV(BELKIN, F5D8055V2), RUN_DEV(BELKIN, F6D4050V1), RUN_DEV(BELKIN, F6D4050V2), RUN_DEV(BELKIN, RT2870_1), RUN_DEV(BELKIN, RT2870_2), RUN_DEV(CISCOLINKSYS, AE1000), RUN_DEV(CISCOLINKSYS2, RT3070), RUN_DEV(CISCOLINKSYS3, RT3070), RUN_DEV(CONCEPTRONIC2, RT2870_1), RUN_DEV(CONCEPTRONIC2, RT2870_2), RUN_DEV(CONCEPTRONIC2, RT2870_3), RUN_DEV(CONCEPTRONIC2, RT2870_4), RUN_DEV(CONCEPTRONIC2, RT2870_5), RUN_DEV(CONCEPTRONIC2, RT2870_6), RUN_DEV(CONCEPTRONIC2, RT2870_7), RUN_DEV(CONCEPTRONIC2, RT2870_8), RUN_DEV(CONCEPTRONIC2, RT3070_1), RUN_DEV(CONCEPTRONIC2, RT3070_2), RUN_DEV(CONCEPTRONIC2, VIGORN61), RUN_DEV(COREGA, CGWLUSB300GNM), RUN_DEV(COREGA, RT2870_1), RUN_DEV(COREGA, RT2870_2), RUN_DEV(COREGA, RT2870_3), RUN_DEV(COREGA, RT3070), RUN_DEV(CYBERTAN, RT2870), RUN_DEV(DLINK, RT2870), RUN_DEV(DLINK, RT3072), RUN_DEV(DLINK, DWA127), RUN_DEV(DLINK, DWA140B3), RUN_DEV(DLINK, DWA160B2), RUN_DEV(DLINK, DWA140D1), RUN_DEV(DLINK, DWA162), RUN_DEV(DLINK2, DWA130), RUN_DEV(DLINK2, RT2870_1), RUN_DEV(DLINK2, RT2870_2), RUN_DEV(DLINK2, RT3070_1), RUN_DEV(DLINK2, RT3070_2), RUN_DEV(DLINK2, RT3070_3), RUN_DEV(DLINK2, RT3070_4), RUN_DEV(DLINK2, RT3070_5), RUN_DEV(DLINK2, RT3072), RUN_DEV(DLINK2, RT3072_1), RUN_DEV(EDIMAX, EW7717), RUN_DEV(EDIMAX, EW7718), RUN_DEV(EDIMAX, EW7733UND), RUN_DEV(EDIMAX, RT2870_1), RUN_DEV(ENCORE, RT3070_1), RUN_DEV(ENCORE, RT3070_2), RUN_DEV(ENCORE, RT3070_3), RUN_DEV(GIGABYTE, GNWB31N), RUN_DEV(GIGABYTE, GNWB32L), RUN_DEV(GIGABYTE, RT2870_1), RUN_DEV(GIGASET, RT3070_1), RUN_DEV(GIGASET, RT3070_2), RUN_DEV(GUILLEMOT, HWNU300), RUN_DEV(HAWKING, HWUN2), RUN_DEV(HAWKING, RT2870_1), RUN_DEV(HAWKING, RT2870_2), RUN_DEV(HAWKING, RT3070), RUN_DEV(IODATA, RT3072_1), RUN_DEV(IODATA, RT3072_2), RUN_DEV(IODATA, RT3072_3), RUN_DEV(IODATA, RT3072_4), RUN_DEV(LINKSYS4, RT3070), RUN_DEV(LINKSYS4, WUSB100), RUN_DEV(LINKSYS4, WUSB54GCV3), RUN_DEV(LINKSYS4, WUSB600N), RUN_DEV(LINKSYS4, WUSB600NV2), RUN_DEV(LOGITEC, RT2870_1), RUN_DEV(LOGITEC, RT2870_2), RUN_DEV(LOGITEC, RT2870_3), RUN_DEV(LOGITEC, LANW300NU2), RUN_DEV(LOGITEC, LANW150NU2), RUN_DEV(LOGITEC, LANW300NU2S), RUN_DEV(MELCO, WLIUCG300HP), RUN_DEV(MELCO, RT2870_2), RUN_DEV(MELCO, WLIUCAG300N), RUN_DEV(MELCO, WLIUCG300N), RUN_DEV(MELCO, WLIUCG301N), RUN_DEV(MELCO, WLIUCGN), RUN_DEV(MELCO, WLIUCGNM), RUN_DEV(MELCO, WLIUCG300HPV1), RUN_DEV(MELCO, WLIUCGNM2), RUN_DEV(MOTOROLA4, RT2770), RUN_DEV(MOTOROLA4, RT3070), RUN_DEV(MSI, RT3070_1), RUN_DEV(MSI, RT3070_2), RUN_DEV(MSI, RT3070_3), RUN_DEV(MSI, RT3070_4), RUN_DEV(MSI, RT3070_5), RUN_DEV(MSI, RT3070_6), RUN_DEV(MSI, RT3070_7), RUN_DEV(MSI, RT3070_8), RUN_DEV(MSI, RT3070_9), RUN_DEV(MSI, RT3070_10), RUN_DEV(MSI, RT3070_11), RUN_DEV(NETGEAR, WNDA4100), RUN_DEV(OVISLINK, RT3072), RUN_DEV(PARA, RT3070), RUN_DEV(PEGATRON, RT2870), RUN_DEV(PEGATRON, RT3070), RUN_DEV(PEGATRON, RT3070_2), RUN_DEV(PEGATRON, RT3070_3), RUN_DEV(PHILIPS, RT2870), RUN_DEV(PLANEX2, GWUS300MINIS), RUN_DEV(PLANEX2, GWUSMICRON), RUN_DEV(PLANEX2, RT2870), RUN_DEV(PLANEX2, RT3070), RUN_DEV(QCOM, RT2870), RUN_DEV(QUANTA, RT3070), RUN_DEV(RALINK, RT2070), RUN_DEV(RALINK, RT2770), RUN_DEV(RALINK, RT2870), RUN_DEV(RALINK, RT3070), RUN_DEV(RALINK, RT3071), RUN_DEV(RALINK, RT3072), RUN_DEV(RALINK, RT3370), RUN_DEV(RALINK, RT3572), RUN_DEV(RALINK, RT3573), RUN_DEV(RALINK, RT5370), RUN_DEV(RALINK, RT5572), RUN_DEV(RALINK, RT8070), RUN_DEV(SAMSUNG, WIS09ABGN), RUN_DEV(SAMSUNG2, RT2870_1), RUN_DEV(SENAO, RT2870_1), RUN_DEV(SENAO, RT2870_2), RUN_DEV(SENAO, RT2870_3), RUN_DEV(SENAO, RT2870_4), RUN_DEV(SENAO, RT3070), RUN_DEV(SENAO, RT3071), RUN_DEV(SENAO, RT3072_1), RUN_DEV(SENAO, RT3072_2), RUN_DEV(SENAO, RT3072_3), RUN_DEV(SENAO, RT3072_4), RUN_DEV(SENAO, RT3072_5), RUN_DEV(SITECOMEU, RT2770), RUN_DEV(SITECOMEU, RT2870_1), RUN_DEV(SITECOMEU, RT2870_2), RUN_DEV(SITECOMEU, RT2870_3), RUN_DEV(SITECOMEU, RT2870_4), RUN_DEV(SITECOMEU, RT3070), RUN_DEV(SITECOMEU, RT3070_2), RUN_DEV(SITECOMEU, RT3070_3), RUN_DEV(SITECOMEU, RT3070_4), RUN_DEV(SITECOMEU, RT3071), RUN_DEV(SITECOMEU, RT3072_1), RUN_DEV(SITECOMEU, RT3072_2), RUN_DEV(SITECOMEU, RT3072_3), RUN_DEV(SITECOMEU, RT3072_4), RUN_DEV(SITECOMEU, RT3072_5), RUN_DEV(SITECOMEU, RT3072_6), RUN_DEV(SITECOMEU, WL608), RUN_DEV(SPARKLAN, RT2870_1), RUN_DEV(SPARKLAN, RT3070), RUN_DEV(SWEEX2, LW153), RUN_DEV(SWEEX2, LW303), RUN_DEV(SWEEX2, LW313), RUN_DEV(TOSHIBA, RT3070), RUN_DEV(UMEDIA, RT2870_1), RUN_DEV(ZCOM, RT2870_1), RUN_DEV(ZCOM, RT2870_2), RUN_DEV(ZINWELL, RT2870_1), RUN_DEV(ZINWELL, RT2870_2), RUN_DEV(ZINWELL, RT3070), RUN_DEV(ZINWELL, RT3072_1), RUN_DEV(ZINWELL, RT3072_2), RUN_DEV(ZYXEL, RT2870_1), RUN_DEV(ZYXEL, RT2870_2), RUN_DEV(ZYXEL, RT3070), RUN_DEV_EJECT(ZYXEL, NWD2705), RUN_DEV_EJECT(RALINK, RT_STOR), #undef RUN_DEV_EJECT #undef RUN_DEV }; static device_probe_t run_match; static device_attach_t run_attach; static device_detach_t run_detach; static usb_callback_t run_bulk_rx_callback; static usb_callback_t run_bulk_tx_callback0; static usb_callback_t run_bulk_tx_callback1; static usb_callback_t run_bulk_tx_callback2; static usb_callback_t run_bulk_tx_callback3; static usb_callback_t run_bulk_tx_callback4; static usb_callback_t run_bulk_tx_callback5; static void run_autoinst(void *, struct usb_device *, struct usb_attach_arg *); static int run_driver_loaded(struct module *, int, void *); static void run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index); static struct ieee80211vap *run_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void run_vap_delete(struct ieee80211vap *); static void run_cmdq_cb(void *, int); static void run_setup_tx_list(struct run_softc *, struct run_endpoint_queue *); static void run_unsetup_tx_list(struct run_softc *, struct run_endpoint_queue *); static int run_load_microcode(struct run_softc *); static int run_reset(struct run_softc *); static usb_error_t run_do_request(struct run_softc *, struct usb_device_request *, void *); static int run_read(struct run_softc *, uint16_t, uint32_t *); static int run_read_region_1(struct run_softc *, uint16_t, uint8_t *, int); static int run_write_2(struct run_softc *, uint16_t, uint16_t); static int run_write(struct run_softc *, uint16_t, uint32_t); static int run_write_region_1(struct run_softc *, uint16_t, const uint8_t *, int); static int run_set_region_4(struct run_softc *, uint16_t, uint32_t, int); static int run_efuse_read(struct run_softc *, uint16_t, uint16_t *, int); static int run_efuse_read_2(struct run_softc *, uint16_t, uint16_t *); static int run_eeprom_read_2(struct run_softc *, uint16_t, uint16_t *); static int run_rt2870_rf_write(struct run_softc *, uint32_t); static int run_rt3070_rf_read(struct run_softc *, uint8_t, uint8_t *); static int run_rt3070_rf_write(struct run_softc *, uint8_t, uint8_t); static int run_bbp_read(struct run_softc *, uint8_t, uint8_t *); static int run_bbp_write(struct run_softc *, uint8_t, uint8_t); static int run_mcu_cmd(struct run_softc *, uint8_t, uint16_t); static const char *run_get_rf(uint16_t); static void run_rt3593_get_txpower(struct run_softc *); static void run_get_txpower(struct run_softc *); static int run_read_eeprom(struct run_softc *); static struct ieee80211_node *run_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static int run_media_change(struct ifnet *); static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int run_wme_update(struct ieee80211com *); static void run_key_set_cb(void *); static int run_key_set(struct ieee80211vap *, struct ieee80211_key *); static void run_key_delete_cb(void *); static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *); static void run_ratectl_to(void *); static void run_ratectl_cb(void *, int); static void run_drain_fifo(void *); static void run_iter_func(void *, struct ieee80211_node *); static void run_newassoc_cb(void *); static void run_newassoc(struct ieee80211_node *, int); static void run_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, const struct ieee80211_rx_stats *, int, int); static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t); static void run_tx_free(struct run_endpoint_queue *pq, struct run_tx_data *, int); static void run_set_tx_desc(struct run_softc *, struct run_tx_data *); static int run_tx(struct run_softc *, struct mbuf *, struct ieee80211_node *); static int run_tx_mgt(struct run_softc *, struct mbuf *, struct ieee80211_node *); static int run_sendprot(struct run_softc *, const struct mbuf *, struct ieee80211_node *, int, int); static int run_tx_param(struct run_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int run_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int run_transmit(struct ieee80211com *, struct mbuf *); static void run_start(struct run_softc *); static void run_parent(struct ieee80211com *); static void run_iq_calib(struct run_softc *, u_int); static void run_set_agc(struct run_softc *, uint8_t); static void run_select_chan_group(struct run_softc *, int); static void run_set_rx_antenna(struct run_softc *, int); static void run_rt2870_set_chan(struct run_softc *, u_int); static void run_rt3070_set_chan(struct run_softc *, u_int); static void run_rt3572_set_chan(struct run_softc *, u_int); static void run_rt3593_set_chan(struct run_softc *, u_int); static void run_rt5390_set_chan(struct run_softc *, u_int); static void run_rt5592_set_chan(struct run_softc *, u_int); static int run_set_chan(struct run_softc *, struct ieee80211_channel *); static void run_set_channel(struct ieee80211com *); static void run_scan_start(struct ieee80211com *); static void run_scan_end(struct ieee80211com *); static void run_update_beacon(struct ieee80211vap *, int); static void run_update_beacon_cb(void *); static void run_updateprot(struct ieee80211com *); static void run_updateprot_cb(void *); static void run_usb_timeout_cb(void *); static void run_reset_livelock(struct run_softc *); static void run_enable_tsf_sync(struct run_softc *); static void run_enable_tsf(struct run_softc *); static void run_get_tsf(struct run_softc *, uint64_t *); static void run_enable_mrr(struct run_softc *); static void run_set_txpreamble(struct run_softc *); static void run_set_basicrates(struct run_softc *); static void run_set_leds(struct run_softc *, uint16_t); static void run_set_bssid(struct run_softc *, const uint8_t *); static void run_set_macaddr(struct run_softc *, const uint8_t *); static void run_updateslot(struct ieee80211com *); static void run_updateslot_cb(void *); static void run_update_mcast(struct ieee80211com *); static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t); static void run_update_promisc_locked(struct run_softc *); static void run_update_promisc(struct ieee80211com *); static void run_rt5390_bbp_init(struct run_softc *); static int run_bbp_init(struct run_softc *); static int run_rt3070_rf_init(struct run_softc *); static void run_rt3593_rf_init(struct run_softc *); static void run_rt5390_rf_init(struct run_softc *); static int run_rt3070_filter_calib(struct run_softc *, uint8_t, uint8_t, uint8_t *); static void run_rt3070_rf_setup(struct run_softc *); static void run_rt3593_rf_setup(struct run_softc *); static void run_rt5390_rf_setup(struct run_softc *); static int run_txrx_enable(struct run_softc *); static void run_adjust_freq_offset(struct run_softc *); static void run_init_locked(struct run_softc *); static void run_stop(void *); static void run_delay(struct run_softc *, u_int); static eventhandler_tag run_etag; static const struct rt2860_rate { uint8_t rate; uint8_t mcs; enum ieee80211_phytype phy; uint8_t ctl_ridx; uint16_t sp_ack_dur; uint16_t lp_ack_dur; } rt2860_rates[] = { { 2, 0, IEEE80211_T_DS, 0, 314, 314 }, { 4, 1, IEEE80211_T_DS, 1, 258, 162 }, { 11, 2, IEEE80211_T_DS, 2, 223, 127 }, { 22, 3, IEEE80211_T_DS, 3, 213, 117 }, { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 }, { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 }, { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 }, { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 }, { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 }, { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 }, { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 }, { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 } }; static const struct { uint16_t reg; uint32_t val; } rt2870_def_mac[] = { RT2870_DEF_MAC }; static const struct { uint8_t reg; uint8_t val; } rt2860_def_bbp[] = { RT2860_DEF_BBP },rt5390_def_bbp[] = { RT5390_DEF_BBP },rt5592_def_bbp[] = { RT5592_DEF_BBP }; /* * Default values for BBP register R196 for RT5592. */ static const uint8_t rt5592_bbp_r196[] = { 0xe0, 0x1f, 0x38, 0x32, 0x08, 0x28, 0x19, 0x0a, 0xff, 0x00, 0x16, 0x10, 0x10, 0x0b, 0x36, 0x2c, 0x26, 0x24, 0x42, 0x36, 0x30, 0x2d, 0x4c, 0x46, 0x3d, 0x40, 0x3e, 0x42, 0x3d, 0x40, 0x3c, 0x34, 0x2c, 0x2f, 0x3c, 0x35, 0x2e, 0x2a, 0x49, 0x41, 0x36, 0x31, 0x30, 0x30, 0x0e, 0x0d, 0x28, 0x21, 0x1c, 0x16, 0x50, 0x4a, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x14, 0x32, 0x2c, 0x36, 0x4c, 0x43, 0x2c, 0x2e, 0x36, 0x30, 0x6e }; static const struct rfprog { uint8_t chan; uint32_t r1, r2, r3, r4; } rt2860_rf2850[] = { RT2860_RF2850 }; struct { uint8_t n, r, k; } rt3070_freqs[] = { RT3070_RF3052 }; static const struct rt5592_freqs { uint16_t n; uint8_t k, m, r; } rt5592_freqs_20mhz[] = { RT5592_RF5592_20MHZ },rt5592_freqs_40mhz[] = { RT5592_RF5592_40MHZ }; static const struct { uint8_t reg; uint8_t val; } rt3070_def_rf[] = { RT3070_DEF_RF },rt3572_def_rf[] = { RT3572_DEF_RF },rt3593_def_rf[] = { RT3593_DEF_RF },rt5390_def_rf[] = { RT5390_DEF_RF },rt5392_def_rf[] = { RT5392_DEF_RF },rt5592_def_rf[] = { RT5592_DEF_RF },rt5592_2ghz_def_rf[] = { RT5592_2GHZ_DEF_RF },rt5592_5ghz_def_rf[] = { RT5592_5GHZ_DEF_RF }; static const struct { u_int firstchan; u_int lastchan; uint8_t reg; uint8_t val; } rt5592_chan_5ghz[] = { RT5592_CHAN_5GHZ }; static const struct usb_config run_config[RUN_N_XFER] = { [RUN_BULK_TX_BE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .ep_index = 0, .direction = UE_DIR_OUT, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback0, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_BK] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 1, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback1, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_VI] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 2, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback2, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_VO] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 3, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback3, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_HCCA] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 4, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, .callback = run_bulk_tx_callback4, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_PRIO] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 5, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, .callback = run_bulk_tx_callback5, .timeout = 5000, /* ms */ }, [RUN_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = RUN_MAX_RXSZ, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = run_bulk_rx_callback, } }; static void run_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; struct usb_interface_descriptor *id; if (uaa->dev_state != UAA_DEV_READY) return; iface = usbd_get_iface(udev, 0); if (iface == NULL) return; id = iface->idesc; if (id == NULL || id->bInterfaceClass != UICLASS_MASS) return; if (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)) return; if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0) uaa->dev_state = UAA_DEV_EJECTING; } static int run_driver_loaded(struct module *mod, int what, void *arg) { switch (what) { case MOD_LOAD: run_etag = EVENTHANDLER_REGISTER(usb_dev_configured, run_autoinst, NULL, EVENTHANDLER_PRI_ANY); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(usb_dev_configured, run_etag); break; default: return (EOPNOTSUPP); } return (0); } static int run_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != RT2860_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)); } static int run_attach(device_t self) { struct run_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); struct ieee80211com *ic = &sc->sc_ic; uint32_t ver; uint8_t bands[IEEE80211_MODE_BYTES]; uint8_t iface_index; int ntries, error; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT) sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED; mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); mbufq_init(&sc->sc_snd, ifqmaxlen); iface_index = RT2860_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx); if (error) { device_printf(self, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto detach; } RUN_LOCK(sc); /* wait for the chip to settle */ for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_ASIC_VER_ID, &ver) != 0) { RUN_UNLOCK(sc); goto detach; } if (ver != 0 && ver != 0xffffffff) break; run_delay(sc, 10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for NIC to initialize\n"); RUN_UNLOCK(sc); goto detach; } sc->mac_ver = ver >> 16; sc->mac_rev = ver & 0xffff; /* retrieve RF rev. no and various other things from EEPROM */ run_read_eeprom(sc); device_printf(sc->sc_dev, "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n", sc->mac_ver, sc->mac_rev, run_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr)); RUN_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA | /* station mode supported */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_WDS | /* 4-address traffic works */ IEEE80211_C_MBSS | IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WME | /* WME */ IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM | IEEE80211_CRYPTO_TKIPMIC | IEEE80211_CRYPTO_TKIP; ic->ic_flags |= IEEE80211_F_DATAPAD; ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 || sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 || sc->rf_rev == RT5592_RF_5592) setbit(bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, bands); ieee80211_ifattach(ic); ic->ic_scan_start = run_scan_start; ic->ic_scan_end = run_scan_end; ic->ic_set_channel = run_set_channel; ic->ic_node_alloc = run_node_alloc; ic->ic_newassoc = run_newassoc; ic->ic_updateslot = run_updateslot; ic->ic_update_mcast = run_update_mcast; ic->ic_wme.wme_update = run_wme_update; ic->ic_raw_xmit = run_raw_xmit; ic->ic_update_promisc = run_update_promisc; ic->ic_vap_create = run_vap_create; ic->ic_vap_delete = run_vap_delete; ic->ic_transmit = run_transmit; ic->ic_parent = run_parent; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RUN_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RUN_RX_RADIOTAP_PRESENT); TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc); TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc); usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0); if (bootverbose) ieee80211_announce(ic); return (0); detach: run_detach(self); return (ENXIO); } static void run_drain_mbufq(struct run_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; RUN_LOCK_ASSERT(sc, MA_OWNED); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static int run_detach(device_t self) { struct run_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; int i; RUN_LOCK(sc); sc->sc_detached = 1; RUN_UNLOCK(sc); /* stop all USB transfers */ usbd_transfer_unsetup(sc->sc_xfer, RUN_N_XFER); RUN_LOCK(sc); sc->ratectl_run = RUN_RATECTL_OFF; sc->cmdq_run = sc->cmdq_key_set = RUN_CMDQ_ABORT; /* free TX list, if any */ for (i = 0; i != RUN_EP_QUEUES; i++) run_unsetup_tx_list(sc, &sc->sc_epq[i]); /* Free TX queue */ run_drain_mbufq(sc); RUN_UNLOCK(sc); if (sc->sc_ic.ic_softc == sc) { /* drain tasks */ usb_callout_drain(&sc->ratectl_ch); ieee80211_draintask(ic, &sc->cmdq_task); ieee80211_draintask(ic, &sc->ratectl_task); ieee80211_ifdetach(ic); } mtx_destroy(&sc->sc_mtx); return (0); } static struct ieee80211vap * run_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct run_softc *sc = ic->ic_softc; struct run_vap *rvp; struct ieee80211vap *vap; int i; if (sc->rvp_cnt >= RUN_VAP_MAX) { device_printf(sc->sc_dev, "number of VAPs maxed out\n"); return (NULL); } switch (opmode) { case IEEE80211_M_STA: /* enable s/w bmiss handling for sta mode */ flags |= IEEE80211_CLONE_NOBEACONS; /* fall though */ case IEEE80211_M_IBSS: case IEEE80211_M_MONITOR: case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: /* other than WDS vaps, only one at a time */ if (!TAILQ_EMPTY(&ic->ic_vaps)) return (NULL); break; case IEEE80211_M_WDS: TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next){ if(vap->iv_opmode != IEEE80211_M_HOSTAP) continue; /* WDS vap's always share the local mac address. */ flags &= ~IEEE80211_CLONE_BSSID; break; } if (vap == NULL) { device_printf(sc->sc_dev, "wds only supported in ap mode\n"); return (NULL); } break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); return (NULL); } rvp = malloc(sizeof(struct run_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &rvp->vap; if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) != 0) { /* out of memory */ free(rvp, M_80211_VAP); return (NULL); } vap->iv_update_beacon = run_update_beacon; vap->iv_max_aid = RT2870_WCID_MAX; /* * To delete the right key from h/w, we need wcid. * Luckily, there is unused space in ieee80211_key{}, wk_pad, * and matching wcid will be written into there. So, cast * some spells to remove 'const' from ieee80211_key{} */ vap->iv_key_delete = (void *)run_key_delete; vap->iv_key_set = (void *)run_key_set; /* override state transition machine */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = run_newstate; if (opmode == IEEE80211_M_IBSS) { rvp->recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = run_recv_mgmt; } ieee80211_ratectl_init(vap); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status, mac); /* make sure id is always unique */ for (i = 0; i < RUN_VAP_MAX; i++) { if((sc->rvp_bmap & 1 << i) == 0){ sc->rvp_bmap |= 1 << i; rvp->rvp_id = i; break; } } if (sc->rvp_cnt++ == 0) ic->ic_opmode = opmode; if (opmode == IEEE80211_M_HOSTAP) sc->cmdq_run = RUN_CMDQ_GO; DPRINTF("rvp_id=%d bmap=%x rvp_cnt=%d\n", rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt); return (vap); } static void run_vap_delete(struct ieee80211vap *vap) { struct run_vap *rvp = RUN_VAP(vap); struct ieee80211com *ic; struct run_softc *sc; uint8_t rvp_id; if (vap == NULL) return; ic = vap->iv_ic; sc = ic->ic_softc; RUN_LOCK(sc); m_freem(rvp->beacon_mbuf); rvp->beacon_mbuf = NULL; rvp_id = rvp->rvp_id; sc->ratectl_run &= ~(1 << rvp_id); sc->rvp_bmap &= ~(1 << rvp_id); run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128); run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512); --sc->rvp_cnt; DPRINTF("vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt); RUN_UNLOCK(sc); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } /* * There are numbers of functions need to be called in context thread. * Rather than creating taskqueue event for each of those functions, * here is all-for-one taskqueue callback function. This function - * gurantees deferred functions are executed in the same order they + * guarantees deferred functions are executed in the same order they * were enqueued. * '& RUN_CMDQ_MASQ' is to loop cmdq[]. */ static void run_cmdq_cb(void *arg, int pending) { struct run_softc *sc = arg; uint8_t i; /* call cmdq[].func locked */ RUN_LOCK(sc); for (i = sc->cmdq_exec; sc->cmdq[i].func && pending; i = sc->cmdq_exec, pending--) { DPRINTFN(6, "cmdq_exec=%d pending=%d\n", i, pending); if (sc->cmdq_run == RUN_CMDQ_GO) { /* * If arg0 is NULL, callback func needs more * than one arg. So, pass ptr to cmdq struct. */ if (sc->cmdq[i].arg0) sc->cmdq[i].func(sc->cmdq[i].arg0); else sc->cmdq[i].func(&sc->cmdq[i]); } sc->cmdq[i].arg0 = NULL; sc->cmdq[i].func = NULL; sc->cmdq_exec++; sc->cmdq_exec &= RUN_CMDQ_MASQ; } RUN_UNLOCK(sc); } static void run_setup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) { struct run_tx_data *data; memset(pq, 0, sizeof(*pq)); STAILQ_INIT(&pq->tx_qh); STAILQ_INIT(&pq->tx_fh); for (data = &pq->tx_data[0]; data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) { data->sc = sc; STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); } pq->tx_nfree = RUN_TX_RING_COUNT; } static void run_unsetup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) { struct run_tx_data *data; /* make sure any subsequent use of the queues will fail */ pq->tx_nfree = 0; STAILQ_INIT(&pq->tx_fh); STAILQ_INIT(&pq->tx_qh); /* free up all node references and mbufs */ for (data = &pq->tx_data[0]; data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) { if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int run_load_microcode(struct run_softc *sc) { usb_device_request_t req; const struct firmware *fw; const u_char *base; uint32_t tmp; int ntries, error; const uint64_t *temp; uint64_t bytes; RUN_UNLOCK(sc); fw = firmware_get("runfw"); RUN_LOCK(sc); if (fw == NULL) { device_printf(sc->sc_dev, "failed loadfirmware of file %s\n", "runfw"); return ENOENT; } if (fw->datasize != 8192) { device_printf(sc->sc_dev, "invalid firmware size (should be 8KB)\n"); error = EINVAL; goto fail; } /* * RT3071/RT3072 use a different firmware * run-rt2870 (8KB) contains both, * first half (4KB) is for rt2870, * last half is for rt3071. */ base = fw->data; if ((sc->mac_ver) != 0x2860 && (sc->mac_ver) != 0x2872 && (sc->mac_ver) != 0x3070) { base += 4096; } /* cheap sanity check */ temp = fw->data; bytes = *temp; if (bytes != be64toh(0xffffff0210280210ULL)) { device_printf(sc->sc_dev, "firmware checksum failed\n"); error = EINVAL; goto fail; } /* write microcode image */ if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) { run_write_region_1(sc, RT2870_FW_BASE, base, 4096); run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff); run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_RESET; USETW(req.wValue, 8); USETW(req.wIndex, 0); USETW(req.wLength, 0); if ((error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)) != 0) { device_printf(sc->sc_dev, "firmware reset failed\n"); goto fail; } run_delay(sc, 10); run_write(sc, RT2860_H2M_BBPAGENT, 0); run_write(sc, RT2860_H2M_MAILBOX, 0); run_write(sc, RT2860_H2M_INTSRC, 0); if ((error = run_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0)) != 0) goto fail; /* wait until microcontroller is ready */ for (ntries = 0; ntries < 1000; ntries++) { if ((error = run_read(sc, RT2860_SYS_CTRL, &tmp)) != 0) goto fail; if (tmp & RT2860_MCU_READY) break; run_delay(sc, 10); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for MCU to initialize\n"); error = ETIMEDOUT; goto fail; } device_printf(sc->sc_dev, "firmware %s ver. %u.%u loaded\n", (base == fw->data) ? "RT2870" : "RT3071", *(base + 4092), *(base + 4093)); fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static int run_reset(struct run_softc *sc) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_RESET; USETW(req.wValue, 1); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static usb_error_t run_do_request(struct run_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; RUN_LOCK_ASSERT(sc, MA_OWNED); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; DPRINTFN(1, "Control request failed, %s (retrying)\n", usbd_errstr(err)); run_delay(sc, 10); } return (err); } static int run_read(struct run_softc *sc, uint16_t reg, uint32_t *val) { uint32_t tmp; int error; error = run_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp); if (error == 0) *val = le32toh(tmp); else *val = 0xffffffff; return (error); } static int run_read_region_1(struct run_softc *sc, uint16_t reg, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2870_READ_REGION_1; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); return (run_do_request(sc, &req, buf)); } static int run_write_2(struct run_softc *sc, uint16_t reg, uint16_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_WRITE_2; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); return (run_do_request(sc, &req, NULL)); } static int run_write(struct run_softc *sc, uint16_t reg, uint32_t val) { int error; if ((error = run_write_2(sc, reg, val & 0xffff)) == 0) error = run_write_2(sc, reg + 2, val >> 16); return (error); } static int run_write_region_1(struct run_softc *sc, uint16_t reg, const uint8_t *buf, int len) { #if 1 int i, error = 0; /* * NB: the WRITE_REGION_1 command is not stable on RT2860. * We thus issue multiple WRITE_2 commands instead. */ KASSERT((len & 1) == 0, ("run_write_region_1: Data too long.\n")); for (i = 0; i < len && error == 0; i += 2) error = run_write_2(sc, reg + i, buf[i] | buf[i + 1] << 8); return (error); #else usb_device_request_t req; int error = 0; /* * NOTE: It appears the WRITE_REGION_1 command cannot be * passed a huge amount of data, which will crash the * firmware. Limit amount of data passed to 64-bytes at a * time. */ while (len > 0) { int delta = 64; if (delta > len) delta = len; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_WRITE_REGION_1; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, delta); error = run_do_request(sc, &req, __DECONST(uint8_t *, buf)); if (error != 0) break; reg += delta; buf += delta; len -= delta; } return (error); #endif } static int run_set_region_4(struct run_softc *sc, uint16_t reg, uint32_t val, int len) { int i, error = 0; KASSERT((len & 3) == 0, ("run_set_region_4: Invalid data length.\n")); for (i = 0; i < len && error == 0; i += 4) error = run_write(sc, reg + i, val); return (error); } static int run_efuse_read(struct run_softc *sc, uint16_t addr, uint16_t *val, int count) { uint32_t tmp; uint16_t reg; int error, ntries; if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) return (error); if (count == 2) addr *= 2; /*- * Read one 16-byte block into registers EFUSE_DATA[0-3]: * DATA0: F E D C * DATA1: B A 9 8 * DATA2: 7 6 5 4 * DATA3: 3 2 1 0 */ tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK); tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK; run_write(sc, RT3070_EFUSE_CTRL, tmp); for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) return (error); if (!(tmp & RT3070_EFSROM_KICK)) break; run_delay(sc, 2); } if (ntries == 100) return (ETIMEDOUT); if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) { *val = 0xffff; /* address not found */ return (0); } /* determine to which 32-bit register our 16-bit word belongs */ reg = RT3070_EFUSE_DATA3 - (addr & 0xc); if ((error = run_read(sc, reg, &tmp)) != 0) return (error); tmp >>= (8 * (addr & 0x3)); *val = (addr & 1) ? tmp >> 16 : tmp & 0xffff; return (0); } /* Read 16-bit from eFUSE ROM for RT3xxx. */ static int run_efuse_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) { return (run_efuse_read(sc, addr, val, 2)); } static int run_eeprom_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) { usb_device_request_t req; uint16_t tmp; int error; addr *= 2; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2870_EEPROM_READ; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, sizeof(tmp)); error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp); if (error == 0) *val = le16toh(tmp); else *val = 0xffff; return (error); } static __inline int run_srom_read(struct run_softc *sc, uint16_t addr, uint16_t *val) { /* either eFUSE ROM or EEPROM */ return sc->sc_srom_read(sc, addr, val); } static int run_rt2870_rf_write(struct run_softc *sc, uint32_t val) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_RF_CSR_CFG0, &tmp)) != 0) return (error); if (!(tmp & RT2860_RF_REG_CTRL)) break; } if (ntries == 10) return (ETIMEDOUT); return (run_write(sc, RT2860_RF_CSR_CFG0, val)); } static int run_rt3070_rf_read(struct run_softc *sc, uint8_t reg, uint8_t *val) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT3070_RF_KICK)) break; } if (ntries == 100) return (ETIMEDOUT); tmp = RT3070_RF_KICK | reg << 8; if ((error = run_write(sc, RT3070_RF_CSR_CFG, tmp)) != 0) return (error); for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT3070_RF_KICK)) break; } if (ntries == 100) return (ETIMEDOUT); *val = tmp & 0xff; return (0); } static int run_rt3070_rf_write(struct run_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT3070_RF_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val; return (run_write(sc, RT3070_RF_CSR_CFG, tmp)); } static int run_bbp_read(struct run_softc *sc, uint8_t reg, uint8_t *val) { uint32_t tmp; int ntries, error; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT2860_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = RT2860_BBP_CSR_READ | RT2860_BBP_CSR_KICK | reg << 8; if ((error = run_write(sc, RT2860_BBP_CSR_CFG, tmp)) != 0) return (error); for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT2860_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); *val = tmp & 0xff; return (0); } static int run_bbp_write(struct run_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int ntries, error; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT2860_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = RT2860_BBP_CSR_KICK | reg << 8 | val; return (run_write(sc, RT2860_BBP_CSR_CFG, tmp)); } /* * Send a command to the 8051 microcontroller unit. */ static int run_mcu_cmd(struct run_softc *sc, uint8_t cmd, uint16_t arg) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT2860_H2M_MAILBOX, &tmp)) != 0) return error; if (!(tmp & RT2860_H2M_BUSY)) break; } if (ntries == 100) return ETIMEDOUT; tmp = RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg; if ((error = run_write(sc, RT2860_H2M_MAILBOX, tmp)) == 0) error = run_write(sc, RT2860_HOST_CMD, cmd); return (error); } /* * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. * Used to adjust per-rate Tx power registers. */ static __inline uint32_t b4inc(uint32_t b32, int8_t delta) { int8_t i, b4; for (i = 0; i < 8; i++) { b4 = b32 & 0xf; b4 += delta; if (b4 < 0) b4 = 0; else if (b4 > 0xf) b4 = 0xf; b32 = b32 >> 4 | b4 << 28; } return (b32); } static const char * run_get_rf(uint16_t rev) { switch (rev) { case RT2860_RF_2820: return "RT2820"; case RT2860_RF_2850: return "RT2850"; case RT2860_RF_2720: return "RT2720"; case RT2860_RF_2750: return "RT2750"; case RT3070_RF_3020: return "RT3020"; case RT3070_RF_2020: return "RT2020"; case RT3070_RF_3021: return "RT3021"; case RT3070_RF_3022: return "RT3022"; case RT3070_RF_3052: return "RT3052"; case RT3593_RF_3053: return "RT3053"; case RT5592_RF_5592: return "RT5592"; case RT5390_RF_5370: return "RT5370"; case RT5390_RF_5372: return "RT5372"; } return ("unknown"); } static void run_rt3593_get_txpower(struct run_softc *sc) { uint16_t addr, val; int i; /* Read power settings for 2GHz channels. */ for (i = 0; i < 14; i += 2) { addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE1 : RT2860_EEPROM_PWR2GHZ_BASE1; run_srom_read(sc, addr + i / 2, &val); sc->txpow1[i + 0] = (int8_t)(val & 0xff); sc->txpow1[i + 1] = (int8_t)(val >> 8); addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE2 : RT2860_EEPROM_PWR2GHZ_BASE2; run_srom_read(sc, addr + i / 2, &val); sc->txpow2[i + 0] = (int8_t)(val & 0xff); sc->txpow2[i + 1] = (int8_t)(val >> 8); if (sc->ntxchains == 3) { run_srom_read(sc, RT3593_EEPROM_PWR2GHZ_BASE3 + i / 2, &val); sc->txpow3[i + 0] = (int8_t)(val & 0xff); sc->txpow3[i + 1] = (int8_t)(val >> 8); } } /* Fix broken Tx power entries. */ for (i = 0; i < 14; i++) { if (sc->txpow1[i] > 31) sc->txpow1[i] = 5; if (sc->txpow2[i] > 31) sc->txpow2[i] = 5; if (sc->ntxchains == 3) { if (sc->txpow3[i] > 31) sc->txpow3[i] = 5; } } /* Read power settings for 5GHz channels. */ for (i = 0; i < 40; i += 2) { run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 14] = (int8_t)(val & 0xff); sc->txpow1[i + 15] = (int8_t)(val >> 8); run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 14] = (int8_t)(val & 0xff); sc->txpow2[i + 15] = (int8_t)(val >> 8); if (sc->ntxchains == 3) { run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE3 + i / 2, &val); sc->txpow3[i + 14] = (int8_t)(val & 0xff); sc->txpow3[i + 15] = (int8_t)(val >> 8); } } } static void run_get_txpower(struct run_softc *sc) { uint16_t val; int i; /* Read power settings for 2GHz channels. */ for (i = 0; i < 14; i += 2) { run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 0] = (int8_t)(val & 0xff); sc->txpow1[i + 1] = (int8_t)(val >> 8); if (sc->mac_ver != 0x5390) { run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 0] = (int8_t)(val & 0xff); sc->txpow2[i + 1] = (int8_t)(val >> 8); } } /* Fix broken Tx power entries. */ for (i = 0; i < 14; i++) { if (sc->mac_ver >= 0x5390) { if (sc->txpow1[i] < 0 || sc->txpow1[i] > 39) sc->txpow1[i] = 5; } else { if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) sc->txpow1[i] = 5; } if (sc->mac_ver > 0x5390) { if (sc->txpow2[i] < 0 || sc->txpow2[i] > 39) sc->txpow2[i] = 5; } else if (sc->mac_ver < 0x5390) { if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) sc->txpow2[i] = 5; } DPRINTF("chan %d: power1=%d, power2=%d\n", rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]); } /* Read power settings for 5GHz channels. */ for (i = 0; i < 40; i += 2) { run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 14] = (int8_t)(val & 0xff); sc->txpow1[i + 15] = (int8_t)(val >> 8); run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 14] = (int8_t)(val & 0xff); sc->txpow2[i + 15] = (int8_t)(val >> 8); } /* Fix broken Tx power entries. */ for (i = 0; i < 40; i++ ) { if (sc->mac_ver != 0x5592) { if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) sc->txpow1[14 + i] = 5; if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) sc->txpow2[14 + i] = 5; } DPRINTF("chan %d: power1=%d, power2=%d\n", rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], sc->txpow2[14 + i]); } } static int run_read_eeprom(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int8_t delta_2ghz, delta_5ghz; uint32_t tmp; uint16_t val; int ridx, ant, i; /* check whether the ROM is eFUSE ROM or EEPROM */ sc->sc_srom_read = run_eeprom_read_2; if (sc->mac_ver >= 0x3070) { run_read(sc, RT3070_EFUSE_CTRL, &tmp); DPRINTF("EFUSE_CTRL=0x%08x\n", tmp); if ((tmp & RT3070_SEL_EFUSE) || sc->mac_ver == 0x3593) sc->sc_srom_read = run_efuse_read_2; } /* read ROM version */ run_srom_read(sc, RT2860_EEPROM_VERSION, &val); DPRINTF("EEPROM rev=%d, FAE=%d\n", val >> 8, val & 0xff); /* read MAC address */ run_srom_read(sc, RT2860_EEPROM_MAC01, &val); ic->ic_macaddr[0] = val & 0xff; ic->ic_macaddr[1] = val >> 8; run_srom_read(sc, RT2860_EEPROM_MAC23, &val); ic->ic_macaddr[2] = val & 0xff; ic->ic_macaddr[3] = val >> 8; run_srom_read(sc, RT2860_EEPROM_MAC45, &val); ic->ic_macaddr[4] = val & 0xff; ic->ic_macaddr[5] = val >> 8; if (sc->mac_ver < 0x3593) { /* read vender BBP settings */ for (i = 0; i < 10; i++) { run_srom_read(sc, RT2860_EEPROM_BBP_BASE + i, &val); sc->bbp[i].val = val & 0xff; sc->bbp[i].reg = val >> 8; DPRINTF("BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val); } if (sc->mac_ver >= 0x3071) { /* read vendor RF settings */ for (i = 0; i < 10; i++) { run_srom_read(sc, RT3071_EEPROM_RF_BASE + i, &val); sc->rf[i].val = val & 0xff; sc->rf[i].reg = val >> 8; DPRINTF("RF%d=0x%02x\n", sc->rf[i].reg, sc->rf[i].val); } } } /* read RF frequency offset from EEPROM */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : RT3593_EEPROM_FREQ, &val); sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; DPRINTF("EEPROM freq offset %d\n", sc->freq & 0xff); run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : RT3593_EEPROM_FREQ_LEDS, &val); if (val >> 8 != 0xff) { /* read LEDs operating mode */ sc->leds = val >> 8; run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED1 : RT3593_EEPROM_LED1, &sc->led[0]); run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED2 : RT3593_EEPROM_LED2, &sc->led[1]); run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED3 : RT3593_EEPROM_LED3, &sc->led[2]); } else { /* broken EEPROM, use default settings */ sc->leds = 0x01; sc->led[0] = 0x5555; sc->led[1] = 0x2221; sc->led[2] = 0x5627; /* differs from RT2860 */ } DPRINTF("EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", sc->leds, sc->led[0], sc->led[1], sc->led[2]); /* read RF information */ if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) run_srom_read(sc, 0x00, &val); else run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); if (val == 0xffff) { device_printf(sc->sc_dev, "invalid EEPROM antenna info, using default\n"); DPRINTF("invalid EEPROM antenna info, using default\n"); if (sc->mac_ver == 0x3572) { /* default to RF3052 2T2R */ sc->rf_rev = RT3070_RF_3052; sc->ntxchains = 2; sc->nrxchains = 2; } else if (sc->mac_ver >= 0x3070) { /* default to RF3020 1T1R */ sc->rf_rev = RT3070_RF_3020; sc->ntxchains = 1; sc->nrxchains = 1; } else { /* default to RF2820 1T2R */ sc->rf_rev = RT2860_RF_2820; sc->ntxchains = 1; sc->nrxchains = 2; } } else { if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) { sc->rf_rev = val; run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); } else sc->rf_rev = (val >> 8) & 0xf; sc->ntxchains = (val >> 4) & 0xf; sc->nrxchains = val & 0xf; } DPRINTF("EEPROM RF rev=0x%04x chains=%dT%dR\n", sc->rf_rev, sc->ntxchains, sc->nrxchains); /* check if RF supports automatic Tx access gain control */ run_srom_read(sc, RT2860_EEPROM_CONFIG, &val); DPRINTF("EEPROM CFG 0x%04x\n", val); /* check if driver should patch the DAC issue */ if ((val >> 8) != 0xff) sc->patch_dac = (val >> 15) & 1; if ((val & 0xff) != 0xff) { sc->ext_5ghz_lna = (val >> 3) & 1; sc->ext_2ghz_lna = (val >> 2) & 1; /* check if RF supports automatic Tx access gain control */ sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1; /* check if we have a hardware radio switch */ sc->rfswitch = val & 1; } /* Read Tx power settings. */ if (sc->mac_ver == 0x3593) run_rt3593_get_txpower(sc); else run_get_txpower(sc); /* read Tx power compensation for each Tx rate */ run_srom_read(sc, RT2860_EEPROM_DELTAPWR, &val); delta_2ghz = delta_5ghz = 0; if ((val & 0xff) != 0xff && (val & 0x80)) { delta_2ghz = val & 0xf; if (!(val & 0x40)) /* negative number */ delta_2ghz = -delta_2ghz; } val >>= 8; if ((val & 0xff) != 0xff && (val & 0x80)) { delta_5ghz = val & 0xf; if (!(val & 0x40)) /* negative number */ delta_5ghz = -delta_5ghz; } DPRINTF("power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz); for (ridx = 0; ridx < 5; ridx++) { uint32_t reg; run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val); reg = val; run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1, &val); reg |= (uint32_t)val << 16; sc->txpow20mhz[ridx] = reg; sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); DPRINTF("ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]); } /* Read RSSI offsets and LNA gains from EEPROM. */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_2GHZ : RT3593_EEPROM_RSSI1_2GHZ, &val); sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ sc->rssi_2ghz[1] = val >> 8; /* Ant B */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_2GHZ : RT3593_EEPROM_RSSI2_2GHZ, &val); if (sc->mac_ver >= 0x3070) { if (sc->mac_ver == 0x3593) { sc->txmixgain_2ghz = 0; sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ } else { /* * On RT3070 chips (limited to 2 Rx chains), this ROM * field contains the Tx mixer gain for the 2GHz band. */ if ((val & 0xff) != 0xff) sc->txmixgain_2ghz = val & 0x7; } DPRINTF("tx mixer gain=%u (2GHz)\n", sc->txmixgain_2ghz); } else sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ if (sc->mac_ver == 0x3593) run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); sc->lna[2] = val >> 8; /* channel group 2 */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_5GHZ : RT3593_EEPROM_RSSI1_5GHZ, &val); sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ sc->rssi_5ghz[1] = val >> 8; /* Ant B */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_5GHZ : RT3593_EEPROM_RSSI2_5GHZ, &val); if (sc->mac_ver == 0x3572) { /* * On RT3572 chips (limited to 2 Rx chains), this ROM * field contains the Tx mixer gain for the 5GHz band. */ if ((val & 0xff) != 0xff) sc->txmixgain_5ghz = val & 0x7; DPRINTF("tx mixer gain=%u (5GHz)\n", sc->txmixgain_5ghz); } else sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ if (sc->mac_ver == 0x3593) { sc->txmixgain_5ghz = 0; run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); } sc->lna[3] = val >> 8; /* channel group 3 */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LNA : RT3593_EEPROM_LNA, &val); sc->lna[0] = val & 0xff; /* channel group 0 */ sc->lna[1] = val >> 8; /* channel group 1 */ /* fix broken 5GHz LNA entries */ if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { DPRINTF("invalid LNA for channel group %d\n", 2); sc->lna[2] = sc->lna[1]; } if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { DPRINTF("invalid LNA for channel group %d\n", 3); sc->lna[3] = sc->lna[1]; } /* fix broken RSSI offset entries */ for (ant = 0; ant < 3; ant++) { if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { DPRINTF("invalid RSSI%d offset: %d (2GHz)\n", ant + 1, sc->rssi_2ghz[ant]); sc->rssi_2ghz[ant] = 0; } if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { DPRINTF("invalid RSSI%d offset: %d (5GHz)\n", ant + 1, sc->rssi_5ghz[ant]); sc->rssi_5ghz[ant] = 0; } } return (0); } static struct ieee80211_node * run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return malloc(sizeof (struct run_node), M_DEVBUF, M_NOWAIT | M_ZERO); } static int run_media_change(struct ifnet *ifp) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_txparam *tp; struct run_softc *sc = ic->ic_softc; uint8_t rate, ridx; int error; RUN_LOCK(sc); error = ieee80211_media_change(ifp); if (error != ENETRESET) { RUN_UNLOCK(sc); return (error); } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { struct ieee80211_node *ni; struct run_node *rn; rate = ic->ic_sup_rates[ic->ic_curmode]. rs_rates[tp->ucastrate] & IEEE80211_RATE_VAL; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; ni = ieee80211_ref_node(vap->iv_bss); rn = RUN_NODE(ni); rn->fix_ridx = ridx; DPRINTF("rate=%d, fix_ridx=%d\n", rate, rn->fix_ridx); ieee80211_free_node(ni); } #if 0 if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & RUN_RUNNING)){ run_init_locked(sc); } #endif RUN_UNLOCK(sc); return (0); } static int run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { const struct ieee80211_txparam *tp; struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct run_vap *rvp = RUN_VAP(vap); enum ieee80211_state ostate; uint32_t sta[3]; uint32_t tmp; uint8_t ratectl; uint8_t restart_ratectl = 0; uint8_t bid = 1 << rvp->rvp_id; ostate = vap->iv_state; DPRINTF("%s -> %s\n", ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); RUN_LOCK(sc); ratectl = sc->ratectl_run; /* remember current state */ sc->ratectl_run = RUN_RATECTL_OFF; usb_callout_stop(&sc->ratectl_ch); if (ostate == IEEE80211_S_RUN) { /* turn link LED off */ run_set_leds(sc, RT2860_LED_RADIO); } switch (nstate) { case IEEE80211_S_INIT: restart_ratectl = 1; if (ostate != IEEE80211_S_RUN) break; ratectl &= ~bid; sc->runbmap &= ~bid; /* abort TSF synchronization if there is no vap running */ if (--sc->running == 0) { run_read(sc, RT2860_BCN_TIME_CFG, &tmp); run_write(sc, RT2860_BCN_TIME_CFG, tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN)); } break; case IEEE80211_S_RUN: if (!(sc->runbmap & bid)) { if(sc->running++) restart_ratectl = 1; sc->runbmap |= bid; } m_freem(rvp->beacon_mbuf); rvp->beacon_mbuf = NULL; switch (vap->iv_opmode) { case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: sc->ap_running |= bid; ic->ic_opmode = vap->iv_opmode; run_update_beacon_cb(vap); break; case IEEE80211_M_IBSS: sc->adhoc_running |= bid; if (!sc->ap_running) ic->ic_opmode = vap->iv_opmode; run_update_beacon_cb(vap); break; case IEEE80211_M_STA: sc->sta_running |= bid; if (!sc->ap_running && !sc->adhoc_running) ic->ic_opmode = vap->iv_opmode; /* read statistic counters (clear on read) */ run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta, sizeof sta); break; default: ic->ic_opmode = vap->iv_opmode; break; } if (vap->iv_opmode != IEEE80211_M_MONITOR) { struct ieee80211_node *ni; if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { RUN_UNLOCK(sc); IEEE80211_LOCK(ic); return (-1); } run_updateslot(ic); run_enable_mrr(sc); run_set_txpreamble(sc); run_set_basicrates(sc); ni = ieee80211_ref_node(vap->iv_bss); IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); run_set_bssid(sc, sc->sc_bssid); ieee80211_free_node(ni); run_enable_tsf_sync(sc); /* enable automatic rate adaptation */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) ratectl |= bid; } else run_enable_tsf(sc); /* turn link LED on */ run_set_leds(sc, RT2860_LED_RADIO | (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); break; default: DPRINTFN(6, "undefined case\n"); break; } /* restart amrr for running VAPs */ if ((sc->ratectl_run = ratectl) && restart_ratectl) usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); RUN_UNLOCK(sc); IEEE80211_LOCK(ic); return(rvp->newstate(vap, nstate, arg)); } static int run_wme_update(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; const struct wmeParams *ac = ic->ic_wme.wme_chanParams.cap_wmeParams; int aci, error = 0; /* update MAC TX configuration registers */ RUN_LOCK(sc); for (aci = 0; aci < WME_NUM_AC; aci++) { error = run_write(sc, RT2860_EDCA_AC_CFG(aci), ac[aci].wmep_logcwmax << 16 | ac[aci].wmep_logcwmin << 12 | ac[aci].wmep_aifsn << 8 | ac[aci].wmep_txopLimit); if (error) goto err; } /* update SCH/DMA registers too */ error = run_write(sc, RT2860_WMM_AIFSN_CFG, ac[WME_AC_VO].wmep_aifsn << 12 | ac[WME_AC_VI].wmep_aifsn << 8 | ac[WME_AC_BK].wmep_aifsn << 4 | ac[WME_AC_BE].wmep_aifsn); if (error) goto err; error = run_write(sc, RT2860_WMM_CWMIN_CFG, ac[WME_AC_VO].wmep_logcwmin << 12 | ac[WME_AC_VI].wmep_logcwmin << 8 | ac[WME_AC_BK].wmep_logcwmin << 4 | ac[WME_AC_BE].wmep_logcwmin); if (error) goto err; error = run_write(sc, RT2860_WMM_CWMAX_CFG, ac[WME_AC_VO].wmep_logcwmax << 12 | ac[WME_AC_VI].wmep_logcwmax << 8 | ac[WME_AC_BK].wmep_logcwmax << 4 | ac[WME_AC_BE].wmep_logcwmax); if (error) goto err; error = run_write(sc, RT2860_WMM_TXOP0_CFG, ac[WME_AC_BK].wmep_txopLimit << 16 | ac[WME_AC_BE].wmep_txopLimit); if (error) goto err; error = run_write(sc, RT2860_WMM_TXOP1_CFG, ac[WME_AC_VO].wmep_txopLimit << 16 | ac[WME_AC_VI].wmep_txopLimit); err: RUN_UNLOCK(sc); if (error) DPRINTF("WME update failed\n"); return (error); } static void run_key_set_cb(void *arg) { struct run_cmdq *cmdq = arg; struct ieee80211vap *vap = cmdq->arg1; struct ieee80211_key *k = cmdq->k; struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct ieee80211_node *ni; u_int cipher = k->wk_cipher->ic_cipher; uint32_t attr; uint16_t base, associd; uint8_t mode, wcid, iv[8]; RUN_LOCK_ASSERT(sc, MA_OWNED); if (vap->iv_opmode == IEEE80211_M_HOSTAP) ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac); else ni = vap->iv_bss; associd = (ni != NULL) ? ni->ni_associd : 0; /* map net80211 cipher to RT2860 security mode */ switch (cipher) { case IEEE80211_CIPHER_WEP: if(k->wk_keylen < 8) mode = RT2860_MODE_WEP40; else mode = RT2860_MODE_WEP104; break; case IEEE80211_CIPHER_TKIP: mode = RT2860_MODE_TKIP; break; case IEEE80211_CIPHER_AES_CCM: mode = RT2860_MODE_AES_CCMP; break; default: DPRINTF("undefined case\n"); return; } DPRINTFN(1, "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n", associd, k->wk_keyix, mode, (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise", (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); if (k->wk_flags & IEEE80211_KEY_GROUP) { wcid = 0; /* NB: update WCID0 for group keys */ base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix); } else { wcid = (vap->iv_opmode == IEEE80211_M_STA) ? 1 : RUN_AID2WCID(associd); base = RT2860_PKEY(wcid); } if (cipher == IEEE80211_CIPHER_TKIP) { if(run_write_region_1(sc, base, k->wk_key, 16)) return; if(run_write_region_1(sc, base + 16, &k->wk_key[16], 8)) /* wk_txmic */ return; if(run_write_region_1(sc, base + 24, &k->wk_key[24], 8)) /* wk_rxmic */ return; } else { /* roundup len to 16-bit: XXX fix write_region_1() instead */ if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1)) return; } if (!(k->wk_flags & IEEE80211_KEY_GROUP) || (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) { /* set initial packet number in IV+EIV */ if (cipher == IEEE80211_CIPHER_WEP) { memset(iv, 0, sizeof iv); iv[3] = vap->iv_def_txkey << 6; } else { if (cipher == IEEE80211_CIPHER_TKIP) { iv[0] = k->wk_keytsc >> 8; iv[1] = (iv[0] | 0x20) & 0x7f; iv[2] = k->wk_keytsc; } else /* CCMP */ { iv[0] = k->wk_keytsc; iv[1] = k->wk_keytsc >> 8; iv[2] = 0; } iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV; iv[4] = k->wk_keytsc >> 16; iv[5] = k->wk_keytsc >> 24; iv[6] = k->wk_keytsc >> 32; iv[7] = k->wk_keytsc >> 40; } if (run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8)) return; } if (k->wk_flags & IEEE80211_KEY_GROUP) { /* install group key */ if (run_read(sc, RT2860_SKEY_MODE_0_7, &attr)) return; attr &= ~(0xf << (k->wk_keyix * 4)); attr |= mode << (k->wk_keyix * 4); if (run_write(sc, RT2860_SKEY_MODE_0_7, attr)) return; } else { /* install pairwise key */ if (run_read(sc, RT2860_WCID_ATTR(wcid), &attr)) return; attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; if (run_write(sc, RT2860_WCID_ATTR(wcid), attr)) return; } /* TODO create a pass-thru key entry? */ /* need wcid to delete the right key later */ k->wk_pad = wcid; } /* * Don't have to be deferred, but in order to keep order of * execution, i.e. with run_key_delete(), defer this and let * run_cmdq_cb() maintain the order. * * return 0 on error */ static int run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; uint32_t i; i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", i); sc->cmdq[i].func = run_key_set_cb; sc->cmdq[i].arg0 = NULL; sc->cmdq[i].arg1 = vap; sc->cmdq[i].k = k; IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr); ieee80211_runtask(ic, &sc->cmdq_task); /* * To make sure key will be set when hostapd * calls iv_key_set() before if_init(). */ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { RUN_LOCK(sc); sc->cmdq_key_set = RUN_CMDQ_GO; RUN_UNLOCK(sc); } return (1); } /* * If wlan is destroyed without being brought down i.e. without * wlan down or wpa_cli terminate, this function is called after * vap is gone. Don't refer it. */ static void run_key_delete_cb(void *arg) { struct run_cmdq *cmdq = arg; struct run_softc *sc = cmdq->arg1; struct ieee80211_key *k = &cmdq->key; uint32_t attr; uint8_t wcid; RUN_LOCK_ASSERT(sc, MA_OWNED); if (k->wk_flags & IEEE80211_KEY_GROUP) { /* remove group key */ DPRINTF("removing group key\n"); run_read(sc, RT2860_SKEY_MODE_0_7, &attr); attr &= ~(0xf << (k->wk_keyix * 4)); run_write(sc, RT2860_SKEY_MODE_0_7, attr); } else { /* remove pairwise key */ DPRINTF("removing key for wcid %x\n", k->wk_pad); /* matching wcid was written to wk_pad in run_key_set() */ wcid = k->wk_pad; run_read(sc, RT2860_WCID_ATTR(wcid), &attr); attr &= ~0xf; run_write(sc, RT2860_WCID_ATTR(wcid), attr); run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8); } k->wk_pad = 0; } /* * return 0 on error */ static int run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct ieee80211_key *k0; uint32_t i; /* * When called back, key might be gone. So, make a copy * of some values need to delete keys before deferring. * But, because of LOR with node lock, cannot use lock here. * So, use atomic instead. */ i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", i); sc->cmdq[i].func = run_key_delete_cb; sc->cmdq[i].arg0 = NULL; sc->cmdq[i].arg1 = sc; k0 = &sc->cmdq[i].key; k0->wk_flags = k->wk_flags; k0->wk_keyix = k->wk_keyix; /* matching wcid was written to wk_pad in run_key_set() */ k0->wk_pad = k->wk_pad; ieee80211_runtask(ic, &sc->cmdq_task); return (1); /* return fake success */ } static void run_ratectl_to(void *arg) { struct run_softc *sc = arg; /* do it in a process context, so it can go sleep */ ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task); /* next timeout will be rescheduled in the callback task */ } /* ARGSUSED */ static void run_ratectl_cb(void *arg, int pending) { struct run_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap == NULL) return; if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) { /* * run_reset_livelock() doesn't do anything with AMRR, * but Ralink wants us to call it every 1 sec. So, we * piggyback here rather than creating another callout. * Livelock may occur only in HOSTAP or IBSS mode * (when h/w is sending beacons). */ RUN_LOCK(sc); run_reset_livelock(sc); /* just in case, there are some stats to drain */ run_drain_fifo(sc); RUN_UNLOCK(sc); } ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc); RUN_LOCK(sc); if(sc->ratectl_run != RUN_RATECTL_OFF) usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); RUN_UNLOCK(sc); } static void run_drain_fifo(void *arg) { struct run_softc *sc = arg; uint32_t stat; uint16_t (*wstat)[3]; uint8_t wcid, mcs, pid; int8_t retry; RUN_LOCK_ASSERT(sc, MA_OWNED); for (;;) { /* drain Tx status FIFO (maxsize = 16) */ run_read(sc, RT2860_TX_STAT_FIFO, &stat); DPRINTFN(4, "tx stat 0x%08x\n", stat); if (!(stat & RT2860_TXQ_VLD)) break; wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff; /* if no ACK was requested, no feedback is available */ if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX || wcid == 0) continue; /* * Even though each stat is Tx-complete-status like format, * the device can poll stats. Because there is no guarantee * that the referring node is still around when read the stats. * So that, if we use ieee80211_ratectl_tx_update(), we will * have hard time not to refer already freed node. * * To eliminate such page faults, we poll stats in softc. * Then, update the rates later with ieee80211_ratectl_tx_update(). */ wstat = &(sc->wcid_stats[wcid]); (*wstat)[RUN_TXCNT]++; if (stat & RT2860_TXQ_OK) (*wstat)[RUN_SUCCESS]++; else counter_u64_add(sc->sc_ic.ic_oerrors, 1); /* * Check if there were retries, ie if the Tx success rate is * different from the requested rate. Note that it works only * because we do not allow rate fallback from OFDM to CCK. */ mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; if ((retry = pid -1 - mcs) > 0) { (*wstat)[RUN_TXCNT] += retry; (*wstat)[RUN_RETRY] += retry; } } DPRINTFN(3, "count=%d\n", sc->fifo_cnt); sc->fifo_cnt = 0; } static void run_iter_func(void *arg, struct ieee80211_node *ni) { struct run_softc *sc = arg; struct ieee80211vap *vap = ni->ni_vap; struct run_node *rn = RUN_NODE(ni); union run_stats sta[2]; uint16_t (*wstat)[3]; int txcnt, success, retrycnt, error; RUN_LOCK(sc); /* Check for special case */ if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA && ni != vap->iv_bss) goto fail; if (sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_STA)) { /* read statistic counters (clear on read) and update AMRR state */ error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta, sizeof sta); if (error != 0) goto fail; /* count failed TX as errors */ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, le16toh(sta[0].error.fail)); retrycnt = le16toh(sta[1].tx.retry); success = le16toh(sta[1].tx.success); txcnt = retrycnt + success + le16toh(sta[0].error.fail); DPRINTFN(3, "retrycnt=%d success=%d failcnt=%d\n", retrycnt, success, le16toh(sta[0].error.fail)); } else { wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]); if (wstat == &(sc->wcid_stats[0]) || wstat > &(sc->wcid_stats[RT2870_WCID_MAX])) goto fail; txcnt = (*wstat)[RUN_TXCNT]; success = (*wstat)[RUN_SUCCESS]; retrycnt = (*wstat)[RUN_RETRY]; DPRINTFN(3, "retrycnt=%d txcnt=%d success=%d\n", retrycnt, txcnt, success); memset(wstat, 0, sizeof(*wstat)); } ieee80211_ratectl_tx_update(vap, ni, &txcnt, &success, &retrycnt); rn->amrr_ridx = ieee80211_ratectl_rate(ni, NULL, 0); fail: RUN_UNLOCK(sc); DPRINTFN(3, "ridx=%d\n", rn->amrr_ridx); } static void run_newassoc_cb(void *arg) { struct run_cmdq *cmdq = arg; struct ieee80211_node *ni = cmdq->arg1; struct run_softc *sc = ni->ni_vap->iv_ic->ic_softc; uint8_t wcid = cmdq->wcid; RUN_LOCK_ASSERT(sc, MA_OWNED); run_write_region_1(sc, RT2860_WCID_ENTRY(wcid), ni->ni_macaddr, IEEE80211_ADDR_LEN); memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid])); } static void run_newassoc(struct ieee80211_node *ni, int isnew) { struct run_node *rn = RUN_NODE(ni); struct ieee80211_rateset *rs = &ni->ni_rates; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; uint8_t rate; uint8_t ridx; uint8_t wcid; int i, j; wcid = (vap->iv_opmode == IEEE80211_M_STA) ? 1 : RUN_AID2WCID(ni->ni_associd); if (wcid > RT2870_WCID_MAX) { device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid); return; } /* only interested in true associations */ if (isnew && ni->ni_associd != 0) { /* * This function could is called though timeout function. * Need to defer. */ uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", cnt); sc->cmdq[cnt].func = run_newassoc_cb; sc->cmdq[cnt].arg0 = NULL; sc->cmdq[cnt].arg1 = ni; sc->cmdq[cnt].wcid = wcid; ieee80211_runtask(ic, &sc->cmdq_task); } DPRINTF("new assoc isnew=%d associd=%x addr=%s\n", isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr)); for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; /* convert 802.11 rate to hardware rate index */ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; rn->ridx[i] = ridx; /* determine rate of control response frames */ for (j = i; j >= 0; j--) { if ((rs->rs_rates[j] & IEEE80211_RATE_BASIC) && rt2860_rates[rn->ridx[i]].phy == rt2860_rates[rn->ridx[j]].phy) break; } if (j >= 0) { rn->ctl_ridx[i] = rn->ridx[j]; } else { /* no basic rate found, use mandatory one */ rn->ctl_ridx[i] = rt2860_rates[ridx].ctl_ridx; } DPRINTF("rate=0x%02x ridx=%d ctl_ridx=%d\n", rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i]); } rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; rn->mgt_ridx = ridx; DPRINTF("rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx); RUN_LOCK(sc); if(sc->ratectl_run != RUN_RATECTL_OFF) usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); RUN_UNLOCK(sc); } /* * Return the Rx chain with the highest RSSI for a given frame. */ static __inline uint8_t run_maxrssi_chain(struct run_softc *sc, const struct rt2860_rxwi *rxwi) { uint8_t rxchain = 0; if (sc->nrxchains > 1) { if (rxwi->rssi[1] > rxwi->rssi[rxchain]) rxchain = 1; if (sc->nrxchains > 2) if (rxwi->rssi[2] > rxwi->rssi[rxchain]) rxchain = 2; } return (rxchain); } static void run_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; struct run_softc *sc = vap->iv_ic->ic_softc; struct run_vap *rvp = RUN_VAP(vap); uint64_t ni_tstamp, rx_tstamp; rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); if (vap->iv_state == IEEE80211_S_RUN && (subtype == IEEE80211_FC0_SUBTYPE_BEACON || subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { ni_tstamp = le64toh(ni->ni_tstamp.tsf); RUN_LOCK(sc); run_get_tsf(sc, &rx_tstamp); RUN_UNLOCK(sc); rx_tstamp = le64toh(rx_tstamp); if (ni_tstamp >= rx_tstamp) { DPRINTF("ibss merge, tsf %ju tstamp %ju\n", (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); (void) ieee80211_ibss_merge(ni); } } } static void run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct rt2870_rxd *rxd; struct rt2860_rxwi *rxwi; uint32_t flags; uint16_t len, rxwisize; uint8_t ant, rssi; int8_t nf; rxwi = mtod(m, struct rt2860_rxwi *); len = le16toh(rxwi->len) & 0xfff; rxwisize = sizeof(struct rt2860_rxwi); if (sc->mac_ver == 0x5592) rxwisize += sizeof(uint64_t); else if (sc->mac_ver == 0x3593) rxwisize += sizeof(uint32_t); if (__predict_false(len > dmalen)) { m_freem(m); counter_u64_add(ic->ic_ierrors, 1); DPRINTF("bad RXWI length %u > %u\n", len, dmalen); return; } /* Rx descriptor is located at the end */ rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen); flags = le32toh(rxd->flags); if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { m_freem(m); counter_u64_add(ic->ic_ierrors, 1); DPRINTF("%s error.\n", (flags & RT2860_RX_CRCERR)?"CRC":"ICV"); return; } m->m_data += rxwisize; m->m_pkthdr.len = m->m_len -= rxwisize; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; m->m_flags |= M_WEP; } if (flags & RT2860_RX_L2PAD) { DPRINTFN(8, "received RT2860_RX_L2PAD frame\n"); len += 2; } ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (__predict_false(flags & RT2860_RX_MICERR)) { /* report MIC failures to net80211 for TKIP */ if (ni != NULL) ieee80211_notify_michael_failure(ni->ni_vap, wh, rxwi->keyidx); m_freem(m); counter_u64_add(ic->ic_ierrors, 1); DPRINTF("MIC error. Someone is lying.\n"); return; } ant = run_maxrssi_chain(sc, rxwi); rssi = rxwi->rssi[ant]; nf = run_rssi2dbm(sc, rssi, ant); m->m_pkthdr.len = m->m_len = len; if (__predict_false(ieee80211_radiotap_active(ic))) { struct run_rx_radiotap_header *tap = &sc->sc_rxtap; uint16_t phy; tap->wr_flags = 0; tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antsignal = rssi; tap->wr_antenna = ant; tap->wr_dbm_antsignal = run_rssi2dbm(sc, rssi, ant); tap->wr_rate = 2; /* in case it can't be found below */ run_get_tsf(sc, &tap->wr_tsf); phy = le16toh(rxwi->phy); switch (phy & RT2860_PHY_MODE) { case RT2860_PHY_CCK: switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) { case 0: tap->wr_rate = 2; break; case 1: tap->wr_rate = 4; break; case 2: tap->wr_rate = 11; break; case 3: tap->wr_rate = 22; break; } if (phy & RT2860_PHY_SHPRE) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; break; case RT2860_PHY_OFDM: switch (phy & RT2860_PHY_MCS) { case 0: tap->wr_rate = 12; break; case 1: tap->wr_rate = 18; break; case 2: tap->wr_rate = 24; break; case 3: tap->wr_rate = 36; break; case 4: tap->wr_rate = 48; break; case 5: tap->wr_rate = 72; break; case 6: tap->wr_rate = 96; break; case 7: tap->wr_rate = 108; break; } break; } } if (ni != NULL) { (void)ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else { (void)ieee80211_input_all(ic, m, rssi, nf); } } static void run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct run_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m = NULL; struct mbuf *m0; uint32_t dmalen; uint16_t rxwisize; int xferlen; rxwisize = sizeof(struct rt2860_rxwi); if (sc->mac_ver == 0x5592) rxwisize += sizeof(uint64_t); else if (sc->mac_ver == 0x3593) rxwisize += sizeof(uint32_t); usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xferlen); if (xferlen < (int)(sizeof(uint32_t) + rxwisize + sizeof(struct rt2870_rxd))) { DPRINTF("xfer too short %d\n", xferlen); goto tr_setup; } m = sc->rx_m; sc->rx_m = NULL; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if (sc->rx_m == NULL) { sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE /* xfer can be bigger than MCLBYTES */); } if (sc->rx_m == NULL) { DPRINTF("could not allocate mbuf - idle with stall\n"); counter_u64_add(ic->ic_ierrors, 1); usbd_xfer_set_stall(xfer); usbd_xfer_set_frames(xfer, 0); } else { /* * Directly loading a mbuf cluster into DMA to * save some data copying. This works because * there is only one cluster. */ usbd_xfer_set_frame_data(xfer, 0, mtod(sc->rx_m, caddr_t), RUN_MAX_RXSZ); usbd_xfer_set_frames(xfer, 1); } usbd_transfer_submit(xfer); break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); if (error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } if (sc->rx_m != NULL) { m_freem(sc->rx_m); sc->rx_m = NULL; } break; } if (m == NULL) return; /* inputting all the frames must be last */ RUN_UNLOCK(sc); m->m_pkthdr.len = m->m_len = xferlen; /* HW can aggregate multiple 802.11 frames in a single USB xfer */ for(;;) { dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff; if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || ((dmalen & 3) != 0)) { DPRINTF("bad DMA length %u\n", dmalen); break; } if ((dmalen + 8) > (uint32_t)xferlen) { DPRINTF("bad DMA length %u > %d\n", dmalen + 8, xferlen); break; } /* If it is the last one or a single frame, we won't copy. */ if ((xferlen -= dmalen + 8) <= 8) { /* trim 32-bit DMA-len header */ m->m_data += 4; m->m_pkthdr.len = m->m_len -= 4; run_rx_frame(sc, m, dmalen); m = NULL; /* don't free source buffer */ break; } /* copy aggregated frames to another mbuf */ m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m0 == NULL)) { DPRINTF("could not allocate mbuf\n"); counter_u64_add(ic->ic_ierrors, 1); break; } m_copydata(m, 4 /* skip 32-bit DMA-len header */, dmalen + sizeof(struct rt2870_rxd), mtod(m0, caddr_t)); m0->m_pkthdr.len = m0->m_len = dmalen + sizeof(struct rt2870_rxd); run_rx_frame(sc, m0, dmalen); /* update data ptr */ m->m_data += dmalen + 8; m->m_pkthdr.len = m->m_len -= dmalen + 8; } /* make sure we free the source buffer, if any */ m_freem(m); RUN_LOCK(sc); } static void run_tx_free(struct run_endpoint_queue *pq, struct run_tx_data *data, int txerr) { ieee80211_tx_complete(data->ni, data->m, txerr); data->m = NULL; data->ni = NULL; STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); pq->tx_nfree++; } static void run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) { struct run_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct run_tx_data *data; struct ieee80211vap *vap = NULL; struct usb_page_cache *pc; struct run_endpoint_queue *pq = &sc->sc_epq[index]; struct mbuf *m; usb_frlength_t size; int actlen; int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete: %d " "bytes @ index %d\n", actlen, index); data = usbd_xfer_get_priv(xfer); run_tx_free(pq, data, 0); usbd_xfer_set_priv(xfer, NULL); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&pq->tx_qh); if (data == NULL) break; STAILQ_REMOVE_HEAD(&pq->tx_qh, next); m = data->m; size = (sc->mac_ver == 0x5592) ? sizeof(data->desc) + sizeof(uint32_t) : sizeof(data->desc); if ((m->m_pkthdr.len + size + 3 + 8) > RUN_MAX_TXSZ) { DPRINTF("data overflow, %u bytes\n", m->m_pkthdr.len); run_tx_free(pq, data, 1); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &data->desc, size); usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len); size += m->m_pkthdr.len; /* * Align end on a 4-byte boundary, pad 8 bytes (CRC + * 4-byte padding), and be sure to zero those trailing * bytes: */ usbd_frame_zero(pc, size, ((-size) & 3) + 8); size += ((-size) & 3) + 8; vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { struct run_tx_radiotap_header *tap = &sc->sc_txtap; struct rt2860_txwi *txwi = (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_txd)); tap->wt_flags = 0; tap->wt_rate = rt2860_rates[data->ridx].rate; run_get_tsf(sc, &tap->wt_tsf); tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_hwqueue = index; if (le16toh(txwi->phy) & RT2860_PHY_SHPRE) tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; ieee80211_radiotap_tx(vap, m); } DPRINTFN(11, "sending frame len=%u/%u @ index %d\n", m->m_pkthdr.len, size, index); usbd_xfer_set_frame_len(xfer, 0, size); usbd_xfer_set_priv(xfer, data); usbd_transfer_submit(xfer); run_start(sc); break; default: DPRINTF("USB transfer error, %s\n", usbd_errstr(error)); data = usbd_xfer_get_priv(xfer); if (data != NULL) { if(data->ni != NULL) vap = data->ni->ni_vap; run_tx_free(pq, data, error); usbd_xfer_set_priv(xfer, NULL); } if (vap == NULL) vap = TAILQ_FIRST(&ic->ic_vaps); if (error != USB_ERR_CANCELLED) { if (error == USB_ERR_TIMEOUT) { device_printf(sc->sc_dev, "device timeout\n"); uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", i); sc->cmdq[i].func = run_usb_timeout_cb; sc->cmdq[i].arg0 = vap; ieee80211_runtask(ic, &sc->cmdq_task); } /* * Try to clear stall first, also if other * errors occur, hence clearing stall * introduces a 50 ms delay: */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void run_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 0); } static void run_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 1); } static void run_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 2); } static void run_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 3); } static void run_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 4); } static void run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 5); } static void run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data) { struct mbuf *m = data->m; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = data->ni->ni_vap; struct ieee80211_frame *wh; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint16_t xferlen, txwisize; uint16_t mcs; uint8_t ridx = data->ridx; uint8_t pad; /* get MCS code from rate index */ mcs = rt2860_rates[ridx].mcs; txwisize = (sc->mac_ver == 0x5592) ? sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi); xferlen = txwisize + m->m_pkthdr.len; /* roundup to 32-bit alignment */ xferlen = (xferlen + 3) & ~3; txd = (struct rt2870_txd *)&data->desc; txd->len = htole16(xferlen); wh = mtod(m, struct ieee80211_frame *); /* * Ether both are true or both are false, the header * are nicely aligned to 32-bit. So, no L2 padding. */ if(IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh)) pad = 0; else pad = 2; /* setup TX Wireless Information */ txwi = (struct rt2860_txwi *)(txd + 1); txwi->len = htole16(m->m_pkthdr.len - pad); if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { mcs |= RT2860_PHY_CCK; if (ridx != RT2860_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) mcs |= RT2860_PHY_SHPRE; } else mcs |= RT2860_PHY_OFDM; txwi->phy = htole16(mcs); /* check if RTS/CTS or CTS-to-self protection is required */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold || ((ic->ic_flags & IEEE80211_F_USEPROT) && rt2860_rates[ridx].phy == IEEE80211_T_OFDM))) txwi->txop |= RT2860_TX_TXOP_HT; else txwi->txop |= RT2860_TX_TXOP_BACKOFF; if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh)) txwi->xflags |= RT2860_TX_NSEQ; } /* This function must be called locked */ static int run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct ieee80211_channel *chan; const struct ieee80211_txparam *tp; struct run_node *rn = RUN_NODE(ni); struct run_tx_data *data; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint16_t qos; uint16_t dur; uint16_t qid; uint8_t type; uint8_t tid; uint8_t ridx; uint8_t ctl_ridx; uint8_t qflags; uint8_t xflags = 0; int hasqos; RUN_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* * There are 7 bulk endpoints: 1 for RX * and 6 for TX (4 EDCAs + HCCA + Prio). * Update 03-14-2009: some devices like the Planex GW-US300MiniS * seem to have only 4 TX bulk endpoints (Fukaumi Naoki). */ if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { uint8_t *frm; if(IEEE80211_HAS_ADDR4(wh)) frm = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos; else frm =((struct ieee80211_qosframe *)wh)->i_qos; qos = le16toh(*(const uint16_t *)frm); tid = qos & IEEE80211_QOS_TID; qid = TID_TO_WME_AC(tid); } else { qos = 0; tid = 0; qid = WME_AC_BE; } qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA; DPRINTFN(8, "qos %d\tqid %d\ttid %d\tqflags %x\n", qos, qid, tid, qflags); chan = (ni->ni_chan != IEEE80211_CHAN_ANYC)?ni->ni_chan:ic->ic_curchan; tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; /* pickup a rate index */ if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) { ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; ctl_ridx = rt2860_rates[ridx].ctl_ridx; } else { if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) ridx = rn->fix_ridx; else ridx = rn->amrr_ridx; ctl_ridx = rt2860_rates[ridx].ctl_ridx; } if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK)) { xflags |= RT2860_TX_ACK; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) dur = rt2860_rates[ctl_ridx].sp_ack_dur; else dur = rt2860_rates[ctl_ridx].lp_ack_dur; USETW(wh->i_dur, dur); } /* reserve slots for mgmt packets, just in case */ if (sc->sc_epq[qid].tx_nfree < 3) { DPRINTFN(10, "tx ring %d is full\n", qid); return (-1); } data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next); sc->sc_epq[qid].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = qflags; txwi = (struct rt2860_txwi *)(txd + 1); txwi->xflags = xflags; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txwi->wcid = 0; else txwi->wcid = (vap->iv_opmode == IEEE80211_M_STA) ? 1 : RUN_AID2WCID(ni->ni_associd); /* clear leftover garbage bits */ txwi->flags = 0; txwi->txop = 0; data->m = m; data->ni = ni; data->ridx = ridx; run_set_tx_desc(sc, data); /* * The chip keeps track of 2 kind of Tx stats, * * TX_STAT_FIFO, for per WCID stats, and * * TX_STA_CNT0 for all-TX-in-one stats. * * To use FIFO stats, we need to store MCS into the driver-private * PacketID field. So that, we can tell whose stats when we read them. * We add 1 to the MCS because setting the PacketID field to 0 means * that we don't want feedback in TX_STAT_FIFO. * And, that's what we want for STA mode, since TX_STA_CNT0 does the job. * * FIFO stats doesn't count Tx with WCID 0xff, so we do this in run_tx(). */ if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf; txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); /* * Unlike PCI based devices, we don't get any interrupt from * USB devices, so we simulate FIFO-is-full interrupt here. - * Ralink recomends to drain FIFO stats every 100 ms, but 16 slots + * Ralink recommends to drain FIFO stats every 100 ms, but 16 slots * quickly get fulled. To prevent overflow, increment a counter on * every FIFO stat request, so we know how many slots are left. * We do this only in HOSTAP or multiple vap mode since FIFO stats * are used only in those modes. * We just drain stats. AMRR gets updated every 1 sec by * run_ratectl_cb() via callout. * Call it early. Otherwise overflow. */ if (sc->fifo_cnt++ == 10) { /* * With multiple vaps or if_bridge, if_start() is called * with a non-sleepable lock, tcpinp. So, need to defer. */ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTFN(6, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_drain_fifo; sc->cmdq[i].arg0 = sc; ieee80211_runtask(ic, &sc->cmdq_task); } } STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[qid]); DPRINTFN(8, "sending data frame len=%d rate=%d qid=%d\n", m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate, qid); return (0); } static int run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct run_node *rn = RUN_NODE(ni); struct run_tx_data *data; struct ieee80211_frame *wh; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint16_t dur; uint8_t ridx = rn->mgt_ridx; uint8_t type; uint8_t xflags = 0; uint8_t wflags = 0; RUN_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) wflags |= RT2860_TX_TS; else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { xflags |= RT2860_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } if (sc->sc_epq[0].tx_nfree == 0) /* let caller free mbuf */ return (EIO); data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct rt2860_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->flags = wflags; txwi->xflags = xflags; txwi->txop = 0; /* clear leftover garbage bits */ data->m = m; data->ni = ni; data->ridx = ridx; run_set_tx_desc(sc, data); DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int run_sendprot(struct run_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; struct run_tx_data *data; struct rt2870_txd *txd; struct rt2860_txwi *txwi; struct mbuf *mprot; int ridx; int protrate; int ackrate; int pktlen; int isshort; uint16_t dur; uint8_t type; uint8_t wflags = 0; uint8_t xflags = 0; RUN_LOCK_ASSERT(sc, MA_OWNED); KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("protection %d", prot)); wh = mtod(m, struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; protrate = ieee80211_ctl_rate(ic->ic_rt, rate); ackrate = ieee80211_ack_rate(ic->ic_rt, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort) + ieee80211_ack_duration(ic->ic_rt, rate, isshort); wflags = RT2860_TX_FRAG; /* check that there are free slots before allocating the mbuf */ if (sc->sc_epq[0].tx_nfree == 0) /* let caller free mbuf */ return (ENOBUFS); if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); xflags |= RT2860_TX_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); DPRINTF("could not allocate mbuf\n"); return (ENOBUFS); } data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct rt2860_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->flags = wflags; txwi->xflags = xflags; txwi->txop = 0; /* clear leftover garbage bits */ data->m = mprot; data->ni = ieee80211_ref_node(ni); for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == protrate) break; data->ridx = ridx; run_set_tx_desc(sc, data); DPRINTFN(1, "sending prot len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; struct run_tx_data *data; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint8_t type; uint8_t ridx; uint8_t rate; uint8_t opflags = 0; uint8_t xflags = 0; int error; RUN_LOCK_ASSERT(sc, MA_OWNED); KASSERT(params != NULL, ("no raw xmit params")); wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; rate = params->ibp_rate0; if (!ieee80211_isratevalid(ic->ic_rt, rate)) { /* let caller free mbuf */ return (EINVAL); } if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) xflags |= RT2860_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = run_sendprot(sc, m, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error) { /* let caller free mbuf */ return error; } opflags |= /*XXX RT2573_TX_LONG_RETRY |*/ RT2860_TX_TXOP_SIFS; } if (sc->sc_epq[0].tx_nfree == 0) { /* let caller free mbuf */ DPRINTF("sending raw frame, but tx ring is full\n"); return (EIO); } data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct rt2860_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->xflags = xflags; txwi->txop = opflags; txwi->flags = 0; /* clear leftover garbage bits */ data->m = m; data->ni = ni; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; data->ridx = ridx; run_set_tx_desc(sc, data); DPRINTFN(10, "sending raw frame len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct run_softc *sc = ni->ni_ic->ic_softc; int error = 0; RUN_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & RUN_RUNNING)) { error = ENETDOWN; goto done; } if (params == NULL) { /* tx mgt packet */ if ((error = run_tx_mgt(sc, m, ni)) != 0) { DPRINTF("mgt tx failed\n"); goto done; } } else { /* tx raw packet with param */ if ((error = run_tx_param(sc, m, ni, params)) != 0) { DPRINTF("tx with param failed\n"); goto done; } } done: RUN_UNLOCK(sc); if (error != 0) { if(m != NULL) m_freem(m); } return (error); } static int run_transmit(struct ieee80211com *ic, struct mbuf *m) { struct run_softc *sc = ic->ic_softc; int error; RUN_LOCK(sc); if ((sc->sc_flags & RUN_RUNNING) == 0) { RUN_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { RUN_UNLOCK(sc); return (error); } run_start(sc); RUN_UNLOCK(sc); return (0); } static void run_start(struct run_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; RUN_LOCK_ASSERT(sc, MA_OWNED); if ((sc->sc_flags & RUN_RUNNING) == 0) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (run_tx(sc, m, ni) != 0) { mbufq_prepend(&sc->sc_snd, m); break; } } } static void run_parent(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; int startall = 0; RUN_LOCK(sc); if (sc->sc_detached) { RUN_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (!(sc->sc_flags & RUN_RUNNING)) { startall = 1; run_init_locked(sc); } else run_update_promisc_locked(sc); } else if ((sc->sc_flags & RUN_RUNNING) && sc->rvp_cnt <= 1) run_stop(sc); RUN_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static void run_iq_calib(struct run_softc *sc, u_int chan) { uint16_t val; /* Tx0 IQ gain. */ run_bbp_write(sc, 158, 0x2c); if (chan <= 14) run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ, &val, 1); else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* Tx0 IQ phase. */ run_bbp_write(sc, 158, 0x2d); if (chan <= 14) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ, &val, 1); } else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* Tx1 IQ gain. */ run_bbp_write(sc, 158, 0x4a); if (chan <= 14) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ, &val, 1); } else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* Tx1 IQ phase. */ run_bbp_write(sc, 158, 0x4b); if (chan <= 14) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ, &val, 1); } else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* RF IQ compensation control. */ run_bbp_write(sc, 158, 0x04); run_efuse_read(sc, RT5390_EEPROM_RF_IQ_COMPENSATION_CTL, &val, 1); run_bbp_write(sc, 159, val); /* RF IQ imbalance compensation control. */ run_bbp_write(sc, 158, 0x03); run_efuse_read(sc, RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL, &val, 1); run_bbp_write(sc, 159, val); } static void run_set_agc(struct run_softc *sc, uint8_t agc) { uint8_t bbp; if (sc->mac_ver == 0x3572) { run_bbp_read(sc, 27, &bbp); bbp &= ~(0x3 << 5); run_bbp_write(sc, 27, bbp | 0 << 5); /* select Rx0 */ run_bbp_write(sc, 66, agc); run_bbp_write(sc, 27, bbp | 1 << 5); /* select Rx1 */ run_bbp_write(sc, 66, agc); } else run_bbp_write(sc, 66, agc); } static void run_select_chan_group(struct run_softc *sc, int group) { uint32_t tmp; uint8_t agc; run_bbp_write(sc, 62, 0x37 - sc->lna[group]); run_bbp_write(sc, 63, 0x37 - sc->lna[group]); run_bbp_write(sc, 64, 0x37 - sc->lna[group]); if (sc->mac_ver < 0x3572) run_bbp_write(sc, 86, 0x00); if (sc->mac_ver == 0x3593) { run_bbp_write(sc, 77, 0x98); run_bbp_write(sc, 83, (group == 0) ? 0x8a : 0x9a); } if (group == 0) { if (sc->ext_2ghz_lna) { if (sc->mac_ver >= 0x5390) run_bbp_write(sc, 75, 0x52); else { run_bbp_write(sc, 82, 0x62); run_bbp_write(sc, 75, 0x46); } } else { if (sc->mac_ver == 0x5592) { run_bbp_write(sc, 79, 0x1c); run_bbp_write(sc, 80, 0x0e); run_bbp_write(sc, 81, 0x3a); run_bbp_write(sc, 82, 0x62); run_bbp_write(sc, 195, 0x80); run_bbp_write(sc, 196, 0xe0); run_bbp_write(sc, 195, 0x81); run_bbp_write(sc, 196, 0x1f); run_bbp_write(sc, 195, 0x82); run_bbp_write(sc, 196, 0x38); run_bbp_write(sc, 195, 0x83); run_bbp_write(sc, 196, 0x32); run_bbp_write(sc, 195, 0x85); run_bbp_write(sc, 196, 0x28); run_bbp_write(sc, 195, 0x86); run_bbp_write(sc, 196, 0x19); } else if (sc->mac_ver >= 0x5390) run_bbp_write(sc, 75, 0x50); else { run_bbp_write(sc, 82, (sc->mac_ver == 0x3593) ? 0x62 : 0x84); run_bbp_write(sc, 75, 0x50); } } } else { if (sc->mac_ver == 0x5592) { run_bbp_write(sc, 79, 0x18); run_bbp_write(sc, 80, 0x08); run_bbp_write(sc, 81, 0x38); run_bbp_write(sc, 82, 0x92); run_bbp_write(sc, 195, 0x80); run_bbp_write(sc, 196, 0xf0); run_bbp_write(sc, 195, 0x81); run_bbp_write(sc, 196, 0x1e); run_bbp_write(sc, 195, 0x82); run_bbp_write(sc, 196, 0x28); run_bbp_write(sc, 195, 0x83); run_bbp_write(sc, 196, 0x20); run_bbp_write(sc, 195, 0x85); run_bbp_write(sc, 196, 0x7f); run_bbp_write(sc, 195, 0x86); run_bbp_write(sc, 196, 0x7f); } else if (sc->mac_ver == 0x3572) run_bbp_write(sc, 82, 0x94); else run_bbp_write(sc, 82, (sc->mac_ver == 0x3593) ? 0x82 : 0xf2); if (sc->ext_5ghz_lna) run_bbp_write(sc, 75, 0x46); else run_bbp_write(sc, 75, 0x50); } run_read(sc, RT2860_TX_BAND_CFG, &tmp); tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; run_write(sc, RT2860_TX_BAND_CFG, tmp); /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN; if (sc->mac_ver == 0x3593) tmp |= 1 << 29 | 1 << 28; if (sc->nrxchains > 1) tmp |= RT2860_LNA_PE1_EN; if (group == 0) { /* 2GHz */ tmp |= RT2860_PA_PE_G0_EN; if (sc->ntxchains > 1) tmp |= RT2860_PA_PE_G1_EN; if (sc->mac_ver == 0x3593) { if (sc->ntxchains > 2) tmp |= 1 << 25; } } else { /* 5GHz */ tmp |= RT2860_PA_PE_A0_EN; if (sc->ntxchains > 1) tmp |= RT2860_PA_PE_A1_EN; } if (sc->mac_ver == 0x3572) { run_rt3070_rf_write(sc, 8, 0x00); run_write(sc, RT2860_TX_PIN_CFG, tmp); run_rt3070_rf_write(sc, 8, 0x80); } else run_write(sc, RT2860_TX_PIN_CFG, tmp); if (sc->mac_ver == 0x5592) { run_bbp_write(sc, 195, 0x8d); run_bbp_write(sc, 196, 0x1a); } if (sc->mac_ver == 0x3593) { run_read(sc, RT2860_GPIO_CTRL, &tmp); tmp &= ~0x01010000; if (group == 0) tmp |= 0x00010000; tmp = (tmp & ~0x00009090) | 0x00000090; run_write(sc, RT2860_GPIO_CTRL, tmp); } /* set initial AGC value */ if (group == 0) { /* 2GHz band */ if (sc->mac_ver >= 0x3070) agc = 0x1c + sc->lna[0] * 2; else agc = 0x2e + sc->lna[0]; } else { /* 5GHz band */ if (sc->mac_ver == 0x5592) agc = 0x24 + sc->lna[group] * 2; else if (sc->mac_ver == 0x3572 || sc->mac_ver == 0x3593) agc = 0x22 + (sc->lna[group] * 5) / 3; else agc = 0x32 + (sc->lna[group] * 5) / 3; } run_set_agc(sc, agc); } static void run_rt2870_set_chan(struct run_softc *sc, u_int chan) { const struct rfprog *rfprog = rt2860_rf2850; uint32_t r2, r3, r4; int8_t txpow1, txpow2; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rfprog[i].chan != chan; i++); r2 = rfprog[i].r2; if (sc->ntxchains == 1) r2 |= 1 << 14; /* 1T: disable Tx chain 2 */ if (sc->nrxchains == 1) r2 |= 1 << 17 | 1 << 6; /* 1R: disable Rx chains 2 & 3 */ else if (sc->nrxchains == 2) r2 |= 1 << 6; /* 2R: disable Rx chain 3 */ /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; /* Initialize RF R3 and R4. */ r3 = rfprog[i].r3 & 0xffffc1ff; r4 = (rfprog[i].r4 & ~(0x001f87c0)) | (sc->freq << 15); if (chan > 14) { if (txpow1 >= 0) { txpow1 = (txpow1 > 0xf) ? (0xf) : (txpow1); r3 |= (txpow1 << 10) | (1 << 9); } else { txpow1 += 7; /* txpow1 is not possible larger than 15. */ r3 |= (txpow1 << 10); } if (txpow2 >= 0) { txpow2 = (txpow2 > 0xf) ? (0xf) : (txpow2); r4 |= (txpow2 << 7) | (1 << 6); } else { txpow2 += 7; r4 |= (txpow2 << 7); } } else { /* Set Tx0 power. */ r3 |= (txpow1 << 9); /* Set frequency offset and Tx1 power. */ r4 |= (txpow2 << 6); } run_rt2870_rf_write(sc, rfprog[i].r1); run_rt2870_rf_write(sc, r2); run_rt2870_rf_write(sc, r3 & ~(1 << 2)); run_rt2870_rf_write(sc, r4); run_delay(sc, 10); run_rt2870_rf_write(sc, rfprog[i].r1); run_rt2870_rf_write(sc, r2); run_rt2870_rf_write(sc, r3 | (1 << 2)); run_rt2870_rf_write(sc, r4); run_delay(sc, 10); run_rt2870_rf_write(sc, rfprog[i].r1); run_rt2870_rf_write(sc, r2); run_rt2870_rf_write(sc, r3 & ~(1 << 2)); run_rt2870_rf_write(sc, r4); } static void run_rt3070_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2; uint8_t rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); /* RT3370/RT3390: RF R3 [7:4] is not reserved bits. */ run_rt3070_rf_read(sc, 3, &rf); rf = (rf & ~0x0f) | rt3070_freqs[i].k; run_rt3070_rf_write(sc, 3, rf); run_rt3070_rf_read(sc, 6, &rf); rf = (rf & ~0x03) | rt3070_freqs[i].r; run_rt3070_rf_write(sc, 6, rf); /* set Tx0 power */ run_rt3070_rf_read(sc, 12, &rf); rf = (rf & ~0x1f) | txpow1; run_rt3070_rf_write(sc, 12, rf); /* set Tx1 power */ run_rt3070_rf_read(sc, 13, &rf); rf = (rf & ~0x1f) | txpow2; run_rt3070_rf_write(sc, 13, rf); run_rt3070_rf_read(sc, 1, &rf); rf &= ~0xfc; if (sc->ntxchains == 1) rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ else if (sc->ntxchains == 2) rf |= 1 << 7; /* 2T: disable Tx chain 3 */ if (sc->nrxchains == 1) rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ else if (sc->nrxchains == 2) rf |= 1 << 6; /* 2R: disable Rx chain 3 */ run_rt3070_rf_write(sc, 1, rf); /* set RF offset */ run_rt3070_rf_read(sc, 23, &rf); rf = (rf & ~0x7f) | sc->freq; run_rt3070_rf_write(sc, 23, rf); /* program RF filter */ run_rt3070_rf_read(sc, 24, &rf); /* Tx */ rf = (rf & ~0x3f) | sc->rf24_20mhz; run_rt3070_rf_write(sc, 24, rf); run_rt3070_rf_read(sc, 31, &rf); /* Rx */ rf = (rf & ~0x3f) | sc->rf24_20mhz; run_rt3070_rf_write(sc, 31, rf); /* enable RF tuning */ run_rt3070_rf_read(sc, 7, &rf); run_rt3070_rf_write(sc, 7, rf | 0x01); } static void run_rt3572_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2; uint32_t tmp; uint8_t rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; if (chan <= 14) { run_bbp_write(sc, 25, sc->bbp25); run_bbp_write(sc, 26, sc->bbp26); } else { /* enable IQ phase correction */ run_bbp_write(sc, 25, 0x09); run_bbp_write(sc, 26, 0xff); } run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); run_rt3070_rf_write(sc, 3, rt3070_freqs[i].k); run_rt3070_rf_read(sc, 6, &rf); rf = (rf & ~0x0f) | rt3070_freqs[i].r; rf |= (chan <= 14) ? 0x08 : 0x04; run_rt3070_rf_write(sc, 6, rf); /* set PLL mode */ run_rt3070_rf_read(sc, 5, &rf); rf &= ~(0x08 | 0x04); rf |= (chan <= 14) ? 0x04 : 0x08; run_rt3070_rf_write(sc, 5, rf); /* set Tx power for chain 0 */ if (chan <= 14) rf = 0x60 | txpow1; else rf = 0xe0 | (txpow1 & 0xc) << 1 | (txpow1 & 0x3); run_rt3070_rf_write(sc, 12, rf); /* set Tx power for chain 1 */ if (chan <= 14) rf = 0x60 | txpow2; else rf = 0xe0 | (txpow2 & 0xc) << 1 | (txpow2 & 0x3); run_rt3070_rf_write(sc, 13, rf); /* set Tx/Rx streams */ run_rt3070_rf_read(sc, 1, &rf); rf &= ~0xfc; if (sc->ntxchains == 1) rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ else if (sc->ntxchains == 2) rf |= 1 << 7; /* 2T: disable Tx chain 3 */ if (sc->nrxchains == 1) rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ else if (sc->nrxchains == 2) rf |= 1 << 6; /* 2R: disable Rx chain 3 */ run_rt3070_rf_write(sc, 1, rf); /* set RF offset */ run_rt3070_rf_read(sc, 23, &rf); rf = (rf & ~0x7f) | sc->freq; run_rt3070_rf_write(sc, 23, rf); /* program RF filter */ rf = sc->rf24_20mhz; run_rt3070_rf_write(sc, 24, rf); /* Tx */ run_rt3070_rf_write(sc, 31, rf); /* Rx */ /* enable RF tuning */ run_rt3070_rf_read(sc, 7, &rf); rf = (chan <= 14) ? 0xd8 : ((rf & ~0xc8) | 0x14); run_rt3070_rf_write(sc, 7, rf); /* TSSI */ rf = (chan <= 14) ? 0xc3 : 0xc0; run_rt3070_rf_write(sc, 9, rf); /* set loop filter 1 */ run_rt3070_rf_write(sc, 10, 0xf1); /* set loop filter 2 */ run_rt3070_rf_write(sc, 11, (chan <= 14) ? 0xb9 : 0x00); /* set tx_mx2_ic */ run_rt3070_rf_write(sc, 15, (chan <= 14) ? 0x53 : 0x43); /* set tx_mx1_ic */ if (chan <= 14) rf = 0x48 | sc->txmixgain_2ghz; else rf = 0x78 | sc->txmixgain_5ghz; run_rt3070_rf_write(sc, 16, rf); /* set tx_lo1 */ run_rt3070_rf_write(sc, 17, 0x23); /* set tx_lo2 */ if (chan <= 14) rf = 0x93; else if (chan <= 64) rf = 0xb7; else if (chan <= 128) rf = 0x74; else rf = 0x72; run_rt3070_rf_write(sc, 19, rf); /* set rx_lo1 */ if (chan <= 14) rf = 0xb3; else if (chan <= 64) rf = 0xf6; else if (chan <= 128) rf = 0xf4; else rf = 0xf3; run_rt3070_rf_write(sc, 20, rf); /* set pfd_delay */ if (chan <= 14) rf = 0x15; else if (chan <= 64) rf = 0x3d; else rf = 0x01; run_rt3070_rf_write(sc, 25, rf); /* set rx_lo2 */ run_rt3070_rf_write(sc, 26, (chan <= 14) ? 0x85 : 0x87); /* set ldo_rf_vc */ run_rt3070_rf_write(sc, 27, (chan <= 14) ? 0x00 : 0x01); /* set drv_cc */ run_rt3070_rf_write(sc, 29, (chan <= 14) ? 0x9b : 0x9f); run_read(sc, RT2860_GPIO_CTRL, &tmp); tmp &= ~0x8080; if (chan <= 14) tmp |= 0x80; run_write(sc, RT2860_GPIO_CTRL, tmp); /* enable RF tuning */ run_rt3070_rf_read(sc, 7, &rf); run_rt3070_rf_write(sc, 7, rf | 0x01); run_delay(sc, 2); } static void run_rt3593_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2, txpow3; uint8_t h20mhz, rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; txpow3 = (sc->ntxchains == 3) ? sc->txpow3[i] : 0; if (chan <= 14) { run_bbp_write(sc, 25, sc->bbp25); run_bbp_write(sc, 26, sc->bbp26); } else { /* Enable IQ phase correction. */ run_bbp_write(sc, 25, 0x09); run_bbp_write(sc, 26, 0xff); } run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); run_rt3070_rf_read(sc, 11, &rf); rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); run_rt3070_rf_write(sc, 11, rf); /* Set pll_idoh. */ run_rt3070_rf_read(sc, 11, &rf); rf &= ~0x4c; rf |= (chan <= 14) ? 0x44 : 0x48; run_rt3070_rf_write(sc, 11, rf); if (chan <= 14) rf = txpow1 & 0x1f; else rf = 0x40 | ((txpow1 & 0x18) << 1) | (txpow1 & 0x07); run_rt3070_rf_write(sc, 53, rf); if (chan <= 14) rf = txpow2 & 0x1f; else rf = 0x40 | ((txpow2 & 0x18) << 1) | (txpow2 & 0x07); run_rt3070_rf_write(sc, 55, rf); if (chan <= 14) rf = txpow3 & 0x1f; else rf = 0x40 | ((txpow3 & 0x18) << 1) | (txpow3 & 0x07); run_rt3070_rf_write(sc, 54, rf); rf = RT3070_RF_BLOCK | RT3070_PLL_PD; if (sc->ntxchains == 3) rf |= RT3070_TX0_PD | RT3070_TX1_PD | RT3070_TX2_PD; else rf |= RT3070_TX0_PD | RT3070_TX1_PD; rf |= RT3070_RX0_PD | RT3070_RX1_PD | RT3070_RX2_PD; run_rt3070_rf_write(sc, 1, rf); run_adjust_freq_offset(sc); run_rt3070_rf_write(sc, 31, (chan <= 14) ? 0xa0 : 0x80); h20mhz = (sc->rf24_20mhz & 0x20) >> 5; run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2); run_rt3070_rf_write(sc, 30, rf); run_rt3070_rf_read(sc, 36, &rf); if (chan <= 14) rf |= 0x80; else rf &= ~0x80; run_rt3070_rf_write(sc, 36, rf); /* Set vcolo_bs. */ run_rt3070_rf_write(sc, 34, (chan <= 14) ? 0x3c : 0x20); /* Set pfd_delay. */ run_rt3070_rf_write(sc, 12, (chan <= 14) ? 0x1a : 0x12); /* Set vco bias current control. */ run_rt3070_rf_read(sc, 6, &rf); rf &= ~0xc0; if (chan <= 14) rf |= 0x40; else if (chan <= 128) rf |= 0x80; else rf |= 0x40; run_rt3070_rf_write(sc, 6, rf); run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x18) | 0x10; run_rt3070_rf_write(sc, 30, rf); run_rt3070_rf_write(sc, 10, (chan <= 14) ? 0xd3 : 0xd8); run_rt3070_rf_write(sc, 13, (chan <= 14) ? 0x12 : 0x23); run_rt3070_rf_read(sc, 51, &rf); rf = (rf & ~0x03) | 0x01; run_rt3070_rf_write(sc, 51, rf); /* Set tx_mx1_cc. */ run_rt3070_rf_read(sc, 51, &rf); rf &= ~0x1c; rf |= (chan <= 14) ? 0x14 : 0x10; run_rt3070_rf_write(sc, 51, rf); /* Set tx_mx1_ic. */ run_rt3070_rf_read(sc, 51, &rf); rf &= ~0xe0; rf |= (chan <= 14) ? 0x60 : 0x40; run_rt3070_rf_write(sc, 51, rf); /* Set tx_lo1_ic. */ run_rt3070_rf_read(sc, 49, &rf); rf &= ~0x1c; rf |= (chan <= 14) ? 0x0c : 0x08; run_rt3070_rf_write(sc, 49, rf); /* Set tx_lo1_en. */ run_rt3070_rf_read(sc, 50, &rf); run_rt3070_rf_write(sc, 50, rf & ~0x20); /* Set drv_cc. */ run_rt3070_rf_read(sc, 57, &rf); rf &= ~0xfc; rf |= (chan <= 14) ? 0x6c : 0x3c; run_rt3070_rf_write(sc, 57, rf); /* Set rx_mix1_ic, rxa_lnactr, lna_vc, lna_inbias_en and lna_en. */ run_rt3070_rf_write(sc, 44, (chan <= 14) ? 0x93 : 0x9b); /* Set drv_gnd_a, tx_vga_cc_a and tx_mx2_gain. */ run_rt3070_rf_write(sc, 52, (chan <= 14) ? 0x45 : 0x05); /* Enable VCO calibration. */ run_rt3070_rf_read(sc, 3, &rf); rf &= ~RT5390_VCOCAL; rf |= (chan <= 14) ? RT5390_VCOCAL : 0xbe; run_rt3070_rf_write(sc, 3, rf); if (chan <= 14) rf = 0x23; else if (chan <= 64) rf = 0x36; else if (chan <= 128) rf = 0x32; else rf = 0x30; run_rt3070_rf_write(sc, 39, rf); if (chan <= 14) rf = 0xbb; else if (chan <= 64) rf = 0xeb; else if (chan <= 128) rf = 0xb3; else rf = 0x9b; run_rt3070_rf_write(sc, 45, rf); /* Set FEQ/AEQ control. */ run_bbp_write(sc, 105, 0x34); } static void run_rt5390_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2; uint8_t rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); run_rt3070_rf_read(sc, 11, &rf); rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); run_rt3070_rf_write(sc, 11, rf); run_rt3070_rf_read(sc, 49, &rf); rf = (rf & ~0x3f) | (txpow1 & 0x3f); /* The valid range of the RF R49 is 0x00 to 0x27. */ if ((rf & 0x3f) > 0x27) rf = (rf & ~0x3f) | 0x27; run_rt3070_rf_write(sc, 49, rf); if (sc->mac_ver == 0x5392) { run_rt3070_rf_read(sc, 50, &rf); rf = (rf & ~0x3f) | (txpow2 & 0x3f); /* The valid range of the RF R50 is 0x00 to 0x27. */ if ((rf & 0x3f) > 0x27) rf = (rf & ~0x3f) | 0x27; run_rt3070_rf_write(sc, 50, rf); } run_rt3070_rf_read(sc, 1, &rf); rf |= RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD; if (sc->mac_ver == 0x5392) rf |= RT3070_RX1_PD | RT3070_TX1_PD; run_rt3070_rf_write(sc, 1, rf); if (sc->mac_ver != 0x5392) { run_rt3070_rf_read(sc, 2, &rf); rf |= 0x80; run_rt3070_rf_write(sc, 2, rf); run_delay(sc, 10); rf &= 0x7f; run_rt3070_rf_write(sc, 2, rf); } run_adjust_freq_offset(sc); if (sc->mac_ver == 0x5392) { /* Fix for RT5392C. */ if (sc->mac_rev >= 0x0223) { if (chan <= 4) rf = 0x0f; else if (chan >= 5 && chan <= 7) rf = 0x0e; else rf = 0x0d; run_rt3070_rf_write(sc, 23, rf); if (chan <= 4) rf = 0x0c; else if (chan == 5) rf = 0x0b; else if (chan >= 6 && chan <= 7) rf = 0x0a; else if (chan >= 8 && chan <= 10) rf = 0x09; else rf = 0x08; run_rt3070_rf_write(sc, 59, rf); } else { if (chan <= 11) rf = 0x0f; else rf = 0x0b; run_rt3070_rf_write(sc, 59, rf); } } else { /* Fix for RT5390F. */ if (sc->mac_rev >= 0x0502) { if (chan <= 11) rf = 0x43; else rf = 0x23; run_rt3070_rf_write(sc, 55, rf); if (chan <= 11) rf = 0x0f; else if (chan == 12) rf = 0x0d; else rf = 0x0b; run_rt3070_rf_write(sc, 59, rf); } else { run_rt3070_rf_write(sc, 55, 0x44); run_rt3070_rf_write(sc, 59, 0x8f); } } /* Enable VCO calibration. */ run_rt3070_rf_read(sc, 3, &rf); rf |= RT5390_VCOCAL; run_rt3070_rf_write(sc, 3, rf); } static void run_rt5592_set_chan(struct run_softc *sc, u_int chan) { const struct rt5592_freqs *freqs; uint32_t tmp; uint8_t reg, rf, txpow_bound; int8_t txpow1, txpow2; int i; run_read(sc, RT5592_DEBUG_INDEX, &tmp); freqs = (tmp & RT5592_SEL_XTAL) ? rt5592_freqs_40mhz : rt5592_freqs_20mhz; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++, freqs++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; run_read(sc, RT3070_LDO_CFG0, &tmp); tmp &= ~0x1c000000; if (chan > 14) tmp |= 0x14000000; run_write(sc, RT3070_LDO_CFG0, tmp); /* N setting. */ run_rt3070_rf_write(sc, 8, freqs->n & 0xff); run_rt3070_rf_read(sc, 9, &rf); rf &= ~(1 << 4); rf |= ((freqs->n & 0x0100) >> 8) << 4; run_rt3070_rf_write(sc, 9, rf); /* K setting. */ run_rt3070_rf_read(sc, 9, &rf); rf &= ~0x0f; rf |= (freqs->k & 0x0f); run_rt3070_rf_write(sc, 9, rf); /* Mode setting. */ run_rt3070_rf_read(sc, 11, &rf); rf &= ~0x0c; rf |= ((freqs->m - 0x8) & 0x3) << 2; run_rt3070_rf_write(sc, 11, rf); run_rt3070_rf_read(sc, 9, &rf); rf &= ~(1 << 7); rf |= (((freqs->m - 0x8) & 0x4) >> 2) << 7; run_rt3070_rf_write(sc, 9, rf); /* R setting. */ run_rt3070_rf_read(sc, 11, &rf); rf &= ~0x03; rf |= (freqs->r - 0x1); run_rt3070_rf_write(sc, 11, rf); if (chan <= 14) { /* Initialize RF registers for 2GHZ. */ for (i = 0; i < nitems(rt5592_2ghz_def_rf); i++) { run_rt3070_rf_write(sc, rt5592_2ghz_def_rf[i].reg, rt5592_2ghz_def_rf[i].val); } rf = (chan <= 10) ? 0x07 : 0x06; run_rt3070_rf_write(sc, 23, rf); run_rt3070_rf_write(sc, 59, rf); run_rt3070_rf_write(sc, 55, 0x43); /* * RF R49/R50 Tx power ALC code. * G-band bit<7:6>=1:0, bit<5:0> range from 0x0 ~ 0x27. */ reg = 2; txpow_bound = 0x27; } else { /* Initialize RF registers for 5GHZ. */ for (i = 0; i < nitems(rt5592_5ghz_def_rf); i++) { run_rt3070_rf_write(sc, rt5592_5ghz_def_rf[i].reg, rt5592_5ghz_def_rf[i].val); } for (i = 0; i < nitems(rt5592_chan_5ghz); i++) { if (chan >= rt5592_chan_5ghz[i].firstchan && chan <= rt5592_chan_5ghz[i].lastchan) { run_rt3070_rf_write(sc, rt5592_chan_5ghz[i].reg, rt5592_chan_5ghz[i].val); } } /* * RF R49/R50 Tx power ALC code. * A-band bit<7:6>=1:1, bit<5:0> range from 0x0 ~ 0x2b. */ reg = 3; txpow_bound = 0x2b; } /* RF R49 ch0 Tx power ALC code. */ run_rt3070_rf_read(sc, 49, &rf); rf &= ~0xc0; rf |= (reg << 6); rf = (rf & ~0x3f) | (txpow1 & 0x3f); if ((rf & 0x3f) > txpow_bound) rf = (rf & ~0x3f) | txpow_bound; run_rt3070_rf_write(sc, 49, rf); /* RF R50 ch1 Tx power ALC code. */ run_rt3070_rf_read(sc, 50, &rf); rf &= ~(1 << 7 | 1 << 6); rf |= (reg << 6); rf = (rf & ~0x3f) | (txpow2 & 0x3f); if ((rf & 0x3f) > txpow_bound) rf = (rf & ~0x3f) | txpow_bound; run_rt3070_rf_write(sc, 50, rf); /* Enable RF_BLOCK, PLL_PD, RX0_PD, and TX0_PD. */ run_rt3070_rf_read(sc, 1, &rf); rf |= (RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD); if (sc->ntxchains > 1) rf |= RT3070_TX1_PD; if (sc->nrxchains > 1) rf |= RT3070_RX1_PD; run_rt3070_rf_write(sc, 1, rf); run_rt3070_rf_write(sc, 6, 0xe4); run_rt3070_rf_write(sc, 30, 0x10); run_rt3070_rf_write(sc, 31, 0x80); run_rt3070_rf_write(sc, 32, 0x80); run_adjust_freq_offset(sc); /* Enable VCO calibration. */ run_rt3070_rf_read(sc, 3, &rf); rf |= RT5390_VCOCAL; run_rt3070_rf_write(sc, 3, rf); } static void run_set_rx_antenna(struct run_softc *sc, int aux) { uint32_t tmp; uint8_t bbp152; if (aux) { if (sc->rf_rev == RT5390_RF_5370) { run_bbp_read(sc, 152, &bbp152); run_bbp_write(sc, 152, bbp152 & ~0x80); } else { run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 0); run_read(sc, RT2860_GPIO_CTRL, &tmp); run_write(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08); } } else { if (sc->rf_rev == RT5390_RF_5370) { run_bbp_read(sc, 152, &bbp152); run_bbp_write(sc, 152, bbp152 | 0x80); } else { run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 1); run_read(sc, RT2860_GPIO_CTRL, &tmp); run_write(sc, RT2860_GPIO_CTRL, tmp & ~0x0808); } } } static int run_set_chan(struct run_softc *sc, struct ieee80211_channel *c) { struct ieee80211com *ic = &sc->sc_ic; u_int chan, group; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return (EINVAL); if (sc->mac_ver == 0x5592) run_rt5592_set_chan(sc, chan); else if (sc->mac_ver >= 0x5390) run_rt5390_set_chan(sc, chan); else if (sc->mac_ver == 0x3593) run_rt3593_set_chan(sc, chan); else if (sc->mac_ver == 0x3572) run_rt3572_set_chan(sc, chan); else if (sc->mac_ver >= 0x3070) run_rt3070_set_chan(sc, chan); else run_rt2870_set_chan(sc, chan); /* determine channel group */ if (chan <= 14) group = 0; else if (chan <= 64) group = 1; else if (chan <= 128) group = 2; else group = 3; /* XXX necessary only when group has changed! */ run_select_chan_group(sc, group); run_delay(sc, 10); /* Perform IQ calibration. */ if (sc->mac_ver >= 0x5392) run_iq_calib(sc, chan); return (0); } static void run_set_channel(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; RUN_LOCK(sc); run_set_chan(sc, ic->ic_curchan); RUN_UNLOCK(sc); return; } static void run_scan_start(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; uint32_t tmp; RUN_LOCK(sc); /* abort TSF synchronization */ run_read(sc, RT2860_BCN_TIME_CFG, &tmp); run_write(sc, RT2860_BCN_TIME_CFG, tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN)); run_set_bssid(sc, ieee80211broadcastaddr); RUN_UNLOCK(sc); return; } static void run_scan_end(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; RUN_LOCK(sc); run_enable_tsf_sync(sc); run_set_bssid(sc, sc->sc_bssid); RUN_UNLOCK(sc); return; } /* * Could be called from ieee80211_node_timeout() * (non-sleepable thread) */ static void run_update_beacon(struct ieee80211vap *vap, int item) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211_node *ni = vap->iv_bss; struct run_softc *sc = ic->ic_softc; struct run_vap *rvp = RUN_VAP(vap); int mcast = 0; uint32_t i; switch (item) { case IEEE80211_BEACON_ERP: run_updateslot(ic); break; case IEEE80211_BEACON_HTINFO: run_updateprot(ic); break; case IEEE80211_BEACON_TIM: mcast = 1; /*TODO*/ break; default: break; } setbit(bo->bo_flags, item); if (rvp->beacon_mbuf == NULL) { rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); if (rvp->beacon_mbuf == NULL) return; } ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast); i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", i); sc->cmdq[i].func = run_update_beacon_cb; sc->cmdq[i].arg0 = vap; ieee80211_runtask(ic, &sc->cmdq_task); return; } static void run_update_beacon_cb(void *arg) { struct ieee80211vap *vap = arg; struct ieee80211_node *ni = vap->iv_bss; struct run_vap *rvp = RUN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct rt2860_txwi txwi; struct mbuf *m; uint16_t txwisize; uint8_t ridx; if (ni->ni_chan == IEEE80211_CHAN_ANYC) return; if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) return; /* * No need to call ieee80211_beacon_update(), run_update_beacon() - * is taking care of apropriate calls. + * is taking care of appropriate calls. */ if (rvp->beacon_mbuf == NULL) { rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); if (rvp->beacon_mbuf == NULL) return; } m = rvp->beacon_mbuf; memset(&txwi, 0, sizeof(txwi)); txwi.wcid = 0xff; txwi.len = htole16(m->m_pkthdr.len); /* send beacons at the lowest available rate */ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; txwi.phy = htole16(rt2860_rates[ridx].mcs); if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) txwi.phy |= htole16(RT2860_PHY_OFDM); txwi.txop = RT2860_TX_TXOP_HT; txwi.flags = RT2860_TX_TS; txwi.xflags = RT2860_TX_NSEQ; txwisize = (sc->mac_ver == 0x5592) ? sizeof(txwi) + sizeof(uint32_t) : sizeof(txwi); run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id), (uint8_t *)&txwi, txwisize); run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id) + txwisize, mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1); } static void run_updateprot(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; uint32_t i; i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", i); sc->cmdq[i].func = run_updateprot_cb; sc->cmdq[i].arg0 = ic; ieee80211_runtask(ic, &sc->cmdq_task); } static void run_updateprot_cb(void *arg) { struct ieee80211com *ic = arg; struct run_softc *sc = ic->ic_softc; uint32_t tmp; tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; /* setup protection frame rate (MCS code) */ tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? rt2860_rates[RT2860_RIDX_OFDM6].mcs | RT2860_PHY_OFDM : rt2860_rates[RT2860_RIDX_CCK11].mcs; /* CCK frames don't require protection */ run_write(sc, RT2860_CCK_PROT_CFG, tmp); if (ic->ic_flags & IEEE80211_F_USEPROT) { if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) tmp |= RT2860_PROT_CTRL_RTS_CTS; else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) tmp |= RT2860_PROT_CTRL_CTS; } run_write(sc, RT2860_OFDM_PROT_CFG, tmp); } static void run_usb_timeout_cb(void *arg) { struct ieee80211vap *vap = arg; struct run_softc *sc = vap->iv_ic->ic_softc; RUN_LOCK_ASSERT(sc, MA_OWNED); if(vap->iv_state == IEEE80211_S_RUN && vap->iv_opmode != IEEE80211_M_STA) run_reset_livelock(sc); else if (vap->iv_state == IEEE80211_S_SCAN) { DPRINTF("timeout caused by scan\n"); /* cancel bgscan */ ieee80211_cancel_scan(vap); } else DPRINTF("timeout by unknown cause\n"); } static void run_reset_livelock(struct run_softc *sc) { uint32_t tmp; RUN_LOCK_ASSERT(sc, MA_OWNED); /* * In IBSS or HostAP modes (when the hardware sends beacons), the MAC * can run into a livelock and start sending CTS-to-self frames like * crazy if protection is enabled. Reset MAC/BBP for a while */ run_read(sc, RT2860_DEBUG, &tmp); DPRINTFN(3, "debug reg %08x\n", tmp); if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) { DPRINTF("CTS-to-self livelock detected\n"); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST); run_delay(sc, 1); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); } } static void run_update_promisc_locked(struct run_softc *sc) { uint32_t tmp; run_read(sc, RT2860_RX_FILTR_CFG, &tmp); tmp |= RT2860_DROP_UC_NOME; if (sc->sc_ic.ic_promisc > 0) tmp &= ~RT2860_DROP_UC_NOME; run_write(sc, RT2860_RX_FILTR_CFG, tmp); DPRINTF("%s promiscuous mode\n", (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving"); } static void run_update_promisc(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; if ((sc->sc_flags & RUN_RUNNING) == 0) return; RUN_LOCK(sc); run_update_promisc_locked(sc); RUN_UNLOCK(sc); } static void run_enable_tsf_sync(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; DPRINTF("rvp_id=%d ic_opmode=%d\n", RUN_VAP(vap)->rvp_id, ic->ic_opmode); run_read(sc, RT2860_BCN_TIME_CFG, &tmp); tmp &= ~0x1fffff; tmp |= vap->iv_bss->ni_intval * 16; tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; if (ic->ic_opmode == IEEE80211_M_STA) { /* * Local TSF is always updated with remote TSF on beacon * reception. */ tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; } else if (ic->ic_opmode == IEEE80211_M_IBSS) { tmp |= RT2860_BCN_TX_EN; /* * Local TSF is updated with remote TSF on beacon reception * only if the remote TSF is greater than local TSF. */ tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT; } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) { tmp |= RT2860_BCN_TX_EN; /* SYNC with nobody */ tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT; } else { DPRINTF("Enabling TSF failed. undefined opmode\n"); return; } run_write(sc, RT2860_BCN_TIME_CFG, tmp); } static void run_enable_tsf(struct run_softc *sc) { uint32_t tmp; if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) { tmp &= ~(RT2860_BCN_TX_EN | RT2860_TBTT_TIMER_EN); tmp |= RT2860_TSF_TIMER_EN; run_write(sc, RT2860_BCN_TIME_CFG, tmp); } } static void run_get_tsf(struct run_softc *sc, uint64_t *buf) { run_read_region_1(sc, RT2860_TSF_TIMER_DW0, (uint8_t *)buf, sizeof(*buf)); } static void run_enable_mrr(struct run_softc *sc) { #define CCK(mcs) (mcs) #define OFDM(mcs) (1 << 3 | (mcs)) run_write(sc, RT2860_LG_FBK_CFG0, OFDM(6) << 28 | /* 54->48 */ OFDM(5) << 24 | /* 48->36 */ OFDM(4) << 20 | /* 36->24 */ OFDM(3) << 16 | /* 24->18 */ OFDM(2) << 12 | /* 18->12 */ OFDM(1) << 8 | /* 12-> 9 */ OFDM(0) << 4 | /* 9-> 6 */ OFDM(0)); /* 6-> 6 */ run_write(sc, RT2860_LG_FBK_CFG1, CCK(2) << 12 | /* 11->5.5 */ CCK(1) << 8 | /* 5.5-> 2 */ CCK(0) << 4 | /* 2-> 1 */ CCK(0)); /* 1-> 1 */ #undef OFDM #undef CCK } static void run_set_txpreamble(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t tmp; run_read(sc, RT2860_AUTO_RSP_CFG, &tmp); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RT2860_CCK_SHORT_EN; else tmp &= ~RT2860_CCK_SHORT_EN; run_write(sc, RT2860_AUTO_RSP_CFG, tmp); } static void run_set_basicrates(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; /* set basic rates mask */ if (ic->ic_curmode == IEEE80211_MODE_11B) run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003); else if (ic->ic_curmode == IEEE80211_MODE_11A) run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x150); else /* 11g */ run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); } static void run_set_leds(struct run_softc *sc, uint16_t which) { (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, which | (sc->leds & 0x7f)); } static void run_set_bssid(struct run_softc *sc, const uint8_t *bssid) { run_write(sc, RT2860_MAC_BSSID_DW0, bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); run_write(sc, RT2860_MAC_BSSID_DW1, bssid[4] | bssid[5] << 8); } static void run_set_macaddr(struct run_softc *sc, const uint8_t *addr) { run_write(sc, RT2860_MAC_ADDR_DW0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); run_write(sc, RT2860_MAC_ADDR_DW1, addr[4] | addr[5] << 8 | 0xff << 16); } static void run_updateslot(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; uint32_t i; i = RUN_CMDQ_GET(&sc->cmdq_store); DPRINTF("cmdq_store=%d\n", i); sc->cmdq[i].func = run_updateslot_cb; sc->cmdq[i].arg0 = ic; ieee80211_runtask(ic, &sc->cmdq_task); return; } /* ARGSUSED */ static void run_updateslot_cb(void *arg) { struct ieee80211com *ic = arg; struct run_softc *sc = ic->ic_softc; uint32_t tmp; run_read(sc, RT2860_BKOFF_SLOT_CFG, &tmp); tmp &= ~0xff; tmp |= IEEE80211_GET_SLOTTIME(ic); run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp); } static void run_update_mcast(struct ieee80211com *ic) { } static int8_t run_rssi2dbm(struct run_softc *sc, uint8_t rssi, uint8_t rxchain) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_curchan; int delta; if (IEEE80211_IS_CHAN_5GHZ(c)) { u_int chan = ieee80211_chan2ieee(ic, c); delta = sc->rssi_5ghz[rxchain]; /* determine channel group */ if (chan <= 64) delta -= sc->lna[1]; else if (chan <= 128) delta -= sc->lna[2]; else delta -= sc->lna[3]; } else delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; return (-12 - delta - rssi); } static void run_rt5390_bbp_init(struct run_softc *sc) { int i; uint8_t bbp; /* Apply maximum likelihood detection for 2 stream case. */ run_bbp_read(sc, 105, &bbp); if (sc->nrxchains > 1) run_bbp_write(sc, 105, bbp | RT5390_MLD); /* Avoid data lost and CRC error. */ run_bbp_read(sc, 4, &bbp); run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); if (sc->mac_ver == 0x5592) { for (i = 0; i < nitems(rt5592_def_bbp); i++) { run_bbp_write(sc, rt5592_def_bbp[i].reg, rt5592_def_bbp[i].val); } for (i = 0; i < nitems(rt5592_bbp_r196); i++) { run_bbp_write(sc, 195, i + 0x80); run_bbp_write(sc, 196, rt5592_bbp_r196[i]); } } else { for (i = 0; i < nitems(rt5390_def_bbp); i++) { run_bbp_write(sc, rt5390_def_bbp[i].reg, rt5390_def_bbp[i].val); } } if (sc->mac_ver == 0x5392) { run_bbp_write(sc, 88, 0x90); run_bbp_write(sc, 95, 0x9a); run_bbp_write(sc, 98, 0x12); run_bbp_write(sc, 106, 0x12); run_bbp_write(sc, 134, 0xd0); run_bbp_write(sc, 135, 0xf6); run_bbp_write(sc, 148, 0x84); } run_bbp_read(sc, 152, &bbp); run_bbp_write(sc, 152, bbp | 0x80); /* Fix BBP254 for RT5592C. */ if (sc->mac_ver == 0x5592 && sc->mac_rev >= 0x0221) { run_bbp_read(sc, 254, &bbp); run_bbp_write(sc, 254, bbp | 0x80); } /* Disable hardware antenna diversity. */ if (sc->mac_ver == 0x5390) run_bbp_write(sc, 154, 0); /* Initialize Rx CCK/OFDM frequency offset report. */ run_bbp_write(sc, 142, 1); run_bbp_write(sc, 143, 57); } static int run_bbp_init(struct run_softc *sc) { int i, error, ntries; uint8_t bbp0; /* wait for BBP to wake up */ for (ntries = 0; ntries < 20; ntries++) { if ((error = run_bbp_read(sc, 0, &bbp0)) != 0) return error; if (bbp0 != 0 && bbp0 != 0xff) break; } if (ntries == 20) return (ETIMEDOUT); /* initialize BBP registers to default values */ if (sc->mac_ver >= 0x5390) run_rt5390_bbp_init(sc); else { for (i = 0; i < nitems(rt2860_def_bbp); i++) { run_bbp_write(sc, rt2860_def_bbp[i].reg, rt2860_def_bbp[i].val); } } if (sc->mac_ver == 0x3593) { run_bbp_write(sc, 79, 0x13); run_bbp_write(sc, 80, 0x05); run_bbp_write(sc, 81, 0x33); run_bbp_write(sc, 86, 0x46); run_bbp_write(sc, 137, 0x0f); } /* fix BBP84 for RT2860E */ if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101) run_bbp_write(sc, 84, 0x19); if (sc->mac_ver >= 0x3070 && (sc->mac_ver != 0x3593 && sc->mac_ver != 0x5592)) { run_bbp_write(sc, 79, 0x13); run_bbp_write(sc, 80, 0x05); run_bbp_write(sc, 81, 0x33); } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) { run_bbp_write(sc, 69, 0x16); run_bbp_write(sc, 73, 0x12); } return (0); } static int run_rt3070_rf_init(struct run_softc *sc) { uint32_t tmp; uint8_t bbp4, mingain, rf, target; int i; run_rt3070_rf_read(sc, 30, &rf); /* toggle RF R30 bit 7 */ run_rt3070_rf_write(sc, 30, rf | 0x80); run_delay(sc, 10); run_rt3070_rf_write(sc, 30, rf & ~0x80); /* initialize RF registers to default value */ if (sc->mac_ver == 0x3572) { for (i = 0; i < nitems(rt3572_def_rf); i++) { run_rt3070_rf_write(sc, rt3572_def_rf[i].reg, rt3572_def_rf[i].val); } } else { for (i = 0; i < nitems(rt3070_def_rf); i++) { run_rt3070_rf_write(sc, rt3070_def_rf[i].reg, rt3070_def_rf[i].val); } } if (sc->mac_ver == 0x3070 && sc->mac_rev < 0x0201) { /* * Change voltage from 1.2V to 1.35V for RT3070. * The DAC issue (RT3070_LDO_CFG0) has been fixed * in RT3070(F). */ run_read(sc, RT3070_LDO_CFG0, &tmp); tmp = (tmp & ~0x0f000000) | 0x0d000000; run_write(sc, RT3070_LDO_CFG0, tmp); } else if (sc->mac_ver == 0x3071) { run_rt3070_rf_read(sc, 6, &rf); run_rt3070_rf_write(sc, 6, rf | 0x40); run_rt3070_rf_write(sc, 31, 0x14); run_read(sc, RT3070_LDO_CFG0, &tmp); tmp &= ~0x1f000000; if (sc->mac_rev < 0x0211) tmp |= 0x0d000000; /* 1.3V */ else tmp |= 0x01000000; /* 1.2V */ run_write(sc, RT3070_LDO_CFG0, tmp); /* patch LNA_PE_G1 */ run_read(sc, RT3070_GPIO_SWITCH, &tmp); run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20); } else if (sc->mac_ver == 0x3572) { run_rt3070_rf_read(sc, 6, &rf); run_rt3070_rf_write(sc, 6, rf | 0x40); /* increase voltage from 1.2V to 1.35V */ run_read(sc, RT3070_LDO_CFG0, &tmp); tmp = (tmp & ~0x1f000000) | 0x0d000000; run_write(sc, RT3070_LDO_CFG0, tmp); if (sc->mac_rev < 0x0211 || !sc->patch_dac) { run_delay(sc, 1); /* wait for 1msec */ /* decrease voltage back to 1.2V */ tmp = (tmp & ~0x1f000000) | 0x01000000; run_write(sc, RT3070_LDO_CFG0, tmp); } } /* select 20MHz bandwidth */ run_rt3070_rf_read(sc, 31, &rf); run_rt3070_rf_write(sc, 31, rf & ~0x20); /* calibrate filter for 20MHz bandwidth */ sc->rf24_20mhz = 0x1f; /* default value */ target = (sc->mac_ver < 0x3071) ? 0x16 : 0x13; run_rt3070_filter_calib(sc, 0x07, target, &sc->rf24_20mhz); /* select 40MHz bandwidth */ run_bbp_read(sc, 4, &bbp4); run_bbp_write(sc, 4, (bbp4 & ~0x18) | 0x10); run_rt3070_rf_read(sc, 31, &rf); run_rt3070_rf_write(sc, 31, rf | 0x20); /* calibrate filter for 40MHz bandwidth */ sc->rf24_40mhz = 0x2f; /* default value */ target = (sc->mac_ver < 0x3071) ? 0x19 : 0x15; run_rt3070_filter_calib(sc, 0x27, target, &sc->rf24_40mhz); /* go back to 20MHz bandwidth */ run_bbp_read(sc, 4, &bbp4); run_bbp_write(sc, 4, bbp4 & ~0x18); if (sc->mac_ver == 0x3572) { /* save default BBP registers 25 and 26 values */ run_bbp_read(sc, 25, &sc->bbp25); run_bbp_read(sc, 26, &sc->bbp26); } else if (sc->mac_rev < 0x0201 || sc->mac_rev < 0x0211) run_rt3070_rf_write(sc, 27, 0x03); run_read(sc, RT3070_OPT_14, &tmp); run_write(sc, RT3070_OPT_14, tmp | 1); if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { run_rt3070_rf_read(sc, 17, &rf); rf &= ~RT3070_TX_LO1; if ((sc->mac_ver == 0x3070 || (sc->mac_ver == 0x3071 && sc->mac_rev >= 0x0211)) && !sc->ext_2ghz_lna) rf |= 0x20; /* fix for long range Rx issue */ mingain = (sc->mac_ver == 0x3070) ? 1 : 2; if (sc->txmixgain_2ghz >= mingain) rf = (rf & ~0x7) | sc->txmixgain_2ghz; run_rt3070_rf_write(sc, 17, rf); } if (sc->mac_ver == 0x3071) { run_rt3070_rf_read(sc, 1, &rf); rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD); rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD; run_rt3070_rf_write(sc, 1, rf); run_rt3070_rf_read(sc, 15, &rf); run_rt3070_rf_write(sc, 15, rf & ~RT3070_TX_LO2); run_rt3070_rf_read(sc, 20, &rf); run_rt3070_rf_write(sc, 20, rf & ~RT3070_RX_LO1); run_rt3070_rf_read(sc, 21, &rf); run_rt3070_rf_write(sc, 21, rf & ~RT3070_RX_LO2); } if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { /* fix Tx to Rx IQ glitch by raising RF voltage */ run_rt3070_rf_read(sc, 27, &rf); rf &= ~0x77; if (sc->mac_rev < 0x0211) rf |= 0x03; run_rt3070_rf_write(sc, 27, rf); } return (0); } static void run_rt3593_rf_init(struct run_softc *sc) { uint32_t tmp; uint8_t rf; int i; /* Disable the GPIO bits 4 and 7 for LNA PE control. */ run_read(sc, RT3070_GPIO_SWITCH, &tmp); tmp &= ~(1 << 4 | 1 << 7); run_write(sc, RT3070_GPIO_SWITCH, tmp); /* Initialize RF registers to default value. */ for (i = 0; i < nitems(rt3593_def_rf); i++) { run_rt3070_rf_write(sc, rt3593_def_rf[i].reg, rt3593_def_rf[i].val); } /* Toggle RF R2 to initiate calibration. */ run_rt3070_rf_write(sc, 2, RT5390_RESCAL); /* Initialize RF frequency offset. */ run_adjust_freq_offset(sc); run_rt3070_rf_read(sc, 18, &rf); run_rt3070_rf_write(sc, 18, rf | RT3593_AUTOTUNE_BYPASS); /* * Increase voltage from 1.2V to 1.35V, wait for 1 msec to * decrease voltage back to 1.2V. */ run_read(sc, RT3070_LDO_CFG0, &tmp); tmp = (tmp & ~0x1f000000) | 0x0d000000; run_write(sc, RT3070_LDO_CFG0, tmp); run_delay(sc, 1); tmp = (tmp & ~0x1f000000) | 0x01000000; run_write(sc, RT3070_LDO_CFG0, tmp); sc->rf24_20mhz = 0x1f; sc->rf24_40mhz = 0x2f; /* Save default BBP registers 25 and 26 values. */ run_bbp_read(sc, 25, &sc->bbp25); run_bbp_read(sc, 26, &sc->bbp26); run_read(sc, RT3070_OPT_14, &tmp); run_write(sc, RT3070_OPT_14, tmp | 1); } static void run_rt5390_rf_init(struct run_softc *sc) { uint32_t tmp; uint8_t rf; int i; /* Toggle RF R2 to initiate calibration. */ if (sc->mac_ver == 0x5390) { run_rt3070_rf_read(sc, 2, &rf); run_rt3070_rf_write(sc, 2, rf | RT5390_RESCAL); run_delay(sc, 10); run_rt3070_rf_write(sc, 2, rf & ~RT5390_RESCAL); } else { run_rt3070_rf_write(sc, 2, RT5390_RESCAL); run_delay(sc, 10); } /* Initialize RF registers to default value. */ if (sc->mac_ver == 0x5592) { for (i = 0; i < nitems(rt5592_def_rf); i++) { run_rt3070_rf_write(sc, rt5592_def_rf[i].reg, rt5592_def_rf[i].val); } /* Initialize RF frequency offset. */ run_adjust_freq_offset(sc); } else if (sc->mac_ver == 0x5392) { for (i = 0; i < nitems(rt5392_def_rf); i++) { run_rt3070_rf_write(sc, rt5392_def_rf[i].reg, rt5392_def_rf[i].val); } if (sc->mac_rev >= 0x0223) { run_rt3070_rf_write(sc, 23, 0x0f); run_rt3070_rf_write(sc, 24, 0x3e); run_rt3070_rf_write(sc, 51, 0x32); run_rt3070_rf_write(sc, 53, 0x22); run_rt3070_rf_write(sc, 56, 0xc1); run_rt3070_rf_write(sc, 59, 0x0f); } } else { for (i = 0; i < nitems(rt5390_def_rf); i++) { run_rt3070_rf_write(sc, rt5390_def_rf[i].reg, rt5390_def_rf[i].val); } if (sc->mac_rev >= 0x0502) { run_rt3070_rf_write(sc, 6, 0xe0); run_rt3070_rf_write(sc, 25, 0x80); run_rt3070_rf_write(sc, 46, 0x73); run_rt3070_rf_write(sc, 53, 0x00); run_rt3070_rf_write(sc, 56, 0x42); run_rt3070_rf_write(sc, 61, 0xd1); } } sc->rf24_20mhz = 0x1f; /* default value */ sc->rf24_40mhz = (sc->mac_ver == 0x5592) ? 0 : 0x2f; if (sc->mac_rev < 0x0211) run_rt3070_rf_write(sc, 27, 0x3); run_read(sc, RT3070_OPT_14, &tmp); run_write(sc, RT3070_OPT_14, tmp | 1); } static int run_rt3070_filter_calib(struct run_softc *sc, uint8_t init, uint8_t target, uint8_t *val) { uint8_t rf22, rf24; uint8_t bbp55_pb, bbp55_sb, delta; int ntries; /* program filter */ run_rt3070_rf_read(sc, 24, &rf24); rf24 = (rf24 & 0xc0) | init; /* initial filter value */ run_rt3070_rf_write(sc, 24, rf24); /* enable baseband loopback mode */ run_rt3070_rf_read(sc, 22, &rf22); run_rt3070_rf_write(sc, 22, rf22 | 0x01); /* set power and frequency of passband test tone */ run_bbp_write(sc, 24, 0x00); for (ntries = 0; ntries < 100; ntries++) { /* transmit test tone */ run_bbp_write(sc, 25, 0x90); run_delay(sc, 10); /* read received power */ run_bbp_read(sc, 55, &bbp55_pb); if (bbp55_pb != 0) break; } if (ntries == 100) return (ETIMEDOUT); /* set power and frequency of stopband test tone */ run_bbp_write(sc, 24, 0x06); for (ntries = 0; ntries < 100; ntries++) { /* transmit test tone */ run_bbp_write(sc, 25, 0x90); run_delay(sc, 10); /* read received power */ run_bbp_read(sc, 55, &bbp55_sb); delta = bbp55_pb - bbp55_sb; if (delta > target) break; /* reprogram filter */ rf24++; run_rt3070_rf_write(sc, 24, rf24); } if (ntries < 100) { if (rf24 != init) rf24--; /* backtrack */ *val = rf24; run_rt3070_rf_write(sc, 24, rf24); } /* restore initial state */ run_bbp_write(sc, 24, 0x00); /* disable baseband loopback mode */ run_rt3070_rf_read(sc, 22, &rf22); run_rt3070_rf_write(sc, 22, rf22 & ~0x01); return (0); } static void run_rt3070_rf_setup(struct run_softc *sc) { uint8_t bbp, rf; int i; if (sc->mac_ver == 0x3572) { /* enable DC filter */ if (sc->mac_rev >= 0x0201) run_bbp_write(sc, 103, 0xc0); run_bbp_read(sc, 138, &bbp); if (sc->ntxchains == 1) bbp |= 0x20; /* turn off DAC1 */ if (sc->nrxchains == 1) bbp &= ~0x02; /* turn off ADC1 */ run_bbp_write(sc, 138, bbp); if (sc->mac_rev >= 0x0211) { /* improve power consumption */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } run_rt3070_rf_read(sc, 16, &rf); rf = (rf & ~0x07) | sc->txmixgain_2ghz; run_rt3070_rf_write(sc, 16, rf); } else if (sc->mac_ver == 0x3071) { if (sc->mac_rev >= 0x0211) { /* enable DC filter */ run_bbp_write(sc, 103, 0xc0); /* improve power consumption */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } run_bbp_read(sc, 138, &bbp); if (sc->ntxchains == 1) bbp |= 0x20; /* turn off DAC1 */ if (sc->nrxchains == 1) bbp &= ~0x02; /* turn off ADC1 */ run_bbp_write(sc, 138, bbp); run_write(sc, RT2860_TX_SW_CFG1, 0); if (sc->mac_rev < 0x0211) { run_write(sc, RT2860_TX_SW_CFG2, sc->patch_dac ? 0x2c : 0x0f); } else run_write(sc, RT2860_TX_SW_CFG2, 0); } else if (sc->mac_ver == 0x3070) { if (sc->mac_rev >= 0x0201) { /* enable DC filter */ run_bbp_write(sc, 103, 0xc0); /* improve power consumption */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } if (sc->mac_rev < 0x0201) { run_write(sc, RT2860_TX_SW_CFG1, 0); run_write(sc, RT2860_TX_SW_CFG2, 0x2c); } else run_write(sc, RT2860_TX_SW_CFG2, 0); } /* initialize RF registers from ROM for >=RT3071*/ if (sc->mac_ver >= 0x3071) { for (i = 0; i < 10; i++) { if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff) continue; run_rt3070_rf_write(sc, sc->rf[i].reg, sc->rf[i].val); } } } static void run_rt3593_rf_setup(struct run_softc *sc) { uint8_t bbp, rf; if (sc->mac_rev >= 0x0211) { /* Enable DC filter. */ run_bbp_write(sc, 103, 0xc0); } run_write(sc, RT2860_TX_SW_CFG1, 0); if (sc->mac_rev < 0x0211) { run_write(sc, RT2860_TX_SW_CFG2, sc->patch_dac ? 0x2c : 0x0f); } else run_write(sc, RT2860_TX_SW_CFG2, 0); run_rt3070_rf_read(sc, 50, &rf); run_rt3070_rf_write(sc, 50, rf & ~RT3593_TX_LO2); run_rt3070_rf_read(sc, 51, &rf); rf = (rf & ~(RT3593_TX_LO1 | 0x0c)) | ((sc->txmixgain_2ghz & 0x07) << 2); run_rt3070_rf_write(sc, 51, rf); run_rt3070_rf_read(sc, 38, &rf); run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); run_rt3070_rf_read(sc, 39, &rf); run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); run_rt3070_rf_read(sc, 1, &rf); run_rt3070_rf_write(sc, 1, rf & ~(RT3070_RF_BLOCK | RT3070_PLL_PD)); run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x18) | 0x10; run_rt3070_rf_write(sc, 30, rf); /* Apply maximum likelihood detection for 2 stream case. */ run_bbp_read(sc, 105, &bbp); if (sc->nrxchains > 1) run_bbp_write(sc, 105, bbp | RT5390_MLD); /* Avoid data lost and CRC error. */ run_bbp_read(sc, 4, &bbp); run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); run_bbp_write(sc, 92, 0x02); run_bbp_write(sc, 82, 0x82); run_bbp_write(sc, 106, 0x05); run_bbp_write(sc, 104, 0x92); run_bbp_write(sc, 88, 0x90); run_bbp_write(sc, 148, 0xc8); run_bbp_write(sc, 47, 0x48); run_bbp_write(sc, 120, 0x50); run_bbp_write(sc, 163, 0x9d); /* SNR mapping. */ run_bbp_write(sc, 142, 0x06); run_bbp_write(sc, 143, 0xa0); run_bbp_write(sc, 142, 0x07); run_bbp_write(sc, 143, 0xa1); run_bbp_write(sc, 142, 0x08); run_bbp_write(sc, 143, 0xa2); run_bbp_write(sc, 31, 0x08); run_bbp_write(sc, 68, 0x0b); run_bbp_write(sc, 105, 0x04); } static void run_rt5390_rf_setup(struct run_softc *sc) { uint8_t bbp, rf; if (sc->mac_rev >= 0x0211) { /* Enable DC filter. */ run_bbp_write(sc, 103, 0xc0); if (sc->mac_ver != 0x5592) { /* Improve power consumption. */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } } run_bbp_read(sc, 138, &bbp); if (sc->ntxchains == 1) bbp |= 0x20; /* turn off DAC1 */ if (sc->nrxchains == 1) bbp &= ~0x02; /* turn off ADC1 */ run_bbp_write(sc, 138, bbp); run_rt3070_rf_read(sc, 38, &rf); run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); run_rt3070_rf_read(sc, 39, &rf); run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); /* Avoid data lost and CRC error. */ run_bbp_read(sc, 4, &bbp); run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x18) | 0x10; run_rt3070_rf_write(sc, 30, rf); if (sc->mac_ver != 0x5592) { run_write(sc, RT2860_TX_SW_CFG1, 0); if (sc->mac_rev < 0x0211) { run_write(sc, RT2860_TX_SW_CFG2, sc->patch_dac ? 0x2c : 0x0f); } else run_write(sc, RT2860_TX_SW_CFG2, 0); } } static int run_txrx_enable(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t tmp; int error, ntries; run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); for (ntries = 0; ntries < 200; ntries++) { if ((error = run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp)) != 0) return (error); if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) break; run_delay(sc, 50); } if (ntries == 200) return (ETIMEDOUT); run_delay(sc, 50); tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | RT2860_TX_WB_DDONE; run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); /* enable Rx bulk aggregation (set timeout and limit) */ tmp = RT2860_USB_TX_EN | RT2860_USB_RX_EN | RT2860_USB_RX_AGG_EN | RT2860_USB_RX_AGG_TO(128) | RT2860_USB_RX_AGG_LMT(2); run_write(sc, RT2860_USB_DMA_CFG, tmp); /* set Rx filter */ tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | RT2860_DROP_CFACK | RT2860_DROP_CFEND; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; } run_write(sc, RT2860_RX_FILTR_CFG, tmp); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); return (0); } static void run_adjust_freq_offset(struct run_softc *sc) { uint8_t rf, tmp; run_rt3070_rf_read(sc, 17, &rf); tmp = rf; rf = (rf & ~0x7f) | (sc->freq & 0x7f); rf = MIN(rf, 0x5f); if (tmp != rf) run_mcu_cmd(sc, 0x74, (tmp << 8 ) | rf); } static void run_init_locked(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; uint8_t bbp1, bbp3; int i; int ridx; int ntries; if (ic->ic_nrunning > 1) return; run_stop(sc); if (run_load_microcode(sc) != 0) { device_printf(sc->sc_dev, "could not load 8051 microcode\n"); goto fail; } for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_ASIC_VER_ID, &tmp) != 0) goto fail; if (tmp != 0 && tmp != 0xffffffff) break; run_delay(sc, 10); } if (ntries == 100) goto fail; for (i = 0; i != RUN_EP_QUEUES; i++) run_setup_tx_list(sc, &sc->sc_epq[i]); run_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) goto fail; if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) break; run_delay(sc, 10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); goto fail; } tmp &= 0xff0; tmp |= RT2860_TX_WB_DDONE; run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); /* turn off PME_OEN to solve high-current issue */ run_read(sc, RT2860_SYS_CTRL, &tmp); run_write(sc, RT2860_SYS_CTRL, tmp & ~RT2860_PME_OEN); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); run_write(sc, RT2860_USB_DMA_CFG, 0); if (run_reset(sc) != 0) { device_printf(sc->sc_dev, "could not reset chipset\n"); goto fail; } run_write(sc, RT2860_MAC_SYS_CTRL, 0); /* init Tx power for all Tx rates (from EEPROM) */ for (ridx = 0; ridx < 5; ridx++) { if (sc->txpow20mhz[ridx] == 0xffffffff) continue; run_write(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); } for (i = 0; i < nitems(rt2870_def_mac); i++) run_write(sc, rt2870_def_mac[i].reg, rt2870_def_mac[i].val); run_write(sc, RT2860_WMM_AIFSN_CFG, 0x00002273); run_write(sc, RT2860_WMM_CWMIN_CFG, 0x00002344); run_write(sc, RT2860_WMM_CWMAX_CFG, 0x000034aa); if (sc->mac_ver >= 0x5390) { run_write(sc, RT2860_TX_SW_CFG0, 4 << RT2860_DLY_PAPE_EN_SHIFT | 4); if (sc->mac_ver >= 0x5392) { run_write(sc, RT2860_MAX_LEN_CFG, 0x00002fff); if (sc->mac_ver == 0x5592) { run_write(sc, RT2860_HT_FBK_CFG1, 0xedcba980); run_write(sc, RT2860_TXOP_HLDR_ET, 0x00000082); } else { run_write(sc, RT2860_HT_FBK_CFG1, 0xedcb4980); run_write(sc, RT2860_LG_FBK_CFG0, 0xedcba322); } } } else if (sc->mac_ver == 0x3593) { run_write(sc, RT2860_TX_SW_CFG0, 4 << RT2860_DLY_PAPE_EN_SHIFT | 2); } else if (sc->mac_ver >= 0x3070) { /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ run_write(sc, RT2860_TX_SW_CFG0, 4 << RT2860_DLY_PAPE_EN_SHIFT); } /* wait while MAC is busy */ for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_MAC_STATUS_REG, &tmp) != 0) goto fail; if (!(tmp & (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) break; run_delay(sc, 10); } if (ntries == 100) goto fail; /* clear Host to MCU mailbox */ run_write(sc, RT2860_H2M_BBPAGENT, 0); run_write(sc, RT2860_H2M_MAILBOX, 0); run_delay(sc, 10); if (run_bbp_init(sc) != 0) { device_printf(sc->sc_dev, "could not initialize BBP\n"); goto fail; } /* abort TSF synchronization */ run_read(sc, RT2860_BCN_TIME_CFG, &tmp); tmp &= ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN); run_write(sc, RT2860_BCN_TIME_CFG, tmp); /* clear RX WCID search table */ run_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512); /* clear WCID attribute table */ run_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 8 * 32); /* hostapd sets a key before init. So, don't clear it. */ if (sc->cmdq_key_set != RUN_CMDQ_GO) { /* clear shared key table */ run_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); /* clear shared key mode */ run_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); } run_read(sc, RT2860_US_CYC_CNT, &tmp); tmp = (tmp & ~0xff) | 0x1e; run_write(sc, RT2860_US_CYC_CNT, tmp); if (sc->mac_rev != 0x0101) run_write(sc, RT2860_TXOP_CTRL_CFG, 0x0000583f); run_write(sc, RT2860_WMM_TXOP0_CFG, 0); run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96); /* write vendor-specific BBP values (from EEPROM) */ if (sc->mac_ver < 0x3593) { for (i = 0; i < 10; i++) { if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) continue; run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); } } /* select Main antenna for 1T1R devices */ if (sc->rf_rev == RT3070_RF_3020 || sc->rf_rev == RT5390_RF_5370) run_set_rx_antenna(sc, 0); /* send LEDs operating mode to microcontroller */ (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); if (sc->mac_ver >= 0x5390) run_rt5390_rf_init(sc); else if (sc->mac_ver == 0x3593) run_rt3593_rf_init(sc); else if (sc->mac_ver >= 0x3070) run_rt3070_rf_init(sc); /* disable non-existing Rx chains */ run_bbp_read(sc, 3, &bbp3); bbp3 &= ~(1 << 3 | 1 << 4); if (sc->nrxchains == 2) bbp3 |= 1 << 3; else if (sc->nrxchains == 3) bbp3 |= 1 << 4; run_bbp_write(sc, 3, bbp3); /* disable non-existing Tx chains */ run_bbp_read(sc, 1, &bbp1); if (sc->ntxchains == 1) bbp1 &= ~(1 << 3 | 1 << 4); run_bbp_write(sc, 1, bbp1); if (sc->mac_ver >= 0x5390) run_rt5390_rf_setup(sc); else if (sc->mac_ver == 0x3593) run_rt3593_rf_setup(sc); else if (sc->mac_ver >= 0x3070) run_rt3070_rf_setup(sc); /* select default channel */ run_set_chan(sc, ic->ic_curchan); /* setup initial protection mode */ run_updateprot_cb(ic); /* turn radio LED on */ run_set_leds(sc, RT2860_LED_RADIO); sc->sc_flags |= RUN_RUNNING; sc->cmdq_run = RUN_CMDQ_GO; for (i = 0; i != RUN_N_XFER; i++) usbd_xfer_set_stall(sc->sc_xfer[i]); usbd_transfer_start(sc->sc_xfer[RUN_BULK_RX]); if (run_txrx_enable(sc) != 0) goto fail; return; fail: run_stop(sc); } static void run_stop(void *arg) { struct run_softc *sc = (struct run_softc *)arg; uint32_t tmp; int i; int ntries; RUN_LOCK_ASSERT(sc, MA_OWNED); if (sc->sc_flags & RUN_RUNNING) run_set_leds(sc, 0); /* turn all LEDs off */ sc->sc_flags &= ~RUN_RUNNING; sc->ratectl_run = RUN_RATECTL_OFF; sc->cmdq_run = sc->cmdq_key_set; RUN_UNLOCK(sc); for(i = 0; i < RUN_N_XFER; i++) usbd_transfer_drain(sc->sc_xfer[i]); RUN_LOCK(sc); run_drain_mbufq(sc); if (sc->rx_m != NULL) { m_free(sc->rx_m); sc->rx_m = NULL; } /* Disable Tx/Rx DMA. */ if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) return; tmp &= ~(RT2860_RX_DMA_EN | RT2860_TX_DMA_EN); run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) return; if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) break; run_delay(sc, 10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); return; } /* disable Tx/Rx */ run_read(sc, RT2860_MAC_SYS_CTRL, &tmp); tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); run_write(sc, RT2860_MAC_SYS_CTRL, tmp); /* wait for pending Tx to complete */ for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_TXRXQ_PCNT, &tmp) != 0) { DPRINTF("Cannot read Tx queue count\n"); break; } if ((tmp & RT2860_TX2Q_PCNT_MASK) == 0) { DPRINTF("All Tx cleared\n"); break; } run_delay(sc, 10); } if (ntries >= 100) DPRINTF("There are still pending Tx\n"); run_delay(sc, 10); run_write(sc, RT2860_USB_DMA_CFG, 0); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); run_write(sc, RT2860_MAC_SYS_CTRL, 0); for (i = 0; i != RUN_EP_QUEUES; i++) run_unsetup_tx_list(sc, &sc->sc_epq[i]); } static void run_delay(struct run_softc *sc, u_int ms) { usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms)); } static device_method_t run_methods[] = { /* Device interface */ DEVMETHOD(device_probe, run_match), DEVMETHOD(device_attach, run_attach), DEVMETHOD(device_detach, run_detach), DEVMETHOD_END }; static driver_t run_driver = { .name = "run", .methods = run_methods, .size = sizeof(struct run_softc) }; static devclass_t run_devclass; DRIVER_MODULE(run, uhub, run_driver, run_devclass, run_driver_loaded, NULL); MODULE_DEPEND(run, wlan, 1, 1, 1); MODULE_DEPEND(run, usb, 1, 1, 1); MODULE_DEPEND(run, firmware, 1, 1, 1); MODULE_VERSION(run, 1); USB_PNP_HOST_INFO(run_devs);