KitFreeMiNT
programmer documentation
/* interface via kerinfo struct */
struct dma
{
ulong _cdecl (*get_channel) (void);
long _cdecl (*free_channel) (ulong);
void _cdecl (*dma_start) (ulong);
void _cdecl (*dma_end) (ulong);
void * _cdecl (*block) (ulong, ulong, void _cdecl (*)(PROC *p));
void _cdecl (*deblock) (ulong, void *);
};
- every DMA driver first registers with the kernel. The kernel allocates a unique channel descriptor for the driver which is then used for further function calls.
for registration the driver must use get_channel; get_channel return then an unique channel number; a return value of 0 indicate that there are no free channel available; any other value indicate a valid channel
- A DMA driver can unregister (if it wants to terminate).
for unregistration the driver must use free_channel; free_channel return -1 for a channel that was not allocated; in general, this is mostly a FATAL error and the driver must halt the system after he printed some information message
-
When entering the driver, the driver calls dma_start. The kernel automatically sets a semaphore for the driver and blocks here if another process is already using the driver.
-
Then the driver can do whatever is necessary to start operation and enable an interrupt. When this is done, the driver calls dma_block(channel) which will block the current process and give up the processor.
dma_block can also automatically handle timeout events. For this dma_block get an timeout value. The value specifiy the time in milliseconds. If the timeout expire it will call the function that is the third argument to dma_block or a default timeout function if the third argument is NULL. The default timeout function simply call deblock(channel, -2) to unblock the process. The return value of dma_block is then -2 if the timeout deblock the process. If you doesn't need a timeout simply set all other arguments of dma_block to 0. A value of 0 for the timeout means an infinite timeout that effectivly disable this feature.
Note: It's not granted that the timeout happen exactly after the specified number of milliseconds.
-
Now another process can run; if this process also tries to access the driver, it will automatically be blocked because the semaphore is locked.
-
When the device has finished its operation, it causes an interrupt which calls the ISR in the driver. The driver can either restart the device (if further processing is required for the current task, eg. when doing scatter-gather DMA), or it can call dma_deblock(channel). In either case, the ISR ends and control returns to the process that was running when the interrupt happened. A call to dma_deblock(channel) unblocks the driver task. The kernel then switches back to the driver task, which continues by returning from dma_block. The driver may now either restart the device (using operations that take too long to do this in the ISR, or that need kernel calls) and call dma_block again, or it finishes by cleaning up and calling dma_end.
Note: After dma_start the interrupt can happen at any time and call dma_deblock. The dma_block/dma_deblock interaction is interrupt safe and dma_block will not block in such case and return immediate.
Note: dma_deblock get an additional parameter, called idev, 32bit long; this is simply and internal driver value that is returned unchanged from dma_block as return value if your driver doesn't need an additional value simply set it to 0; dma_block return then 0
-
When leaving the driver, the driver calls dma_end. This resets the semaphore and unblocks other processes (one at a time) waiting to enter the driver.
-
dma_deblock is the only routine that may be called from within an interrupt service routine.