Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 22 Sep 2007 13:23:41 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 126691 for review
Message-ID:  <200709221323.l8MDNfMV000333@repoman.freebsd.org>

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

Change 126691 by hselasky@hselasky_laptop001 on 2007/09/22 13:23:03

	FYI; The comments follow the P4 diff from top to bottom.
	
	- replace ADD_BYTES() by USBD_ADD_BYTES()
	
	- move a detach DELAY outside holding a mutex
	
	- new function "ehci_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. This
	  function will compute the next data toggle based on the
	  written back Data Toggle in the EHCI Transfer Descriptor
	  structure.
	
	- update "ehci_non_isoc_done()" to use "ehci_non_isoc_done_sub()"
	  instead of scanning the TD chain by itself.
	
	- updates to "ehci_check_transfer()", mainly to add support
	  for the Alternative Next pointer, which is followed in 
	  case of a short USB frame, for sake of completeness.
	
	- updated "ehci_pcd_enable()" to use factored out code in
	  "usbd_std_root_transfer()".
	
	- new function "ehci_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 4*4K
	  bytes worth of data per transfer descriptor, contrary to 1*4K
	  in the old version. Result; Less transfer descriptors
	  required.
	
	- changes to "ehci_setup_standard_chain()". The code is
	  doing exactly the same, just refactored a little bit.
	
	- new elements to "struct usbd_device" which are setup when
	  "struct usbd_device" is allocated to hold the correct values.
	
	   - "udev->hs_hub_addr"
	   - "udev->hs_port_no"
	
	- "ehci_root_intr_done()" is doing the same like before, only
	  now using a temporary scratch buffer in the "struct ehci_softc"
	  which is called "sc->sc_hub_idata".
	
	- some small changes to "ehci_isoc_fs_done()" and "ehci_isoc_hs_done()"
	
	- "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.
	
	- removed all data bouncing stuff (copy_out/copy_in)
	
	- "usbd_std_ctrl_enter()" is now 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()".
	
	- the new function "ehci_root_ctrl_task_td_sub()" does the
	  same as "ehci_root_ctrl_task_td()", but through the standard
	  root transfer framework.
	
	- major parts of "ehci_xfer_setup()" has been factored out and
	  only the EHCI specific part is left. Across all the Host
	  Controller Drivers this saves some code.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/ehci.c#37 edit
.. //depot/projects/usb/src/sys/dev/usb/ehci.h#14 edit

Differences ...

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

@@ -71,7 +71,6 @@
 #include <dev/usb/usb_subr.h>
 #include <dev/usb/ehci.h>
 
-#define MS_TO_TICKS(ms) (((ms) * hz) / 1000)
 #define EHCI_BUS2SC(bus) ((ehci_softc_t *)(((u_int8_t *)(bus)) - \
    POINTER_TO_UNSIGNED(&(((ehci_softc_t *)0)->sc_bus))))
 
@@ -109,10 +108,26 @@
 static void ehci_root_ctrl_task_td(struct ehci_softc *sc, struct thread *ctd);
 static void ehci_do_poll(struct usbd_bus *bus);
 
+static usbd_std_root_transfer_func_t ehci_root_intr_done;
+static usbd_std_root_transfer_func_t ehci_root_ctrl_task_td_sub;
+
 #define SC_HW_PHYSADDR(sc,what) \
   ((sc)->sc_hw_page.physaddr +	\
    POINTER_TO_UNSIGNED(&(((struct ehci_hw_softc *)0)->what)))
 
+struct ehci_std_temp {
+	struct usbd_page_cache *pc;
+	ehci_qtd_t *td;
+	ehci_qtd_t *td_next;
+	uint32_t average;
+	uint32_t qtd_status;
+	uint32_t len;
+	uint16_t max_frame_size;
+	uint8_t shortpkt;
+	uint8_t auto_data_toggle;
+	uint8_t setup_alt_next;
+};
+
 usbd_status
 ehci_init(ehci_softc_t *sc)
 {
@@ -398,10 +413,10 @@
 	EOWRITE4(sc, EHCI_USBCMD, 0);
 	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
 
+	mtx_unlock(&sc->sc_bus.mtx);
+
 	DELAY(1000*300); /* XXX let stray task complete */
 
-	mtx_unlock(&sc->sc_bus.mtx);
-
 	__callout_drain(&(sc->sc_tmo_pcd));
 
 	return;
@@ -1054,84 +1069,150 @@
 	return(last);
 }
 
+static uint32_t
+ehci_non_isoc_done_sub(struct usbd_xfer *xfer)
+{
+	ehci_qtd_t *td;
+	ehci_qtd_t *td_alt_next;
+	uint32_t status;
+	uint16_t len;
+	uint8_t last_toggle;
+
+	td = xfer->td_transfer_cache;
+	td_alt_next = td->alt_next;
+	status = EHCI_QTD_HALTED;
+	last_toggle = 0;
+
+	while (1) {
+
+	    usbd_page_dma_exit(td->page);
+	    status = le32toh(td->qtd_status);
+	    usbd_page_dma_enter(td->page);
+
+	    len = EHCI_QTD_GET_BYTES(status);
+
+	    /*
+	     * Verify the status length and subtract
+	     * the remainderfrom "frlengths[]":
+	     */
+	    if (len > td->len) {
+	        /* should not happen */
+	        DPRINTFN(0, ("Invalid status length, "
+			     "0x%04x/0x%04x bytes\n", len, td->len));
+		status |= EHCI_QTD_HALTED;
+	    } else {
+	        xfer->frlengths[xfer->aframes] -= len;
+	    }
+
+	    /* Make a copy of the data toggle */
+	    last_toggle = (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0;
+
+	    /* Check for last transfer */
+	    if (((void *)td) == xfer->td_transfer_last) {
+	        if (len == 0) {
+		    /*
+		     * Halt is ok if descriptor is last,
+		     * and complete:
+		     */
+		    status &= ~EHCI_QTD_HALTED;
+		}
+		td = NULL;
+	        break;
+	    }
+
+	    /* Check for transfer error */
+	    if (status & EHCI_QTD_HALTED) {
+	        td = NULL;
+	        break;
+	    }
+
+	    /* Check for short transfer */
+	    if (len > 0) {
+		td = td->alt_next;
+		break;
+	    }
+
+	    td = td->obj_next;
+
+	    if (td->alt_next != td_alt_next) {
+		/* we are finished */
+		break;
+	    }
+	}
+
+	/* update transfer cache */
+
+	xfer->td_transfer_cache = td;
+
+	/* update data toggle */
+
+	xfer->pipe->toggle_next = last_toggle;
+
+	return status;
+}
+
 static void
 ehci_device_done(struct usbd_xfer *xfer, usbd_status error);
 
 static void
 ehci_non_isoc_done(struct usbd_xfer *xfer)
 {
-	uint32_t temp;
 	u_int32_t status = 0;
-	u_int32_t actlen = 0;
-	u_int16_t len = 0;
-	u_int16_t last_len = 0;
-	u_int8_t last_toggle = 0;
-	ehci_qtd_t *td = xfer->td_transfer_first;
 
 	DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n",
 		      xfer, xfer->pipe));
 
 #ifdef USB_DEBUG
-	if(ehcidebug > 10)
-	{
-		ehci_dump_sqtds(td);
+	if (ehcidebug > 10) {
+	    ehci_dump_sqtds(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);
-		temp = le32toh(td->qtd_status);
-		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) {
+
+	    status = ehci_non_isoc_done_sub(xfer);
 
-		if (temp & EHCI_QTD_ACTIVE) {
-			break;
-		}
+	    if (status & EHCI_QTD_HALTED) {
+	        goto done;
+	    }
 
-		status = temp;
+	    xfer->aframes = 1;
+	}
 
-		len = EHCI_QTD_GET_BYTES(status);
+	while (xfer->aframes != xfer->nframes) {
 
-		/* The status length should always be
-		 * less than or equal to the setup 
-		 * length!
-		 */
-		if (len <= td->len) {
-			last_len = td->len - len;
-			actlen += last_len;
-		} else {
-			/* should not happen */
-			DPRINTFN(0, ("Invalid status length, "
-				     "0x%04x/0x%04x bytes\n", len, td->len));
-			last_len = 0;
-		}
+	    if ((!xfer->flags_int.control_xfr) ||
+		(xfer->frlengths[xfer->aframes] > 0)) {
 
-		/* Make a copy of the data toggle */
-		last_toggle = (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0;
+	        status = ehci_non_isoc_done_sub(xfer);
 
-		/* Check if this is the last transfer */
-		if (((void *)td) == xfer->td_transfer_last) {
-			if (len == 0) {
-			    /* halt is ok if descriptor is last,
-			     * and complete:
-			     */
-			    status &= ~EHCI_QTD_HALTED;
-			}
-			td = NULL;
-			break;
+		if (status & EHCI_QTD_HALTED) {
+		    goto done;
 		}
+	    }
+
+	    xfer->aframes ++;
 	}
 
-	/* update data toggle */
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
 
-	xfer->pipe->toggle_next = last_toggle;
+	    status = ehci_non_isoc_done_sub(xfer);
 
-	DPRINTFN(10, ("actlen=%d\n", actlen));
+	    if (status & EHCI_QTD_HALTED) {
+	        if (xfer->frlengths[xfer->nframes-1] == 0) {
+		    xfer->aframes--;
+		}
+		goto done;
+	    }
+	}
 
-	xfer->actlen = actlen;
+ done:
 
 #ifdef USB_DEBUG
 	if (status & EHCI_QTD_STATERRS) {
@@ -1162,6 +1243,8 @@
 static u_int8_t
 ehci_check_transfer(struct usbd_xfer *xfer, struct thread *ctd)
 {
+	struct usbd_pipe_methods *methods = xfer->pipe->methods;
+
 	uint32_t status;
 
 	if(xfer->usb_thread != ctd)
@@ -1174,7 +1257,7 @@
 
 	DPRINTFN(12, ("xfer=%p checking transfer\n", xfer));
 
-	if(xfer->pipe->methods == &ehci_device_isoc_fs_methods)
+	if(methods == &ehci_device_isoc_fs_methods)
 	{
 		ehci_sitd_t *td = xfer->td_transfer_last;
 
@@ -1191,7 +1274,7 @@
 			goto transferred;
 		}
 	}
-	else if(xfer->pipe->methods == &ehci_device_isoc_hs_methods)
+	else if(methods == &ehci_device_isoc_hs_methods)
 	{
 		ehci_itd_t *td = xfer->td_transfer_last;
 
@@ -1225,14 +1308,11 @@
 		 * 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->qtd_status);
-
 			usbd_page_dma_enter(td->page);
 
 			/* if there is an active TD 
@@ -1243,6 +1323,13 @@
 				xfer->td_transfer_cache = td;
 				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 
@@ -1251,17 +1338,22 @@
 				break;
 			}
 
-			/* a short packet also makes
-			 * the transfer done
+			/* if there is no alternate
+			 * next transfer, a short packet
+			 * also makes the transfer done
 			 */
 			if (EHCI_QTD_GET_BYTES(status)) {
+				if (xfer->flags_int.short_frames_ok) {
+				    /* follow alt next */
+				    if (td->alt_next) {
+				        td = td->alt_next;
+					continue;
+				    }
+				}
+				/* transfer is done */
 				break;
 			}
-
-			if (((void *)td) == xfer->td_transfer_last) {
-				td = NULL;
-				break;
-			}
+			td = td->obj_next;
 		}
 		ehci_non_isoc_done(xfer);
 		goto transferred;
@@ -1278,12 +1370,6 @@
 static void
 ehci_pcd_enable(ehci_softc_t *sc)
 {
-	struct thread *td;
-	struct usbd_xfer *xfer;
-	struct usbd_xfer *xlist[2];
-
-	td = curthread;
-
 	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
 
 	sc->sc_eintrs |= EHCI_STS_PCD;
@@ -1292,28 +1378,10 @@
 	/* acknowledge any PCD interrupt */
 	EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD);
 
-	xfer = sc->sc_intrxfer;
-
- 	if(xfer)
-	{
-	    /* transfer is transferred */
-	    ehci_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);
+	if (usbd_std_root_transfer(&(sc->sc_root_intr), NULL,
+				   &ehci_root_intr_done)) {
+	    mtx_unlock(&(sc->sc_bus.mtx));
 	}
-	else
-	{
-	    mtx_unlock(&sc->sc_bus.mtx);
-	}
 	return;
 }
 
@@ -1403,20 +1471,6 @@
 
 	if(status & EHCI_STS_PCD)
 	{
-		xfer = sc->sc_intrxfer;
-
-		if(xfer)
-		{
-		    ehci_device_done(xfer, USBD_NORMAL_COMPLETION);
-
-		    /* queue callback */
-
-		    *(xptr++) = xfer;
-
-		    xfer->usb_thread = td;
-		    xfer->usb_root->memory_refcount++;
-		}
-
 		/*
 		 * Disable PCD interrupt for now, because it will be
 		 * on until the port has been reset.
@@ -1424,6 +1478,11 @@
 		sc->sc_eintrs &= ~EHCI_STS_PCD;
 		EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
 
+		if (!usbd_std_root_transfer(&(sc->sc_root_intr), ctd,
+					    &ehci_root_intr_done)) {
+		    mtx_lock(&(sc->sc_bus.mtx));
+		}
+
 		/* do not allow RHSC interrupts > 1 per second */
 		__callout_reset(&sc->sc_tmo_pcd, hz,
 				(void *)(void *)ehci_pcd_enable, sc);
@@ -1552,248 +1611,339 @@
 }
 
 static void
-ehci_setup_standard_chain(struct usbd_xfer *xfer, ehci_qh_t **qh_last)
+ehci_setup_standard_chain_sub(struct ehci_std_temp *temp)
 {
 	struct usbd_page_search buf_res;
-	/* the EHCI hardware can handle at most five 4k crossing per TD */
-	u_int32_t average = (EHCI_PAGE_SIZE - (EHCI_PAGE_SIZE % 
-					       xfer->max_packet_size));
-	u_int32_t qtd_status;
-	uint32_t qh_endp;
-	uint32_t qh_endphub;
-	u_int32_t buf_offset;
-	u_int32_t len;
-	u_int32_t c_error = 
-	  (usbd_get_speed(xfer->udev) == USB_SPEED_HIGH) ? 0 : 
-	  htole32(EHCI_QTD_SET_CERR(3));
-	u_int8_t isread;
-	u_int8_t shortpkt = 0;
-	u_int8_t force_short;
-	struct usbd_pipe_methods *methods = xfer->pipe->methods;
 	ehci_qtd_t *td;
-	ehci_qtd_t *td_last = NULL;
-	ehci_qh_t *qh;
+	ehci_qtd_t *td_next;
+	ehci_qtd_t *td_alt_next;
+	uint32_t qtd_altnext;
+	uint32_t buf_offset;
+	uint32_t average;
+	uint32_t len_old;
+	uint8_t shortpkt_old;
+	uint8_t precompute;
+
+	qtd_altnext = htole32(EHCI_LINK_TERMINATE);
+	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_frame_size) {
+		        temp->shortpkt = 1;
+		    }
+		    average = temp->len;
+		}
+	    }
+
+	    if (td_next == NULL) {
+	        panic("%s: out of EHCI 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, usbd_get_speed(xfer->udev)));
+	        /* update remaining length */
 
-	td = (xfer->td_transfer_first = 
-	      xfer->td_transfer_cache = xfer->td_start);
+		temp->len -= average;
 
-	buf_offset = 0;
-	usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
+		continue;
+	    }
 
-	force_short = (xfer->flags & USBD_FORCE_SHORT_XFER) ? 1 : 0;
+	    usbd_page_dma_exit(td->page);
 
-	len = xfer->length;
+	    /* fill out current TD */
 
-	if(methods == &ehci_device_ctrl_methods)
-	{
-	    isread = xfer->control_isread;
+	    td->qtd_status = 
+	      temp->qtd_status | htole32(EHCI_QTD_SET_BYTES(average));
 
-	    if (xfer->flags & USBD_DEV_CONTROL_HEADER) {
+	    if (average == 0) {
 
-		xfer->pipe->toggle_next = 1;
+	        if (temp->auto_data_toggle == 0) {
 
-		usbd_page_dma_exit(td->page);
+		    /* update data toggle, ZLP case */
 
-		/* SETUP message */
+		    temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK);
+		}
 
-		td->qtd_status = c_error | htole32
-		  (EHCI_QTD_ACTIVE |
-		   EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
-		   EHCI_QTD_SET_TOGGLE(0) |
-		   EHCI_QTD_SET_BYTES(sizeof(usb_device_request_t)));
+		td->len = 0;
 
-		td->qtd_buffer[0] = htole32(buf_res.physaddr);
+		td->qtd_buffer[0] = 0;
 		td->qtd_buffer_hi[0] = 0;
 
-		buf_offset += sizeof(usb_device_request_t);
-		usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
+		td->qtd_buffer[1] = 0;
+		td->qtd_buffer_hi[1] = 0;
+
+	    } else {
+
+	        uint8_t x;
 
-		td->qtd_buffer[1] = 
-		  htole32(buf_res.physaddr & (~0xFFF));
-		td->qtd_buffer_hi[1] = 0;
+	        if (temp->auto_data_toggle == 0) {
 
-		td->len = sizeof(usb_device_request_t);
-		len -= sizeof(usb_device_request_t);
-		td_last = td;
-		td = td->obj_next;
+		    /* update data toggle */
 
-		if (td) {
-		    /* link the last TD with the next one */
-		    td_last->qtd_next = td->qtd_self;
-		    /* short transfers should terminate the transfer: */
-		    td_last->qtd_altnext = htole32(EHCI_LINK_TERMINATE);
+		    if (((average + temp->max_frame_size - 1) / 
+			 temp->max_frame_size) & 1) {
+			temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK);
+		    }
 		}
 
-		usbd_page_dma_enter(td_last->page);
-	    } 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;
+	        td->len = average;
+
+		/* update remaining length */
+
+		temp->len -= average;
+
+		/* fill out buffer pointers */
+
+		usbd_get_page(temp->pc, buf_offset, &buf_res);
+		td->qtd_buffer[0] = htole32(buf_res.physaddr);
+		td->qtd_buffer_hi[0] = 0;
+
+		x = 1;
+
+		while (average > EHCI_PAGE_SIZE) {
+		    average -= EHCI_PAGE_SIZE;
+		    buf_offset += EHCI_PAGE_SIZE;
+		    usbd_get_page(temp->pc, buf_offset, &buf_res);
+		    td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF));
+		    td->qtd_buffer_hi[x] = 0;
+		    x++;
 		}
+
+		/* 
+		 * NOTE: The "average" variable is
+		 * never zero after exiting the loop
+		 * above !
+		 *
+		 * NOTE: We have to subtract one from the 
+		 * offset to ensure that we are computing
+		 * the physical address of a valid page !
+		 */
+		buf_offset += average;
+		usbd_get_page(temp->pc, buf_offset-1, &buf_res);
+		td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF));
+		td->qtd_buffer_hi[x] = 0;
+	    }
+
+	    if (td_next) {
+		/* link the current TD with the next one */
+		td->qtd_next = td_next->qtd_self;
 	    }
+
+	    td->qtd_altnext = qtd_altnext;
+	    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 */
+	    if (td_next) {
+	        td_alt_next = td_next;
+		if (temp->setup_alt_next) {
+		    qtd_altnext = td_alt_next->qtd_self;
 		}
+	    }
+	    
+	    /* restore */
+	    temp->shortpkt = shortpkt_old;
+	    temp->len = len_old;
+	    goto restart;
 	}
 
-	qtd_status = c_error | (isread ?
-	  htole32
-	  (EHCI_QTD_ACTIVE |
-	   EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) :
-	  htole32
-	  (EHCI_QTD_ACTIVE |
-	   EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT)));
+	temp->td = td;
+	temp->td_next = td_next;
+
+	return;
+}
+
+static void
+ehci_setup_standard_chain(struct usbd_xfer *xfer, ehci_qh_t **qh_last)
+{
+	struct ehci_std_temp temp;
+	struct usbd_pipe_methods *methods;
+	ehci_qh_t *qh;
+	ehci_qtd_t *td;
+	uint32_t qh_endp;
+	uint32_t qh_endphub;
+	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.qtd_status = 0;
+	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
 
-	if(xfer->pipe->toggle_next)
-	{
-		qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1));
+	if (xfer->flags_int.control_xfr) {
+	    if (xfer->pipe->toggle_next) {
+		/* DATA1 is next */
+		temp.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1));
+	    }
+	    temp.auto_data_toggle = 0;
+	} else {
+	    temp.auto_data_toggle = 1;
 	}
 
-	while(1)
-	{
-		if(len == 0)
-		{
-			if (force_short)
-			{
-				if(shortpkt)
-				{
-					break;
-				}
-			}
-			else
-			{
-				break;
-			}
-		}
+	if (usbd_get_speed(xfer->udev) != USB_SPEED_HIGH) {
+	    /* max 3 retries */
+	    temp.qtd_status |= htole32(EHCI_QTD_SET_CERR(3));
+	}
 
-		if(len < average)
-		{
-			if((len % xfer->max_packet_size) || 
-			   (len == 0))
-			{
-				shortpkt = 1;
-			}
+	x = 0;
 
-			average = len;
-		}
+	/* check if we should prepend a setup message */
 
-		if(td == NULL)
-		{
-			panic("%s: software wants to write more data "
-			      "than there is in the buffer!", __FUNCTION__);
-		}
+	if (xfer->flags_int.control_xfr &&
+	    xfer->flags_int.control_hdr) {
 
-		usbd_page_dma_exit(td->page);
+		temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3));
+		temp.qtd_status |= htole32
+		  (EHCI_QTD_ACTIVE |
+		   EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
+		   EHCI_QTD_SET_TOGGLE(0));
 
-		/* fill out current TD */
+		temp.len = xfer->frlengths[0];
+		temp.pc = xfer->frbuffers + 0;
+		temp.shortpkt = temp.len ? 1 : 0;
 
-		td->qtd_status = 
-		  qtd_status | htole32(EHCI_QTD_SET_BYTES(average));
+		ehci_setup_standard_chain_sub(&temp);
 
-		td->qtd_buffer[0] = htole32(buf_res.physaddr);
-		td->qtd_buffer_hi[0] = 0;
+		x = 1;
+	}
 
-		buf_offset += average;
-		usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res);
+	while (x != xfer->nframes) {
 
-		td->qtd_buffer[1] = 
-		  htole32(buf_res.physaddr & (~0xFFF));
-		td->qtd_buffer_hi[1] = 0;
+		/* DATA0 / DATA1 message */
 
-		td->len = average;
+		temp.len = xfer->frlengths[x];
+		temp.pc = xfer->frbuffers + x;
 
-		/* adjust the toggle based on the 
-		 * number of packets in this qtd
-		 */
-		if ((average == 0) ||
-		    (((average + xfer->max_packet_size - 1) / 
-		      xfer->max_packet_size) & 1))
-		{
-		    xfer->pipe->toggle_next =
-		      xfer->pipe->toggle_next ? 0 : 1;
+		x++;
 
-		    qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK);
+		if (x == xfer->nframes) {
+		    temp.setup_alt_next = 0;
 		}
 
-		len -= average;
-		td_last = td;
-		td = td->obj_next;
+		/* keep previous data toggle and error count */
+
+		temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3)|
+					   EHCI_QTD_SET_TOGGLE(1));
+
+		if (temp.len == 0) {
+
+		    /* make sure that we send an USB packet */
+
+		    temp.shortpkt = 0;
+
+		    if (xfer->flags_int.control_xfr) {
+		        /* we ignore zero length frames */
+		        continue;
+		    }
+
+		} else {
+
+		    /* regular data transfer */
 
-		if (td) {
-		    /* link the last TD with the next one */
-		    td_last->qtd_next = td->qtd_self;
-		    /* short transfers should terminate the transfer: */
-		    td_last->qtd_altnext = htole32(EHCI_LINK_TERMINATE);
+		    temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1;
 		}
 
-		usbd_page_dma_enter(td_last->page);
-	}
+		/* set endpoint direction */
 
-	if(methods == &ehci_device_ctrl_methods)
-	{
-	    if (xfer->control_remainder == 0) {
+		temp.qtd_status |= 
+		  (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ?
+		  htole32(EHCI_QTD_ACTIVE |
+			  EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) :
+		  htole32(EHCI_QTD_ACTIVE |
+			  EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT));
 
-		usbd_page_dma_exit(td->page);
+		ehci_setup_standard_chain_sub(&temp);
+	}
 
-		/* STATUS message */
+	/* check if we should append a status stage */
 
-		td->qtd_status = c_error | (isread ?
-		  htole32
-		  (EHCI_QTD_ACTIVE |
-		   EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) |
-		   EHCI_QTD_SET_TOGGLE(1) |
-		   EHCI_QTD_IOC) :
-		  htole32
-		  (EHCI_QTD_ACTIVE |
-		   EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) |
-		   EHCI_QTD_SET_TOGGLE(1) |
-		   EHCI_QTD_IOC));
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
 
-		td->qtd_buffer[0] = 0; 
-		td->qtd_buffer_hi[0] = 0;
+		/* 
+		 * Send a DATA1 message and invert 
+		 * the current endpoint direction.
+		 */
 
-		td->qtd_buffer[1] = 0;
-		td->qtd_buffer_hi[1] = 0;
+		temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3)|
+					   EHCI_QTD_SET_TOGGLE(1));
+		temp.qtd_status |=
+		  (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ?
+		    htole32(EHCI_QTD_ACTIVE |
+			    EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) |
+			    EHCI_QTD_SET_TOGGLE(1)) :
+		    htole32(EHCI_QTD_ACTIVE |
+			    EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) |
+			    EHCI_QTD_SET_TOGGLE(1));
 
-		td->len = 0;
-		td_last = td;
+		temp.len = 0;
+		temp.pc = NULL;
+		temp.shortpkt = 0;
 
-		usbd_page_dma_enter(td_last->page);
-	    }
+		ehci_setup_standard_chain_sub(&temp);
 	}
 
-	usbd_page_dma_exit(td_last->page);
+	td = temp.td;
+
+	usbd_page_dma_exit(td->page);
 
 	/* the last TD terminates the transfer: */
-	td_last->qtd_next = htole32(EHCI_LINK_TERMINATE);
-	td_last->qtd_altnext = htole32(EHCI_LINK_TERMINATE);
-	td_last->qtd_status |= htole32(EHCI_QTD_IOC);
+	td->qtd_next = htole32(EHCI_LINK_TERMINATE);

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



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