socsvn commit: r257392 - soc2013/bguan/head/sys/dev/xen/usbfront

bguan at FreeBSD.org bguan at FreeBSD.org
Mon Sep 16 01:05:35 UTC 2013


Author: bguan
Date: Mon Sep 16 01:05:35 2013
New Revision: 257392
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=257392

Log:
  debug method xenhci_roothub_exec() for xen usb host controller

Modified:
  soc2013/bguan/head/sys/dev/xen/usbfront/xenhci.c

Modified: soc2013/bguan/head/sys/dev/xen/usbfront/xenhci.c
==============================================================================
--- soc2013/bguan/head/sys/dev/xen/usbfront/xenhci.c	Mon Sep 16 00:06:54 2013	(r257391)
+++ soc2013/bguan/head/sys/dev/xen/usbfront/xenhci.c	Mon Sep 16 01:05:35 2013	(r257392)
@@ -93,92 +93,1424 @@
 #define	XENHCI_INTR_ENDPT 1
 
 extern struct usb_bus_methods xenhci_bus_methods;
+extern struct usb_pipe_methods xenhci_device_generic_methods;
 
 
+static void	xenhci_timeout(void *);
+static void	xenhci_device_done(struct usb_xfer *, usb_error_t);
+
 /*
 static void
-xenhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
+xenhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
+{
+	//TODO need this method()?
+}*/
+
+usb_error_t
+xenhci_start_controller(struct xenhci_softc *sc)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_start_controller()\n");
+	#endif
+	//TODO
+	/* catch any lost interrupts */
+	xenhci_do_poll(&sc->sc_bus);
+
+	return (0);
+}
+
+usb_error_t
+xenhci_halt_controller(struct xenhci_softc *sc)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_halt_controller()\n");
+	#endif
+	//TODO
+	return (0);
+}
+
+usb_error_t
+xenhci_init(struct xenhci_softc *sc, device_t dev)
+{
+	/* initialise some bus fields */
+	sc->sc_bus.parent = dev;
+
+	/* set up the bus struct */
+	sc->sc_bus.methods = &xenhci_bus_methods;
+
+	/* setup devices array */
+	sc->sc_bus.devices = sc->sc_devices;
+	sc->sc_bus.devices_max = XENHCI_MAX_DEVICES;
+
+	/* get all DMA memory */
+	//if (usb_bus_mem_alloc_all(&sc->sc_bus,
+	//    USB_GET_DMA_TAG(dev), &xenhci_iterate_hw_softc)) {
+        if (usb_bus_mem_alloc_all(&sc->sc_bus,
+	    USB_GET_DMA_TAG(dev), NULL)) {
+		return (ENOMEM);
+	}
+
+	return (0);
+}
+
+void
+xenhci_uninit(struct xenhci_softc *sc)
+{
+	/* TODO?
+	 * NOTE: At this point the control transfer process is gone
+	 * and "xenhci_configure_msg" is no longer called. Consequently
+	 * waiting for the configuration messages to complete is not
+	 * needed.
+	 */
+	usb_bus_mem_free_all(&sc->sc_bus, NULL);
+}
+
+static void
+xenhci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_set_hw_power_sleep()\n");
+	#endif
+	//TODO
+	struct xenhci_softc *sc = XENHCI_BUS2SC(bus);
+
+	switch (state) {
+	case USB_HW_POWER_SUSPEND:
+		DPRINTF("Stopping the XENHCI\n");
+		xenhci_halt_controller(sc);
+		break;
+	case USB_HW_POWER_SHUTDOWN:
+		DPRINTF("Stopping the XENHCI\n");
+		xenhci_halt_controller(sc);
+		break;
+	case USB_HW_POWER_RESUME:
+		DPRINTF("Starting the XENHCI\n");
+		xenhci_start_controller(sc);
+		break;
+	default:
+		break;
+	}
+}
+
+static usb_error_t
+xenhci_generic_done_sub(struct usb_xfer *xfer)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_generic_done_sub()\n");
+	#endif
+
+	struct xenhci_td *td;
+	struct xenhci_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] rem=%u/%u status=%u\n",
+		    xfer, (unsigned int)xfer->aframes,
+		    (unsigned int)xfer->nframes,
+		    (unsigned int)len, (unsigned int)td->len,
+		    (unsigned int)status);
+		#ifdef XENUSB_BEBUG
+		printf("[xenusb_debug]xfer=%p[%u/%u] rem=%u/%u status=%u\n",
+		    xfer, (unsigned int)xfer->aframes,
+		    (unsigned int)xfer->nframes,
+		    (unsigned int)len, (unsigned int)td->len,
+		    (unsigned int)status);
+		#endif
+
+		/*
+	         * 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);
+
+			#ifdef XENUSB_BEBUG
+			printf("[xenusb_debug]Invalid status length, "
+			    "0x%04x/0x%04x bytes\n", len, td->len);
+			#endif
+
+			status = XENHCI_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 != XENHCI_TRB_ERROR_SHORT_PKT &&
+		    status != XENHCI_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 ||
+			    xfer->flags_int.control_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 == XENHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED : 
+	    (status != XENHCI_TRB_ERROR_SHORT_PKT && 
+	    status != XENHCI_TRB_ERROR_SUCCESS) ? USB_ERR_IOERROR :
+	    USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+xenhci_generic_done(struct usb_xfer *xfer)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_generic_done()\n");
+	#endif
+
+	usb_error_t err = 0;
+
+	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
+	    xfer, xfer->endpoint);
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xfer=%p endpoint=%p transfer done\n",
+	    xfer, xfer->endpoint);
+	#endif
+
+	/* reset scanner */
+	xfer->td_transfer_cache = xfer->td_transfer_first;
+
+	if (xfer->flags_int.control_xfr) {
+		if (xfer->flags_int.control_hdr)
+			err = xenhci_generic_done_sub(xfer);
+
+		xfer->aframes = 1;
+
+		if (xfer->td_transfer_cache == NULL)
+			goto done;
+	}
+
+	while (xfer->aframes != xfer->nframes) {
+
+		err = xenhci_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 = xenhci_generic_done_sub(xfer);
+done:
+	/* transfer is complete */
+	//xhci_device_done(xfer, err);
+}
+
+static void
+xenhci_activate_transfer(struct usb_xfer *xfer)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_activate_transfer()\n");
+	#endif
+
+	struct xenhci_td *td;
+
+	td = xfer->td_transfer_cache;
+
+	usb_pc_cpu_invalidate(td->page_cache);
+
+	if (!(td->td_trb[0].dwTrb3 & htole32(XENHCI_TRB_3_CYCLE_BIT))) {
+
+		/* activate the transfer */
+		td->td_trb[0].dwTrb3 |= htole32(XENHCI_TRB_3_CYCLE_BIT);
+		usb_pc_cpu_flush(td->page_cache);
+
+		//xhci_endpoint_doorbell(xfer);//?
+	}
+}
+
+static void
+xenhci_skip_transfer(struct usb_xfer *xfer)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_skip_transfer()\n");
+	#endif
+
+	struct xenhci_td *td;
+	struct xenhci_td *td_last;
+
+	td = xfer->td_transfer_cache;
+	td_last = xfer->td_transfer_last;
+
+	td = td->alt_next;
+
+	usb_pc_cpu_invalidate(td->page_cache);
+
+	if (!(td->td_trb[0].dwTrb3 & htole32(XENHCI_TRB_3_CYCLE_BIT))) {
+
+		usb_pc_cpu_invalidate(td_last->page_cache);
+
+		/* copy LINK TRB to current waiting location */
+		td->td_trb[0].qwTrb0 = td_last->td_trb[td_last->ntrb].qwTrb0;
+		td->td_trb[0].dwTrb2 = td_last->td_trb[td_last->ntrb].dwTrb2;
+		usb_pc_cpu_flush(td->page_cache);
+
+		td->td_trb[0].dwTrb3 = td_last->td_trb[td_last->ntrb].dwTrb3;
+		usb_pc_cpu_flush(td->page_cache);
+
+		//xhci_endpoint_doorbell(xfer);//?
+	}
+}
+
+/*------------------------------------------------------------------------*
+ *	xenhci_check_transfer
+ *------------------------------------------------------------------------*/
+static void
+xenhci_check_transfer(struct xenhci_softc *sc, struct xenhci_trb *trb)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_check_transfer()\n");
+	#endif
+
+	struct xenhci_endpoint_ext *pepext;
+	int64_t offset;
+	uint64_t td_event;
+	uint32_t temp;
+	uint32_t remainder;
+	uint16_t stream_id;
+	uint16_t i;
+	uint8_t status;
+	uint8_t halted;
+	uint8_t epno;
+	uint8_t index;
+
+	/* decode TRB */
+	td_event = le64toh(trb->qwTrb0);
+	temp = le32toh(trb->dwTrb2);
+
+	remainder = XENHCI_TRB_2_REM_GET(temp);
+
+	status = XENHCI_TRB_2_ERROR_GET(temp);
+	stream_id = XENHCI_TRB_2_STREAM_GET(temp);
+
+	temp = le32toh(trb->dwTrb3);
+	epno = XENHCI_TRB_3_EP_GET(temp);
+	index = XENHCI_TRB_3_SLOT_GET(temp);
+
+	/* check if error means halted */
+	halted = (status != XENHCI_TRB_ERROR_SHORT_PKT &&
+	    status != XENHCI_TRB_ERROR_SUCCESS);
+
+	DPRINTF("slot=%u epno=%u stream=%u remainder=%u status=%u\n",
+	    index, epno, stream_id, remainder, status);
+	#ifdef XENUSB_BEBUG
+	printf("[gbtest]slot=%u epno=%u stream=%u remainder=%u status=%u\n",
+	    index, epno, stream_id, remainder, status);
+	#endif
+
+	if (index > sc->sc_noslot) {
+		DPRINTF("Invalid slot.\n");
+		return;
+	}
+
+	if ((epno == 0) || (epno >= XENHCI_MAX_ENDPOINTS)) {
+		DPRINTF("Invalid endpoint.\n");
+		return;
+	}
+
+	pepext = &sc->sc_hw.devs[index].endp[epno];
+
+	if (pepext->trb_ep_mode != USB_EP_MODE_STREAMS) {
+		stream_id = 0;
+		DPRINTF("stream_id=0\n");
+	} else if (stream_id >= XENHCI_MAX_STREAMS) {
+		DPRINTF("Invalid stream ID.\n");
+		return;
+	}
+
+	/* try to find the USB transfer that generated the event */
+	for (i = 0; i != (XENHCI_MAX_TRANSFERS - 1); i++) {
+		struct usb_xfer *xfer;
+		struct xenhci_td *td;
+
+		xfer = pepext->xfer[i + (XENHCI_MAX_TRANSFERS * stream_id)];
+		if (xfer == NULL)
+			continue;
+
+		td = xfer->td_transfer_cache;
+
+		DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n",
+			(long long)td_event,
+			(long long)td->td_self,
+			(long long)td->td_self + sizeof(td->td_trb));
+		#ifdef XENUSB_BEBUG
+		printf("[gbtest]Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n",
+			(long long)td_event,
+			(long long)td->td_self,
+			(long long)td->td_self + sizeof(td->td_trb));
+		#endif
+
+		/*
+		 * NOTE: Some XENHCI implementations might not trigger
+		 * an event on the last LINK TRB so we need to
+		 * consider both the last and second last event
+		 * address as conditions for a successful transfer.
+		 *
+		 * NOTE: We assume that the XENHCI will only trigger one
+		 * event per chain of TRBs.
+		 */
+
+		offset = td_event - td->td_self;
+
+		if (offset >= 0 &&
+		    offset < (int64_t)sizeof(td->td_trb)) {
+
+			usb_pc_cpu_invalidate(td->page_cache);
+
+			/* compute rest of remainder, if any */
+			for (i = (offset / 16) + 1; i < td->ntrb; i++) {
+				temp = le32toh(td->td_trb[i].dwTrb2);
+				remainder += XENHCI_TRB_2_BYTES_GET(temp);
+			}
+
+			DPRINTFN(5, "New remainder: %u\n", remainder);
+
+			/* clear isochronous transfer errors */
+			if (xfer->flags_int.isochronous_xfr) {
+				if (halted) {
+					halted = 0;
+					status = XENHCI_TRB_ERROR_SUCCESS;
+					remainder = td->len;
+				}
+			}
+
+			/* "td->remainder" is verified later */
+			td->remainder = remainder;
+			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) {
+				DPRINTF("TD is last\n");
+				xenhci_generic_done(xfer);
+				break;
+			}
+
+			/*
+			 * 2) Any kind of error makes the transfer
+			 * done
+			 */
+			if (halted) {
+				DPRINTF("TD has I/O error\n");
+				xenhci_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 (td->alt_next == NULL) {
+					DPRINTF(
+					    "short TD has no alternate next\n");
+					xenhci_generic_done(xfer);
+					break;
+				}
+				DPRINTF("TD has short pkt\n");
+				if (xfer->flags_int.short_frames_ok ||
+				    xfer->flags_int.isochronous_xfr ||
+				    xfer->flags_int.control_xfr) {
+					/* follow the alt next */
+					xfer->td_transfer_cache = td->alt_next;
+					xenhci_activate_transfer(xfer);
+					break;
+				}
+				xenhci_skip_transfer(xfer);
+				xenhci_generic_done(xfer);
+				break;
+			}
+
+			/*
+			 * 4) Transfer complete - go to next TD
+			 */
+			DPRINTF("Following next TD\n");
+			xfer->td_transfer_cache = td->obj_next;
+			xenhci_activate_transfer(xfer);
+			break;		/* there should only be one match */
+		}
+	}
+}
+
+static void
+xenhci_check_command(struct xenhci_softc *sc, struct xenhci_trb *trb)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_check_command()\n");
+	#endif
+
+	if (sc->sc_cmd_addr == trb->qwTrb0) {
+		DPRINTF("Received command event\n");
+		sc->sc_cmd_result[0] = trb->dwTrb2;
+		sc->sc_cmd_result[1] = trb->dwTrb3;
+		cv_signal(&sc->sc_cmd_cv);
+	}
+}
+
+static void
+xenhci_interrupt_poll(struct xenhci_softc *sc)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_interrupt_poll()\n");
+	#endif
+
+	struct usb_page_search buf_res;
+	struct xenhci_hw_root *phwr;
+	//uint64_t addr;
+	uint32_t temp;
+	uint16_t i;
+	uint8_t event;
+	uint8_t j;
+	uint8_t k;
+	uint8_t t;
+
+	usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
+
+	phwr = buf_res.buffer;
+
+	/* Receive any events */
+
+	usb_pc_cpu_invalidate(&sc->sc_hw.root_pc);
+
+	i = sc->sc_event_idx;
+	j = sc->sc_event_ccs;
+	t = 2;
+
+	while (1) {
+
+		temp = le32toh(phwr->hwr_events[i].dwTrb3);
+
+		k = (temp & XENHCI_TRB_3_CYCLE_BIT) ? 1 : 0;
+
+		if (j != k)
+			break;
+
+		event = XENHCI_TRB_3_TYPE_GET(temp);
+
+		DPRINTFN(10, "event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n",
+		    i, event, (long long)le64toh(phwr->hwr_events[i].qwTrb0),
+		    (long)le32toh(phwr->hwr_events[i].dwTrb2),
+		    (long)le32toh(phwr->hwr_events[i].dwTrb3));
+		#ifdef XENUSB_BEBUG
+		printf("[gbtest]event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n",
+		    i, event, (long long)le64toh(phwr->hwr_events[i].qwTrb0),
+		    (long)le32toh(phwr->hwr_events[i].dwTrb2),
+		    (long)le32toh(phwr->hwr_events[i].dwTrb3));
+		#endif
+
+		switch (event) {
+		case XENHCI_TRB_EVENT_TRANSFER:
+			#ifdef XENUSB_BEBUG
+			printf("[xenusb_debug]xenhci.c: XENHCI_TRB_EVENT_TRANSFER\n");
+			#endif
+			xenhci_check_transfer(sc, &phwr->hwr_events[i]);
+			break;
+		case XENHCI_TRB_EVENT_CMD_COMPLETE:
+			#ifdef XENUSB_BEBUG
+			printf("[xenusb_debug]xenhci.c: XENHCI_TRB_EVENT_CMD_COMPLETE\n");
+			#endif
+			xenhci_check_command(sc, &phwr->hwr_events[i]);
+			break;
+		default:
+			DPRINTF("Unhandled event = %u\n", event);
+			break;
+		}
+
+		i++;
+
+		if (i == XENHCI_MAX_EVENTS) {
+			i = 0;
+			j ^= 1;
+
+			/* check for timeout */
+			if (!--t)
+				break;
+		}
+	}
+
+	sc->sc_event_idx = i;
+	sc->sc_event_ccs = j;
+
+	/*
+	 * NOTE: The Event Ring Dequeue Pointer Register is 64-bit
+	 * latched. That means to activate the register we need to
+	 * write both the low and high double word of the 64-bit
+	 * register.
+	 */
+
+	//addr = (uint32_t)buf_res.physaddr;
+	//addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[i];
+
+	/* try to clear busy bit */
+	//addr |= XHCI_ERDP_LO_BUSY;
+
+	//XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
+	//XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
+}
+
+
+/*------------------------------------------------------------------------*
+ *	xenhci_timeout - XENHCI timeout handler
+ *------------------------------------------------------------------------*/
+static void
+xenhci_timeout(void *arg)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_timeout()\n");
+	#endif
+
+	struct usb_xfer *xfer = arg;
+
+	DPRINTF("xfer=%p\n", xfer);
+
+	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+	/* transfer is transferred */
+	xenhci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+xenhci_do_poll(struct usb_bus *bus)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_do_poll()\n");
+	#endif
+
+	struct xenhci_softc *sc = XENHCI_BUS2SC(bus);
+
+	USB_BUS_LOCK(&sc->sc_bus);
+	xenhci_interrupt_poll(sc);
+	USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static usb_error_t
+xenhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address)
+{
+	#ifdef XENUSB_BEBUG
+	printf("[xenusb_debug]xenhci.c: xenhci_set_address()\n");
+	#endif
+	//TODO
+	return (0);
+}
+
+
+
+
+
+/*------------------------------------------------------------------------*
+ *
+ */
+static void
+xenhci_setup_generic_chain_sub(struct xenhci_std_temp *temp)
+{
+	struct usb_page_search buf_res;
+	struct xenhci_td *td;
+	struct xenhci_td *td_next;
+	struct xenhci_td *td_alt_next;
+	struct xenhci_td *td_first;
+	uint32_t buf_offset;
+	uint32_t average;
+	uint32_t len_old;
+	uint32_t npkt_off;
+	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;
+	npkt_off = 0;
+	precompute = 1;
+
+restart:
+
+	td = temp->td;
+	td_next = td_first = 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 = 0;
+		td->status = 0;
+
+		/* update remaining length */
+
+		temp->len -= average;
+
+		/* reset TRB index */
+
+		x = 0;
+
+		if (temp->trb_type == XENHCI_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 = XENHCI_TRB_2_BYTES_SET(8) |
+			    XENHCI_TRB_2_TDSZ_SET(0) |
+			    XENHCI_TRB_2_IRQ_SET(0);
+
+			td->td_trb[0].dwTrb2 = htole32(dword);
+
+			dword = XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_SETUP_STAGE) |
+			  XENHCI_TRB_3_IDT_BIT | XENHCI_TRB_3_CYCLE_BIT;
+
+			/* check wLength */
+			if (td->td_trb[0].qwTrb0 &
+			   htole64(XENHCI_TRB_0_WLENGTH_MASK)) {
+				if (td->td_trb[0].qwTrb0 & htole64(1))
+					dword |= XENHCI_TRB_3_TRT_IN;
+				else
+					dword |= XENHCI_TRB_3_TRT_OUT;
+			}
+
+			td->td_trb[0].dwTrb3 = htole32(dword);
+#ifdef USB_DEBUG
+			xhci_dump_trb(&td->td_trb[x]);
+#endif
+			x++;
+
+		} else do {
+
+			uint32_t npkt;
+
+			/* fill out buffer pointers */
+
+			if (average == 0) {
+				npkt = 0;
+				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 > XENHCI_TD_PAGE_SIZE)
+					buf_res.length = XENHCI_TD_PAGE_SIZE;
+
+				npkt_off += buf_res.length;
+
+				/* setup npkt */
+				npkt = (len_old - npkt_off + 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 =
+			  XENHCI_TRB_2_BYTES_SET(buf_res.length) |
+			  XENHCI_TRB_2_TDSZ_SET(npkt) | 
+			  XENHCI_TRB_2_IRQ_SET(0);
+
+			td->td_trb[x].dwTrb2 = htole32(dword);
+
+			switch (temp->trb_type) {
+			case XENHCI_TRB_TYPE_ISOCH:
+				/* BEI: Interrupts are inhibited until EOT */
+				dword = XENHCI_TRB_3_CHAIN_BIT | XENHCI_TRB_3_CYCLE_BIT |
+				    XENHCI_TRB_3_BEI_BIT |
+				    XENHCI_TRB_3_TBC_SET(temp->tbc) |
+				    XENHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+				if (td != td_first) {
+					dword |= XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_NORMAL);
+				} else if (temp->do_isoc_sync != 0) {
+					temp->do_isoc_sync = 0;
+					/* wait until "isoc_frame" */
+					dword |= XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_ISOCH) |
+					    XENHCI_TRB_3_FRID_SET(temp->isoc_frame / 8);
+				} else {
+					/* start data transfer at next interval */
+					dword |= XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_ISOCH) |
+					    XENHCI_TRB_3_ISO_SIA_BIT;
+				}
+				if (temp->direction == UE_DIR_IN)
+					dword |= XENHCI_TRB_3_DIR_IN | XENHCI_TRB_3_ISP_BIT;
+				break;
+			case XENHCI_TRB_TYPE_DATA_STAGE:
+				dword = XENHCI_TRB_3_CHAIN_BIT | XENHCI_TRB_3_CYCLE_BIT |
+				    XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_DATA_STAGE) |
+				    XENHCI_TRB_3_TBC_SET(temp->tbc) |
+				    XENHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+				if (temp->direction == UE_DIR_IN)
+					dword |= XENHCI_TRB_3_DIR_IN | XENHCI_TRB_3_ISP_BIT;
+				break;
+			case XENHCI_TRB_TYPE_STATUS_STAGE:
+				dword = XENHCI_TRB_3_CHAIN_BIT | XENHCI_TRB_3_CYCLE_BIT |
+				    XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_STATUS_STAGE) |
+				    XENHCI_TRB_3_TBC_SET(temp->tbc) |
+				    XENHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+				if (temp->direction == UE_DIR_IN)
+					dword |= XHCI_TRB_3_DIR_IN;
+				break;
+			default:	/* XENHCI_TRB_TYPE_NORMAL */
+				/* BEI: Interrupts are inhibited until EOT */
+				dword = XENHCI_TRB_3_CHAIN_BIT | XENHCI_TRB_3_CYCLE_BIT |
+				    XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_NORMAL) |
+				    XENHCI_TRB_3_BEI_BIT |
+				    XENHCI_TRB_3_TBC_SET(temp->tbc) |
+				    XENHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+				if (temp->direction == UE_DIR_IN)
+					dword |= XENHCI_TRB_3_DIR_IN | XENHCI_TRB_3_ISP_BIT;
+				break;
+			}
+			td->td_trb[x].dwTrb3 = htole32(dword);
+
+			average -= buf_res.length;
+			buf_offset += buf_res.length;
+#ifdef USB_DEBUG
+			xhci_dump_trb(&td->td_trb[x]);
+#endif
+			x++;
+
+		} while (average != 0);
+
+		td->td_trb[x-1].dwTrb3 |= htole32(XENHCI_TRB_3_IOC_BIT);
+
+		/* store number of data TRB's */
+
+		td->ntrb = x;
+
+		DPRINTF("NTRB=%u\n", x);
+
+		/* fill out link TRB */
+
+		if (td_next != NULL) {
+			/* link the current TD with the next one */
+			td->td_trb[x].qwTrb0 = htole64((uint64_t)td_next->td_self);
+			DPRINTF("LINK=0x%08llx\n", (long long)td_next->td_self);
+		} else {
+			/* this field will get updated later */
+			DPRINTF("NOLINK\n");
+		}
+
+		dword = XENHCI_TRB_2_IRQ_SET(0);
+
+		td->td_trb[x].dwTrb2 = htole32(dword);
+
+		dword = XENHCI_TRB_3_TYPE_SET(XENHCI_TRB_TYPE_LINK) |
+		    XENHCI_TRB_3_CYCLE_BIT | XENHCI_TRB_3_IOC_BIT;
+
+		td->td_trb[x].dwTrb3 = htole32(dword);
+
+		td->alt_next = td_alt_next;
+#ifdef USB_DEBUG
+		xhci_dump_trb(&td->td_trb[x]);
+#endif
+		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;
+	}
+
+	/*
+	 * Remove cycle bit from the first TRB if we are
+	 * stepping them:
+	 */
+	if (temp->step_td != 0) {
+		td_first->td_trb[0].dwTrb3 &= ~htole32(XENHCI_TRB_3_CYCLE_BIT);
+		usb_pc_cpu_flush(td_first->page_cache);
+	}
+
+	/* remove chain bit because this is the last TRB in the chain */
+	td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XENHCI_TRB_2_TDSZ_SET(15));
+	td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XENHCI_TRB_3_CHAIN_BIT);
+
+	usb_pc_cpu_flush(td->page_cache);
+
+	temp->td = td;
+	temp->td_next = td_next;
+}
+
+static void
+xenhci_setup_generic_chain(struct usb_xfer *xfer)
 {
-	//TODO need this method()?
-}*/
+	//struct xhci_std_temp temp;
+	struct xenhci_td *td;
+	uint32_t x;
+	uint32_t y;
+	uint8_t mult;
+
+	temp.do_isoc_sync = 0;
+	temp.step_td = 0;
+	temp.tbc = 0;
+	temp.tlbpc = 0;
+	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.control_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;
+
+		/* compute multiplier for ISOCHRONOUS transfers */
+		mult = xfer->endpoint->ecomp ?
+		    UE_GET_SS_ISO_MULT(xfer->endpoint->ecomp->bmAttributes)
+		    : 0;
+		/* check for USB 2.0 multiplier */
+		if (mult == 0) {
+			mult = (xfer->endpoint->edesc->

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-soc-all mailing list