Subject: ISA bounce buffer/DMA interface
To: None <tech-kern@NetBSD.ORG>
From: Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
List: tech-kern
Date: 09/14/1995 10:54:47
I'm about to implement the ISA bounce buffer & DMA interface ironed out by
Chris Demetriou on the i386 port. I have modified his last interface
specification (dated Sun, 04 Jun 1995) a little bit.
Main change is the introduction of a 'sizes' array to hold the size
for each 'mappings' entry. The result will be an array of (phys addr, size)
pairs. This is a more general representation for scattered DMA segments.
Comments?
---
Juergen Hannken-Illjes - hannken@eis.cs.tu-bs.de - TU Braunschweig (W Germany)
Here comes sys/dev/isa/isadmavar.h (changes flagged by '|' in first column):
/*
* Machine-independent functions to manipulate the DMA controllers.
*/
/* Enable the use of a DMA channel by a bus master. */
void isadma_cascade __P((int chan));
COMMENTARY:
This is the same as the old isa_dma_cascade; renamed for
consistency.
/* Start/abort/finish a DMAC-controlled DMA transfer. */
void isadma_start __P((caddr_t addr, vm_size_t size, int chan, int flags));
void isadma_abort __P((int chan));
void isadma_done __P((int chan));
COMMENTARY:
Flags below (which just specify read/write). (I didn't
particularly like the old use of B_READ. Also didn't want
anybody to not get caught when quickly converting their code.)
DMAs started with isadma_start() _MUST_ be finished by a call
to either isadma_abort() or isadma_done(). The MI ISA DMA
code keeps track of per channel resources, and will panic
if the protocol isn't followed. This is necessary because
some machines must allocate resources to enable the DMA
(in addition to the DMA channel itself), and those resource
must be freed.
If a DMA transfer would cross a 64k/128k boundary (for 8/16
bit transfers, respectively), it will be bounce buffered.
(8/16 bit transfer size is determined by channel number.)
/*
* Machine-dependent functions to do mappings from virtual addresses
* to physical addresses, bounce buffering, and DMA buffer copying.
* Used by drivers for bus-mastering ISA devices and by the functions
* which do DMAC-controlled DMA.
*/
int isadma_map __P((caddr_t addr, vm_size_t size, vm_offset_t *mappings,
| vm_offset_t *sizes, int flags));
void isadma_unmap __P((caddr_t addr, vm_size_t size, int nmappings,
| vm_offset_t *mappings, vm_offset_t *sizes));
void isadma_copytobuf __P((caddr_t addr, vm_size_t size, int nmappings,
| vm_offset_t *mappings, vm_offset_t *sizes));
void isadma_copyfrombuf __P((caddr_t addr, vm_size_t size, int nmappings,
| vm_offset_t *mappings, vm_offset_t *sizes));
COMMENTARY:
These functions do what I indicated before: virtual ->
physical mapping, disposal of that mapping, and any copying
to or from the mapped buffers. isadma_map() flags explained
below.
'mappings' is an array of physical addresses. later, the type
will be changed to isa_addr_t or something similar -- a 32
bit quantity. (24 bits would be better, but i dunno if one
can get the compiler to do that reasonably... 8-) The pointer
given to isadma_map() must be large enough to hold
| roundup(size,PAGE_SIZE)+1
| elements. 'sizes' is an array of sizes corresponding to 'mappings'.
| ? Would it be better to use a 'struct isadma_seg' instead ?
| ? What about an initial array size parameter to 'isadma_map' ?
| If ISADMA_MAP_CONTIG is specified, only one (addr,size) pair
| will be used.
isadma_map() returns the number of mappings filled in,
and that number must be passed (along with the mappings
array) to the rest of the functions. If isadma_map()
fails, it returns 0.
Regions that are mapped with isadma_map() must be unmapped
with isadma_unmap() when the driver is finished with them.
As before, they may be consuming host resources.
isadma_copytobuffer() and isadma_copyfrombuffer() should
copy efficiently, i.e. only copy when the data has been
bounce-buffered.
isadma_start(), etc., are implemented in a machine-independent
fashion with these functions.
/*
* Flags passed to isadma_start(). (Note that ISADMA_START_WRITE
* is a pseudo-flag.)
*/
#define ISADMA_START_READ 0x0001 /* DMA data from the device */
| #define ISADMA_START_WRITE 000002 /* DMA data to the device */
COMMENTARY:
pretty obvious.
| ISADMA_START_WRITE set to '2' to detect missing direction.
/*
| * Flags passed to isadma_map().
*/
#define ISADMA_MAP_WAITOK 0x0001 /* OK for isadma_map to sleep */
COMMENTARY:
pretty obvious. former to be used when isadma_map() not called
at interrupt time. it's likely (but not guaranteed) that
isadma_map() will only sleep when/if creating a bounce buffer.
(Note that it may sleep at any time, if ISADMA_MAP_WAITOK is
specified.) isadma_map() will fail of it needs to sleep but
ISADMA_MAP_NOWAIT is specified.
#define ISADMA_MAP_BOUNCE 0x0002 /* use bounce buffer if necessary */
COMMENTARY:
pretty obvious. The latter should be used if a driver is
likely to hold the buffer for an extended period of time
(e.g. if it's trying to map a kernel variable, or something.)
Note that on some systems, ISADMA_MAP_NOBOUNCE may not
completely supported, and if used may cause isadma_map()
to fail on an otherwise-reasonable request.
#define ISADMA_MAP_CONTIG 0x0004 /* must be physically contiguous */
COMMENTARY:
ISADMA_MAP_CONTIG is used by the isadma_start(), and may
be used by drivers.
Note that if ISADMA_MAP_CONTIG|ISADMA_MAP_NOBOUNCE is
specified, the buffer must be contiguous and mappable without
bounce buffering, or the request will fail.
#define ISADMA_MAP_16BIT 0x0008 /* 16-bit DMA (channels 4-7) */
| #define ISADMA_MAP_8BIT 0x0010 /* 8-bit DMA (channels 0-3) */
COMMENTARY:
These are provided to select the address constraints on the
DMA buffer. 8-bit DMA tranfers can be no longer than 64k
and cannot cross a 64k boundary. 16-bit transfers can be
no longer than 128k, must start and end on a two-byte
boundary, and must not cross a 128k boundary.
| ISADMA_MAP_CONTIG must be set with this modifiers.
| ISADMA_MAP_8BIT is not zero to allow a request with no constrains.
| All #define XXX 0 have been removed. Simply use '0 | FLAG1 | ...'