Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Sep 2007 22:19:17 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 126805 for review
Message-ID:  <200709252219.l8PMJH8W005346@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=126805

Change 126805 by hselasky@hselasky_laptop001 on 2007/09/25 22:18:18

	
	FYI: The comments follow the P4 diff from top to bottom.
	
	In general: Get the UHCI HC driver up to date with the latest
	USB API changes and factor out some functions to reduce the
	code size. Almost all changes follow the changes in change
	126691. See there if you want more details.
	
	- define "alt_next" pointer like a macro, hence when it is used
	  we are reusing an unused pointer, "td->next". Probably
	  I will change this into a "union" in a separate patch.
	
	- "struct uhci_std_temp" reflects the "struct ehci_std_temp"
	  and work in the same manner.
	
	- there is now only one fixup offset in the UHCI Transfer
	  Descriptor to save memory. This leads to some changes in 
	  the code, like that we have to compute the other offset by
	  algorithm.
	
	- new function "uhci_non_isoc_done_sub()" similar to
	  "ehci_non_isoc_done_sub()".
	
	- "uhci_non_isoc_done()" has now the same structure 
	  as "ehci_non_isoc_done()".
	
	- new function "uhci_check_transfer_sub()" that will fixup the
	  Data Toggle and queue the next USB frame when multi frame USB
	  transfers are used, hence the UHCI hardware does not support
	  this.
	
	- "uhci_setup_standard_chain_sub()" is almost identical to
	  "ehci_setup_standard_chain_sub()".
	
	- "uhci_setup_standard_chain()" is almost identical to
	  "ehci_setup_standard_chain()".
	
	- "usbd_transfer_done()" is now a part
	  of "usbd_transfer_dequeue()".
	
	- substituted USBD_XXX by the corresponding bitfield.
	  For example:
		flags & USBD_USE_POLLING -> flags.use_polling
	
	- the check for "(nframes == 0)" has been factored out into
	  "usbd_start_hardware()".
	
	- use "token" field in UHCI Transfer Descriptor to decide if a
	  USB transfer is read or write, instead of "xfer->endpoint".
	  This is the case in "uhci_mem_layout_fixup()". The reason is
	  that we don't have access to the "struct usbd_xfer *" any more.
	
	- UHCI root control and interrupt transfers now use the
	  factored out "usbd_std_root_transfer()" system to transfer
	  data to/from the USB transfer.
	
	- removed all "copy_in/copy_out" data bouncing stuff.
	
	- changes to "uhci_xfer_setup()" follow the changes to
	  "ehci_xfer_setup()". See change 126691.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/uhci.c#31 edit
.. //depot/projects/usb/src/sys/dev/usb/uhci.h#11 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/uhci.c#31 (text+ko) ====

@@ -78,7 +78,7 @@
 #include <dev/usb/usb_subr.h>
 #include <dev/usb/uhci.h>
 
-#define MS_TO_TICKS(ms) (((ms) * hz) / 1000)
+#define	alt_next next
 #define UHCI_BUS2SC(bus) ((uhci_softc_t *)(((u_int8_t *)(bus)) - \
    POINTER_TO_UNSIGNED(&(((uhci_softc_t *)0)->sc_bus))))
 
@@ -122,8 +122,6 @@
 
 #define UHCI_INTR_ENDPT 1
 
-#define ADD_BYTES(ptr,size) ((void *)(((u_int8_t *)(ptr)) + (size)))
-
 #define	SC_HW_PHYSADDR(sc,what) \
   ((sc)->sc_hw_page.physaddr + \
    POINTER_TO_UNSIGNED(&(((struct uhci_hw_softc *)0)->what)))
@@ -133,12 +131,27 @@
 	struct usbd_page_search buf_res;
 	struct usbd_page_search fix_res;
 
-	struct usbd_xfer *xfer;
+	struct usbd_page_cache *buf_pc;
+	struct usbd_page_cache *fix_pc;
 
 	uint32_t buf_offset;
 	uint32_t fix_offset;
 
-	uint8_t isread;
+	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;
 };
 
 extern struct usbd_bus_methods uhci_bus_methods;
@@ -156,140 +169,88 @@
 static void
 uhci_mem_layout_init(struct uhci_mem_layout *ml, struct usbd_xfer *xfer)
 {
+	ml->buf_pc = xfer->frbuffers + 0;
+	ml->fix_pc = &(xfer->buf_fixup);
+
 	ml->buf_offset = 0;
-	usbd_get_page(&(xfer->buf_data), ml->buf_offset, &(ml->buf_res));
-
 	ml->fix_offset = 0;
-	usbd_get_page(&(xfer->buf_fixup), ml->fix_offset, &(ml->fix_res));
 
-	ml->isread = 0; /* write */
+	ml->max_frame_size = xfer->max_frame_size;
 
-	ml->xfer = xfer; /* save a push */
 	return;
 }
 
 static void
-uhci_mem_layout_fit(struct uhci_mem_layout *ml, struct uhci_td *td, uint16_t len)
+uhci_mem_layout_fixup(struct uhci_mem_layout *ml, struct uhci_td *td)
 {
-	struct usbd_page_search tmp_res;
-	struct usbd_xfer *xfer = ml->xfer;
+	usbd_get_page(ml->buf_pc, ml->buf_offset, &(ml->buf_res));
+
+	if (ml->buf_res.length < td->len) {
+
+	    while (1) {
+
+		/* need to do a fixup */
 
-	if (ml->buf_res.length < len) {
+		usbd_get_page(ml->fix_pc, ml->fix_offset, &(ml->fix_res));
 
-	    /* need to do a fixup */
+		/* check if there is room for the current transfer */
 
-	    if (ml->fix_res.length < len) {
+		if (ml->fix_res.length >= td->len) {
+		    break;
+		}
 
-		/* need to do a sub-fixup */
+		/* need to do a sub-fixup (goto next page) */
 
 		ml->fix_offset += ml->fix_res.length;
-		usbd_get_page(&(xfer->buf_fixup), ml->fix_offset, &(ml->fix_res));
 	    }
 
 	    td->td_buffer = htole32(ml->fix_res.physaddr);
 
-	    if (!ml->isread) {
+	    /*
+	     * The UHCI driver cannot handle
+	     * page crossings, so a fixup is
+	     * needed:
+	     *
+	     *  +----+----+ - - -
+	     *  | YYY|Y   |
+	     *  +----+----+ - - -
+	     *     \    \
+	     *      \    \
+	     *       +----+
+	     *       |YYYY|  (fixup)
+	     *       +----+
+	     */
 
-		/*
-		 * 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_offset = ml->fix_offset;
+	    } else {
+		td->fix_offset = UHCI_FIX_OFFSET_NONE;
 
-		/* no copy back needed: */
-
-		td->fixup_dst_offset = 0xffffffff;
-		td->fixup_src_offset = 0xffffffff;
+		/* copy data to fixup location */
 
-		usbd_get_page(&(xfer->buf_data), ml->buf_offset + 
-			      ml->buf_res.length, &tmp_res);
-
-		/* copy first data part to fixup location */
-
 		usbd_page_dma_exit(ml->fix_res.page);
 
-		usbd_page_dma_exit(ml->buf_res.page);
-		bcopy(ml->buf_res.buffer, ml->fix_res.buffer, ml->buf_res.length);
-		usbd_page_dma_enter(ml->buf_res.page);
+		usbd_copy_out(ml->buf_pc, ml->buf_offset,
+		  ml->fix_res.buffer, td->len);
 
-		/* copy second data part to fixup location */
-
-		usbd_page_dma_exit(tmp_res.page);
-		bcopy(tmp_res.buffer, ADD_BYTES(ml->fix_res.buffer, 
-						ml->buf_res.length),
-		      len - ml->buf_res.length);
-		usbd_page_dma_enter(tmp_res.page);
-
 		usbd_page_dma_enter(ml->fix_res.page);
-
-	    } else {
-		td->fixup_dst_offset = ml->buf_offset;
-		td->fixup_src_offset = ml->fix_offset;
 	    }
 
 	    /* prepare next fixup */
-	    ml->fix_offset += xfer->max_frame_size;
-	    usbd_get_page(&(xfer->buf_fixup), ml->fix_offset, &(ml->fix_res));
+
+	    ml->fix_offset += ml->max_frame_size;
 
 	} else {
+
 	    td->td_buffer = htole32(ml->buf_res.physaddr);
-	    td->fixup_dst_offset = 0xffffffff;
-	    td->fixup_src_offset = 0xffffffff;
+	    td->fix_offset = UHCI_FIX_OFFSET_NONE;
 	}
 
 	/* prepare next data location */
-	ml->buf_offset += len;
-	usbd_get_page(&(xfer->buf_data), ml->buf_offset, &(ml->buf_res));
 
-	return;
-}
+	ml->buf_offset += td->len;
 
-static void
-uhci_mem_layout_do_fixup(struct usbd_xfer *xfer, struct uhci_td *td, uint16_t len)
-{
-	struct usbd_page_search buf_res;
-	struct usbd_page_search fix_res;
-	u_int16_t temp;
-
-	usbd_get_page(&(xfer->buf_data), td->fixup_dst_offset, &buf_res);
-	usbd_get_page(&(xfer->buf_fixup), td->fixup_src_offset, &fix_res);
- 
-	temp = min(buf_res.length, len);
-
-	usbd_page_dma_exit(fix_res.page);
-
-	usbd_page_dma_exit(buf_res.page);
-	bcopy(fix_res.buffer, buf_res.buffer, temp);
-	usbd_page_dma_enter(buf_res.page);
-
-	len -= temp;
-
-	if (len) {
-		usbd_get_page(&(xfer->buf_data), 
-			      td->fixup_dst_offset + temp, &buf_res);
-
-		if (len > buf_res.length) {
-		    /* overflow protection - should not happen */
-		    len = buf_res.length;
-		}
-
-		usbd_page_dma_exit(buf_res.page);
-		bcopy(ADD_BYTES(fix_res.buffer, temp), 
-		      buf_res.buffer, len);
-		usbd_page_dma_enter(buf_res.page);
-	}
-
-	usbd_page_dma_enter(fix_res.page);
-
 	return;
 }
 
@@ -1038,10 +999,11 @@
 static u_int8_t
 uhci_isoc_done(uhci_softc_t *sc, struct usbd_xfer *xfer)
 {
+	struct usbd_page_search res;
 	u_int32_t nframes = xfer->nframes;
-	u_int32_t actlen = 0;
 	uint32_t status;
-	u_int16_t *plen = xfer->frlengths;
+	uint32_t offset = 0;
+	uint32_t *plen = xfer->frlengths;
 	u_int16_t len = 0;
 	u_int8_t need_delay = 0;
 	uhci_td_t *td = xfer->td_transfer_first;
@@ -1086,13 +1048,24 @@
 		len = *plen;
 	  }
 
-	  *plen = len;
-	  actlen += len;
+	  if (td->fix_offset != UHCI_FIX_OFFSET_NONE) {
+
+		usbd_get_page(&(xfer->buf_fixup), td->fix_offset, &res);
+
+		/* copy data from fixup location to real location */
+
+		usbd_page_dma_exit(res.page);
+
+		usbd_copy_in(xfer->frbuffers + 0, offset, 
+		    res.buffer, len);
 
-	  if (td->fixup_src_offset != 0xffffffff) {
-		uhci_mem_layout_do_fixup(xfer, td, len);
+		usbd_page_dma_enter(res.page);
 	  }
 
+	  offset += *plen;
+
+	  *plen = len;
+
 	  /* remove TD from schedule */
 	  UHCI_REMOVE_TD(td, *pp_last);
 
@@ -1100,98 +1073,256 @@
 	  plen++;
 	  td = td->obj_next;
 	}
-	xfer->actlen = actlen;
+
+	xfer->aframes = xfer->nframes;
 
 	return need_delay;
 }
 
+static usbd_status
+uhci_non_isoc_done_sub(struct usbd_xfer *xfer)
+{
+	struct usbd_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;
+	status = UHCI_TD_STALLED;
+	token = 0;
+
+	if (xfer->aframes != xfer->nframes) {
+	    xfer->frlengths[xfer->aframes] = 0;
+	}
+
+	while (1) {
+
+	    usbd_page_dma_exit(td->page);
+	    status = le32toh(td->td_status);
+	    token = le32toh(td->td_token);
+	    usbd_page_dma_enter(td->page);
+
+	    /*
+	     * Verify the status and add
+	     * up the actual length:
+	     */
+
+	    len = UHCI_TD_GET_ACTLEN(status);
+	    if (len > td->len) {
+	        /* should not happen */
+	        DPRINTFN(0, ("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_offset != UHCI_FIX_OFFSET_NONE) {
+
+		    usbd_get_page(&(xfer->buf_fixup), td->fix_offset, &res);
+
+		    /* copy data from fixup location to real location */
+
+		    usbd_page_dma_exit(res.page);
+
+		    usbd_copy_in(xfer->frbuffers + xfer->aframes,
+			xfer->frlengths[xfer->aframes], res.buffer, len);
+
+		    usbd_page_dma_enter(res.page);
+		}
+
+		/* 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) {
+	        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->pipe->toggle_next = (token & UHCI_TD_SET_DT(1)) ? 0 : 1;
+
+#ifdef USB_DEBUG
+	if (status & UHCI_TD_ERROR) {
+	    DPRINTFN(10, ("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->endpoint, 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
+	return (status & UHCI_TD_STALLED) ? 
+	  USBD_STALLED : USBD_NORMAL_COMPLETION;
+}
+
+
 static void
 uhci_non_isoc_done(struct usbd_xfer *xfer)
 {
-	uint32_t status = 0;
-	uint32_t token = 0;
-	uint32_t actlen = 0;
-	uint16_t len;
-	uhci_td_t *td = xfer->td_transfer_first;
+	usbd_status err = 0;
 
 	DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n",
 		      xfer, xfer->pipe));
 
 #ifdef USB_DEBUG
-	if(uhcidebug > 10)
-	{
-		uhci_dump_tds(td);
+	if (uhcidebug > 10) {
+	    uhci_dump_tds(xfer->td_transfer_first);
 	}
 #endif
 
-	/* the transfer is done, compute actual length and status */
-	for (;
-	     td != NULL;
-	     td = td->obj_next)
-	{
-		usbd_page_dma_exit(td->page);
-		status = le32toh(td->td_status);
-		token = le32toh(td->td_token);
-		usbd_page_dma_enter(td->page);
+	/* reset scanner */
+
+	xfer->td_transfer_cache = xfer->td_transfer_first;
+
+	if (xfer->flags_int.control_xfr &&
+	    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) {
 
-		if (status & (UHCI_TD_ACTIVE|UHCI_TD_STALLED)) {
-			break;
-		}
+	    if ((!xfer->flags_int.control_xfr) ||
+		(xfer->frlengths[xfer->aframes] > 0)) {
 
-		len = UHCI_TD_GET_ACTLEN(status);
-		actlen += len;
+	        err = uhci_non_isoc_done_sub(xfer);
+	    }
 
-		if (td->fixup_src_offset != 0xffffffff) {
-			uhci_mem_layout_do_fixup(xfer, td, len);
-		}
+	    xfer->aframes ++;
 
-		if (((void *)td) == xfer->td_transfer_last) {
-			td = NULL;
-			break;
-		}
+	    if (xfer->td_transfer_cache == NULL) {
+	        goto done;
+	    }
 	}
 
-	/* if there are left over TDs 
-	 * the toggle needs to be updated
-	 */
-	if(td != NULL)
-	{
-		xfer->pipe->toggle_next = (token & UHCI_TD_SET_DT(1)) ? 1 : 0;
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
+
+	    err = uhci_non_isoc_done_sub(xfer);
 	}
 
-	DPRINTFN(10, ("actlen=%d\n", actlen));
+ done:
+	uhci_device_done(xfer, err);
+	return;
+}
+
+/*------------------------------------------------------------------------*
+ *	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 usbd_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;
+
+	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 ((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1))) {
+
+	    /*
+	     * The data toggle is wrong and
+	     * we need to switch it !
+	     */
+
+	    while (1) {
+
+	        usbd_page_dma_exit(td->page);
+		td->td_token ^= htole32(UHCI_TD_SET_DT(1));
+		usbd_page_dma_enter(td->page);
+
+		if (td == xfer->td_transfer_last) {
+		    /* last transfer */
+		    break;
+		}
 
-	xfer->actlen = actlen;
+		td = td->obj_next;
 
-#ifdef USB_DEBUG
-	if (status & UHCI_TD_ERROR) {
-		DPRINTFN(10,
-			 ("error, addr=%d, endpt=0x%02x, "
-			  "status=%s%s%s%s%s%s%s%s%s%s%s\n",
-			  xfer->address,
-			  xfer->endpoint,
-			  (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" : "",
-			  (status & UHCI_TD_IOC) ? "-IOC" : "",
-			  (status & UHCI_TD_IOS) ? "-IOS" : "",
-			  (status & UHCI_TD_LS) ? "-LS" : "",
-			  (status & UHCI_TD_SPD) ? "-SPD" : ""));
+		if (td->alt_next != td_alt_next) {
+		    /* next frame */
+		    break;
+		}
+	    }
 	}
-#endif
-	uhci_device_done(xfer, (status & UHCI_TD_STALLED) ? 
-			 USBD_STALLED : 
-			 USBD_NORMAL_COMPLETION);
+
+	/* update the QH */
+	usbd_page_dma_exit(qh->page);
+	qh->qh_e_next = td_self;
+	usbd_page_dma_enter(qh->page);
+
+	DPRINTFN(12, ("xfer=%p following alt next\n", xfer));
 	return;
 }
 
-/* returns one when transfer is finished 
- * and callback must be called; else zero
- */
+/*------------------------------------------------------------------------*
+ *	uhci_check_transfer
+ *
+ * Return values:
+ *    0: USB transfer is not finished
+ * Else: USB transfer is finished
+ *------------------------------------------------------------------------*/
 static u_int8_t
 uhci_check_transfer(struct usbd_xfer *xfer, struct thread *ctd)
 {
@@ -1233,15 +1364,12 @@
 		 * in the middle, or whether there was a short
 		 * packet (SPD and not ACTIVE)
 		 */
-		for(td = xfer->td_transfer_cache;
-		    td != NULL;
-		    td = td->obj_next)
-		{
+		td = xfer->td_transfer_cache;
+
+		while (1) {
 			usbd_page_dma_exit(td->page);
-
 			status = le32toh(td->td_status);
 			token = le32toh(td->td_token);
-
 			usbd_page_dma_enter(td->page);
 
 			/* if there is an active TD 
@@ -1253,6 +1381,13 @@
 				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 
 			 */
@@ -1260,19 +1395,28 @@
 				break;
 			}
 
-			/* a short packet also makes
-			 * the transfer done
+			/*
+			 * check if we reached the last packet
+			 * or if there is a short packet:
 			 */
-			if ((status & UHCI_TD_SPD) &&
-			    (UHCI_TD_GET_ACTLEN(status) <
-			     UHCI_TD_GET_MAXLEN(token))) {
+			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;
 			}
 
-			if (((void *)td) == xfer->td_transfer_last) {
-				td = NULL;
-				break;
-			}
+			td = td->obj_next;
 		}
 		uhci_non_isoc_done(xfer);
 		goto transferred;
@@ -1509,221 +1653,297 @@
 	return;
 }
 
-static uhci_td_t *
-uhci_setup_standard_chain(struct usbd_xfer *xfer)
+static void
+uhci_setup_standard_chain_sub(struct uhci_std_temp *temp)
 {
-	struct uhci_mem_layout ml;
-	u_int32_t td_status;
-	u_int32_t td_token;
-	u_int32_t average = xfer->max_packet_size;
-	u_int32_t len;
-	u_int8_t isread;
-	u_int8_t shortpkt = 0;
-	u_int8_t force_short;
 	uhci_td_t *td;
-	uhci_td_t *td_last = NULL;
+	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;
+
+	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) {
 
-	DPRINTFN(8, ("addr=%d endpt=%d len=%d speed=%d\n", 
-		     xfer->address, UE_GET_ADDR(xfer->endpoint),
-		     xfer->length, xfer->udev->speed));
+	        /* update remaining length */
 
-	td = (xfer->td_transfer_first = 
-	      xfer->td_transfer_cache = xfer->td_start);
+		temp->len -= average;
 
-	uhci_mem_layout_init(&ml, xfer);
+		continue;
+	    }
 
-	td_status = htole32(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3)|
-						UHCI_TD_ACTIVE));
+	    usbd_page_dma_exit(td->page);
 
-	if(xfer->udev->speed == USB_SPEED_LOW)
-	{
-		td_status |= htole32(UHCI_TD_LS);
-	}
+	    /* fill out current TD */
 
- 	if(xfer->flags & USBD_SHORT_XFER_OK)
-	{
-		/* set UHCI_TD_SPD */
-		td_status |= htole32(UHCI_TD_SPD);
-	}
+	    td->td_status = temp->td_status;
+	    td->td_token = temp->td_token;
 
-	force_short = (xfer->flags & USBD_FORCE_SHORT_XFER) ? 1 : 0;
+	    /* update data toggle */
 
-	len = xfer->length;
+	    temp->td_token ^= htole32(UHCI_TD_SET_DT(1));
 
-	if(xfer->pipe->methods == &uhci_device_ctrl_methods)
-	{
-	    isread = xfer->control_isread;
+	    if (average == 0) {
 
-	    if (xfer->flags & USBD_DEV_CONTROL_HEADER) {
+		td->len = 0;
+		td->td_buffer = 0;
+		td->fix_offset = UHCI_FIX_OFFSET_NONE;
 
-		xfer->pipe->toggle_next = 1;
+	    } else {
 
-		/* SETUP message */
+		/* update remaining length */
 
-		usbd_page_dma_exit(td->page);
+		temp->len -= average;
 
-		td->td_status = td_status & htole32(~UHCI_TD_SPD);
-		td_token = 
-		  (UHCI_TD_SET_ENDPT(xfer->endpoint) |
-		   UHCI_TD_SET_DEVADDR(xfer->address) |
-		   UHCI_TD_SET_MAXLEN(sizeof(usb_device_request_t)) |
-		   UHCI_TD_PID_SETUP|
-		   UHCI_TD_SET_DT(0));
-		td->td_token = htole32(td_token);
+		td->len = average;
 
-		uhci_mem_layout_fit(&ml, td, sizeof(usb_device_request_t));
+		/* fill out buffer pointer and do fixup, if any */
 
-		len -= sizeof(usb_device_request_t);
-		td_last = td;
-		td = td->obj_next;
+		uhci_mem_layout_fixup(&(temp->ml), td);
+	    }
 
-		if (td) {
-		    /* link the last TD with the next one */
-		    td_last->td_next = td->td_self;
-		}
+	    td->alt_next = td_alt_next;
 
-		usbd_page_dma_enter(td_last->page);
+	    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 (len == 0) {
-			/* When the length is zero we
-			 * queue a short packet!
-			 * This also makes "td_last"
-			 * non-zero.
-			 */
-			DPRINTFN(0, ("short transfer!\n"));
-		        force_short = 1;
+	        if (td_next) {
+		    /* link the current TD with the next one */
+		    td->td_next = td_next->td_self;
 		}
 	    }
+
+	    usbd_page_dma_enter(td->page);
+	}
+
+	if (precompute) {
+	    precompute = 0;
+
+	    /* store alt next pointer */
+	    td_alt_next = td_next;
+	    
+	    /* restore */
+	    temp->shortpkt = shortpkt_old;
+	    temp->len = len_old;
+	    goto restart;
+	}
+
+	temp->td = td;
+	temp->td_next = td_next;
+
+	return;
+}
+
+static uhci_td_t *
+uhci_setup_standard_chain(struct usbd_xfer *xfer)
+{
+	struct uhci_std_temp temp;
+	uhci_td_t *td;
+	uint32_t x;
+
+	DPRINTFN(8, ("addr=%d endpt=%d sumlen=%d speed=%d\n", 
+		     xfer->address, UE_GET_ADDR(xfer->endpoint),
+		     xfer->sumlen, usbd_get_speed(xfer->udev)));
+
+	temp.average = xfer->max_frame_size;
+	temp.max_frame_size = xfer->max_frame_size;
+
+	xfer->td_transfer_first = xfer->td_start;
+	xfer->td_transfer_cache = xfer->td_start;
+
+	temp.td = NULL;
+	temp.td_next = xfer->td_start;
+	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->udev->speed == USB_SPEED_LOW) {
+		temp.td_status |= htole32(UHCI_TD_LS);
 	}
-	else
-	{
-		isread = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN);
+
+	temp.td_token =
+	  htole32(UHCI_TD_SET_ENDPT(xfer->endpoint) |
+		  UHCI_TD_SET_DEVADDR(xfer->address));
 
-		if (len == 0) {
-			/* When the length is zero we
-			 * queue a short packet!
-			 * This also makes "td_last"
-			 * non-zero.
-			 */
-			DPRINTFN(0, ("short transfer!\n"));
-			force_short = 1;
-		}
+	if (xfer->pipe->toggle_next) {
+	    /* DATA1 is next */
+	    temp.td_token |= htole32(UHCI_TD_SET_DT(1));
 	}
 

>>> TRUNCATED FOR MAIL (1000 lines) <<<



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200709252219.l8PMJH8W005346>