Date: Fri, 6 Jul 2007 16:04:04 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 123022 for review Message-ID: <200707061604.l66G44ff021774@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=123022 Change 123022 by hselasky@hselasky_mini_itx on 2007/07/06 16:04:03 Hi, My main hacking location did not have internet access, so here is a multiple changes submit again. Summed up: 1) Added a completely new FULL speed over HIGH speed USB HUB bandwidth allocator. See the "usbd_fs_isoc_xxx()" functions in "usb_subr.c". Finally it should be possible to connect several FULL speed isochronous devices to a HIGH speed USB HUB at the same time, without problems. NOTE: The sum of the isochronous bandwidth must not exceed 6.0Mbit/s per HS USB HUB! This is because I have made a simple hack and reserved 3.0 Mbit/s for interrupt endpoints so that the scheduling becomes extremely fast and simple. The bandwidth distribution is tunable through the "USB_FS_ISOC_UFRAME_MAX" definition in "usb.h". 2) Fixed a data-toggle error in the EHCI driver. Now the hardware/DMA data toggle value is used as reference for the next transfer. 3) Fixed a possible control transfer problem when using the Linux emulation layer with the UHCI driver. Part of this fix is to always use a fixup buffer independent of transfer type. This way it will also be easier to make virtual kernel mappings from the allocated memory pages. 4) Instead of comparing memory pointers with "less than", compute the end-pointer and use the "not equal to" comparison instead. This is safer and faster. 5) Removed interrupt counting in the interrupt handlers. 6) ISOCHRONOUS transfers will always have a timeout. If the driver does not specify a timeout, 250ms will be used. 7) Make sure that LOW speed devices cannot specify FULL speed ISOCHRONOUS transfers. The check is done in the host controller "pipe_init" callbacks. 8) EHCI-driver: Lower EHCI NAK-reload count to 1 for non-HIGH-speed devices. 9) New mechanism: If the "pipe_init" callback wants to say that it does not support a certain transfer type, it can do so by not setting "pipe->methods". 10) Added maximum string length arguments to "usbd_devinfo_vp()" and "usbd_printBCD()", instead of depending on some old assumptions. 11) Replaced the remaining "strcpy()" with "strlcpy()". This does not fix anything. 12) "usbd_page_fit_obj()" does no longer clobber the "page->length" variable, which turned out to not be a good idea. 13) Fixed a bug in the UHCI driver, where the wrong page was exited/entered from/to DMA. 14) Some optimizations and nits. Affected files ... .. //depot/projects/usb/src/sys/dev/usb/README#15 edit .. //depot/projects/usb/src/sys/dev/usb/ehci.c#35 edit .. //depot/projects/usb/src/sys/dev/usb/ehci.h#13 edit .. //depot/projects/usb/src/sys/dev/usb/ohci.c#29 edit .. //depot/projects/usb/src/sys/dev/usb/ugen.c#19 edit .. //depot/projects/usb/src/sys/dev/usb/uhci.c#29 edit .. //depot/projects/usb/src/sys/dev/usb/uhub.c#14 edit .. //depot/projects/usb/src/sys/dev/usb/umass.c#19 edit .. //depot/projects/usb/src/sys/dev/usb/usb.c#15 edit .. //depot/projects/usb/src/sys/dev/usb/usb.h#12 edit .. //depot/projects/usb/src/sys/dev/usb/usb_subr.c#40 edit .. //depot/projects/usb/src/sys/dev/usb/usb_subr.h#43 edit .. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#28 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/README#15 (text+ko) ==== @@ -215,8 +215,9 @@ NOTE: The transfer timeout, if any, is started after that the pre-delay has elapsed! -- The "timeout" field, if non-zero, will set the transfer timeout, in - milliseconds. +- The "timeout" field, if non-zero, will set the transfer timeout in + milliseconds. If the "timeout" field is zero and the transfer type + is ISOCHRONOUS a timeout of 250ms will be used. - The "frames" field sets the number of isochronous frames, for "type" = UE_ISOCHRONOUS. ==== //depot/projects/usb/src/sys/dev/usb/ehci.c#35 (text+ko) ==== @@ -1108,7 +1108,7 @@ } /* Make a copy of the data toggle */ - last_toggle = td->toggle_curr; + last_toggle = (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0; /* Check if this is the last transfer */ if (((void *)td) == xfer->td_transfer_last) { @@ -1125,12 +1125,6 @@ /* update data toggle */ - if ((last_len == 0) || - (((last_len + xfer->max_packet_size - 1) / - xfer->max_packet_size) & 1)) { - last_toggle = !last_toggle; - } - xfer->pipe->toggle_next = last_toggle; DPRINTFN(10, ("actlen=%d\n", actlen)); @@ -1362,8 +1356,6 @@ td = curthread; /* NULL is not a valid thread */ - sc->sc_bus.no_intrs++; - DPRINTFN(15,("%s: real interrupt\n", device_get_nameunit(sc->sc_bus.bdev))); @@ -1570,18 +1562,19 @@ u_int32_t buf_offset; u_int32_t len; u_int32_t c_error = - (xfer->udev->speed == USB_SPEED_HIGH) ? 0 : + (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; DPRINTFN(8, ("addr=%d endpt=%d len=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), - xfer->length, xfer->udev->speed)); + xfer->length, usbd_get_speed(xfer->udev))); td = (xfer->td_transfer_first = xfer->td_transfer_cache = xfer->td_start); @@ -1593,7 +1586,7 @@ len = xfer->length; - if(xfer->pipe->methods == &ehci_device_ctrl_methods) + if(methods == &ehci_device_ctrl_methods) { isread = xfer->control_isread; @@ -1620,7 +1613,6 @@ td->qtd_buffer[1] = htole32(buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[1] = 0; - td->toggle_curr = 0; td->len = sizeof(usb_device_request_t); len -= sizeof(usb_device_request_t); @@ -1726,7 +1718,6 @@ htole32(buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[1] = 0; - td->toggle_curr = xfer->pipe->toggle_next; td->len = average; /* adjust the toggle based on the @@ -1756,7 +1747,7 @@ usbd_page_dma_enter(td_last->page); } - if(xfer->pipe->methods == &ehci_device_ctrl_methods) + if(methods == &ehci_device_ctrl_methods) { if (xfer->control_remainder == 0) { @@ -1820,38 +1811,37 @@ qh_endp = (EHCI_QH_SET_ADDR(xfer->address) | EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) | - EHCI_QH_DTC | - EHCI_QH_SET_MPL(xfer->max_packet_size) | - EHCI_QH_SET_NRL(8) /* XXX */ - ); + EHCI_QH_SET_MPL(xfer->max_packet_size)); + + if (usbd_get_speed(xfer->udev) == USB_SPEED_HIGH) { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | + EHCI_QH_DTC | EHCI_QH_SET_NRL(8)); + } else { + + if (usbd_get_speed(xfer->udev) == USB_SPEED_FULL) { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL) | + EHCI_QH_DTC); + } else { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW) | + EHCI_QH_DTC); + } - switch (xfer->udev->speed) { - case USB_SPEED_LOW: - qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW)| - EHCI_QH_CTL); - break; - case USB_SPEED_FULL: - qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL)| - EHCI_QH_CTL); - break; - case USB_SPEED_HIGH: - qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); - break; - default: - panic("%s: bad device speed %d!", __FUNCTION__, xfer->udev->speed); - break; - } + if (methods == &ehci_device_ctrl_methods) { + qh_endp |= EHCI_QH_CTL; + } - if(xfer->pipe->methods != &ehci_device_ctrl_methods) - { - qh_endp &= ~EHCI_QH_CTL; + if (methods != &ehci_device_intr_methods) { + /* Only try one time per microframe! */ + qh_endp |= EHCI_QH_SET_NRL(1); + } } qh->qh_endp = htole32(qh_endp); qh_endphub = (EHCI_QH_SET_MULT(xfer->max_packet_count & 3)| - EHCI_QH_SET_CMASK(0xf0)); + EHCI_QH_SET_CMASK(xfer->usb_cmask)| + EHCI_QH_SET_SMASK(xfer->usb_smask)); if(xfer->udev->myhsport) { @@ -1860,12 +1850,6 @@ EHCI_QH_SET_PORT(xfer->udev->myhsport->portno)); } - if(xfer->pipe->methods == &ehci_device_intr_methods) - { - /* execute the transfer one time per 1ms */ - qh_endphub |= EHCI_QH_SET_SMASK(0x04); - } - qh->qh_endphub = htole32(qh_endphub); qh->qh_curqtd = htole32(0); @@ -2354,7 +2338,27 @@ u_int16_t best; u_int16_t bit; u_int16_t x; + uint8_t slot; + + /* Allocate a microframe slot first: */ + + slot = usbd_intr_schedule_adjust + (xfer->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX); + + if (usbd_get_speed(xfer->udev) == USB_SPEED_HIGH) { + xfer->usb_uframe = slot; + xfer->usb_smask = (1 << slot) & 0xFF; + xfer->usb_cmask = 0; + } else { + xfer->usb_uframe = slot; + xfer->usb_smask = (1 << slot) & 0x3F; + xfer->usb_cmask = (-(4 << slot)) & 0xFE; + } + /* Find the best QH position corresponding + * to the given interval: + */ + best = 0; bit = EHCI_VIRTUAL_FRAMELIST_COUNT/2; while(bit) @@ -2389,6 +2393,10 @@ ehci_device_intr_close(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; + uint8_t slot; + + slot = usbd_intr_schedule_adjust + (xfer->udev, -(xfer->max_frame_size), xfer->usb_uframe); sc->sc_intr_stat[xfer->qh_pos]--; @@ -2447,20 +2455,15 @@ sitd_portaddr = EHCI_SITD_SET_ADDR(xfer->address)| - EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)); + EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint))| + EHCI_SITD_SET_HUBA(xfer->udev->myhsport->parent->address)| + EHCI_SITD_SET_PORT(xfer->udev->myhsport->portno); if(UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { sitd_portaddr |= EHCI_SITD_SET_DIR_IN; } - if(xfer->udev->myhsport) - { - sitd_portaddr |= - EHCI_SITD_SET_HUBA(xfer->udev->myhsport->parent->address)| - EHCI_SITD_SET_PORT(xfer->udev->myhsport->portno); - } - sitd_portaddr = htole32(sitd_portaddr); /* initialize all TD's */ @@ -2476,24 +2479,7 @@ * * micro-frame usage * (8 microframes per 1ms) - * - * 0: isoc-IN - * 1: isoc-OUT - * 2: interrupt transfers - * . - * . - * 7: */ - if(UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) - { - td->sitd_mask = htole32(EHCI_SITD_SET_SMASK(0x01)| - EHCI_SITD_SET_CMASK(0xFC)); - } - else - { - td->sitd_mask = htole32(EHCI_SITD_SET_SMASK(0x02)| - EHCI_SITD_SET_CMASK(0xF8)); - } td->sitd_back = htole32(EHCI_LINK_TERMINATE); usbd_page_dma_enter(td->page); @@ -2513,13 +2499,20 @@ { struct usbd_page_search buf_res; ehci_softc_t *sc = xfer->usb_sc; + struct usbd_fs_isoc_schedule *fss_start; + struct usbd_fs_isoc_schedule *fss_end; + struct usbd_fs_isoc_schedule *fss; u_int32_t buf_offset; u_int32_t nframes; uint32_t temp; + uint32_t sitd_mask; u_int16_t *plen; + uint16_t tlen; #ifdef USB_DEBUG u_int8_t once = 1; #endif + uint8_t sa; + uint8_t sb; ehci_sitd_t *td; ehci_sitd_t *td_last = NULL; ehci_sitd_t **pp_last; @@ -2562,7 +2555,8 @@ * will be finished: */ xfer->isoc_time_complete = - usbd_isoc_time_expand(&(sc->sc_bus), nframes) + buf_offset + + usbd_fs_isoc_schedule_isoc_time_expand + (xfer->udev, &fss_start, &fss_end, nframes) + buf_offset + xfer->nframes; /* get the real number of frames */ @@ -2593,6 +2587,8 @@ xfer->qh_pos = xfer->pipe->isoc_next; + fss = fss_start + (xfer->qh_pos % USB_ISOC_TIME_MAX); + while(nframes--) { if(td == NULL) @@ -2606,9 +2602,12 @@ pp_last = &sc->sc_isoc_fs_p_last[0]; } + if (fss >= fss_end) { + fss = fss_start; + } + /* reuse sitd_portaddr and sitd_back from last transfer */ - /* TODO: implement support for multiple transactions */ if(*plen > xfer->max_frame_size) { #ifdef USB_DEBUG @@ -2624,6 +2623,11 @@ *plen = xfer->max_frame_size; } + /* We currently don't care if the + * ISOCHRONOUS schedule is full! + */ + sa = usbd_fs_isoc_schedule_alloc(fss, *plen); + usbd_page_dma_exit(td->page); td->sitd_bp[0] = htole32(buf_res.physaddr); @@ -2633,13 +2637,41 @@ temp = buf_res.physaddr & (~0xFFF); - if(UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) - { - temp |= 1; /* T-count == 1*/ + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) { + tlen = *plen; + if (tlen <= 188) { + temp |= 1; /* T-count = 1, TP = ALL*/ + tlen = 1; + } else { + tlen += 187; + tlen /= 188; + temp |= tlen; /* T-count = [1..6] */ + temp |= 8; /* TP = Begin */ + } + + tlen += sa; + + if (tlen >= 8) { + sb = 0; + } else { + sb = (1 << tlen); + } + + sa = (1 << sa); + sa = (sb - sa) & 0x3F; + sb = 0; + } else { + sb = (-(4 << sa)) & 0xFE; + sa = (1 << sa) & 0x3F; } + sitd_mask = (EHCI_SITD_SET_SMASK(sa)| + EHCI_SITD_SET_CMASK(sb)); + td->sitd_bp[1] = htole32(temp); + td->sitd_mask = htole32(sitd_mask); + if(nframes == 0) { td->sitd_status = htole32 @@ -2667,6 +2699,7 @@ pp_last++; plen++; + fss++; td_last = td; td = td->obj_next; } @@ -3838,11 +3871,23 @@ (xfer->pipe->methods == &ehci_device_bulk_methods) || (xfer->pipe->methods == &ehci_device_intr_methods)) { - if ((xfer->pipe->methods == &ehci_device_intr_methods) && - (udev->speed == USB_SPEED_HIGH)) - usbd_std_transfer_setup(udev, xfer, setup, 0x400, 0xC00, 3); - else - usbd_std_transfer_setup(udev, xfer, setup, 0x400, 0x400, 1); + if (xfer->pipe->methods == &ehci_device_intr_methods) { + if (udev->speed == USB_SPEED_HIGH) { + usbd_std_transfer_setup(udev, xfer, setup, + 0x400, 0xC00, 3); + } else if (udev->speed == USB_SPEED_FULL) { + usbd_std_transfer_setup(udev, xfer, setup, + USB_FS_BYTES_PER_HS_UFRAME, + USB_FS_BYTES_PER_HS_UFRAME, 1); + } else { + usbd_std_transfer_setup(udev, xfer, setup, + USB_FS_BYTES_PER_HS_UFRAME / 8, + USB_FS_BYTES_PER_HS_UFRAME / 8, 1); + } + } else { + usbd_std_transfer_setup(udev, xfer, setup, + 0x400, 0x400, 1); + } nqh = 1; nqtd = (1+ /* SETUP */ 1+ /* STATUS */ 1 /* SHORTPKT */) + @@ -3850,7 +3895,7 @@ } else if(xfer->pipe->methods == &ehci_device_isoc_fs_methods) { - usbd_std_transfer_setup(udev, xfer, setup, 188, 188, 1); + usbd_std_transfer_setup(udev, xfer, setup, 0x3FF, 0x3FF, 1); if(xfer->nframes == 0) { @@ -3967,8 +4012,8 @@ n < nitd; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ehci_itd_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ehci_itd_t)); + if(buf) { register ehci_itd_t *td; @@ -3995,8 +4040,8 @@ n < nsitd; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ehci_sitd_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ehci_sitd_t)); + if(buf) { register ehci_sitd_t *td; @@ -4023,8 +4068,8 @@ n < nqtd; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ehci_qtd_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ehci_qtd_t)); + if(buf) { register ehci_qtd_t *qtd; @@ -4061,8 +4106,8 @@ n < nqh; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ehci_qh_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ehci_qh_t)); + if(buf) { register ehci_qh_t *qh; @@ -4154,13 +4199,18 @@ pipe->methods = &ehci_root_intr_methods; break; default: - panic("invalid endpoint address: 0x%02x", - edesc->bEndpointAddress); + /* do nothing */ break; } } else { + if ((udev->speed != USB_SPEED_HIGH) && + (udev->myhsport == NULL)) { + /* We need a transaction translator */ + goto done; + } + switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: @@ -4174,7 +4224,7 @@ { pipe->methods = &ehci_device_isoc_hs_methods; } - else + else if (udev->speed == USB_SPEED_FULL) { pipe->methods = &ehci_device_isoc_fs_methods; } @@ -4182,8 +4232,12 @@ case UE_BULK: pipe->methods = &ehci_device_bulk_methods; break; + default: + /* do nothing */ + break; } } + done: return; } ==== //depot/projects/usb/src/sys/dev/usb/ehci.h#13 (text+ko) ==== @@ -319,8 +319,6 @@ struct usbd_page *page; uint32_t qtd_self; uint16_t len; - uint8_t toggle_curr; - uint8_t unused; } __aligned(EHCI_QTD_ALIGN) ehci_qtd_t; /* Queue Head */ ==== //depot/projects/usb/src/sys/dev/usb/ohci.c#29 (text+ko) ==== @@ -1052,7 +1052,6 @@ td = curthread; /* NULL is not a valid thread */ - sc->sc_bus.no_intrs++; hw_ptr = sc->sc_hw_ptr; DPRINTFN(15,("%s: real interrupt\n", @@ -2114,12 +2113,6 @@ /**/ ohci_add_interrupt_info(sc, xfer); - if(!xfer->timeout) - { - /* in case the frame start number is wrong */ - xfer->timeout = 1000/4; - } - /* enqueue transfer * (so that it can be aborted through pipe abort) */ @@ -2948,8 +2941,8 @@ n < ntd; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ohci_td_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ohci_td_t)); + if(buf) { register ohci_td_t *td; @@ -2976,8 +2969,8 @@ n < nitd; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ohci_itd_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ohci_itd_t)); + if(buf) { register ohci_itd_t *itd; @@ -3011,8 +3004,8 @@ n < nqh; n++) { - size[1] += usbd_page_fit_obj(page_ptr, size[1], - sizeof(ohci_ed_t)); + size[1] += usbd_page_fit_obj(size[1], sizeof(ohci_ed_t)); + if(buf) { register ohci_ed_t *ed; @@ -3104,8 +3097,7 @@ pipe->methods = &ohci_root_intr_methods; break; default: - panic("invalid endpoint address: 0x%02x", - edesc->bEndpointAddress); + /* do nothing */ break; } } @@ -3120,11 +3112,16 @@ pipe->methods = &ohci_device_intr_methods; break; case UE_ISOCHRONOUS: - pipe->methods = &ohci_device_isoc_methods; + if (udev->speed == USB_SPEED_FULL) { + pipe->methods = &ohci_device_isoc_methods; + } break; case UE_BULK: pipe->methods = &ohci_device_bulk_methods; break; + default: + /* do nothing */ + break; } } return; ==== //depot/projects/usb/src/sys/dev/usb/ugen.c#19 (text+ko) ==== @@ -1092,7 +1092,8 @@ /* start transfer */ usbd_transfer_start(xfer); - while (xfer->flags & USBD_DEV_TRANSFERRING) { + while ((xfer->flags & USBD_DEV_TRANSFERRING) || + (sce->xfer_in[1]->flags & USBD_DEV_TRANSFERRING)) { /* wait for data */ @@ -1104,6 +1105,7 @@ sce->state &= ~(UGEN_RD_SLP|UGEN_RD_WUP); if (error) { + usbd_transfer_stop(sce->xfer_in[1]); usbd_transfer_stop(xfer); break; } @@ -1217,7 +1219,8 @@ /* start transfer */ usbd_transfer_start(xfer); - while (xfer->flags & USBD_DEV_TRANSFERRING) { + while ((xfer->flags & USBD_DEV_TRANSFERRING) || + (sce->xfer_out[1]->flags & USBD_DEV_TRANSFERRING)) { /* wait for data */ @@ -1229,6 +1232,7 @@ sce->state &= ~(UGEN_WR_SLP|UGEN_WR_WUP); if (error) { + usbd_transfer_stop(sce->xfer_out[1]); usbd_transfer_stop(xfer); break; } @@ -1489,7 +1493,7 @@ if (usbd_clear_stall_callback(xfer, xfer_other)) { PRINTFN(4, ("sce=%p: stall cleared\n", sce)); - sce->write_stall = 1; + sce->write_stall = 0; usbd_transfer_start(xfer_other); } return; ==== //depot/projects/usb/src/sys/dev/usb/uhci.c#29 (text+ko) ==== @@ -128,6 +128,19 @@ ((sc)->sc_hw_page.physaddr + \ POINTER_TO_UNSIGNED(&(((struct uhci_hw_softc *)0)->what))) +struct uhci_mem_layout { + + struct usbd_page_search buf_res; + struct usbd_page_search fix_res; + + struct usbd_xfer *xfer; + + uint32_t buf_offset; + uint32_t fix_offset; + + uint8_t isread; +}; + extern struct usbd_bus_methods uhci_bus_methods; extern struct usbd_pipe_methods uhci_device_bulk_methods; extern struct usbd_pipe_methods uhci_device_ctrl_methods; @@ -140,6 +153,146 @@ static void uhci_root_ctrl_task_td(struct uhci_softc *sc, struct thread *ctd); static void uhci_do_poll(struct usbd_bus *bus); +static void +uhci_mem_layout_init(struct uhci_mem_layout *ml, struct usbd_xfer *xfer) +{ + ml->buf_offset = 0; + usbd_get_page(&(xfer->buf_data), ml->buf_offset, &(ml->buf_res)); + + ml->fix_offset = 0; + usbd_get_page(&(xfer->buf_fixup), ml->fix_offset, &(ml->fix_res)); + + ml->isread = 0; /* write */ + + ml->xfer = xfer; /* save a push */ + return; +} + +static void +uhci_mem_layout_fit(struct uhci_mem_layout *ml, struct uhci_td *td, uint16_t len) +{ + struct usbd_page_search tmp_res; + struct usbd_xfer *xfer = ml->xfer; + + if (ml->buf_res.length < len) { + + /* need to do a fixup */ + + if (ml->fix_res.length < len) { + + /* need to do a sub-fixup */ + + ml->fix_offset += ml->fix_res.length; + usbd_get_page(&(xfer->buf_fixup), ml->fix_offset, &(ml->fix_res)); + } + + td->td_buffer = htole32(ml->fix_res.physaddr); + + if (!ml->isread) { + + /* + * The UHCI driver cannot handle + * page crossings, so a fixup is + * needed: + * + * +----+----+ - - - + * | YYY|Y | + * +----+----+ - - - + * \ \ + * \ \ + * +----+ + * |YYYY| (fixup) + * +----+ + */ + + /* no copy back needed: */ + + td->fixup_dst_offset = 0xffffffff; + td->fixup_src_offset = 0xffffffff; + + usbd_get_page(&(xfer->buf_data), ml->buf_offset + + ml->buf_res.length, &tmp_res); + + /* copy first data part to fixup location */ + + usbd_page_dma_exit(ml->fix_res.page); + + usbd_page_dma_exit(ml->buf_res.page); + bcopy(ml->buf_res.buffer, ml->fix_res.buffer, ml->buf_res.length); + usbd_page_dma_enter(ml->buf_res.page); + + /* copy second data part to fixup location */ + + usbd_page_dma_exit(tmp_res.page); + bcopy(tmp_res.buffer, ADD_BYTES(ml->fix_res.buffer, + ml->buf_res.length), + len - ml->buf_res.length); + usbd_page_dma_enter(tmp_res.page); + + usbd_page_dma_enter(ml->fix_res.page); + + } else { + td->fixup_dst_offset = ml->buf_offset; + td->fixup_src_offset = ml->fix_offset; + } + + /* prepare next fixup */ + ml->fix_offset += xfer->max_frame_size; + usbd_get_page(&(xfer->buf_fixup), ml->fix_offset, &(ml->fix_res)); + + } else { + td->td_buffer = htole32(ml->buf_res.physaddr); + td->fixup_dst_offset = 0xffffffff; + td->fixup_src_offset = 0xffffffff; + } + + /* prepare next data location */ + ml->buf_offset += len; + usbd_get_page(&(xfer->buf_data), ml->buf_offset, &(ml->buf_res)); + + return; +} + +static void +uhci_mem_layout_do_fixup(struct usbd_xfer *xfer, struct uhci_td *td, uint16_t len) +{ + struct usbd_page_search buf_res; + struct usbd_page_search fix_res; + u_int16_t temp; + + usbd_get_page(&(xfer->buf_data), td->fixup_dst_offset, &buf_res); + usbd_get_page(&(xfer->buf_fixup), td->fixup_src_offset, &fix_res); + + temp = min(buf_res.length, len); + + usbd_page_dma_exit(fix_res.page); + + usbd_page_dma_exit(buf_res.page); + bcopy(fix_res.buffer, buf_res.buffer, temp); + usbd_page_dma_enter(buf_res.page); + + len -= temp; + + if (len) { + usbd_get_page(&(xfer->buf_data), + td->fixup_dst_offset + temp, &buf_res); + + if (len > buf_res.length) { + /* overflow protection - should not happen */ + len = buf_res.length; + } + + usbd_page_dma_exit(buf_res.page); + bcopy(ADD_BYTES(fix_res.buffer, temp), + buf_res.buffer, len); + usbd_page_dma_enter(buf_res.page); + } + + usbd_page_dma_enter(fix_res.page); + + return; +} + void uhci_reset(uhci_softc_t *sc) { @@ -618,7 +771,6 @@ struct uhci_hw_softc *hw_ptr = sc->sc_hw_ptr; uhci_dumpregs(sc); - printf("intrs=%d\n", sc->sc_bus.no_intrs); uhci_dump_qh(&(hw_ptr->ls_ctl_start)); uhci_dump_qh(&(hw_ptr->hs_ctl_start)); uhci_dump_qh(&(hw_ptr->bulk_start)); @@ -886,14 +1038,11 @@ static u_int8_t uhci_isoc_done(uhci_softc_t *sc, struct usbd_xfer *xfer) { - struct usbd_page_search buf_res; - struct usbd_page_search fix_res; u_int32_t nframes = xfer->nframes; u_int32_t actlen = 0; uint32_t status; u_int16_t *plen = xfer->frlengths; u_int16_t len = 0; - u_int16_t temp; u_int8_t need_delay = 0; uhci_td_t *td = xfer->td_transfer_first; uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos]; @@ -941,32 +1090,7 @@ actlen += len; if (td->fixup_src_offset != 0xffffffff) { - - usbd_get_page(&(xfer->buf_data), td->fixup_dst_offset, &buf_res); - usbd_get_page(&(xfer->buf_fixup), td->fixup_src_offset, &fix_res); - - temp = min(buf_res.length, len); - - usbd_page_dma_exit(fix_res.page); - - usbd_page_dma_exit(buf_res.page); - bcopy(fix_res.buffer, buf_res.buffer, temp); - usbd_page_dma_enter(buf_res.page); - - len -= temp; - - if (len) { - - usbd_get_page(&(xfer->buf_data), - td->fixup_dst_offset + temp, &buf_res); - - usbd_page_dma_exit(buf_res.page); - bcopy(ADD_BYTES(fix_res.buffer, temp), - buf_res.buffer, len); - usbd_page_dma_enter(buf_res.page); - } - - usbd_page_dma_enter(fix_res.page); + uhci_mem_layout_do_fixup(xfer, td, len); } /* remove TD from schedule */ @@ -986,7 +1110,8 @@ { uint32_t status = 0; uint32_t token = 0; - u_int32_t actlen = 0; + uint32_t actlen = 0; + uint16_t len; uhci_td_t *td = xfer->td_transfer_first; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", @@ -1013,7 +1138,12 @@ break; } - actlen += UHCI_TD_GET_ACTLEN(status); + len = UHCI_TD_GET_ACTLEN(status); + actlen += len; + + if (td->fixup_src_offset != 0xffffffff) { + uhci_mem_layout_do_fixup(xfer, td, len); + } if (((void *)td) == xfer->td_transfer_last) { td = NULL; @@ -1202,8 +1332,6 @@ td = curthread; /* NULL is not a valid thread */ - sc->sc_bus.no_intrs++; - DPRINTFN(15,("%s: real interrupt\n", device_get_nameunit(sc->sc_bus.bdev))); @@ -1384,11 +1512,10 @@ static uhci_td_t * >>> TRUNCATED FOR MAIL (1000 lines) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200707061604.l66G44ff021774>