#ifndef _DMA_H
#define _DMA_H


#define MAX_DMA_CHANNELS    8

/* 8237 DMA controllers */
#define IO_DMA1_BASE            0x00        /* 8 bit slave DMA, channels 0..3 */
#define IO_DMA2_BASE            0xC0        /* 16 bit master DMA, ch 4(=slave input)..7 */

/* DMA controller registers */
#define DMA1_CMD_REG            0x08        /* command register (w) */
#define DMA1_STAT_REG           0x08        /* status register (r) */
#define DMA1_REQ_REG            0x09        /* request register (w) */
#define DMA1_MASK_REG           0x0A        /* single-channel mask (w) */
#define DMA1_MODE_REG           0x0B        /* mode register (w) */
#define DMA1_CLEAR_FF_REG       0x0C        /* clear pointer flip-flop (w) */
#define DMA1_TEMP_REG           0x0D        /* Temporary Register (r) */
#define DMA1_RESET_REG          0x0D        /* Master Clear (w) */
#define DMA1_CLR_MASK_REG       0x0E        /* Clear Mask */
#define DMA1_MASK_ALL_REG       0x0F        /* all-channels mask (w) */

#define DMA2_CMD_REG            0xD0        /* command register (w) */
#define DMA2_STAT_REG           0xD0        /* status register (r) */
#define DMA2_REQ_REG            0xD2        /* request register (w) */
#define DMA2_MASK_REG           0xD4        /* single-channel mask (w) */
#define DMA2_MODE_REG           0xD6        /* mode register (w) */
#define DMA2_CLEAR_FF_REG       0xD8        /* clear pointer flip-flop (w) */
#define DMA2_TEMP_REG           0xDA        /* Temporary Register (r) */
#define DMA2_RESET_REG          0xDA        /* Master Clear (w) */
#define DMA2_CLR_MASK_REG       0xDC        /* Clear Mask */
#define DMA2_MASK_ALL_REG       0xDE        /* all-channels mask (w) */

#define DMA_ADDR_0              0x00        /* DMA address registers */
#define DMA_ADDR_1              0x02
#define DMA_ADDR_2              0x04
#define DMA_ADDR_3              0x06
#define DMA_ADDR_4              0xC0
#define DMA_ADDR_5              0xC4
#define DMA_ADDR_6              0xC8
#define DMA_ADDR_7              0xCC

#define DMA_CNT_0               0x01        /* DMA count registers */
#define DMA_CNT_1               0x03
#define DMA_CNT_2               0x05
#define DMA_CNT_3               0x07
#define DMA_CNT_4               0xC2
#define DMA_CNT_5               0xC6
#define DMA_CNT_6               0xCA
#define DMA_CNT_7               0xCE

#define DMA_PAGE_0              0x87        /* DMA page registers */
#define DMA_PAGE_1              0x83
#define DMA_PAGE_2              0x81
#define DMA_PAGE_3              0x82
#define DMA_PAGE_5              0x8B
#define DMA_PAGE_6              0x89
#define DMA_PAGE_7              0x8A

#define DMA_MODE_READ           0x44        /* I/O to memory, no autoinit, increment, single mode */
#define DMA_MODE_WRITE          0x48        /* memory to I/O, no autoinit, increment, single mode */
#define DMA_MODE_CASCADE        0xC0        /* pass thru DREQ->HRQ, DACK<-HLDA only */

/*
 * 启用指定的DMA通道
 */
static __inline__ void EnableDma(unsigned int dmanr)
{
    if (dmanr<=3)
        OUTB(dmanr,  DMA1_MASK_REG);
    else
        OUTB(dmanr & 3,  DMA2_MASK_REG);
}

/*
 * 禁用指定的DMA通道
 */
static __inline__ void DisableDma(unsigned int dmanr)
{
    if (dmanr<=3)
        OUTB(dmanr | 4,  DMA1_MASK_REG);
    else
        OUTB((dmanr & 3) | 4,  DMA2_MASK_REG);
}

/*
 * 清空DMA 晶体计数器
 */
static __inline__ void ClearDmaFF(unsigned int dmanr)
{
    if (dmanr<=3)
        OUTB(0,  DMA1_CLEAR_FF_REG);
    else
        OUTB(0,  DMA2_CLEAR_FF_REG);
}

/* 
 * 清空DMA 晶体计数器
 */
static __inline__ void SetDmaMode(unsigned int dmanr, char mode)
{
    if (dmanr<=3)
        OUTB(mode | dmanr,  DMA1_MODE_REG);
    else
        OUTB(mode | (dmanr&3),  DMA2_MODE_REG);
}

/*
 *  设定DMA 页面寄存器
 */
static __inline__ void SetDmaPage(unsigned int dmanr, char pagenr)
{
    switch(dmanr) {
        case 0:
            OUTB(pagenr, DMA_PAGE_0);
            break;
        case 1:
            OUTB(pagenr, DMA_PAGE_1);
            break;
        case 2:
            OUTB(pagenr, DMA_PAGE_2);
            break;
        case 3:
            OUTB(pagenr, DMA_PAGE_3);
            break;
        case 5:
            OUTB(pagenr & 0xfe, DMA_PAGE_5);
            break;
        case 6:
            OUTB(pagenr & 0xfe, DMA_PAGE_6);
            break;
        case 7:
            OUTB(pagenr & 0xfe, DMA_PAGE_7);
            break;
    }
}


/*
 * 设定DMA 传输高速缓冲区地址
 */
static __inline__ void SetDmaAddr(unsigned int dmanr, unsigned int a)
{
    SetDmaPage(dmanr, a>>16);
    if (dmanr <= 3)  {
        OUTB( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
            OUTB( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
    }  else  {
        OUTB( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
        OUTB( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
    }
}


/*
 * 设定DMA 传输块数
 */
static __inline__ void SetDmaCount(unsigned int dmanr, unsigned int count)
{
        count--;
    if (dmanr <= 3)  {
        OUTB( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
        OUTB( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
        } else {
        OUTB( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
        OUTB( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
        }
}


/*
 *  获得DMA 传输剩余块数
 */
static __inline__ int GetDmaResidue(unsigned int dmanr)
{
    unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
                     : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;

    /* using short to get 16-bit wrap around */
    unsigned short count;
    count = 1 + inb(io_port);
    count += inb(io_port) << 8;
    return (dmanr<=3)? count : (count<<1);
}

#endif