From owner-p4-projects@FreeBSD.ORG Sat Sep 22 13:23:41 2007 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id AD1F816A420; Sat, 22 Sep 2007 13:23:41 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 82DAD16A41A for ; Sat, 22 Sep 2007 13:23:41 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 6B12B13C45B for ; Sat, 22 Sep 2007 13:23:41 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id l8MDNfbo000338 for ; Sat, 22 Sep 2007 13:23:41 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id l8MDNfMV000333 for perforce@freebsd.org; Sat, 22 Sep 2007 13:23:41 GMT (envelope-from hselasky@FreeBSD.org) Date: Sat, 22 Sep 2007 13:23:41 GMT Message-Id: <200709221323.l8MDNfMV000333@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 126691 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 22 Sep 2007 13:23:42 -0000 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 #include -#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) <<<