Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 1 Oct 2007 17:10:00 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 127064 for review
Message-ID:  <200710011710.l91HA02U011356@repoman.freebsd.org>

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

Change 127064 by hselasky@hselasky_laptop001 on 2007/10/01 17:09:40

	
	FYI: The comments follow the P4 diff from top to bottom.
	
	In general the changes in diff are about getting the OHCI HC
	driver up to date with the latest changes in the USB API. The
	changes follow the changes made to ehci.c and uhci.c. See
	change xxx and change xxx.
	
	- replace ADD_BYTES() by USBD_ADD_BYTES()
	
	- "xfer->frlengths[]" is not 32-bit unsigned
	
	- new function "ohci_non_isoc_done_sub()". The main puropose
	  of this function is to scan an USB BULK/INTERRUPT/CONTROL
	  descriptor chain and extract information. This function will
	  update "xfer->frlengths[]" to the actual transfer length.
	
	- update "ohci_non_isoc_done()" to use "ohci_non_isoc_done_sub()"
	  instead of scanning the TD chain by itself.
	
	- new function "ohci_check_transfer_sub()". The main purpose
	  of this function is to setup the next short incoming
	  transfer in case multiple short frames should be received.
	
	- update "ohci_check_transfer()" to use "ohci_check_transfer_sub()".
	
	- parts of "ohci_rhsc_enable()" has been factored out 
	  into "usbd_std_root_transfer()".
	
	- new function "ohci_setup_standard_chain_sub()". The main
	  purpose of this function is to build up DMA chains for BULK,
	  INTERRUPT and CONTROL transfers. This function will queue 4K
	  bytes worth of data per transfer descriptor.
	
	- changes to "ohci_setup_standard_chain()". The code is
	  doing exactly the same, just refactored a little bit.
	
	- use "xfer->max_frame_size" instead of "xfer->max_packet_size".
	  The two values should be identical for the OHCI HC driver.
	
	- "ohci_root_intr_done()" is doing the same like before, only
	  now using a temporary scratch buffer in the "struct ohci_softc"
	  which is called "sc->sc_hub_idata".
	
	- "usbd_transfer_done()" is now part of "usbd_transfer_dequeue()"
	
	- updated several "flags & XXX" to "flags{_int}.xxx".
	
	- substituted MS_TO_TICKS by USBD_MS_TO_TICKS.
	
	- "usbd_std_ctrl_enter()" is now obsolete and factored out
	  like a standard part of "usbd_start_hardware()".
	
	- check for "nframes == 0" has been factored out as a part of
	  "usbd_start_hardware()".
	
	- moved inversion operator outside of "htoleXX", hence a year
	  ago there was a bug in this macro that didn't handle this case
	  properly.
	
	- the new function "ohci_root_ctrl_task_td_sub()" does the
	  same as "ohci_root_ctrl_task_td()", but through the standard
	  root transfer framework.
	
	- major parts of "ohci_xfer_setup()" has been factored out and
	  only the OHCI specific part is left. Across all the Host
	  Controller Drivers this saves some code.

Affected files ...

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

Differences ...

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

@@ -76,7 +76,6 @@
 #include <dev/usb/usb_subr.h>
 #include <dev/usb/ohci.h>
 
-#define MS_TO_TICKS(ms) (((ms) * hz) / 1000)
 #define OHCI_BUS2SC(bus) ((ohci_softc_t *)(((u_int8_t *)(bus)) - \
    POINTER_TO_UNSIGNED(&(((ohci_softc_t *)0)->sc_bus))))
 
@@ -123,10 +122,25 @@
 static void ohci_root_ctrl_task_td(struct ohci_softc *sc, struct thread *ctd);
 static void ohci_do_poll(struct usbd_bus *bus);
 
+static usbd_std_root_transfer_func_t ohci_root_intr_done;
+static usbd_std_root_transfer_func_t ohci_root_ctrl_task_td_sub;
+
 #define SC_HW_PHYSADDR(sc,what) \
   ((sc)->sc_hw_page.physaddr + \
    POINTER_TO_UNSIGNED(&(((struct ohci_hw_softc *)0)->what)))
 
+struct ohci_std_temp {
+	struct usbd_page_cache *pc;
+	ohci_td_t *td;
+	ohci_td_t *td_next;
+	uint32_t average;
+	uint32_t td_flags;
+	uint32_t len;
+	uint16_t max_frame_size;
+	uint8_t shortpkt;
+	uint8_t setup_alt_next;
+};
+
 static usbd_status
 ohci_controller_init(ohci_softc_t *sc)
 {
@@ -734,8 +748,7 @@
 ohci_isoc_done(struct usbd_xfer *xfer)
 {
 	u_int8_t nframes;
-	u_int32_t actlen = 0;
-	u_int16_t *plen = xfer->frlengths;
+	uint32_t *plen = xfer->frlengths;
 	__volatile__ u_int16_t *olen;
 	u_int16_t len = 0;
 	ohci_itd_t *td = xfer->td_transfer_first;
@@ -782,7 +795,6 @@
 			}
 
 			*plen = len;
-			actlen += len;
 			plen++;
 			olen++;
 		}
@@ -796,7 +808,8 @@
 
 		td = td->obj_next;
 	}
-	xfer->actlen = actlen;
+
+	xfer->aframes = xfer->nframes;
 	ohci_device_done(xfer,USBD_NORMAL_COMPLETION);
 	return;
 }
@@ -827,98 +840,226 @@
 };
 #endif
 
+static usbd_status
+ohci_non_isoc_done_sub(struct usbd_xfer *xfer)
+{
+	ohci_td_t *td;
+	ohci_td_t *td_alt_next;
+	uint32_t temp;
+	uint32_t phy_start;
+	uint32_t phy_end;
+	uint32_t td_flags;
+	uint16_t cc;
+
+	td = xfer->td_transfer_cache;
+	td_alt_next = td->alt_next;
+	td_flags = 0;
+
+ 	while (1) {
+
+	    usbd_page_dma_exit(td->page);
+	    phy_start = le32toh(td->td_cbp);
+	    td_flags = le32toh(td->td_flags);
+	    cc = OHCI_TD_GET_CC(td_flags);
+
+	    if (phy_start) {
+		/* 
+		 * short transfer - compute the number of
+		 * remaining bytes in the hardware buffer:
+		 */
+		phy_end = le32toh(td->td_be);
+		temp = (OHCI_PAGE(phy_start ^ phy_end) ? 
+			(OHCI_PAGE_SIZE+1) : 0x0001);
+		temp += OHCI_PAGE_OFFSET(phy_end);
+		temp -= OHCI_PAGE_OFFSET(phy_start);
+
+		if (temp > td->len) {
+		    /* guard against corruption */
+		    td_flags = OHCI_TD_SET_CC(OHCI_CC_STALL);
+		} else if (xfer->aframes != xfer->nframes) {
+		    /* subtract remaining length from "frlengths[]" */
+		    xfer->frlengths[xfer->aframes] -= temp;
+		}
+	    }
+
+	    usbd_page_dma_enter(td->page);
+
+	    /* Check for last transfer */
+	    if (((void *)td) == xfer->td_transfer_last) {
+		td = NULL;
+		break;
+	    }
+
+	    /* Check transfer status */
+	    if (cc) {
+		/* the transfer is finished */
+		td = NULL;
+		break;
+	    }
+
+	    /* Check for short transfer */
+	    if (phy_start) {
+		if (xfer->flags_int.short_frames_ok) {
+		    /* follow alt next */
+		    td = td->alt_next;
+		} else {
+		    /* the transfer is finished */
+		    td = NULL;
+		}
+		break;
+	    }
+
+	    td = td->obj_next;
+
+	    if (td->alt_next != td_alt_next) {
+		/* this USB frame is complete */
+		break;
+	    }
+	}
+
+	/* update transfer cache */
+
+	xfer->td_transfer_cache = td;
+
+	DPRINTFN(15,("error cc=%d (%s)\n",
+	    cc, ohci_cc_strs[cc]));
+
+	return ((cc == 0) ? USBD_NORMAL_COMPLETION :
+		(cc == OHCI_CC_STALL) ? USBD_STALLED : USBD_IOERROR);
+}
+
 static void
 ohci_non_isoc_done(struct usbd_xfer *xfer)
 {
-	u_int16_t cc = 0;
-	u_int32_t actlen = 0;
-	u_int32_t len;
-	u_int32_t temp;
-	u_int32_t phy_start;
-	u_int32_t phy_end;
-	uint32_t td_flags;
-	ohci_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(ohcidebug > 10)
-	{
-		ohci_dump_tds(td);
+	if (ohcidebug > 10) {
+		ohci_dump_tds(xfer->td_transfer_first);
 	}
 #endif
 
-	while(1)
-	{
-		if(td == NULL)
-		{
-			panic("%s:%d: out of TD's\n",
-			      __FUNCTION__, __LINE__);
-		}
+	/* reset scanner */
+
+	xfer->td_transfer_cache = xfer->td_transfer_first;
+
+	if (xfer->flags_int.control_xfr &&
+	    xfer->flags_int.control_hdr) {
+
+	    err = ohci_non_isoc_done_sub(xfer);
+
+	    xfer->aframes = 1;
+
+	    if (xfer->td_transfer_cache == NULL) {
+	        goto done;
+	    }
+	}
+
+	while (xfer->aframes != xfer->nframes) {
+
+	    if ((!xfer->flags_int.control_xfr) ||
+		(xfer->frlengths[xfer->aframes] > 0)) {
+
+	        err = ohci_non_isoc_done_sub(xfer);
+	    }
+
+	    xfer->aframes ++;
+
+	    if (xfer->td_transfer_cache == NULL) {
+	        goto done;
+	    }
+	}
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
 
-		usbd_page_dma_exit(td->page);
+	    err = ohci_non_isoc_done_sub(xfer);
+	}
 
-		phy_start = le32toh(td->td_cbp);
-		td_flags = le32toh(td->td_flags);
+ done:
+	ohci_device_done(xfer, err);
+	return;
+}
 
-		len = td->len;
-		if (phy_start) {
-			/* compute the number of remaining
-			 * bytes in the hardware buffer:
-			 */
-			phy_end = le32toh(td->td_be);
-			temp = (OHCI_PAGE(phy_start ^ phy_end) ? 
-				(OHCI_PAGE_SIZE+1) : 0x0001);
-			temp += OHCI_PAGE_OFFSET(phy_end);
-			temp -= OHCI_PAGE_OFFSET(phy_start);
+/*------------------------------------------------------------------------*
+ *	ohci_check_transfer_sub
+ *------------------------------------------------------------------------*/
+static void
+ohci_check_transfer_sub(struct usbd_xfer *xfer)
+{
+	ohci_td_t *td;
+	ohci_ed_t *ed;
+	uint32_t phy_start;
+	uint32_t td_flags;
+	uint32_t td_next;
+	uint16_t cc;
 
-			if (temp > len) {
-			    /* guard against corruption */
-			    len = 0;
-			} else {
-			    len -= temp;
-			}
-		}
+	td = xfer->td_transfer_cache;
 
-		usbd_page_dma_enter(td->page);
+ 	while (1) {
 
-		DPRINTFN(10, ("len=%d\n", len));
+	    usbd_page_dma_exit(td->page);
+	    phy_start = le32toh(td->td_cbp);
+	    td_flags = le32toh(td->td_flags);
+	    td_next = le32toh(td->td_next);
+	    usbd_page_dma_enter(td->page);
 
-		actlen += len;
+	    /* Check for last transfer */
+	    if (((void *)td) == xfer->td_transfer_last) {
+		/* the transfer is finished */
+		td = NULL;
+		break;
+	    }
 
-		cc = OHCI_TD_GET_CC(td_flags);
-		if (cc) {
-			DPRINTFN(15,("error cc=%d (%s)\n",
-				     cc, ohci_cc_strs[cc]));
-			break;
-		}
+	    /* Check transfer status */
+	    cc = OHCI_TD_GET_CC(td_flags);
+	    if (cc) {
+		/* the transfer is finished */
+		td = NULL;
+		break;
+	    }
 
-		if (phy_start) {
-			/* short transfer */
-			break;
-		}
+	    /*
+	     * Check if we reached the last packet
+	     * or if there is a short packet:
+	     */
 
-		if (((void *)td) == xfer->td_transfer_last) {
-			break;
-		}
+	    if (((td_next & (~0xF)) == OHCI_TD_NEXT_END) || phy_start) {
+		/* follow alt next */
+	        td = td->alt_next;
+	        break;
+	    }
 
-		td = td->obj_next;
+	    td = td->obj_next;
 	}
 
-	DPRINTFN(10, ("actlen=%d\n", actlen));
+	/* update transfer cache */
+
+	xfer->td_transfer_cache = td;
+
+	if (td) {
+
+	    ed = xfer->qh_start;
 
-	xfer->actlen = actlen;
+	    usbd_page_dma_exit(ed->page);
+	    ed->ed_headp = td->td_self;
+	    usbd_page_dma_enter(ed->page);
 
-	ohci_device_done(xfer, 
-			 (cc == 0) ? USBD_NORMAL_COMPLETION :
-			 (cc == OHCI_CC_STALL) ? USBD_STALLED : USBD_IOERROR);
+	    DPRINTFN(12, ("xfer=%p following alt next\n", xfer));
+	}
 	return;
 }
 
-/* returns one when transfer is finished 
- * and callback must be called; else zero
- */
+/*------------------------------------------------------------------------*
+ *	ohci_check_transfer
+ *
+ * Return values:
+ *    0: USB transfer is not finished
+ * Else: USB transfer is finished
+ *------------------------------------------------------------------------*/
 static u_int8_t
 ohci_check_transfer(struct usbd_xfer *xfer, struct thread *ctd)
 {
@@ -954,6 +1095,14 @@
 		}
 		else
 		{
+			if (xfer->flags_int.short_frames_ok) {
+			    ohci_check_transfer_sub(xfer);
+			    if (xfer->td_transfer_cache) {
+				/* not finished yet */
+				return 0;
+			    }
+			}
+
 			/* store data-toggle */
 			if (ed_headp & OHCI_TOGGLECARRY) {
 				xfer->pipe->toggle_next = 1;
@@ -974,14 +1123,8 @@
 static void
 ohci_rhsc_enable(ohci_softc_t *sc)
 {
-	struct thread *td;
-	struct usbd_xfer *xfer;
-	struct usbd_xfer *xlist[2];
-
 	DPRINTFN(4, ("\n"));
 
-	td = curthread;
-
 	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
 
 	sc->sc_eintrs |= OHCI_RHSC;
@@ -990,27 +1133,9 @@
 	/* acknowledge any RHSC interrupt */
 	OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_RHSC);
 
-	xfer = sc->sc_intrxfer;
-
- 	if(xfer)
-	{
-	    /* transfer is transferred */
-	    ohci_device_done(xfer, USBD_NORMAL_COMPLETION);
-
-	    /* queue callback */
-	    xlist[0] = xfer;
-	    xlist[1] = NULL;
-
-	    xfer->usb_thread = td;
-	    xfer->usb_root->memory_refcount++;
-
-	    mtx_unlock(&sc->sc_bus.mtx);
-
-	    usbd_do_callback(xlist, td);
-	}
-	else
-	{
-	    mtx_unlock(&sc->sc_bus.mtx);
+	if (usbd_std_root_transfer(&(sc->sc_root_intr), NULL,
+				   &ohci_root_intr_done)) {
+	    mtx_unlock(&(sc->sc_bus.mtx));
 	}
 	return;
 }
@@ -1133,20 +1258,6 @@
 	}
 	if(status & OHCI_RHSC)
 	{
-		xfer = sc->sc_intrxfer;
-
-		if(xfer)
-		{
-		    ohci_device_done(xfer, USBD_NORMAL_COMPLETION);
-
-		    /* queue callback */
-
-		    *(xptr++) = xfer;
-
-		    xfer->usb_thread = td;
-		    xfer->usb_root->memory_refcount++;
-		}
-
 		/*
 		 * Disable RHSC interrupt for now, because it will be
 		 * on until the port has been reset.
@@ -1154,6 +1265,11 @@
 		sc->sc_eintrs &= ~OHCI_RHSC;
 		OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC);
 
+		if (!usbd_std_root_transfer(&(sc->sc_root_intr), ctd,
+					    &ohci_root_intr_done)) {
+		    mtx_lock(&(sc->sc_bus.mtx));
+		}
+
 		/* do not allow RHSC interrupts > 1 per second */
 		__callout_reset(&sc->sc_tmo_rhsc, hz,
 				(void *)(void *)ohci_rhsc_enable, sc);
@@ -1285,234 +1401,291 @@
 }
 
 static void
-ohci_setup_standard_chain(struct usbd_xfer *xfer, ohci_ed_t **ed_last)
+ohci_setup_standard_chain_sub(struct ohci_std_temp *temp)
 {
 	struct usbd_page_search buf_res;
-	/* the OHCI hardware can handle at most one 4k crossing per TD */
-	u_int32_t average = (OHCI_PAGE_SIZE - (OHCI_PAGE_SIZE % 
-					       xfer->max_packet_size));
-	u_int32_t td_flags;
-	uint32_t ed_flags;
-	u_int32_t buf_offset;
-	u_int32_t len;
-	u_int8_t isread;
-	u_int8_t shortpkt = 0;
-	u_int8_t force_short;
 	ohci_td_t *td;
-	ohci_td_t *td_last = NULL;
-	ohci_ed_t *ed;
+	ohci_td_t *td_next;
+	ohci_td_t *td_alt_next;
+	uint32_t buf_offset;
+	uint32_t average;
+	uint32_t len_old;
+	uint8_t shortpkt_old;
+	uint8_t precompute;
+
+	td_alt_next = NULL;
+	buf_offset = 0;
+	shortpkt_old = temp->shortpkt;
+	len_old = temp->len;
+	precompute = 1;
+
+	/* software is used to detect short incoming transfers */
+
+	if ((temp->td_flags & htole32(OHCI_TD_DP_MASK)) == htole32(OHCI_TD_IN)) {
+	    temp->td_flags |= htole32(OHCI_TD_R);
+	} else {
+	    temp->td_flags &= ~htole32(OHCI_TD_R);
+	}
+
+ restart:
+
+	td = temp->td;
+	td_next = temp->td_next;
+
+	while (1) {
+
+	    if (temp->len == 0) {
+
+		if (temp->shortpkt) {
+		    break;
+		}
+
+		/* send a Zero Length Packet, ZLP, last */
+
+		temp->shortpkt = 1;
+		average = 0;
+
+	    } else {
+
+		average = temp->average;
+
+		if (temp->len < average) {
+		    if (temp->len % temp->max_frame_size) {
+			temp->shortpkt = 1;
+		    }
+		    average = temp->len;
+		}
+	    }
+
+	    if (td_next == NULL) {
+	        panic("%s: out of OHCI transfer descriptors!", __FUNCTION__);
+	    }
+
+	    /* get next TD */
+
+	    td = td_next;
+	    td_next = td->obj_next;
 
-	DPRINTFN(8, ("addr=%d endpt=%d len=%d speed=%d\n", 
-		     xfer->address, UE_GET_ADDR(xfer->endpoint),
-		     xfer->length, xfer->udev->speed));
+	    /* check if we are pre-computing */
 
-	td = (xfer->td_transfer_first = xfer->td_start);
+	    if (precompute) {
 
-	buf_offset = 0;
-	usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
+	        /* update remaining length */
 
-	force_short = (xfer->flags & USBD_FORCE_SHORT_XFER) ? 1 : 0;
+		temp->len -= average;
 
-	len = xfer->length;
+		continue;
+	    }
 
-	if(xfer->pipe->methods == &ohci_device_ctrl_methods)
-	{
-	    isread = xfer->control_isread;
+	    usbd_page_dma_exit(td->page);
 
-	    if (xfer->flags & USBD_DEV_CONTROL_HEADER) {
+	    /* fill out current TD */
+	    td->td_flags = temp->td_flags;
 
-		xfer->pipe->toggle_next = 1;
+	    /* the next TD uses TOGGLE_CARRY */
+	    temp->td_flags &= ~htole32(OHCI_TD_TOGGLE_MASK);
 
-		usbd_page_dma_exit(td->page);
+	    if (average == 0) {
 
-		/* SETUP message */
+		td->td_cbp = 0;
+		td->td_be = ~0;
+		td->len = 0;
 
-		td->td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC |
-				       OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
+	    } else {
 
+		usbd_get_page(temp->pc, buf_offset, &buf_res);
 		td->td_cbp = htole32(buf_res.physaddr);
+		buf_offset += (average - 1);
 
-		buf_offset += (sizeof(usb_device_request_t) - 1);
-		usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
-
+		usbd_get_page(temp->pc, buf_offset, &buf_res);
 		td->td_be = htole32(buf_res.physaddr);
-		td->len = sizeof(usb_device_request_t);
+		buf_offset ++;
 
-		buf_offset += 1;
-		usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
+		td->len = average;
 
-		len -= sizeof(usb_device_request_t);
-		td_last = td;
-		td = td->obj_next;
+		/* update remaining length */
 
-		if (td) {
-		    /* link the last TD with the next one */
-		    td_last->td_next = td->td_self;
-		}
+		temp->len -= average;
+	    }
 
-		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_flags &= htole32(~OHCI_TD_INTR_MASK);
+		td->td_flags |= htole32(OHCI_TD_SET_DI(1));
+		td->td_next = htole32(OHCI_TD_NEXT_END);
 	    } else {
-	        if (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;
 		}
 	    }
+
+	    td->alt_next = td_alt_next;
+
+	    usbd_page_dma_enter(td->page);
 	}
-	else
-	{
-		isread = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN);
+
+	if (precompute) {
+	    precompute = 0;
 
-		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;
-		}
+	    /* setup alt next pointer, if any */
+	    td_alt_next = td_next;
+	    
+	    /* restore */
+	    temp->shortpkt = shortpkt_old;
+	    temp->len = len_old;
+	    goto restart;
 	}
 
-	td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR);
+	temp->td = td;
+	temp->td_next = td_next;
+
+	return;
+}
+
+static void
+ohci_setup_standard_chain(struct usbd_xfer *xfer, ohci_ed_t **ed_last)
+{
+	struct ohci_std_temp temp;
+	struct usbd_pipe_methods *methods;
+	ohci_ed_t *ed;
+	ohci_td_t *td;
+	uint32_t ed_flags;
+	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_usb_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;
+
+	methods = xfer->pipe->methods;
+
+	x = 0;
+
+	/* check if we should prepend a setup message */
+
+	if (xfer->flags_int.control_xfr &&
+	    xfer->flags_int.control_hdr) {
+
+		temp.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC |
+			OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
+
+		temp.len = xfer->frlengths[0];
+		temp.pc = xfer->frbuffers + 0;
+		temp.shortpkt = temp.len ? 1 : 0;
+
+		ohci_setup_standard_chain_sub(&temp);
+
+		x = 1;
 
- 	if(xfer->flags & USBD_SHORT_XFER_OK)
-	{
-		td_flags |= htole32(OHCI_TD_R);
+		/*
+		 * XXX assume that the setup message is 
+		 * contained within one USB packet:
+		 */
+		xfer->pipe->toggle_next = 1;
 	}
-	if(xfer->pipe->toggle_next)
-	{
-		td_flags |= htole32(OHCI_TD_TOGGLE_1);
+
+	temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR);
+
+	/* set data toggle */
+
+	if (xfer->pipe->toggle_next) {
+	    temp.td_flags |= htole32(OHCI_TD_TOGGLE_1);
+	} else {
+	    temp.td_flags |= htole32(OHCI_TD_TOGGLE_0);
 	}
-	else
-	{
-		td_flags |= htole32(OHCI_TD_TOGGLE_0);
-	}
-	if(isread)
-        {
-		td_flags |= htole32(OHCI_TD_IN);
-	}
-	else
-	{
-		td_flags |= htole32(OHCI_TD_OUT);
-	}
 
-	while(1)
-	{
-		if(len == 0)
-		{
-			if (force_short)
-			{
-				if(shortpkt)
-				{
-					break;
-				}
-			}
-			else
-			{
-				break;
-			}
-		}
+	/* set endpoint direction */
 
-		if(len < average)
-		{
-			if((len % xfer->max_packet_size) || 
-			   (len == 0))
-			{
-				shortpkt = 1;
-			}
+	if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+	    temp.td_flags |= htole32(OHCI_TD_IN);
+	} else {
+	    temp.td_flags |= htole32(OHCI_TD_OUT);
+	}
 
-			average = len;
-		}
+	while (x != xfer->nframes) {
 
-		if(td == NULL)
-		{
-			panic("%s: software wants to write more data "
-			      "than there is in the buffer!", __FUNCTION__);
-		}
+		/* DATA0 / DATA1 message */
 
-		usbd_page_dma_exit(td->page);
+		temp.len = xfer->frlengths[x];
+		temp.pc = xfer->frbuffers + x;
 
-		/* fill out TD */
+		x++;
 
-		td->td_flags = td_flags;
+		if (x == xfer->nframes) {
+		    temp.setup_alt_next = 0;
+		}
 
-		/* the next TD uses TOGGLE_CARRY */
-		td_flags &= htole32(~OHCI_TD_TOGGLE_MASK);
+		if (temp.len == 0) {
 
-		if(average == 0)
-		{
-			td->td_cbp = 0;
-			td->td_be = ~0;
-		}
-		else
-		{
-			td->td_cbp = htole32(buf_res.physaddr);
+		    /* make sure that we send an USB packet */
 
-			buf_offset += (average - 1);
-			usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
+		    temp.shortpkt = 0;
 
-			td->td_be = htole32(buf_res.physaddr);
+		    if (xfer->flags_int.control_xfr) {
+		        /* we ignore zero length frames */
+		        continue;
+		    }
 
-			buf_offset += 1;
-			usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
-		}
-		td->len = average;
+		} else {
 
-		len -= average;
-		td_last = td;
-		td = td->obj_next;
+		    /* regular data transfer */
 
-		if (td) {
-		    /* link the last TD with the next one */
-		    td_last->td_next = td->td_self;
+		    temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1;
 		}
 
-		usbd_page_dma_enter(td_last->page);
+		ohci_setup_standard_chain_sub(&temp);
 	}
 
-	if(xfer->pipe->methods == &ohci_device_ctrl_methods)
-	{
-	    if (xfer->control_remainder == 0) {
+	/* check if we should append a status stage */
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
 
-		usbd_page_dma_exit(td->page);
+		/* 
+		 * Send a DATA1 message and invert 
+		 * the current endpoint direction.
+		 */
 
-		/* STATUS message */
+		/* set endpoint direction and data toggle */
 
-		td_flags = (OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | 
-			    OHCI_TD_SET_DI(1));
-		if (isread) {
-			td_flags |= OHCI_TD_OUT;
+		if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+		    temp.td_flags = htole32(OHCI_TD_OUT |
+			OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1));
 		} else {
-			td_flags |= OHCI_TD_IN;
+		    temp.td_flags = htole32(OHCI_TD_IN | 
+			OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1));
 		}
 
-		td->td_flags = htole32(td_flags);
-		td->td_cbp = 0;
-		td->td_be = ~0;
-		td->len = 0;
+		temp.len = 0;
+		temp.pc = NULL;
+		temp.shortpkt = 0;
 
-		td_last = td;
+		ohci_setup_standard_chain_sub(&temp);
+	}
 
-		usbd_page_dma_enter(td_last->page);
-	    }
-	}
+	td = temp.td;
 
-	usbd_page_dma_exit(td_last->page);
+	usbd_page_dma_exit(td->page);
 
-	td_last->td_next = 0;
-	td_last->td_flags &= htole32(~OHCI_TD_INTR_MASK);
-	td_last->td_flags |= htole32(OHCI_TD_SET_DI(1));
+	td->td_next = htole32(OHCI_TD_NEXT_END);
+	td->td_flags &= ~htole32(OHCI_TD_INTR_MASK);
+	td->td_flags |= htole32(OHCI_TD_SET_DI(1));
 
-	usbd_page_dma_enter(td_last->page);
+	usbd_page_dma_enter(td->page);
 
 	/* must have at least one frame! */
 
-	xfer->td_transfer_last = td_last;
+	xfer->td_transfer_last = td;
 
 #ifdef USB_DEBUG
 	if(ohcidebug > 8)
@@ -1529,7 +1702,7 @@
 
 	ed_flags = (OHCI_ED_SET_FA(xfer->address)|
 		    OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint))|
-		    OHCI_ED_SET_MAXP(xfer->max_packet_size));
+		    OHCI_ED_SET_MAXP(xfer->max_frame_size));
 
 	ed_flags |= (OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD);
 	
@@ -1545,13 +1718,13 @@
 
 	OHCI_APPEND_QH(ed, td->td_self, *ed_last);
 
-	if(xfer->pipe->methods == &ohci_device_bulk_methods)
+	if (methods == &ohci_device_bulk_methods)
 	{
 		ohci_softc_t *sc = xfer->usb_sc;
 		OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
 	}

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



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