PERFORCE change 181631 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Sat Jul 31 09:20:52 UTC 2010
http://p4web.freebsd.org/@@181631?ac=10
Change 181631 by hselasky at 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) <<<
More information about the p4-projects
mailing list