Add special memory allocation routines that supports allocation

DMA-able memory in small chunks (USB uses a lot of 8 byte chunks).
Using the bus_dma functions directly is inefficient.
This commit is contained in:
augustss 1998-07-24 21:09:07 +00:00
parent 91f18e727f
commit b916de0f86
6 changed files with 422 additions and 259 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ohci.c,v 1.3 1998/07/23 13:41:04 augustss Exp $ */
/* $NetBSD: ohci.c,v 1.4 1998/07/24 21:09:07 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -52,14 +52,13 @@
#include <sys/queue.h>
#include <sys/select.h>
#include <dev/usb/usb.h>
#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_quirks.h>
#include <machine/bus.h>
#include <dev/usb/usb_mem.h>
#include <dev/usb/ohcireg.h>
#include <dev/usb/ohcivar.h>
@ -82,9 +81,6 @@ void ohci_ctrl_done __P((ohci_softc_t *, usbd_request_handle));
void ohci_intr_done __P((ohci_softc_t *, usbd_request_handle));
void ohci_bulk_done __P((ohci_softc_t *, usbd_request_handle));
usbd_status ohci_allocmem __P((ohci_softc_t *,size_t,size_t, ohci_dma_t*));
void ohci_freemem __P((ohci_softc_t *, ohci_dma_t *));
usbd_status ohci_device_request __P((usbd_request_handle reqh));
void ohci_add_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *));
void ohci_rem_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *));
@ -146,20 +142,20 @@ struct ohci_pipe {
union {
/* Control pipe */
struct {
ohci_dma_t datadma;
ohci_dma_t reqdma;
usb_dma_t datadma;
usb_dma_t reqdma;
u_int length;
ohci_soft_td_t *setup, *xfer, *stat;
} ctl;
/* Interrupt pipe */
struct {
ohci_dma_t datadma;
usb_dma_t datadma;
int nslots;
int pos;
} intr;
/* Bulk pipe */
struct {
ohci_dma_t datadma;
usb_dma_t datadma;
u_int length;
} bulk;
} u;
@ -197,59 +193,6 @@ struct usbd_methods ohci_device_bulk_methods = {
ohci_device_bulk_close,
};
usbd_status
ohci_allocmem(sc, size, align, p)
ohci_softc_t *sc;
size_t size;
size_t align;
ohci_dma_t *p;
{
int error;
DPRINTFN(5, ("ohci_allocmem: size=%d align=%d\n", size, align));
p->size = size;
error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
&p->nsegs, BUS_DMA_NOWAIT);
if (error)
return (USBD_NOMEM);
error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
&p->kaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
if (error)
goto free;
error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
0, BUS_DMA_NOWAIT, &p->map);
if (error)
goto unmap;
error = bus_dmamap_load(sc->sc_dmatag, p->map, p->kaddr,p->size, NULL,
BUS_DMA_NOWAIT);
if (error)
goto destroy;
return 0;
destroy:
bus_dmamap_destroy(sc->sc_dmatag, p->map);
unmap:
bus_dmamem_unmap(sc->sc_dmatag, p->kaddr, p->size);
free:
bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
return (USBD_NOMEM);
}
void
ohci_freemem(sc, p)
ohci_softc_t *sc;
ohci_dma_t *p;
{
bus_dmamap_unload(sc->sc_dmatag, p->map);
bus_dmamap_destroy(sc->sc_dmatag, p->map);
bus_dmamem_unmap(sc->sc_dmatag, p->kaddr, p->size);
bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
}
ohci_soft_ed_t *
ohci_alloc_sed(sc)
ohci_softc_t *sc;
@ -257,7 +200,7 @@ ohci_alloc_sed(sc)
ohci_soft_ed_t *sed;
usbd_status r;
int i, offs;
ohci_dma_t dma;
usb_dma_t dma;
if (!sc->sc_freeeds) {
DPRINTFN(2, ("ohci_alloc_sed: allocating chunk\n"));
@ -265,8 +208,8 @@ ohci_alloc_sed(sc)
M_USBDEV, M_NOWAIT);
if (!sed)
return 0;
r = ohci_allocmem(sc, OHCI_ED_SIZE * OHCI_ED_CHUNK,
OHCI_ED_ALIGN, &dma);
r = usb_allocmem(sc->sc_dmatag, OHCI_ED_SIZE * OHCI_ED_CHUNK,
OHCI_ED_ALIGN, &dma);
if (r != USBD_NORMAL_COMPLETION) {
free(sed, M_USBDEV);
return 0;
@ -303,7 +246,7 @@ ohci_alloc_std(sc)
ohci_soft_td_t *std;
usbd_status r;
int i, offs;
ohci_dma_t dma;
usb_dma_t dma;
if (!sc->sc_freetds) {
DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n"));
@ -311,8 +254,8 @@ ohci_alloc_std(sc)
M_USBDEV, M_NOWAIT);
if (!std)
return 0;
r = ohci_allocmem(sc, OHCI_TD_SIZE * OHCI_TD_CHUNK,
OHCI_TD_ALIGN, &dma);
r = usb_allocmem(sc->sc_dmatag, OHCI_TD_SIZE * OHCI_TD_CHUNK,
OHCI_TD_ALIGN, &dma);
if (r != USBD_NORMAL_COMPLETION) {
free(std, M_USBDEV);
return 0;
@ -367,7 +310,8 @@ ohci_init(sc)
LIST_INIT(&sc->sc_hash_tds[i]);
/* Allocate the HCCA area. */
r = ohci_allocmem(sc, OHCI_HCCA_SIZE, OHCI_HCCA_ALIGN,&sc->sc_hccadma);
r = usb_allocmem(sc->sc_dmatag, OHCI_HCCA_SIZE,
OHCI_HCCA_ALIGN, &sc->sc_hccadma);
if (r != USBD_NORMAL_COMPLETION)
return (r);
sc->sc_hcca = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma);
@ -516,7 +460,7 @@ ohci_init(sc)
bad2:
ohci_free_sed(sc, sc->sc_bulk_head);
bad1:
ohci_freemem(sc, &sc->sc_hccadma);
usb_freemem(sc->sc_dmatag, &sc->sc_hccadma);
return (r);
}
@ -736,7 +680,7 @@ ohci_ctrl_done(sc, reqh)
{
struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe;
u_int len = opipe->u.ctl.length;
ohci_dma_t *dma;
usb_dma_t *dma;
DPRINTFN(10,("ohci_ctrl_done: reqh=%p\n", reqh));
@ -749,7 +693,7 @@ ohci_ctrl_done(sc, reqh)
dma = &opipe->u.ctl.datadma;
if (reqh->request.bmRequestType & UT_READ)
memcpy(reqh->buffer, KERNADDR(dma), len);
ohci_freemem(sc, dma);
usb_freemem(sc->sc_dmatag, dma);
}
}
@ -759,7 +703,7 @@ ohci_intr_done(sc, reqh)
usbd_request_handle reqh;
{
struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe;
ohci_dma_t *dma;
usb_dma_t *dma;
ohci_soft_ed_t *sed = opipe->sed;
ohci_soft_td_t *xfer, *tail;
@ -795,7 +739,7 @@ ohci_intr_done(sc, reqh)
sed->ed->ed_tailp = tail->physaddr;
opipe->tail = tail;
} else {
ohci_freemem(sc, dma);
usb_freemem(sc->sc_dmatag, dma);
}
}
@ -805,7 +749,7 @@ ohci_bulk_done(sc, reqh)
usbd_request_handle reqh;
{
struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe;
ohci_dma_t *dma;
usb_dma_t *dma;
DPRINTFN(10,("ohci_bulk_done: reqh=%p, actlen=%d\n",
@ -814,7 +758,7 @@ ohci_bulk_done(sc, reqh)
dma = &opipe->u.bulk.datadma;
if (reqh->request.bmRequestType & UT_READ)
memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen);
ohci_freemem(sc, dma);
usb_freemem(sc->sc_dmatag, dma);
}
void
@ -854,7 +798,7 @@ ohci_rhsc(sc, reqh)
if (reqh->pipe->intrreqh != reqh) {
sc->sc_intrreqh = 0;
ohci_freemem(sc, &opipe->u.intr.datadma);
usb_freemem(sc->sc_dmatag, &opipe->u.intr.datadma);
}
}
@ -903,7 +847,7 @@ ohci_device_request(reqh)
int addr = dev->address;
ohci_soft_td_t *setup, *xfer = 0, *stat, *next, *tail;
ohci_soft_ed_t *sed;
ohci_dma_t *dmap;
usb_dma_t *dmap;
int isread;
int len;
usbd_status r;
@ -946,7 +890,7 @@ ohci_device_request(reqh)
r = USBD_NOMEM;
goto bad3;
}
r = ohci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
goto bad4;
xfer->td->td_flags =
@ -1218,8 +1162,9 @@ ohci_open(pipe)
switch (ed->bmAttributes & UE_XFERTYPE) {
case UE_CONTROL:
pipe->methods = &ohci_device_ctrl_methods;
r = ohci_allocmem(sc, sizeof(ohci_dma_t), 0,
&opipe->u.ctl.reqdma);
r = usb_allocmem(sc->sc_dmatag,
sizeof(usb_device_request_t),
0, &opipe->u.ctl.reqdma);
if (r != USBD_NORMAL_COMPLETION)
goto bad;
s = splusb();
@ -1654,7 +1599,7 @@ ohci_root_intr_transfer(reqh)
usbd_pipe_handle pipe = reqh->pipe;
ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
struct ohci_pipe *upipe = (struct ohci_pipe *)pipe;
ohci_dma_t *dmap;
usb_dma_t *dmap;
usbd_status r;
int len;
@ -1663,7 +1608,7 @@ ohci_root_intr_transfer(reqh)
if (len == 0)
return (USBD_INVAL); /* XXX should it be? */
r = ohci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
return (r);
sc->sc_intrreqh = reqh;
@ -1756,7 +1701,7 @@ ohci_device_bulk_transfer(reqh)
int addr = dev->address;
ohci_soft_td_t *xfer, *tail;
ohci_soft_ed_t *sed;
ohci_dma_t *dmap;
usb_dma_t *dmap;
usbd_status r;
int s, len, isread;
@ -1773,7 +1718,7 @@ ohci_device_bulk_transfer(reqh)
opipe->u.bulk.length = len;
r = ohci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
goto ret1;
@ -1820,7 +1765,7 @@ ohci_device_bulk_transfer(reqh)
return (USBD_IN_PROGRESS);
ret2:
ohci_freemem(sc, dmap);
usb_freemem(sc->sc_dmatag, dmap);
ret1:
return (r);
}
@ -1869,7 +1814,7 @@ ohci_device_intr_transfer(reqh)
ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
ohci_soft_ed_t *sed = opipe->sed;
ohci_soft_td_t *xfer, *tail;
ohci_dma_t *dmap;
usb_dma_t *dmap;
usbd_status r;
int len;
int s;
@ -1893,7 +1838,7 @@ ohci_device_intr_transfer(reqh)
}
tail->reqh = 0;
r = ohci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
goto ret2;

View File

@ -1,4 +1,4 @@
/* $NetBSD: ohcivar.h,v 1.1 1998/07/12 19:51:59 augustss Exp $ */
/* $NetBSD: ohcivar.h,v 1.2 1998/07/24 21:09:07 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -36,17 +36,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
typedef struct ohci_dma {
bus_dmamap_t map;
caddr_t kaddr;
bus_dma_segment_t segs[1];
int nsegs;
size_t size;
struct ohci_dma *next;
} ohci_dma_t;
#define DMAADDR(dma) ((dma)->segs[0].ds_addr)
#define KERNADDR(dma) ((void *)((dma)->kaddr))
typedef struct ohci_soft_ed {
ohci_ed_t *ed;
struct ohci_soft_ed *next;
@ -79,7 +68,7 @@ typedef struct ohci_softc {
bus_dma_tag_t sc_dmatag; /* DMA tag */
/* XXX should keep track of all DMA memory */
ohci_dma_t sc_hccadma;
usb_dma_t sc_hccadma;
struct ohci_hcca *sc_hcca;
ohci_soft_ed_t *sc_eds[OHCI_NO_EDS];
u_int sc_bws[OHCI_NO_INTRS];

View File

@ -1,4 +1,4 @@
/* $NetBSD: uhci.c,v 1.6 1998/07/23 13:44:21 augustss Exp $ */
/* $NetBSD: uhci.c,v 1.7 1998/07/24 21:09:07 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -55,15 +55,14 @@
#include <sys/queue.h>
#include <sys/select.h>
#include <dev/usb/usb.h>
#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>
#include <dev/usb/usb_quirks.h>
#include <machine/bus.h>
#include <dev/usb/uhcireg.h>
#include <dev/usb/uhcivar.h>
@ -78,21 +77,21 @@ struct uhci_pipe {
/* Control pipe */
struct {
uhci_soft_qh_t *sqh;
uhci_dma_t reqdma;
uhci_dma_t datadma;
usb_dma_t reqdma;
usb_dma_t datadma;
uhci_soft_td_t *setup, *stat, *xferend;
u_int length;
} ctl;
/* Interrupt pipe */
struct {
uhci_dma_t datadma;
usb_dma_t datadma;
int npoll;
uhci_soft_qh_t **qhs;
} intr;
/* Bulk pipe */
struct {
uhci_soft_qh_t *sqh;
uhci_dma_t datadma;
usb_dma_t datadma;
u_int length;
int isread;
} bulk;
@ -107,8 +106,6 @@ int uhci_global_init_done = 0;
LIST_HEAD(, uhci_intr_info) uhci_ii_free;
void uhci_busreset __P((uhci_softc_t *));
usbd_status uhci_allocmem __P((uhci_softc_t *,size_t,size_t,uhci_dma_t *));
void uhci_freemem __P((uhci_softc_t *, uhci_dma_t *));
void uhci_run __P((uhci_softc_t *, int run));
uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *));
void uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *));
@ -123,7 +120,7 @@ void uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *));
void uhci_free_std_chain __P((uhci_softc_t *,
uhci_soft_td_t *, uhci_soft_td_t *));
usbd_status uhci_alloc_std_chain __P((struct uhci_pipe *, uhci_softc_t *,
int, int, uhci_dma_t *,
int, int, usb_dma_t *,
uhci_soft_td_t **,
uhci_soft_td_t **));
void uhci_timo __P((void *));
@ -244,7 +241,7 @@ uhci_init(sc)
int i, j;
uhci_soft_qh_t *csqh, *bsqh, *sqh;
uhci_soft_td_t *std;
uhci_dma_t dma;
usb_dma_t dma;
DPRINTFN(1,("uhci_init: start\n"));
@ -262,9 +259,9 @@ uhci_init(sc)
UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
/* Allocate and initialize real frame array. */
r = uhci_allocmem(sc, UHCI_FRAMELIST_COUNT *
sizeof(uhci_physaddr_t),
UHCI_FRAMELIST_ALIGN, &dma);
r = usb_allocmem(sc->sc_dmatag,
UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t),
UHCI_FRAMELIST_ALIGN, &dma);
if (r != USBD_NORMAL_COMPLETION)
return (r);
sc->sc_pframes = KERNADDR(&dma);
@ -443,7 +440,7 @@ uhci_timo(addr)
if (reqh->pipe->intrreqh == reqh) {
timeout(uhci_timo, addr, sc->sc_ival);
} else {
uhci_freemem(sc, &upipe->u.intr.datadma);
usb_freemem(sc->sc_dmatag, &upipe->u.intr.datadma);
}
}
@ -696,13 +693,26 @@ uhci_ii_done(ii, timo)
u_int32_t tst;
int len, status;
DPRINTFN(10, ("uhci_check_intr: ii=%p ready\n", ii));
DPRINTFN(10, ("uhci_ii_done: ii=%p ready %d\n", ii, timo));
#ifdef DIAGNOSTIC
{
int s = splhigh();
if (ii->isdone) {
printf("uhci_ii_done: is done!\n");
splx(s);
return;
}
ii->isdone = 1;
splx(s);
}
#endif
/* The transfer is done, compute length and status. */
for (len = status = 0, std = ii->stdstart;
std != 0;
std = std->td->link.std) {
tst = std->td->td_status;;
tst = std->td->td_status;
status |= tst;
#ifdef USB_DEBUG
if ((tst & UHCI_TD_ERROR) && uhcidebug) {
@ -743,6 +753,7 @@ uhci_ii_done(ii, timo)
break;
case UE_ISOCHRONOUS:
printf("uhci_ii_done: ISO??\n");
break;
case UE_BULK:
uhci_bulk_done(ii);
break;
@ -842,69 +853,14 @@ uhci_run(sc, run)
/*
* Memory management routines.
* uhci_allocmem allocates DMAable memory.
* uhci_alloc_std allocates TDs
* uhci_alloc_sqh allocates QHs
* uhci_alloc_buffer allocates transfer buffers.
* The latter three routines do their own free list management,
* These two routines do their own free list management,
* partly for speed, partly because allocating DMAable memory
* has page size granularaity so much memory would be wasted if
* only one TD/QH (32 bytes) was placed in each alloacted chunk.
*/
usbd_status
uhci_allocmem(sc, size, align, p)
uhci_softc_t *sc;
size_t size;
size_t align;
uhci_dma_t *p;
{
int error;
DPRINTFN(5, ("uhci_allocmem: size=%d align=%d\n", size, align));
p->size = size;
error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
&p->nsegs, BUS_DMA_NOWAIT);
if (error)
return (USBD_NOMEM);
error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
&p->kaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
if (error)
goto free;
error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
0, BUS_DMA_NOWAIT, &p->map);
if (error)
goto unmap;
error = bus_dmamap_load(sc->sc_dmatag, p->map, p->kaddr,p->size, NULL,
BUS_DMA_NOWAIT);
if (error)
goto destroy;
return 0;
destroy:
bus_dmamap_destroy(sc->sc_dmatag, p->map);
unmap:
bus_dmamem_unmap(sc->sc_dmatag, p->kaddr, p->size);
free:
bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
return (USBD_NOMEM);
}
void
uhci_freemem(sc, p)
uhci_softc_t *sc;
uhci_dma_t *p;
{
bus_dmamap_unload(sc->sc_dmatag, p->map);
bus_dmamap_destroy(sc->sc_dmatag, p->map);
bus_dmamem_unmap(sc->sc_dmatag, p->kaddr, p->size);
bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
}
uhci_soft_td_t *
uhci_alloc_std(sc)
uhci_softc_t *sc;
@ -912,7 +868,7 @@ uhci_alloc_std(sc)
uhci_soft_td_t *std;
usbd_status r;
int i;
uhci_dma_t dma;
usb_dma_t dma;
if (!sc->sc_freetds) {
DPRINTFN(2,("uhci_alloc_std: allocating chunk\n"));
@ -920,8 +876,8 @@ uhci_alloc_std(sc)
M_USBDEV, M_NOWAIT);
if (!std)
return 0;
r = uhci_allocmem(sc, UHCI_TD_SIZE * UHCI_TD_CHUNK,
UHCI_TD_ALIGN, &dma);
r = usb_allocmem(sc->sc_dmatag, UHCI_TD_SIZE * UHCI_TD_CHUNK,
UHCI_TD_ALIGN, &dma);
if (r != USBD_NORMAL_COMPLETION) {
free(std, M_USBDEV);
return 0;
@ -946,6 +902,14 @@ uhci_free_std(sc, std)
uhci_softc_t *sc;
uhci_soft_td_t *std;
{
#ifdef DIAGNOSTIC
#define TD_IS_FREE 0x12345678
if (std->td->td_token == TD_IS_FREE) {
printf("uhci_free_std: freeing free TD %p\n", std);
return;
}
std->td->td_token = TD_IS_FREE;
#endif
std->td->link.std = sc->sc_freetds;
sc->sc_freetds = std;
}
@ -957,7 +921,7 @@ uhci_alloc_sqh(sc)
uhci_soft_qh_t *sqh;
usbd_status r;
int i, offs;
uhci_dma_t dma;
usb_dma_t dma;
if (!sc->sc_freeqhs) {
DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n"));
@ -965,8 +929,8 @@ uhci_alloc_sqh(sc)
M_USBDEV, M_NOWAIT);
if (!sqh)
return 0;
r = uhci_allocmem(sc, UHCI_QH_SIZE * UHCI_QH_CHUNK,
UHCI_QH_ALIGN, &dma);
r = usb_allocmem(sc->sc_dmatag, UHCI_QH_SIZE * UHCI_QH_CHUNK,
UHCI_QH_ALIGN, &dma);
if (r != USBD_NORMAL_COMPLETION) {
free(sqh, M_USBDEV);
return 0;
@ -995,47 +959,6 @@ uhci_free_sqh(sc, sqh)
sc->sc_freeqhs = sqh;
}
#if 0
void *
uhci_alloc_buffer(sc, size)
uhci_softc_t *sc;
size_t size;
{
struct uhci_buffer *buf;
usbd_status r;
int i, offs;
uhci_dma_t dma;
if (!sc->sc_freebuffers) {
DPRINTFN(2, ("uhci_alloc_buffer: allocating chunk\n"));
r = uhci_allocmem(sc, UHCI_BUFFER_SIZE * BUFFER_CHUNK,
0, &dma);
if (r != USBD_NORMAL_COMPLETION)
return 0;
for(i = 0; i < BUFFER_CHUNK; i++, sqh++) {
offs = i * UHCI_BUFFER_SIZE;
sqh->physaddr = DMAADDR(&dma) + offs;
sqh->qh = (uhci_qh_t *)
((char *)KERNADDR(&dma) + offs);
sqh->qh->hlink = sc->sc_freeqhs;
sc->sc_freeqhs = sqh;
}
}
sqh = sc->sc_freeqhs;
sc->sc_freeqhs = sqh->qh->hlink;
memset(sqh->qh, 0, UHCI_QH_SIZE);
return sqh;
}
void
uhci_free_buffer(sc, p, size)
uhci_softc_t *sc;
void *p;
size_t size;
{
}
#endif
/*
* Enter a list of transfers onto a control queue.
* Called at splusb()
@ -1069,7 +992,7 @@ uhci_alloc_std_chain(upipe, sc, len, rd, dma, sp, ep)
struct uhci_pipe *upipe;
uhci_softc_t *sc;
int len, rd;
uhci_dma_t *dma;
usb_dma_t *dma;
uhci_soft_td_t **sp, **ep;
{
uhci_soft_td_t *p, *lastp;
@ -1140,7 +1063,7 @@ uhci_device_bulk_transfer(reqh)
uhci_intr_info_t *ii = upipe->iinfo;
uhci_soft_td_t *xfer, *xferend;
uhci_soft_qh_t *sqh;
uhci_dma_t *dmap;
usb_dma_t *dmap;
usbd_status r;
int len, isread;
int s;
@ -1159,7 +1082,7 @@ uhci_device_bulk_transfer(reqh)
upipe->u.bulk.isread = isread;
upipe->u.bulk.length = len;
r = uhci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
goto ret1;
r = uhci_alloc_std_chain(upipe, sc, len, isread,
@ -1182,6 +1105,9 @@ uhci_device_bulk_transfer(reqh)
ii->reqh = reqh;
ii->stdstart = xfer;
ii->stdend = xferend;
#ifdef DIAGNOSTIC
ii->isdone = 0;
#endif
sqh->qh->elink = xfer;
sqh->qh->qh_elink = xfer->physaddr;
@ -1206,7 +1132,7 @@ uhci_device_bulk_transfer(reqh)
ret2:
if (len != 0)
uhci_freemem(sc, dmap);
usb_freemem(sc->sc_dmatag, dmap);
ret1:
return (r);
}
@ -1263,7 +1189,7 @@ uhci_device_intr_transfer(reqh)
uhci_intr_info_t *ii = upipe->iinfo;
uhci_soft_td_t *xfer, *xferend;
uhci_soft_qh_t *sqh;
uhci_dma_t *dmap;
usb_dma_t *dmap;
usbd_status r;
int len, i;
int s;
@ -1279,7 +1205,7 @@ uhci_device_intr_transfer(reqh)
if (len == 0)
return (USBD_INVAL); /* XXX should it be? */
r = uhci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
goto ret1;
r = uhci_alloc_std_chain(upipe, sc, len, 1, dmap, &xfer, &xferend);
@ -1300,6 +1226,9 @@ uhci_device_intr_transfer(reqh)
ii->reqh = reqh;
ii->stdstart = xfer;
ii->stdend = xferend;
#ifdef DIAGNOSTIC
ii->isdone = 0;
#endif
DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", upipe->u.intr.qhs[0]));
for (i = 0; i < upipe->u.intr.npoll; i++) {
@ -1321,7 +1250,7 @@ DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", upipe->u.intr.qhs[0]));
ret2:
if (len != 0)
uhci_freemem(sc, dmap);
usb_freemem(sc->sc_dmatag, dmap);
ret1:
return (r);
}
@ -1354,6 +1283,7 @@ uhci_device_intr_abort(reqh)
{
struct uhci_pipe *upipe;
DPRINTFN(1, ("uhci_device_intr_abort: reqh=%p\n", reqh));
/* XXX inactivate */
usbd_delay_ms(2); /* make sure it is finished */
if (reqh->pipe->intrreqh == reqh) {
@ -1414,7 +1344,7 @@ uhci_device_request(reqh)
uhci_intr_info_t *ii = upipe->iinfo;
uhci_soft_td_t *setup, *xfer, *stat, *next, *xferend;
uhci_soft_qh_t *sqh;
uhci_dma_t *dmap;
usb_dma_t *dmap;
int len;
u_int32_t ls;
usbd_status r;
@ -1437,7 +1367,7 @@ uhci_device_request(reqh)
/* Set up data transaction */
if (len != 0) {
r = uhci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
goto ret1;
upipe->pipe.endpoint->toggle = 1;
@ -1487,6 +1417,9 @@ uhci_device_request(reqh)
ii->reqh = reqh;
ii->stdstart = setup;
ii->stdend = stat;
#ifdef DIAGNOSTIC
ii->isdone = 0;
#endif
sqh->qh->elink = setup;
sqh->qh->qh_elink = setup->physaddr;
@ -1524,7 +1457,7 @@ uhci_device_request(reqh)
ret2:
if (len != 0)
uhci_freemem(sc, dmap);
usb_freemem(sc->sc_dmatag, dmap);
ret1:
return (r);
}
@ -1536,7 +1469,7 @@ uhci_intr_done(ii)
uhci_softc_t *sc = ii->sc;
usbd_request_handle reqh = ii->reqh;
struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
uhci_dma_t *dma;
usb_dma_t *dma;
uhci_soft_qh_t *sqh;
int i, npoll;
@ -1571,13 +1504,16 @@ uhci_intr_done(ii)
ii->stdstart = xfer;
ii->stdend = xferend;
#ifdef DIAGNOSTIC
ii->isdone = 0;
#endif
for (i = 0; i < npoll; i++) {
sqh = upipe->u.intr.qhs[i];
sqh->qh->elink = xfer;
sqh->qh->qh_elink = xfer->physaddr;
}
} else {
uhci_freemem(sc, dma);
usb_freemem(sc->sc_dmatag, dma);
ii->stdstart = 0; /* mark as inactive */
}
}
@ -1591,11 +1527,13 @@ uhci_ctrl_done(ii)
usbd_request_handle reqh = ii->reqh;
struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
u_int len = upipe->u.ctl.length;
uhci_dma_t *dma;
usb_dma_t *dma;
uhci_td_t *htd = ii->stdstart->td;
#ifdef DIAGNOSTIC
if (!reqh->isreq)
panic("uhci_ctrl_done: not a request\n");
#endif
LIST_REMOVE(ii, list); /* remove from active list */
@ -1606,7 +1544,7 @@ uhci_ctrl_done(ii)
if (reqh->request.bmRequestType & UT_READ)
memcpy(reqh->buffer, KERNADDR(dma), len);
uhci_free_std_chain(sc, htd->link.std, ii->stdend);
uhci_freemem(sc, dma);
usb_freemem(sc->sc_dmatag, dma);
}
DPRINTFN(5, ("uhci_ctrl_done: length=%d\n", reqh->actlen));
}
@ -1620,7 +1558,7 @@ uhci_bulk_done(ii)
usbd_request_handle reqh = ii->reqh;
struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
u_int len = upipe->u.bulk.length;
uhci_dma_t *dma;
usb_dma_t *dma;
uhci_td_t *htd = ii->stdstart->td;
LIST_REMOVE(ii, list); /* remove from active list */
@ -1632,7 +1570,7 @@ uhci_bulk_done(ii)
if (upipe->u.bulk.isread && len != 0)
memcpy(reqh->buffer, KERNADDR(dma), len);
uhci_free_std_chain(sc, htd->link.std, 0);
uhci_freemem(sc, dma);
usb_freemem(sc->sc_dmatag, dma);
}
DPRINTFN(4, ("uhci_bulk_done: length=%d\n", reqh->actlen));
/* XXX compute new toggle */
@ -1796,8 +1734,9 @@ uhci_open(pipe)
uhci_free_std(sc, upipe->u.ctl.setup);
goto bad;
}
r = uhci_allocmem(sc, sizeof(uhci_dma_t), 0,
&upipe->u.ctl.reqdma);
r = usb_allocmem(sc->sc_dmatag,
sizeof(usb_device_request_t),
0, &upipe->u.ctl.reqdma);
if (r != USBD_NORMAL_COMPLETION) {
uhci_free_sqh(sc, upipe->u.ctl.sqh);
uhci_free_std(sc, upipe->u.ctl.setup);
@ -2269,7 +2208,7 @@ uhci_root_intr_transfer(reqh)
usbd_pipe_handle pipe = reqh->pipe;
uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
uhci_dma_t *dmap;
usb_dma_t *dmap;
usbd_status r;
int len;
@ -2281,7 +2220,7 @@ uhci_root_intr_transfer(reqh)
if (len == 0)
return (USBD_INVAL); /* XXX should it be? */
r = uhci_allocmem(sc, len, 0, dmap);
r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
if (r != USBD_NORMAL_COMPLETION)
return (r);

View File

@ -1,4 +1,4 @@
/* $NetBSD: uhcivar.h,v 1.1 1998/07/12 19:51:59 augustss Exp $ */
/* $NetBSD: uhcivar.h,v 1.2 1998/07/24 21:09:08 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -54,17 +54,6 @@
typedef struct uhci_soft_qh uhci_soft_qh_t;
typedef struct uhci_soft_td uhci_soft_td_t;
typedef struct uhci_dma {
bus_dmamap_t map;
caddr_t kaddr;
bus_dma_segment_t segs[1];
int nsegs;
size_t size;
struct uhci_dma *next;
} uhci_dma_t;
#define DMAADDR(dma) ((dma)->segs[0].ds_addr)
#define KERNADDR(dma) ((void *)((dma)->kaddr))
/*
* An interrupt info struct contains the information needed to
* execute a requested routine when the controller generates an
@ -78,6 +67,9 @@ typedef struct uhci_intr_info {
uhci_soft_td_t *stdstart;
uhci_soft_td_t *stdend;
LIST_ENTRY(uhci_intr_info) list;
#ifdef DIAGNOSTIC
int isdone;
#endif
} uhci_intr_info_t;
/*
@ -158,7 +150,7 @@ typedef struct uhci_softc {
#define UHCI_HAS_LOCK 1
#define UHCI_WANT_LOCK 2
uhci_dma_t *sc_mallocs;
usb_dma_t *sc_mallocs;
} uhci_softc_t;
usbd_status uhci_init __P((uhci_softc_t *));

238
sys/dev/usb/usb_mem.c Normal file
View File

@ -0,0 +1,238 @@
/* $NetBSD: usb_mem.c,v 1.1 1998/07/24 21:09:08 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Author: Lennart Augustsson <augustss@carlstedt.se>
* Carlstedt Research & Technology
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* USB DMA memory allocation.
* We need to allocate a lot of small (many 8 byte, some larger)
* memory blocks that can be used for DMA. Using the bus_dma
* routines directly would uncur large overheads in space and time.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_mem.h>
#ifdef USB_DEBUG
#define DPRINTF(x) if (usbdebug) printf x
#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
extern int usbdebug;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define USB_MEM_SMALL 64
#define USB_MEM_CHUNKS 64
#define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS)
/* This struct is overlayed on free fragments. */
struct usb_frag_dma {
usb_dma_block_t *block;
u_int offs;
LIST_ENTRY(usb_frag_dma) next;
};
usbd_status usb_block_allocmem
__P((bus_dma_tag_t, size_t, size_t, usb_dma_block_t **));
void usb_block_real_freemem __P((usb_dma_block_t *));
void usb_block_freemem __P((usb_dma_block_t *));
LIST_HEAD(, usb_block_dma) usb_blk_freelist =
LIST_HEAD_INITIALIZER(usb_blk_freelist);
/* XXX should have different free list for different tags */
LIST_HEAD(, usb_frag_dma) usb_frag_freelist =
LIST_HEAD_INITIALIZER(usb_frag_freelist);
usbd_status
usb_block_allocmem(tag, size, align, dmap)
bus_dma_tag_t tag;
size_t size;
size_t align;
usb_dma_block_t **dmap;
{
int error;
usb_dma_block_t *p;
DPRINTFN(5, ("usb_block_allocmem: size=%d align=%d\n", size, align));
/* First check the free list. */
for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) {
if (p->tag == tag && p->size >= size && p->align >= align) {
LIST_REMOVE(p, next);
*dmap = p;
DPRINTFN(6, ("usb_block_allocmem: free list size=%d\n",
p->size));
return (USBD_NORMAL_COMPLETION);
}
}
DPRINTFN(6, ("usb_block_allocmem: no free\n"));
p = malloc(sizeof *p, M_USB, M_NOWAIT);
if (p == 0)
return (USBD_NOMEM);
*dmap = p;
p->tag = tag;
p->size = size;
p->align = align;
error = bus_dmamem_alloc(tag, p->size, align, 0,
p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
&p->nsegs, BUS_DMA_NOWAIT);
if (error)
return (USBD_NOMEM);
error = bus_dmamem_map(tag, p->segs, p->nsegs, p->size,
&p->kaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
if (error)
goto free;
error = bus_dmamap_create(tag, p->size, 1, p->size,
0, BUS_DMA_NOWAIT, &p->map);
if (error)
goto unmap;
error = bus_dmamap_load(tag, p->map, p->kaddr,p->size, NULL,
BUS_DMA_NOWAIT);
if (error)
goto destroy;
return 0;
destroy:
bus_dmamap_destroy(tag, p->map);
unmap:
bus_dmamem_unmap(tag, p->kaddr, p->size);
free:
bus_dmamem_free(tag, p->segs, p->nsegs);
return (USBD_NOMEM);
}
void
usb_block_real_freemem(p)
usb_dma_block_t *p;
{
bus_dmamap_unload(p->tag, p->map);
bus_dmamap_destroy(p->tag, p->map);
bus_dmamem_unmap(p->tag, p->kaddr, p->size);
bus_dmamem_free(p->tag, p->segs, p->nsegs);
free(p, M_USB);
}
/*
* Do not free the memory unconditionally since we might be called
* from an interrupt context and that is BAD.
* XXX when you we really free?
*/
void
usb_block_freemem(p)
usb_dma_block_t *p;
{
DPRINTFN(6, ("usb_block_freemem: size=%d\n", p->size));
LIST_INSERT_HEAD(&usb_blk_freelist, p, next);
}
usbd_status
usb_allocmem(tag, size, align, p)
bus_dma_tag_t tag;
size_t size;
size_t align;
usb_dma_t *p;
{
usbd_status r;
struct usb_frag_dma *f;
usb_dma_block_t *b;
int i;
/* If the request is large then just use a full block. */
if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) {
DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size));
size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1);
r = usb_block_allocmem(tag, size, align, &p->block);
if (r == USBD_NORMAL_COMPLETION) {
p->block->fullblock = 1;
p->offs = 0;
}
return (r);
}
/* Check for free fragments. */
for (f = LIST_FIRST(&usb_frag_freelist); f; f = LIST_NEXT(f, next))
if (f->block->tag == tag)
break;
if (!f) {
DPRINTFN(1, ("usb_allocmem: adding fragments\n"));
r = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL, &b);
for (i = 0; i < USB_MEM_BLOCK; i += USB_MEM_SMALL) {
f = (struct usb_frag_dma *)(b->kaddr + i);
f->block = b;
f->offs = i;
LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
}
f = LIST_FIRST(&usb_frag_freelist);
}
p->block = f->block;
p->offs = f->offs;
LIST_REMOVE(f, next);
DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size));
return (USBD_NORMAL_COMPLETION);
}
void
usb_freemem(tag, p)
bus_dma_tag_t tag;
usb_dma_t *p;
{
struct usb_frag_dma *f;
if (p->block->fullblock) {
usb_block_freemem(p->block);
return;
}
f = KERNADDR(p);
f->block = p->block;
f->offs = p->offs;
LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
DPRINTFN(5, ("usb_freemem: frag=%p\n", f));
}

60
sys/dev/usb/usb_mem.h Normal file
View File

@ -0,0 +1,60 @@
/* $NetBSD: usb_mem.h,v 1.1 1998/07/24 21:09:08 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Author: Lennart Augustsson <augustss@carlstedt.se>
* Carlstedt Research & Technology
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
typedef struct usb_block_dma {
bus_dma_tag_t tag;
bus_dmamap_t map;
caddr_t kaddr;
bus_dma_segment_t segs[1];
int nsegs;
size_t size;
size_t align;
int fullblock;
LIST_ENTRY(usb_block_dma) next;
} usb_dma_block_t;
typedef struct {
usb_dma_block_t *block;
u_int offs;
} usb_dma_t;
#define DMAADDR(dma) ((dma)->block->segs[0].ds_addr + (dma)->offs)
#define KERNADDR(dma) ((void *)((dma)->block->kaddr + (dma)->offs))
usbd_status usb_allocmem __P((bus_dma_tag_t, size_t, size_t, usb_dma_t *));
void usb_freemem __P((bus_dma_tag_t, usb_dma_t *));