From 8f9683e20cf8f33718519876320a1bb3fd101531 Mon Sep 17 00:00:00 2001 From: Nick Hibma Date: Thu, 10 Feb 2000 18:50:19 +0000 Subject: [PATCH] Correctly handle the conversion from virtual to physical addresses. The problem was basically (for offset > 4096): vtophys(addr) + offset != vtophys(addr + offset) Also, use TD's with a maximum size of 4k instead of 8kb for OHCI controllers. This problem occurs in drivers that use large transfer sizes: umass, host2host and ethernet with jumbo frames. --- sys/dev/usb/ohci.c | 80 ++++++++++++++++++++++++++++-------------- sys/dev/usb/ohcireg.h | 1 + sys/dev/usb/uhci.c | 24 ++++++------- sys/dev/usb/usb_mem.h | 10 +++--- sys/dev/usb/usb_port.h | 2 +- sys/dev/usb/usbdi.c | 8 ++--- 6 files changed, 77 insertions(+), 48 deletions(-) diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index 70fea2d0e6fa..c1449fc2a177 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -387,8 +387,8 @@ ohci_alloc_sed(sc) return (0); for(i = 0; i < OHCI_SED_CHUNK; i++) { offs = i * OHCI_SED_SIZE; - sed = (ohci_soft_ed_t *)((char *)KERNADDR(&dma) +offs); - sed->physaddr = DMAADDR(&dma) + offs; + sed = (ohci_soft_ed_t *)((char *)KERNADDR(&dma, offs)); + sed->physaddr = DMAADDR(&dma, offs); sed->next = sc->sc_freeeds; sc->sc_freeeds = sed; } @@ -426,8 +426,8 @@ ohci_alloc_std(sc) return (0); for(i = 0; i < OHCI_STD_CHUNK; i++) { offs = i * OHCI_STD_SIZE; - std = (ohci_soft_td_t *)((char *)KERNADDR(&dma) +offs); - std->physaddr = DMAADDR(&dma) + offs; + std = (ohci_soft_td_t *)((char *)KERNADDR(&dma, offs)); + std->physaddr = DMAADDR(&dma, offs); std->nexttd = sc->sc_freetds; sc->sc_freetds = std; } @@ -449,23 +449,25 @@ ohci_free_std(sc, std) } usbd_status -ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, sp, ep) +ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, std, rstd) struct ohci_pipe *opipe; ohci_softc_t *sc; int len, rd; u_int16_t flags; usb_dma_t *dma; - ohci_soft_td_t *sp, **ep; + ohci_soft_td_t *std, **rstd; { ohci_soft_td_t *next, *cur; ohci_physaddr_t dataphys, dataphysend; u_int32_t intr, tdflags; + int offset = 0; int curlen; DPRINTFN(len < 4096,("ohci_alloc_std_chain: start len=%d\n", len)); - cur = sp; - dataphys = DMAADDR(dma); - dataphysend = OHCI_PAGE(dataphys + len - 1); + + cur = std; + + dataphysend = DMAADDR(dma, len - 1); tdflags = (rd ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | @@ -476,15 +478,41 @@ ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, sp, ep) if (next == 0) goto nomem; + dataphys = DMAADDR(dma, offset); + /* The OHCI hardware can handle at most one page crossing. */ - if (OHCI_PAGE(dataphys) == dataphysend || - OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == dataphysend) { +#if defined(__NetBSD__) || defined(__OpenBSD__) + if (OHCI_PAGE(dataphys) == OHCI_PAGE(dataphysend) || + OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == OHCI_PAGE(dataphysend)) +#elif defined(__FreeBSD__) + /* XXX This is pretty broken: Because we do not allocate + * a contiguous buffer (contiguous in physical pages) we + * can only transfer one page in one go. + * So check whether the start and end of the buffer are on + * the same page. + */ + if (OHCI_PAGE(dataphys) == OHCI_PAGE(dataphysend)) +#endif + { /* we can handle it in this TD */ curlen = len; } else { + /* XXX The calculation below is wrong and could + * result in a packet that is not a multiple of the + * MaxPacketSize in the case where the buffer does not + * start on an appropriate address (like for example in + * the case of an mbuf cluster). You'll get an early + * short packet. + */ +#if defined(__NetBSD__) || defined(__OpenBSD__) /* must use multiple TDs, fill as much as possible. */ curlen = 2 * OHCI_PAGE_SIZE - - (dataphys & (OHCI_PAGE_SIZE-1)); + OHCI_PAGE_MASK(dataphys); +#elif defined(__FreeBSD__) + /* See comment above (XXX) */ + curlen = OHCI_PAGE_SIZE - + OHCI_PAGE_MASK(dataphys); +#endif } DPRINTFN(4,("ohci_alloc_std_chain: dataphys=0x%08x " "dataphysend=0x%08x len=%d curlen=%d\n", @@ -505,7 +533,7 @@ ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, sp, ep) if (len == 0) break; DPRINTFN(10,("ohci_alloc_std_chain: extend chain\n")); - dataphys += curlen; + offset += curlen; cur = next; } if ((flags & USBD_FORCE_SHORT_XFER) && @@ -526,7 +554,7 @@ ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, sp, ep) DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n")); } cur->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; - *ep = next; + *rstd = next; return (USBD_NORMAL_COMPLETION); @@ -568,8 +596,8 @@ ohci_alloc_sitd(sc) return (0); for(i = 0; i < OHCI_STD_CHUNK; i++) { offs = i * OHCI_STD_SIZE; - sitd = (ohci_soft_itd_t *)((char*)KERNADDR(&dma)+offs); - sitd->physaddr = DMAADDR(&dma) + offs; + sitd = (ohci_soft_itd_t *)((char*)KERNADDR(&dma, offs)); + sitd->physaddr = DMAADDR(&dma, offs); sitd->nextitd = sc->sc_freeitds; sc->sc_freeitds = sitd; } @@ -627,7 +655,7 @@ ohci_init(sc) OHCI_HCCA_ALIGN, &sc->sc_hccadma); if (err) return (err); - sc->sc_hcca = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma); + sc->sc_hcca = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma, 0); memset(sc->sc_hcca, 0, OHCI_HCCA_SIZE); sc->sc_eintrs = OHCI_NORMAL_INTRS; @@ -746,7 +774,7 @@ ohci_init(sc) /* The controller is now in SUSPEND state, we have 2ms to finish. */ /* Set up HC registers. */ - OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma)); + OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0)); OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr); OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr); /* disable all interrupts and then switch on all desired interrupts */ @@ -1238,7 +1266,7 @@ ohci_device_intr_done(xfer) OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (xfer->flags & USBD_SHORT_XFER_OK) data->td.td_flags |= LE(OHCI_TD_R); - data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf)); + data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf, 0)); data->nexttd = tail; data->td.td_nexttd = LE(tail->physaddr); data->td.td_be = LE(LE(data->td.td_cbp) + xfer->length - 1); @@ -1287,7 +1315,7 @@ ohci_rhsc(sc, xfer) pipe = xfer->pipe; opipe = (struct ohci_pipe *)pipe; - p = KERNADDR(&xfer->dmabuf); + p = KERNADDR(&xfer->dmabuf, 0); m = min(sc->sc_noport, xfer->length * 8 - 1); memset(p, 0, xfer->length); for (i = 1; i <= m; i++) { @@ -1414,7 +1442,7 @@ ohci_device_request(xfer) (isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_NOINTR | (xfer->flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0)); - data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf)); + data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf, 0)); data->nexttd = stat; data->td.td_nexttd = LE(stat->physaddr); data->td.td_be = LE(LE(data->td.td_cbp) + len - 1); @@ -1430,11 +1458,11 @@ ohci_device_request(xfer) stat->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; } - memcpy(KERNADDR(&opipe->u.ctl.reqdma), req, sizeof *req); + memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req); setup->td.td_flags = LE(OHCI_TD_SETUP | OHCI_TD_NOCC | OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); - setup->td.td_cbp = LE(DMAADDR(&opipe->u.ctl.reqdma)); + setup->td.td_cbp = LE(DMAADDR(&opipe->u.ctl.reqdma, 0)); setup->nexttd = next; setup->td.td_nexttd = LE(next->physaddr); setup->td.td_be = LE(LE(setup->td.td_cbp) + sizeof *req - 1); @@ -2013,7 +2041,7 @@ ohci_root_ctrl_start(xfer) index = UGETW(req->wIndex); if (len != 0) - buf = KERNADDR(&xfer->dmabuf); + buf = KERNADDR(&xfer->dmabuf, 0); #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { @@ -2616,7 +2644,7 @@ ohci_device_intr_start(xfer) OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (xfer->flags & USBD_SHORT_XFER_OK) data->td.td_flags |= LE(OHCI_TD_R); - data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf)); + data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf, 0)); data->nexttd = tail; data->td.td_nexttd = LE(tail->physaddr); data->td.td_be = LE(LE(data->td.td_cbp) + len - 1); @@ -2826,7 +2854,7 @@ ohci_device_isoc_enter(xfer) s = splusb(); sitd = opipe->tail.itd; - buf = DMAADDR(&xfer->dmabuf); + buf = DMAADDR(&xfer->dmabuf, 0); sitd->itd.itd_bp0 = LE(buf & OHCI_ITD_PAGE_MASK); nframes = xfer->nframes; offs = buf & OHCI_ITD_OFFSET_MASK; diff --git a/sys/dev/usb/ohcireg.h b/sys/dev/usb/ohcireg.h index 30bfb2c0ac8b..e61f050eed56 100644 --- a/sys/dev/usb/ohcireg.h +++ b/sys/dev/usb/ohcireg.h @@ -143,6 +143,7 @@ struct ohci_hcca { #define OHCI_PAGE_SIZE 0x1000 #define OHCI_PAGE(x) ((x) &~ 0xfff) +#define OHCI_PAGE_MASK(x) ((x) & 0xfff) typedef struct { u_int32_t ed_flags; diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c index efc86a231af3..4cd3a3dc44c2 100644 --- a/sys/dev/usb/uhci.c +++ b/sys/dev/usb/uhci.c @@ -368,9 +368,9 @@ uhci_init(sc) UHCI_FRAMELIST_ALIGN, &sc->sc_dma); if (err) return (err); - sc->sc_pframes = KERNADDR(&sc->sc_dma); + sc->sc_pframes = KERNADDR(&sc->sc_dma, 0); UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */ - UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma)); /* set frame list*/ + UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); /* set frame list*/ /* Allocate the dummy QH where bulk traffic will be queued. */ bsqh = uhci_alloc_sqh(sc); @@ -761,7 +761,7 @@ uhci_timo(addr) usb_timeout(uhci_timo, xfer, sc->sc_ival, xfer->timo_handle); - p = KERNADDR(&xfer->dmabuf); + p = KERNADDR(&xfer->dmabuf, 0); p[0] = 0; if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) p[0] |= 1<<1; @@ -1345,8 +1345,8 @@ uhci_alloc_std(sc) return (0); for(i = 0; i < UHCI_STD_CHUNK; i++) { offs = i * UHCI_STD_SIZE; - std = (uhci_soft_td_t *)((char *)KERNADDR(&dma) +offs); - std->physaddr = DMAADDR(&dma) + offs; + std = (uhci_soft_td_t *)((char *)KERNADDR(&dma, offs)); + std->physaddr = DMAADDR(&dma, offs); std->link.std = sc->sc_freetds; sc->sc_freetds = std; } @@ -1391,8 +1391,8 @@ uhci_alloc_sqh(sc) return (0); for(i = 0; i < UHCI_SQH_CHUNK; i++) { offs = i * UHCI_SQH_SIZE; - sqh = (uhci_soft_qh_t *)((char *)KERNADDR(&dma) +offs); - sqh->physaddr = DMAADDR(&dma) + offs; + sqh = (uhci_soft_qh_t *)((char *)KERNADDR(&dma, offs)); + sqh->physaddr = DMAADDR(&dma, offs); sqh->hlink = sc->sc_freeqhs; sc->sc_freeqhs = sqh; } @@ -1512,7 +1512,7 @@ uhci_alloc_std_chain(upipe, sc, len, rd, flags, dma, sp, ep) p->td.td_token = LE(rd ? UHCI_TD_IN (l, endpt, addr, tog) : UHCI_TD_OUT(l, endpt, addr, tog)); - p->td.td_buffer = LE(DMAADDR(dma) + i * maxp); + p->td.td_buffer = LE(DMAADDR(dma, i * maxp)); tog ^= 1; } *sp = lastp; @@ -1957,13 +1957,13 @@ uhci_device_request(xfer) } upipe->u.ctl.length = len; - memcpy(KERNADDR(&upipe->u.ctl.reqdma), req, sizeof *req); + memcpy(KERNADDR(&upipe->u.ctl.reqdma, 0), req, sizeof *req); setup->link.std = next; setup->td.td_link = LE(next->physaddr | UHCI_PTR_VF); setup->td.td_status = LE(UHCI_TD_SET_ERRCNT(3) | ls | UHCI_TD_ACTIVE); setup->td.td_token = LE(UHCI_TD_SETUP(sizeof *req, endpt, addr)); - setup->td.td_buffer = LE(DMAADDR(&upipe->u.ctl.reqdma)); + setup->td.td_buffer = LE(DMAADDR(&upipe->u.ctl.reqdma, 0)); stat->link.std = 0; stat->td.td_link = LE(UHCI_PTR_T); @@ -2101,7 +2101,7 @@ uhci_device_isoc_enter(xfer) xfer->status = USBD_IN_PROGRESS; xfer->hcprivint = next; - buf = DMAADDR(&xfer->dmabuf); + buf = DMAADDR(&xfer->dmabuf, 0); status = LE(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS)); @@ -2745,7 +2745,7 @@ uhci_root_ctrl_start(xfer) index = UGETW(req->wIndex); if (len != 0) - buf = KERNADDR(&xfer->dmabuf); + buf = KERNADDR(&xfer->dmabuf, 0); #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { diff --git a/sys/dev/usb/usb_mem.h b/sys/dev/usb/usb_mem.h index 2c12aab250db..ac19b84240f1 100644 --- a/sys/dev/usb/usb_mem.h +++ b/sys/dev/usb/usb_mem.h @@ -51,8 +51,8 @@ typedef struct usb_dma_block { LIST_ENTRY(usb_dma_block) next; } usb_dma_block_t; -#define DMAADDR(dma) ((dma)->block->segs[0].ds_addr + (dma)->offs) -#define KERNADDR(dma) ((void *)((dma)->block->kaddr + (dma)->offs)) +#define DMAADDR(dma, offset) ((dma)->block->segs[0].ds_addr + (dma)->offs + (offset)) +#define KERNADDR(dma, offset) ((void *)((dma)->block->kaddr + (dma)->offs) + (offset)) usbd_status usb_allocmem __P((usbd_bus_handle,size_t,size_t, usb_dma_t *)); void usb_freemem __P((usbd_bus_handle, usb_dma_t *)); @@ -80,10 +80,10 @@ void usb_freemem __P((usbd_bus_handle, usb_dma_t *)); #define usb_freemem(t,p) (free(*(p), M_USB)) #ifdef __alpha__ -#define DMAADDR(dma) (alpha_XXX_dmamap((vm_offset_t) *(dma))) +#define DMAADDR(dma, offset) (alpha_XXX_dmamap((vm_offset_t) *(dma) + (offset))) #else -#define DMAADDR(dma) (vtophys(*(dma))) +#define DMAADDR(dma, offset) (vtophys(*(dma) + (offset))) #endif -#define KERNADDR(dma) ((void *) *(dma)) +#define KERNADDR(dma, offset) ((void *) (*(dma) + (offset))) #endif diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h index 8ab7c31d3167..2e8633811c25 100644 --- a/sys/dev/usb/usb_port.h +++ b/sys/dev/usb/usb_port.h @@ -275,7 +275,7 @@ __CONCAT(dname,_detach)(self, flags) \ #define USBDEVPTRNAME(bdev) device_get_nameunit(bdev) #define USBDEVUNIT(bdev) device_get_unit(bdev) -#define DECLARE_USB_DMA_T typedef void * usb_dma_t +#define DECLARE_USB_DMA_T typedef char * usb_dma_t /* XXX Change this when FreeBSD has memset */ #define memcpy(d, s, l) bcopy((s),(d),(l)) diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c index 23063df61679..2949b231734a 100644 --- a/sys/dev/usb/usbdi.c +++ b/sys/dev/usb/usbdi.c @@ -282,7 +282,7 @@ usbd_transfer(xfer) /* Copy data if going out. */ if (!(xfer->flags & USBD_NO_COPY) && size != 0 && !usbd_xfer_isread(xfer)) - memcpy(KERNADDR(dmap), xfer->buffer, size); + memcpy(KERNADDR(dmap, 0), xfer->buffer, size); err = pipe->methods->transfer(xfer); @@ -348,7 +348,7 @@ usbd_alloc_buffer(xfer, size) if (err) return (0); xfer->rqflags |= URQ_DEV_DMABUF; - return (KERNADDR(&xfer->dmabuf)); + return (KERNADDR(&xfer->dmabuf, 0)); } void @@ -371,7 +371,7 @@ usbd_get_buffer(xfer) { if (!(xfer->rqflags & URQ_DEV_DMABUF)) return (0); - return (KERNADDR(&xfer->dmabuf)); + return (KERNADDR(&xfer->dmabuf, 0)); } usbd_xfer_handle @@ -800,7 +800,7 @@ usb_transfer_complete(xfer) xfer->actlen = xfer->length; } #endif - memcpy(xfer->buffer, KERNADDR(dmap), xfer->actlen); + memcpy(xfer->buffer, KERNADDR(dmap, 0), xfer->actlen); } /* if we allocated the buffer in usbd_transfer() we free it here. */