Compact Flash I/O test

Dependencies:   mbed

FAT_FS/diskio.c

Committer:
emh203
Date:
2011-12-30
Revision:
1:dc171f34db9b
Parent:
0:6b1e6c9e48ba

File content as of revision 1:dc171f34db9b:

#include "mbed.h"
#include "LPC17xx.h"
#include "integer.h"
#include "diskio.h"
#include "stdarg.h"
#include "usbhost_inc.h"


/*--------------------------------------------------------------------------

   Module Private Functions

---------------------------------------------------------------------------*/

static volatile DSTATUS Stat[3] = {STA_NOINIT | STA_NODISK};    /* Disk status */
static volatile BYTE DiskProcTimer[3]={0};            /* 100Hz decrement timer */


BYTE RAM_DISK[512];


//Usb Stuff
uint32_t _numBlks;
uint32_t _blkSize;
USB_INT08U  inquiryResult[INQUIRY_LENGTH];  


//#define _CF_DEBUG_MESSAGES
//#define _CF_DEBUG_READ_ATA

#ifdef _CF_DEBUG_MESSAGES 
   #define  CF_DEBUG(...) printf(__VA_ARGS__)    
#else
   #define  CF_DEBUG     
#endif

void disk_timerproc (void);

#define LED1_MASK  (uint32_t )(1<<18)
#define LED2_MASK  (uint32_t )(1<<20)
#define LED3_MASK  (uint32_t )(1<<21)
#define LED4_MASK  (uint32_t )(1<<23)


#define LED1_ON             LPC_GPIO1->FIOSET=LED1_MASK
#define LED1_OFF            LPC_GPIO1->FIOCLR=LED1_MASK
#define LED1_TOGGLE         LPC_GPIO1->FIOPIN^=LED1_MASK

#define LED2_ON             LPC_GPIO1->FIOSET=LED2_MASK
#define LED2_OFF            LPC_GPIO1->FIOCLR=LED2_MASK
#define LED2_TOGGLE         LPC_GPIO1->FIOPIN^=LED2_MASK

#define LED3_ON             LPC_GPIO1->FIOSET=LED3_MASK
#define LED3_OFF            LPC_GPIO1->FIOCLR=LED3_MASK
#define LED3_TOGGLE         LPC_GPIO1->FIOPIN^=LED3_MASK

#define LED4_ON             LPC_GPIO1->FIOSET=LED4_MASK
#define LED4_OFF            LPC_GPIO1->FIOCLR=LED4_MASK
#define LED4_TOGGLE         LPC_GPIO1->FIOPIN^=LED4_MASK



//8-bit Data Bus is on Pins P0.4 - P0.11
#define DATA_BUS_MASK      (uint32_t )((0xFF)<<4)

//3 bit address is on Port 2.0 - 2.2
#define ADDRESS_BUS_MASK   (uint32_t )(0x7)

//ChipSelects are on port 2
#define CS0_MASK    (uint32_t )(1<<3)
#define CS1_MASK    (uint32_t )(1<<4)

//IORD and IOWR are on port 0
#define IORD_MASK   (uint32_t )(1<<24)
#define IOWR_MASK   (uint32_t )(1<<23)


//Reset and power enable are on port 1
#define COMPACT_FLASH_RESET_MASK           (uint32_t )(1<<30)
#define COMPACT_FLASH_POWER_ENABLE_MASK    (uint32_t )(0x80000000)
//Card Detect is on Port 2
#define COMPACT_FLASH_CARD_DETECT_MASK     (uint32_t )(1<<5)

//Low Level Bus Operation Macros
//Note: LPC 176x have dedicate set and clear registers

#define SET_DATA_BUS_TO_INPUTS                LPC_GPIO0->FIODIR &= ~(DATA_BUS_MASK);
#define SET_DATA_BUS_TO_OUTPUT                LPC_GPIO0->FIODIR |=  (DATA_BUS_MASK);

#define CS0_ACTIVE                            LPC_GPIO2->FIOCLR = CS0_MASK
#define CS0_INACTIVE                          LPC_GPIO2->FIOSET   = CS0_MASK
#define CS1_ACTIVE                            LPC_GPIO2->FIOCLR = CS1_MASK
#define CS1_INACTIVE                          LPC_GPIO2->FIOSET   = CS1_MASK

#define IORD_ACTIVE                           LPC_GPIO0->FIOCLR = IORD_MASK
#define IORD_INACTIVE                         LPC_GPIO0->FIOSET   = IORD_MASK

#define IOWR_ACTIVE                           LPC_GPIO0->FIOCLR   = IOWR_MASK
#define IOWR_INACTIVE                         LPC_GPIO0->FIOSET   = IOWR_MASK

#define COMPACT_FLASH_RESET_ACTIVE            LPC_GPIO1->FIOCLR = COMPACT_FLASH_RESET_MASK
#define COMPACT_FLASH_RESET_INACTIVE          LPC_GPIO1->FIOSET = COMPACT_FLASH_RESET_MASK

#define  COMPACT_FLASH_POWER_ENABLE           LPC_GPIO1->FIOCLR = COMPACT_FLASH_POWER_ENABLE_MASK
#define  COMPACT_FLASH_POWER_DISABLE          LPC_GPIO1->FIOSET = COMPACT_FLASH_POWER_ENABLE_MASK

#define COMPACT_FLASH_CARD_DETECTED            (!((LPC_GPIO2->FIOPIN)&COMPACT_FLASH_CARD_DETECT_MASK))

//To set the Address and Data Bus Lines we will use the convient Mask register in the Port I/O modules
//The Hardware will mask out pins that are set to 1 in the MASK register

#define SET_CF_ADDRESS(ADDRESS)       LPC_GPIO2->FIOMASK=~(ADDRESS_BUS_MASK);   \
                                      LPC_GPIO2->FIOPIN=ADDRESS;                \
                                      LPC_GPIO2->FIOMASK=0                      //Always remember to reset the mask for other operations to complete correctly


#define SET_CF_DATA(DATA)             LPC_GPIO0->FIOMASK=~(DATA_BUS_MASK);      \
                                      LPC_GPIO0->FIOPIN=(((uint32_t)DATA)<<4);           \
                                      LPC_GPIO0->FIOMASK=0                  //Always remember to reset the mask for other operations to complete correctly

#define GET_CF_DATA(DATA)             LPC_GPIO0->FIOMASK=~(DATA_BUS_MASK);        \
                                      (DATA) = (LPC_GPIO0->FIOPIN)>>4;           \
                                      LPC_GPIO0->FIOMASK=0                  //Always remember to reset the mask for other operations to complete correctly

#define SET_DATA_BUS_AS_OUTPUTS       LPC_GPIO0->FIODIR|=DATA_BUS_MASK
#define SET_DATA_BUS_AS_INPUTS        LPC_GPIO0->FIODIR&=~DATA_BUS_MASK

/* ATA command */
#define CMD_RESET        0x08    /* DEVICE RESET */
#define CMD_READ         0x20    /* READ SECTOR(S) */
#define CMD_WRITE        0x30    /* WRITE SECTOR(S) */
#define CMD_IDENTIFY     0xEC    /* DEVICE IDENTIFY */
#define CMD_SETFEATURES  0xEF    /* SET FEATURES */

/* ATA register bit definitions */
#define    LBA             0xE0
#define    BUSY            0x80
#define    DRDY            0x40
#define    DF              0x20
#define    DRQ             0x08
#define    ERR             0x01
#define    SRST            0x40
#define    nIEN            0x20

/* Bit definitions for Control Port */
#define    CTL_READ        0x20
#define    CTL_WRITE       0x40
#define    CTL_RESET       0x80
#define    REG_DATA        0x0
#define    REG_ERROR       0x1
#define    REG_FEATURES    0x1
#define    REG_COUNT       0x2
#define    REG_SECTOR      0x3
#define    REG_CYLL        0x4
#define    REG_CYLH        0x5
#define    REG_DEV         0x6
#define    REG_COMMAND     0x7
#define    REG_STATUS      0x7
#define    REG_DEVCTRL     0xE
#define    REG_ALTSTAT     0xE

void InitCompactFlashInterface()
{
    SET_DATA_BUS_AS_INPUTS;

    LPC_GPIO2->FIODIR  |= ADDRESS_BUS_MASK | CS0_MASK | CS1_MASK;
    LPC_GPIO2->FIODIR  &=~(COMPACT_FLASH_CARD_DETECT_MASK);
    LPC_GPIO1->FIODIR  |= COMPACT_FLASH_RESET_MASK | COMPACT_FLASH_POWER_ENABLE_MASK | LED1_MASK | LED2_MASK | LED3_MASK | LED4_MASK;
    LPC_GPIO0->FIODIR  |= IORD_MASK  | IOWR_MASK ;

    COMPACT_FLASH_RESET_ACTIVE;
    COMPACT_FLASH_POWER_DISABLE;
    CS0_INACTIVE;
    CS1_INACTIVE;

    SysTick_Config(SystemCoreClock/100);
    NVIC_SetVector(SysTick_IRQn, (uint32_t)(&disk_timerproc));
}

/*-----------------------------------------------------------------------*/
/* Read an ATA register                                                  */
/*-----------------------------------------------------------------------*/

static
BYTE read_ata (
    BYTE reg            /* Register to be read */
)
{
    BYTE rd;
 
    CS0_ACTIVE; 
    SET_DATA_BUS_AS_INPUTS;
    SET_CF_ADDRESS(reg); 
    IORD_ACTIVE;  
    __nop();
    __nop();
    __nop();
    __nop();
  
    GET_CF_DATA(rd); 
    __nop();
    __nop();
    __nop();
    __nop();
    IORD_INACTIVE;  
    CS0_INACTIVE; 
    #ifdef _CF_DEBUG_READ_ATA
      CF_DEBUG("rd 0x%2x\r\n",rd);
    #endif
    return rd;
}



/*-----------------------------------------------------------------------*/
/* Write a byte to an ATA register                                       */
/*-----------------------------------------------------------------------*/

static
void write_ata (
    BYTE reg,        /* Register to be written */
    BYTE dat        /* Data to be written */
)
{
   
    __nop();
    CS0_ACTIVE; 
    SET_DATA_BUS_AS_OUTPUTS;
    SET_CF_ADDRESS(reg);
    SET_CF_DATA(dat); 
    IOWR_ACTIVE;  
    __nop();
    __nop();
    __nop();
    __nop();
      IOWR_INACTIVE; 
      __nop();
      __nop();
      __nop();
      __nop();
    CS0_INACTIVE; 
    SET_DATA_BUS_AS_INPUTS;
}



/*-----------------------------------------------------------------------*/
/* Read a part of data block                                             */
/*-----------------------------------------------------------------------*/

static
void read_part (
    BYTE *buff,     /* Data buffer to store read data */
    BYTE ofs,        /* Offset of the part of data in unit of word */
    BYTE count        /* Number of word to pick up */
)
{
    BYTE c = 0, dl, dh;
    
    SET_CF_ADDRESS(REG_DATA);        /* Select Data register */
    __nop();
    __nop();
  
     SET_DATA_BUS_AS_INPUTS;   
    CS0_ACTIVE; 
    __nop();
    __nop();
  do {
        IORD_ACTIVE;        /* IORD = L */
        __nop();
        __nop();
        __nop();
        __nop();
         GET_CF_DATA(dl);                /* Read Even data */
        IORD_INACTIVE;        /* IORD = H */
        __nop();
        __nop();
        __nop();
        __nop();
         IORD_ACTIVE;        /* IORD = L */
        __nop();
        __nop();
        __nop();
        __nop();
        GET_CF_DATA(dh);                /* Read Odd data */
        IORD_INACTIVE;    /* IORD = H */
        __nop();
        __nop();
        __nop();
        __nop();
        if (count && (c >= ofs)) {    /* Pick up a part of block */
            *buff++ = dl;
            *buff++ = dh;
            count--;
        }
    } while (++c);
    CS0_INACTIVE; 
    
    read_ata(REG_ALTSTAT);
    read_ata(REG_STATUS);
}


/*-----------------------------------------------------------------------*/
/* Wait for Data Ready                                                   */
/*-----------------------------------------------------------------------*/

static
int wait_data (void)
{
    BYTE s;

    DiskProcTimer[CF] = 100;    /* Time out = 1 sec */
    do {
        if (!DiskProcTimer[CF]) return 0;            /* Abort when timeout occured */
        s = read_ata(REG_STATUS);        /* Get status */
    } while ((s & (BUSY|DRQ)) != DRQ);    /* Wait for BUSY goes low and DRQ goes high */

    read_ata(REG_ALTSTAT);
    return 1;
}




/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
    BYTE drv        /* Physical drive number (0) */
)
{
    DSTATUS RetVal;
    USB_INT32S  rc;
  
    switch(drv)
    {
        case COMPACT_FLASH:
                 
              Stat[CF] |= STA_NOINIT;
              for (DiskProcTimer[CF] = 10; DiskProcTimer[CF]; );                /* 100ms */
              if (Stat[CF] & STA_NODISK) return Stat[CF];        /* Exit when socket is empty */
              COMPACT_FLASH_POWER_ENABLE;     /* Initialize CFC control port */
              for (DiskProcTimer[CF] = 1;DiskProcTimer[CF]; );                /* 10ms */
         
              SET_DATA_BUS_AS_INPUTS;
              for (DiskProcTimer[CF] = 5; DiskProcTimer[CF]; );                /* 50ms */
              COMPACT_FLASH_RESET_INACTIVE;
              for (DiskProcTimer[CF] = 5; DiskProcTimer[CF]; );                /* 50ms */
              write_ata(REG_DEV, LBA);                /* Select Device 0 */
              DiskProcTimer[CF] = 200;
                             
                do {                                    /* Wait for card goes ready */
                    if (!DiskProcTimer[CF]) 
                    {
                        CF_DEBUG("Timeout waiting for card BUSY to go inactive\r\n");
                        return Stat[CF];
                    }
                } while (read_ata(REG_STATUS) & BUSY);
      
      
                write_ata(REG_DEVCTRL, SRST | nIEN);    /* Software reset */
                for (DiskProcTimer[CF] = 2; DiskProcTimer[CF]; );                /* 20ms */
                
                write_ata(REG_DEVCTRL, nIEN);            /* Release software reset */
                
                for (DiskProcTimer[CF] = 2; DiskProcTimer[CF]; );                /* 20ms */
                
                DiskProcTimer[CF] = 200;
                do {                                    /* Wait for card goes ready */
                    if (!DiskProcTimer[CF])
                    {
                        CF_DEBUG("Timeout waiting for card DRDY\r\n");
                        return Stat[CF];
                     }
                } while ((read_ata(REG_STATUS) & (DRDY|BUSY)) != DRDY);
                
                CF_DEBUG("Setting to 8-bit PIO MOD\r\n");
                write_ata(REG_FEATURES, 0x01);            /* Select 8-bit PIO transfer mode */
                write_ata(REG_COMMAND, CMD_SETFEATURES);
                DiskProcTimer[CF] = 200;
                do {
                    if (!DiskProcTimer[CF])
                    {
                        CF_DEBUG("Timeout waiting after trying to call the SETFEATURES command\r\n");
                        return Stat[CF];
                    }
                     
                } while (read_ata(REG_STATUS) & (BUSY | ERR));
            
                Stat[CF] &= ~STA_NOINIT;                    /* When device goes ready, clear STA_NOINIT */
            
                RetVal = Stat[CF];
      
        break;
        
        case USB:
        
                Host_Init();
                if(!Host_EnumDev())
                {
              
                    rc = MS_Init( &_blkSize, &_numBlks, inquiryResult );
                    if (rc != OK)
                    {
                        TERMINAL_PRINTF("Could not initialize mass storage interface: %d\r\n", rc);
                        Stat[USB] |= STA_NOINIT;
                    }
                    else
                    {
                         Stat[USB] &= ~STA_NOINIT;
                    }
                }
                else
                {
                     Stat[USB] |= STA_NOINIT;
                }
                 
                  RetVal = Stat[USB];
        break;
        
        
        case RAM:
             Stat[RAM] &= ~STA_NOINIT;
              RetVal = Stat[RAM];
        break; 
        
        default:
             RetVal = STA_NOINIT;
        break;
    }

    return RetVal;
}



/*-----------------------------------------------------------------------*/
/* Return Disk Status                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
    BYTE drv     
)
{
    DSTATUS RetVal;
    
    switch(drv)
    {
        case CF:
             RetVal = Stat[CF];
        break;
        
        
        case USB:
           RetVal = Stat[USB];
        break;
        
        case RAM:
           RetVal = Stat[RAM];
        break;
        
        default:
             RetVal = STA_NOINIT; 
        break;
    }
    
    return  RetVal;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
    BYTE drv,        /* Physical drive nmuber (0) */
    BYTE *buff,        /* Data buffer to store read data */
    DWORD sector,    /* Sector number (LBA) */
    BYTE count        /* Sector count (1..255) */
)
{
    BYTE c;
    DWORD i,j;
    
    switch(drv)
    {
        case CF:
              if (Stat[CF] & STA_NOINIT) return RES_NOTRDY;

                /* Issue Read Setor(s) command */
                write_ata(REG_COUNT, count);
                write_ata(REG_SECTOR, (BYTE)sector);
                write_ata(REG_CYLL, (BYTE)(sector >> 8));
                write_ata(REG_CYLH, (BYTE)(sector >> 16));
                write_ata(REG_DEV, ((BYTE)(sector >> 24) & 0x0F) | LBA);
                write_ata(REG_COMMAND, CMD_READ);
                
                 do {
                         if (!wait_data()) return RES_ERROR;    /* Wait data ready */
                   
                                SET_CF_ADDRESS(REG_DATA);
                                __nop();
                                __nop();
                                __nop();
                                __nop();
                               CS0_ACTIVE;
                               c = 0;
                               
                                __nop();
                                __nop();
                               __nop();
                            
                                SET_DATA_BUS_AS_INPUTS;
                                do {
                                        IORD_ACTIVE;        /* IORD = L */
                                        __nop();
                                        __nop();
                                        __nop();
                                        GET_CF_DATA(*buff++);        /* Get even data */
                                        __nop();
                                        __nop();
                                        __nop();
                                    
                                        __nop();
                                        __nop();
                                        __nop();
                                
                                        IORD_INACTIVE;        /* IORD = H */
                                        __nop();
                                        __nop();
                                        __nop();
                                 
                                        IORD_ACTIVE;        /* IORD = L */
                                        __nop();
                                        __nop();
                                        __nop();
                                
                                        GET_CF_DATA(*buff++);    /* Get Odd data */
                                        __nop();
                                        __nop();
                                        __nop();
                                
                                        __nop();
                                        IORD_INACTIVE;            /* IORD = H */
                                        __nop();
                                        __nop();
                                        __nop();
                                 
                                           
                                        } while (--c);
                        } while (--count);
            
                CS0_INACTIVE;
                read_ata(REG_ALTSTAT);
                read_ata(REG_STATUS);
                
                return RES_OK;
        break;
        
        
        case USB:
        
         if ( OK == MS_BulkRecv(sector, 1, (USB_INT08U *)buff) )
             return RES_OK;
         else
             return  RES_NOTRDY;
        break;
        
        case RAM:
                for(i=0;i<512;i++)
                {
                    buff[i] = RAM_DISK[i];
                }
             return RES_OK;
        break;
        
        default:
             return RES_PARERR;
        break;
    }
}


/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

DRESULT disk_write (
    BYTE drv,            /* Physical drive number (0) */
    const BYTE *buff,    /* Data to be written */
    DWORD sector,        /* Sector number (LBA) */
    BYTE count            /* Sector count (1..255) */
)
{
    BYTE s, c;
    DWORD i;
    DRESULT RetVal;
      
    switch(drv)
    {
        case CF:
                 /* Issue Write Setor(s) command */
                write_ata(REG_COUNT, count);
                write_ata(REG_SECTOR, (BYTE)sector);
                write_ata(REG_CYLL, (BYTE)(sector >> 8));
                write_ata(REG_CYLH, (BYTE)(sector >> 16));
                write_ata(REG_DEV, ((BYTE)(sector >> 24) & 0x0F) | LBA);
                write_ata(REG_COMMAND, CMD_WRITE);
                
                 do {
                        if (!wait_data()) return RES_ERROR;
                      
                        SET_CF_ADDRESS(REG_DATA);
                        __nop();
                         __nop();
                         __nop();
                        CS0_ACTIVE;
                        __nop();
                         __nop();
                         __nop();
                    
                        SET_DATA_BUS_AS_OUTPUTS;
                        c = 0;
                        do {
                            SET_CF_DATA(*buff++);    /* Set even data */
                            __nop();
                            __nop();
                            __nop();
                
                            IOWR_ACTIVE;        /* IOWR = L */
                            __nop();
                            __nop();
                            __nop();
                
                            IOWR_INACTIVE;        /* IOWR = H */
                            __nop();
                            __nop();
                            __nop();
                    
                            SET_CF_DATA(*buff++);    /* Set odd data */
                            __nop();
                            __nop();
                            __nop();
                 
                            IOWR_ACTIVE;        /* IOWR = L */
                            __nop();
                            __nop();
                            __nop();
                
                            IOWR_INACTIVE;        /* IOWR = H */
                            __nop();
                            __nop();
                            __nop();
                            } while (--c);
                 
                    } while (--count);
                      
                       SET_DATA_BUS_AS_INPUTS;
                       CS0_INACTIVE;
                
                    DiskProcTimer[CF] = 100;
                    do {
                        if (!DiskProcTimer[CF]) return RES_ERROR;
                        s = read_ata(REG_STATUS);
                    } while (s & BUSY);
                    if (s & ERR) return RES_ERROR;
                
                    read_ata(REG_ALTSTAT);
                    read_ata(REG_STATUS);

                RetVal = RES_OK;
        break;
        
        
        case USB:
            if ( OK == MS_BulkSend(sector, 1, (USB_INT08U *)buff) )
               RetVal = RES_OK;
            else;
               RetVal =  RES_NOTRDY;
        break;
        
        case RAM:
          
                for(i=0;i<512;i++)
                {
                     RAM_DISK[i] = buff[i];
                }
              RetVal =  RES_OK;
         
        break;
        
        default:
              RetVal = RES_PARERR;
        break;
        
        return RetVal;
    }

 }   
 
  
   

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
    BYTE drv,        /* Physical drive nmuber (0) */
    BYTE ctrl,        /* Control code */
    void *buff        /* Buffer to send/receive data block */
)
{
    BYTE n, w, ofs, dl, dh, *ptr = (BYTE *)buff;
    USB_INT32S  rc;
    
    switch(drv)
    {
        case CF:
            switch (ctrl)
              {
                        case GET_SECTOR_COUNT :    /* Get number of sectors on the disk (DWORD) */
                            ofs = 60; w = 2; n = 0;
                            break;
                
                        case GET_BLOCK_SIZE :    /* Get erase block size in sectors (DWORD) */
                            *(DWORD*)buff = 32;
                            return RES_OK;
                        break;
                        case CTRL_SYNC :        /* Nothing to do */
                            return RES_OK;
                        break;
                        case ATA_GET_REV :        /* Get firmware revision (8 chars) */
                            ofs = 23; w = 4; n = 4;
                            break;
                
                        case ATA_GET_MODEL :    /* Get model name (40 chars) */
                            ofs = 27; w = 20; n = 20;
                            break;
                
                        case ATA_GET_SN :        /* Get serial number (20 chars) */
                            ofs = 10; w = 10; n = 10;
                            break;
                
                        default:
                            return RES_PARERR;
             }
                    
            write_ata(REG_COMMAND, CMD_IDENTIFY);
            
            if (!wait_data()) return RES_ERROR;
            
            read_part(ptr, ofs, w);
            
            while (n--)
             {
                dl = *ptr; dh = *(ptr+1);
                *ptr++ = dh; *ptr++ = dl; 
             }
                        
             return RES_OK;
        break;
        
        
        case USB:
                    rc = MS_Init( &_blkSize, &_numBlks, inquiryResult );
                   
                    if (rc != OK)
                    {
                        TERMINAL_PRINTF("Could not get data from USB Inquiry: %d\r\n", rc);
                        return RES_ERROR;
                    }
                    else
                    {
                        Stat[USB] &= ~STA_NOINIT;
                         switch (ctrl)
                              {
                                    case GET_SECTOR_COUNT :    
                                       *(DWORD *)(buff) =_numBlks;
                                        break;
                            
                                    case GET_BLOCK_SIZE :   
                                       *(DWORD *)(buff) = _blkSize;
                                        return RES_OK;
                                    break;
                                    case CTRL_SYNC :      
                                        return RES_OK;
                                    break;
                                    case ATA_GET_REV :        
                                       memcpy ((CHAR *)buff,(const char *)&inquiryResult[32],8);
                                        return RES_OK;
                                        break;
                            
                                    case ATA_GET_MODEL :  
                                        memcpy ((CHAR *)buff, (const char *)&inquiryResult[8],8);
                                         return RES_OK;
                                        break;
                            
                                    case ATA_GET_SN :      
                                         memcpy ((CHAR *)buff,(const char *)&inquiryResult[16],8);
                                          return RES_OK;
                                        break;
                                        
                                    default:
                                        return RES_PARERR;
                               }
                    }
             
        break;
        
        case RAM:
              switch (ctrl)
              {
                        case GET_SECTOR_COUNT :    /* Get number of sectors on the disk (DWORD) */
                           *(DWORD *)(buff) = 1;
                            break;
                
                        case GET_BLOCK_SIZE :    /* Get erase block size in sectors (DWORD) */
                           *(DWORD *)(buff) = 1;
                            return RES_OK;
                        break;
                        case CTRL_SYNC :        /* Nothing to do */
                            return RES_OK;
                        break;
                        case ATA_GET_REV :        /* Get firmware revision (8 chars) */
                           strcpy ((CHAR *)buff,"Rev 0.01");
                            break;
                
                        case ATA_GET_MODEL :    /* Get model name (40 chars) */
                            strcpy ((CHAR *)buff,"Wavenumber RAM Drive");
                            break;
                
                        case ATA_GET_SN :        /* Get serial number (20 chars) */
                             strcpy ((CHAR *)buff,"12345");
                            break;
                        default:
                            return RES_PARERR;
             }
             return RES_OK;
        break;
        
        default:
             return RES_PARERR;
        break;
    }

    return RES_OK;
}


/*-----------------------------------------------------------------------*/
/* Device timer interrupt procedure                                      */
/*-----------------------------------------------------------------------*/
/* This function must be called in period of 10ms */

void disk_timerproc (void)
{
    static BYTE pv;
    BYTE n;
   
    n = DiskProcTimer[CF];                    /* 100Hz decrement timer */
    if (n) DiskProcTimer[CF] = --n;

    n = DiskProcTimer[USB];                    /* 100Hz decrement timer */
    if (n) DiskProcTimer[USB] = --n;

    n = DiskProcTimer[RAM];                    /* 100Hz decrement timer */
    if (n) DiskProcTimer[RAM] = --n;

    n = pv;
    pv = COMPACT_FLASH_CARD_DETECTED ;    /* Sapmle socket switch */


    //Check Compact Flash Card Detect
    if (n == pv) {                    /* Have contacts stabled? */
        if (!COMPACT_FLASH_CARD_DETECTED )
        {            /* CD1 or CD2 is high (Socket empty) */
            Stat[CF] |= (STA_NODISK | STA_NOINIT);
            SET_DATA_BUS_TO_INPUTS;        /* Float D0-D7 */
            COMPACT_FLASH_RESET_ACTIVE;    /* Assert RESET# */
            COMPACT_FLASH_POWER_DISABLE;   /* Power OFF */
            LED1_OFF; 
        }
        else 
        {                    /* CD1 and CD2 are low (Card inserted) */
            Stat[CF] &= ~STA_NODISK;
            LED1_ON;
        }
    }
    
    //Check to see if a USB drive is connected
    if(HOST_RhscIntr>0)
        LED2_ON;
    else
        LED2_OFF;
    
}


DWORD get_fattime(void)
{
    time_t CurrentTimeStamp;
    tm *CurrentLocalTime;
    DWORD FATFSTimeCode;
        
    CurrentTimeStamp = time(NULL);
    CurrentLocalTime = localtime(&CurrentTimeStamp);
        
        //Map the tm struct time into the FatFs time code    
    FATFSTimeCode =  ((CurrentLocalTime->tm_year-80)<<25) | 
                     ((CurrentLocalTime->tm_mon+1)<<21)   | 
                     ((CurrentLocalTime->tm_mday)<<16)    | 
                     ((CurrentLocalTime->tm_hour)<<11)    |
                     ((CurrentLocalTime->tm_min)<<5)     | 
                     ((CurrentLocalTime->tm_sec));

   return FATFSTimeCode;
}