/*
 * util.c
 * 
 * Assorted IRQ/DMA utility functions for DJGPP 2.01
 * 
 * Copyright (C) 1998  Fabian Nunez
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * The author can be reached by email at: fabian@cs.uct.ac.za
 * 
 * or by airmail at: Fabian Nunez
 *                   10 Eastbrooke
 *                   Highstead Road
 *                   Rondebosch 7700
 *                   South Africa
 */

#include <kernel/kernel.h>
#include <kernel/driver.h>
#include <kernel/memory.h>
#include "util.h"

/* definition of DMA channels */
const static DmaChannel dmainfo[] = {
   { 0x87, 0x00, 0x01 },
   { 0x83, 0x02, 0x03 },
   { 0x81, 0x04, 0x05 },
   { 0x82, 0x06, 0x07 }
};

/* 
 * this allocates a 4KB buffer in the < 1M range, maps it and returns the 
 * linear address, also setting the physical in the integer pointed at
 */
long alloc_dma_buffer()
{
	addr_t phys;
	phys = memAllocLow();
	assert(phys + PAGE_SIZE < 0x100000);
	assert(((phys + PAGE_SIZE) & 0xffff) >= ((phys + PAGE_SIZE) & 0xffff));
	wprintf(L"DMA transfer buffer is at 0x%x\n", phys);
	return phys;
}

/*
 * this sets up a DMA trasfer between a device and memory.  Pass the DMA
 * channel number (0..3), the physical address of the buffer and transfer
 * length.  If 'read' is TRUE, then transfer will be from memory to device,
 * else from the device to memory.
 */
void dma_xfer(int channel,long physaddr,int length,BOOL read)
{
   long page,offset;
   
   assert(channel < 4);
   
   /* calculate dma page and offset */
   page = physaddr >> 16;
   offset = physaddr & 0xffff;
   length -= 1;  /* with dma, if you want k bytes, you ask for k - 1 */

   disable();  /* disable irq's */
   
   /* set the mask bit for the channel */
   out(0x0a,channel | 4);
   
   /* clear flipflop */
   out(0x0c,0);
   
   /* set DMA mode (write+single+r/w) */
   out(0x0b,(read ? 0x48 : 0x44) + channel);
   
   /* set DMA page */
   out(dmainfo[channel].page,page);
   
   /* set DMA offset */
   out(dmainfo[channel].offset,offset & 0xff);  /* low byte */
   out(dmainfo[channel].offset,offset >> 8);    /* high byte */
   
   /* set DMA length */
   out(dmainfo[channel].length,length & 0xff);  /* low byte */
   out(dmainfo[channel].length,length >> 8);    /* high byte */
   
   /* clear DMA mask bit */
   out(0x0a,channel);
   
   enable();  /* enable irq's */
}
