PERFORCE change 126691 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Sat Sep 22 06:23:42 PDT 2007
http://perforce.freebsd.org/chv.cgi?CH=126691
Change 126691 by hselasky at 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) <<<
More information about the p4-projects
mailing list