Date: Sat, 31 Jul 2010 09:20:51 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 181631 for review Message-ID: <201007310920.o6V9Kpsj037501@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@181631?ac=10 Change 181631 by hselasky@hselasky_laptop001 on 2010/07/31 09:20:10 USB controller (XHCI): - import current work in progress Affected files ... .. //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#2 edit .. //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#4 edit .. //depot/projects/usb/src/sys/dev/usb/controller/xhcireg.h#4 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#2 (text+ko) ==== @@ -31,3 +31,1734 @@ * and the USB 3.0 spec at * http://www.usb.org/developers/docs/usb_30_spec_060910.zip */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/sys/dev/usb/controller/xhci.c $"); + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/linker_set.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + +#define USB_DEBUG_VAR xhcidebug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_transfer.h> +#include <dev/usb/usb_device.h> +#include <dev/usb/usb_hub.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> +#include <dev/usb/controller/xhci.h> +#include <dev/usb/controller/xhcireg.h> + +#define XHCI_BUS2SC(bus) \ + ((struct xhci_softc *)(((uint8_t *)(bus)) - \ + ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus)))) + +#ifdef USB_DEBUG +static int xhcidebug = 0; + +SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); +SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW, + &xhcidebug, 0, "Debug level"); + +TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug); + +#endif + +#define XHCI_INTR_ENDPT 1 + +struct xhci_std_temp { + struct xhci_softc *sc; + struct usb_page_cache *pc; + struct xhci_td *td; + struct xhci_td *td_next; + uint32_t len; + uint32_t offset; + uint32_t max_packet_size; + uint32_t average; + uint16_t isoc_delta; + uint16_t isoc_frame; + uint8_t shortpkt; + uint8_t multishort; + uint8_t last_frame; + uint8_t trb_type; + uint8_t direction; +}; + +void +xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) +{ + + +} + +usb_error_t +xhci_reset(struct xhci_softc *sc) +{ + + +} + +static usb_error_t +xhci_hcreset(struct xhci_softc *sc) +{ + + +} + +usb_error_t +xhci_init(struct xhci_softc *sc) +{ + + +} + +void +xhci_detach(struct xhci_softc *sc) +{ + + +} + +void +xhci_suspend(struct xhci_softc *sc) +{ + + +} + +void +xhci_resume(struct xhci_softc *sc) +{ + + +} + +void +xhci_shutdown(struct xhci_softc *sc) +{ + + +} + +static void +xhci_transfer_intr_enqueue(struct usb_xfer *xfer) +{ + /* check for early completion */ + if (xhci_check_transfer(xfer)) + return; + + /* put transfer on interrupt queue */ + usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); +} + +static usb_error_t +xhci_generic_done_sub(struct usb_xfer *xfer) +{ + struct xhci_td *td; + struct xhci_td *td_alt_next; + uint32_t len; + uint8_t status; + + 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 = td->status; + len = td->remainder; + + DPRINTFN(4, "xfer=%p[%u/%u] len=%u/%u status=%u\n", + xfer, (unsigned int)xfer->aframes, + (unsigned int)xfer->nframes, + (unsigned int)len, (unsigned int)td->len, + (unsigned int)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 = XHCI_TRB_ERROR_LENGTH; + } else if (xfer->aframes != xfer->nframes) { + xfer->frlengths[xfer->aframes] += td->len - len; + } + /* Check for last transfer */ + if (((void *)td) == xfer->td_transfer_last) { + td = NULL; + break; + } + /* Check for transfer error */ + if ((status != XHCI_TRB_ERROR_SHORT_PKT) && + (status != XHCI_TRB_ERROR_SUCCESS)) { + /* the transfer is finished */ + 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 */ + 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; + + return ((status == XHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED : + ((status != XHCI_TRB_ERROR_SHORT_PKT) && + (status != XHCI_TRB_ERROR_SUCCESS)) ? USB_ERR_IOERROR : + USB_ERR_NORMAL_COMPLETION); +} + +static void +xhci_generic_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 = xhci_generic_done_sub(xfer); + + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) + goto done; + } + + while (xfer->aframes != xfer->nframes) { + + err = xhci_generic_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 = xhci_generic_done_sub(xfer); + +done: + xhci_device_done(xfer, err); +} + +/*------------------------------------------------------------------------* + * xhci_check_transfer + *------------------------------------------------------------------------*/ +static void +xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb) +{ + struct usb_xfer *xfer; + struct xhci_td *td; + uint64_t td_event; + uint32_t temp; + uint32_t actlen; + uint8_t status; + + /* decode TRB */ + td_event = trb->qwTrb0; + temp = le32toh(trb->dwTrb2); + + /* try to find the USB transfer that generated the event */ + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + td = xfer->td_transfer_cache; + + if (td_event == td->td_event) { + + actlen = XHCI_TRB_2_ACTLEN_GET(temp); + status = XHCI_TRB_2_ERROR_GET(temp); + + usb_pc_cpu_invalidate(td->page_cache); + + /* "td->remainder" is verified later */ + td->remainder -= actlen; + td->status = status; + + usb_pc_cpu_flush(td->page_cache); + + /* + * 1) Last transfer descriptor makes the + * transfer done + */ + if (((void *)td) == xfer->td_transfer_last) { + xhci_generic_done(xfer); + break; + } + + /* + * 2) Any kind of error makes the transfer + * done + */ + if ((status != XHCI_TRB_ERROR_SHORT_PKT) && + (status != XHCI_TRB_ERROR_SUCCESS)) { + xhci_generic_done(xfer); + break; + } + + /* + * 3) If there is no alternate next transfer, + * a short packet also makes the transfer done + */ + if (td->remainder > 0) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next != NULL) { + xfer->td_transfer_cache = td->alt_next; + break; + } + } + xhci_generic_done(xfer); + break; + } + + /* + * 4) Transfer complete - go to next TD + */ + xfer->td_transfer_cache = td->obj_next; + break; /* there should only be one match */ + } + } +} + +static void +xhci_pcd_enable(struct xhci_softc *sc) +{ + +} + +static void +xhci_interrupt_poll(struct xhci_softc *sc) +{ + struct usb_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + /* + * check if transfer is transferred + */ + if (xhci_check_transfer(xfer)) { + /* queue has been modified */ + goto repeat; + } + } +} + +/*------------------------------------------------------------------------* + * xhci_interrupt - XHCI interrupt handler + *------------------------------------------------------------------------*/ +void +xhci_interrupt(struct xhci_softc *sc) +{ + + +} + +/*------------------------------------------------------------------------* + * xhci_timeout - XHCI timeout handler + *------------------------------------------------------------------------*/ +static void +xhci_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 */ + xhci_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +xhci_do_poll(struct usb_bus *bus) +{ + struct xhci_softc *sc = XHCI_BUS2SC(bus); + + USB_BUS_LOCK(&sc->sc_bus); + xhci_interrupt_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +static void +xhci_setup_generic_chain_sub(struct xhci_std_temp *temp) +{ + struct usb_page_search buf_res; + struct xhci_td *td; + struct xhci_td *td_next; + struct xhci_td *td_alt_next; + uint32_t buf_offset; + uint32_t average; + uint32_t len_old; + uint32_t dword; + uint8_t shortpkt_old; + uint8_t precompute; + uint8_t x; + + 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_packet_size) { + temp->shortpkt = 1; + } + average = temp->len; + } + } + + if (td_next == NULL) + panic("%s: out of XHCI 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->len = average; + td->remainder = average; + td->status = 0; + + /* update remaining length */ + + temp->len -= average; + + /* reset TRB index */ + + x = 0; + + if (temp->trb_type == XHCI_TRB_TYPE_SETUP_STAGE) { + /* immediate data */ + + if (average > 8) + average = 8; + + td->td_trb[0].qwTrb0 = 0; + + usbd_copy_out(temp->pc, temp->offset + buf_offset, + (uint8_t *)(uintptr_t)&td->td_trb[0].qwTrb0, average); + + dword = XHCI_TRB_2_BYTES_SET(8) | + XHCI_TRB_2_TDSZ_SET(0) | + XHCI_TRB_2_IRQ_SET(0); + + td->td_trb[0].dwTrb2 = htole32(dword); + + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) | + XHCI_TRB_3_IDT_BIT; + + /* check wLength */ + if (td->td_trb[0].qwTrb0 & htole64(0xFFFF00000000ULL)) { + if (td->td_trb[0].qwTrb0 & htole64(1)) + dword |= XHCI_TRB_3_TRT_IN; + else + dword |= XHCI_TRB_3_TRT_OUT; + } + + td->td_trb[0].dwTrb3 = htole32(dword); + + x++; + + } else do { + + uint32_t npkt; + + /* fill out buffer pointers */ + + if (average == 0) { + npkt = 1; + memset(&buf_res, 0, sizeof(buf_res)); + } else { + usbd_get_page(temp->pc, temp->offset + buf_offset, &buf_res); + + /* get length to end of page */ + if (buf_res.length > average) + buf_res.length = average; + + /* check for maximum length */ + if (buf_res.length > XHCI_TD_PAGE_SIZE) + buf_res.length = XHCI_TD_PAGE_SIZE; + + /* setup npkt */ + npkt = (average + temp->max_packet_size - 1) / temp->max_packet_size; + if (npkt > 31) + npkt = 31; + } + + /* fill out TRB's */ + td->td_trb[x].qwTrb0 = + htole64((uint64_t)buf_res.physaddr); + + dword = + XHCI_TRB_2_BYTES_SET(buf_res.length) | + XHCI_TRB_2_TDSZ_SET(npkt) | + XHCI_TRB_2_IRQ_SET(0); + + td->td_trb[x].dwTrb2 = htole32(dword); + + dword = XHCI_TRB_3_CHAIN_BIT | + XHCI_TRB_3_TYPE_SET(temp->trb_type) | + XHCI_TRB_3_FRID_SET(temp->isoc_frame); + + if (temp->direction == UE_DIR_IN) + dword |= XHCI_TRB_3_DIR_IN; + + td->td_trb[x].dwTrb3 = htole32(dword); + + average -= buf_res.length; + buf_offset += buf_res.length; + x++; + + } while (average != 0); + + /* store number of data TRB's */ + + td->ntrb = x; + + /* compute event pointer */ + + if (1) { + uint64_t td_event; + + td_event = le64toh(td->td_self); + td_event += x * sizeof(td->td_trb[0]); + td->td_event = htole64(td_event); + } + + /* fill out link TRB */ + + if (td_next != NULL) { + /* link the current TD with the next one */ + td->td_trb[x].qwTrb0 = td_next->td_self; + } else { + /* this field will get updated later */ + } + + dword = XHCI_TRB_2_IRQ_SET(0); + + td->td_trb[x].dwTrb2 = htole32(dword); + + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | + XHCI_TRB_3_IOC_BIT | XHCI_TRB_3_CHAIN_BIT; + + td->td_trb[x].dwTrb3 = htole32(dword); + + 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; + } else { + /* we use this field internally */ + td_alt_next = td_next; + } + + /* restore */ + temp->shortpkt = shortpkt_old; + temp->len = len_old; + goto restart; + } else { + if (temp->multishort == 0) { + /* remove chain bit and clear TD SIZE - end of frame */ + td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); + td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); + td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); + td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); + usb_pc_cpu_flush(td->page_cache); + } + } + temp->td = td; + temp->td_next = td_next; +} + +static void +xhci_setup_generic_chain(struct usb_xfer *xfer) +{ + struct xhci_std_temp temp; + struct xhci_td *td; + uint32_t x; + uint32_t y; + + temp.average = xfer->max_hc_frame_size; + temp.max_packet_size = xfer->max_packet_size; + temp.sc = XHCI_BUS2SC(xfer->xroot->bus); + temp.pc = NULL; + temp.last_frame = 0; + temp.offset = 0; + temp.multishort = xfer->flags_int.isochronous_xfr || + xfer->flags_int.short_frames_ok; + + /* 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]; + + temp.td = NULL; + temp.td_next = td; + + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + if (xfer->flags_int.isochronous_xfr) { + + uint8_t shift; + + x = XREAD4(temp.sc, runt, XHCI_MFINDEX); + + switch (usbd_get_speed(xfer->xroot->udev)) { + case USB_SPEED_FULL: + shift = 3; + temp.isoc_delta = 8; /* 1ms */ + break; + default: + shift = usbd_xfer_get_fps_shift(xfer); + temp.isoc_delta = 1U << shift; + break; + } + + x += temp.isoc_delta - 1; + x &= ~(temp.isoc_delta - 1); + x += xfer->endpoint->usb_uframe; + + y = XHCI_MFINDEX_GET(x - xfer->endpoint->isoc_next); + + if ((xfer->endpoint->is_synced == 0) || + (y < (xfer->nframes << shift))) { + /* + * 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 = XHCI_MFINDEX_GET(x + (3 * 8)); + xfer->endpoint->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); + } + + x = 0; + temp.isoc_frame = xfer->endpoint->isoc_next; + temp.trb_type = XHCI_TRB_TYPE_ISOCH; + + xfer->endpoint->isoc_next += xfer->nframes << shift; + + } else if (xfer->flags_int.control_xfr) { + + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_hdr) { + + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.shortpkt = temp.len ? 1 : 0; + temp.trb_type = XHCI_TRB_TYPE_SETUP_STAGE; + temp.direction = 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; + } + + xhci_setup_generic_chain_sub(&temp); + } + x = 1; + temp.isoc_delta = 0; + temp.isoc_frame = 0; + temp.trb_type = XHCI_TRB_TYPE_DATA_STAGE; + } else { + x = 0; + temp.isoc_delta = 0; + temp.isoc_frame = 0; + temp.trb_type = XHCI_TRB_TYPE_NORMAL; + } + + if (x != xfer->nframes) { + /* setup page_cache pointer */ + temp.pc = xfer->frbuffers + x; + /* set endpoint direction */ + temp.direction = UE_GET_DIR(xfer->endpointno); + } + + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[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; + } else { + temp.last_frame = 1; + } + } + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.shortpkt = 0; + + } else if (xfer->flags_int.isochronous_xfr) { + + /* isochronous transfers don't have short packet termination */ + + temp.shortpkt = 1; + + /* isochronous transfers have a transfer limit */ + + if (temp.len > xfer->max_frame_size) + temp.len = xfer->max_frame_size; + } else { + + /* regular data transfer */ + + temp.shortpkt = xfer->flags.force_short_xfer ? 0 : 1; + } + + xhci_setup_generic_chain_sub(&temp); + + if (xfer->flags_int.isochronous_xfr) { + temp.offset += xfer->frlengths[x - 1]; + temp.isoc_frame += temp.isoc_delta; + } else { + /* get next Page Cache pointer */ + temp.pc = xfer->frbuffers + x; + } + } + + /* 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.direction = UE_GET_DIR(xfer->endpointno) ^ UE_DIR_IN; + temp.len = 0; + temp.pc = NULL; + temp.shortpkt = 0; + temp.last_frame = 1; + temp.trb_type = XHCI_TRB_TYPE_STATUS_STAGE; + + xhci_setup_generic_chain_sub(&temp); + } + + td = temp.td; + + /* remove chain bit and clear TD SIZE - end of frame */ + td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); + td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); + td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); + td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); + + /* link the current TD with the next one */ + td->td_trb[td->ntrb].qwTrb0 = XXX; + + usb_pc_cpu_flush(td->page_cache); + + /* must have at least one frame! */ + + xfer->td_transfer_last = td; + + if (xfer->xroot->udev->flags.self_suspended == 0) { + EHCI_APPEND_QH(qh, *qh_last); XXX; + } +} + +static void +xhci_root_intr(struct xhci_softc *sc) +{ + +} + +/*------------------------------------------------------------------------* + * xhci_device_done - XHCI done handler + * + * NOTE: This function can be called two times in a row on + * the same USB transfer. From close and from interrupt. + *------------------------------------------------------------------------*/ +static void +xhci_device_done(struct usb_xfer *xfer, usb_error_t error) +{ + DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", + xfer, xfer->endpoint, error); + + /* dequeue transfer and start next transfer */ + usbd_transfer_done(xfer, error); +} + +/*------------------------------------------------------------------------* + * XHCI data transfer support (generic type) + *------------------------------------------------------------------------*/ +static void +xhci_device_generic_open(struct usb_xfer *xfer) +{ + if (xfer->flags_int.isochronous_xfr) + usb_hs_bandwidth_alloc(xfer); +} + +static void +xhci_device_generic_close(struct usb_xfer *xfer) +{ + xhci_device_done(xfer, USB_ERR_CANCELLED); + + if (xfer->flags_int.isochronous_xfr) + usb_hs_bandwidth_free(xfer); +} + +static void +xhci_device_generic_enter(struct usb_xfer *xfer) +{ + /* setup TD's and QH */ + xhci_setup_generic_chain(xfer); + + /* put transfer on interrupt queue */ + xhci_transfer_intr_enqueue(xfer); +} + +static void +xhci_device_generic_start(struct usb_xfer *xfer) +{ + /* start timeout, if any */ + if (xfer->timeout != 0) + usbd_transfer_timeout_ms(xfer, &xhci_timeout, xfer->timeout); +} + +struct usb_pipe_methods xhci_device_generic_methods = +{ + .open = xhci_device_generic_open, + .close = xhci_device_generic_close, + .enter = xhci_device_generic_enter, + .start = xhci_device_generic_start, +}; + + +/*------------------------------------------------------------------------* + * xhci root HUB support + *------------------------------------------------------------------------* + * Simulate a hardware HUB by handling all the necessary requests. + *------------------------------------------------------------------------*/ + +#define HSETW(ptr, val) ptr[0] = (uint8_t)(val), ptr[1] = (uint8_t)((val) >> 8) + +static const +struct usb_device_descriptor xhci_devd = +{ + .bLength = sizeof(xhci_devd), + .bDescriptorType = UDESC_DEVICE, /* type */ + HSETW(.bcdUSB, 0x0300), /* USB version */ + .bDeviceClass = UDCLASS_HUB, /* class */ + .bDeviceSubClass = UDSUBCLASS_HUB, /* subclass */ + .bDeviceProtocol = UDPROTO_SSHUB, /* protocol */ + .bMaxPacketSize = 9, /* max packet size */ + HSETW(.idVendor, 0x0000), /* vendor */ + HSETW(.idProduct, 0x0000), /* product */ + HSETW(.bcdDevice, 0x0100), /* device version */ + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 0, + .bNumConfigurations = 1, /* # of configurations */ +}; + +static const +struct xhci_bos_desc xhci_bosd = { + .bosd = { + .bLength = sizeof(xhci_bosd.bosd), + .bDescriptorType = UDESC_BOS, + HSETW(.wTotalLength, sizeof(xhci_bosd)), + .bNumDeviceCaps = 3, + }, + .usb2extd = { + .bLength = sizeof(xhci_bosd.usb2extd), + .bDescriptorType = 1, + .bDevCapabilityType = 2, + .bmAttributes = 2, + }, + .usbdcd = { + .bLength = sizeof(xhci_bosd.usbdcd), + .bDescriptorType = UDESC_DEVICE_CAPABILITY, + .bDevCapabilityType = 3, + .bmAttributes = XXX, + HSETW(.wSpeedsSupported, 0x000C), + .bFunctionalitySupport = 8, + .bU1DevExitLat = 128, /* dummy - not used */ + .bU2DevExitLat = 128, /* dummy - not used */ + }, + .cidd = { + .bLength = sizeof(xhci_bosd.cidd), + .bDescriptorType = 1, + .bDevCapabilityType = 4, + .bReserved = 0, + .bContainerID = XXX, + }, +}; + +static const +struct xhci_config_desc xhci_confd = { + .confd = { + .bLength = sizeof(xhci_confd.confd), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(xhci_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0 /* max power */ + }, + .ifcd = { + .bLength = sizeof(xhci_confd.ifcd), >>> TRUNCATED FOR MAIL (1000 lines) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201007310920.o6V9Kpsj037501>