Dependencies:   mbed

Revision:
0:76427232f435
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FAT_FS/diskio.c	Thu Feb 16 00:41:26 2012 +0000
@@ -0,0 +1,934 @@
+#include "mbed.h"
+#include "LPC17xx.h"
+#include "integer.h"
+#include "diskio.h"
+#include "stdarg.h"
+#include "USBHost.h"
+#include "Terminal.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];
+
+DWORD BlockSize;
+DWORD BlockCount;
+
+//#define _CF_DEBUG_MESSAGES
+//#define _CF_DEBUG_READ_ATA
+
+#ifdef _CF_DEBUG_MESSAGES 
+   #define  CF_DEBUG(...) printf(__VA_ARGS__)    
+#else
+   #define  CF_DEBUG     do{}while(0);
+#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;
+    
+    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:
+        
+                if(USB_Disk_Detected)
+                {
+                     TERMINAL_PRINTF("USB Disk Detected.\r\n");
+                     Stat[USB] &= ~(STA_NOINIT | STA_NODISK);
+                }
+                else
+                {
+                     TERMINAL_PRINTF("USB Disk not attached.\r\n");
+                     Stat[USB] |= STA_NOINIT | STA_NODISK;
+                }
+              
+                  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;
+    
+    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(MassStorage_Read(sector, 1,(u8 *) buff, 512) == 0)
+                  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(  MassStorage_Write(sector, 1, (u8 *)buff, 512) == 0)
+                  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;
+    
+    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:
+                   if (USB_Disk_Detected == 0)
+                    {
+                       // TERMINAL_PRINTF("USB Disk not attached\r\n");
+                        return RES_ERROR;
+                    }
+                    else
+                    {
+                        Stat[USB] &= ~STA_NOINIT;
+                         switch (ctrl)
+                              {
+                              
+                                    case GET_SECTOR_COUNT:    
+                                     /* if(MassStorage_ReadCapacity(USB_Disk_Device, &BlockCount, &BlockSize) == 0)
+                                        {
+                                               
+                                           *(DWORD *)(buff) = BlockCount;         
+                                                                               
+                                           
+                                        }
+                                        else
+                                        {
+                                            return RES_ERROR;
+                                        }*/
+                                         return RES_OK;
+                                        break;
+                            
+                                    case GET_BLOCK_SIZE :   
+                                      
+                                      /*  if(MassStorage_ReadCapacity(USB_Disk_Device, &BlockCount, &BlockSize) == 0)
+                                        {
+                                           *(DWORD *)(buff) = BlockSize;         
+                                            return RES_OK;
+                                        }
+                                        else
+                                        {
+                                            return RES_ERROR;
+                                        }*/
+                                        return RES_OK;
+                                    
+                                    break;
+                                    
+                                    case CTRL_SYNC :      
+                                        return RES_OK;
+                                    break;
+                                    
+                                    case ATA_GET_REV :        
+                                       strcpy ((CHAR *)buff,"00000");
+                                        return RES_OK;
+                                        break;
+                            
+                                    case ATA_GET_MODEL :  
+                                        strcpy ((CHAR *)buff, "000000");
+                                         return RES_OK;
+                                        break;
+                            
+                                    case ATA_GET_SN :      
+                                         strcpy ((CHAR *)buff,"00000000");
+                                          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(USB_Disk_Detected>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;
+}
\ No newline at end of file