/**************************************************************************** * This file is part of the AVRIDE device driver. * * Copyright (c) 2002-2003 by Michael Fischer. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * **************************************************************************** * History: * * 14.12.02 mifi First Version * 23.12.02 mifi Add FileOpen, FileClose, FileError, FileSize, * FileSeek and FileRead. But the FileSeek function. * does not work in the moment, later... * 28.12.02 mifi Now support FAT16 AND FAT32. * 01.01.03 mifi Support long directory entries, but without to * check the checksum of the short entry. * Change FAT32FileSize return value from int to long. * The max size of a long filename segment is * (FAT_LONG_NAME_LEN-1). But the complete filename can * be longer. * * segment1/segment2/segment3/index.html * * segmentX max length = (FAT_LONG_NAME_LEN-1) * * 04.01.03 mifi Take a look at the return values... * 18.01.03 mifi Change Licence from GPL to BSD. * 25.01.03 mifi Implement a new FindFile function. * I have some trouble with short file names under * Win98. Win98 store a short name like "enlogo.gif" * as a long name, nasty OS. * Remove FAT32_MAX_FILES and the array aFileHandle, * a file handle will now be allocated by NutHeapAlloc, * therefore we have no restrictions about the count of * the open file handle. (Only by available memory) * 27.01.03 mifi Rename all FAT32xxx function to FATxxx. * * 28.01.03 mifi Start porting to Nut/OS 3.X.X * 19.06.03 mifi Change the call of IDEInit, now we use the BaseAddress * of 0. Because the fat module does not need to know the * address. It will be handled in ide.c. * 29.06.03 mifi First ATAPI-Version * Now we can read files from a CD-ROM. But there exist * some open points: * - only first session from a multisession CD is supported * - only iso9660, no Joliet support now, later ****************************************************************************/ #define __FAT_C__ #include #include #include #include #include #include #include #include #include #include /*==========================================================*/ /* DEFINE: All Structures and Common Constants */ /*==========================================================*/ // // FAT return codes // #define FAT_OK 0 #define FAT_ERROR -1 #define FAT_ERROR_EOF -2 #define FAT_ERROR_IDE -3 // // Define for correct return values Nut/OS // #define NUTDEV_OK 0 #define NUTDEV_ERROR -1 #define FAT_MAX_DRIVE 2 // // Some defines for the FAT structures // #define ZIP_DRIVE_BR_SECTOR 32 #define BPB_RsvdSecCnt 32 #define BPB_NumFATs 2 #define BPB_HiddSec 63 #define FAT32_MEDIA 0xf8 #define FAT32_OFFSET_FSINFO 1 #define FAT32_OFFSET_BACKUP_BOOT 6 #define FAT16_CLUSTER_EOF 0x0000FFFF #define FAT16_CLUSTER_ERROR 0x0000FFF7 #define FAT16_CLUSTER_MASK 0x0000FFFF #define FAT32_CLUSTER_EOF 0x0FFFFFFF #define FAT32_CLUSTER_ERROR 0x0FFFFFF7 #define FAT32_CLUSTER_MASK 0x0FFFFFFF #define FAT_SIGNATURE 0xAA55 #define MBR_SIGNATURE FAT_SIGNATURE #define MBR_FAT32 0x0C #define FSINFO_FIRSTSIGNATURE 0x41615252 #define FSINFO_FSINFOSIGNATURE 0x61417272 #define FSINFO_SIGNATURE FAT_SIGNATURE #define DIRECTORY_ATTRIBUTE_READ_ONLY 0x01 #define DIRECTORY_ATTRIBUTE_HIDDEN 0x02 #define DIRECTORY_ATTRIBUTE_SYSTEM_FILE 0x04 #define DIRECTORY_ATTRIBUTE_VOLUME_ID 0x08 #define DIRECTORY_ATTRIBUTE_DIRECTORY 0x10 #define DIRECTORY_ATTRIBUTE_ARCHIVE 0x20 // // DIRECTORY_ATTRIBUTE_READ_ONLY | // DIRECTORY_ATTRIBUTE_HIDDEN | // DIRECTORY_ATTRIBUTE_SYSTEM_FILE | // DIRECTORY_ATTRIBUTE_VOLUME_ID // #define DIRECTORY_ATTRIBUTE_LONG_NAME 0x0F // // DIRECTORY_ATTRIBUTE_READ_ONLY | // DIRECTORY_ATTRIBUTE_HIDDEN | // DIRECTORY_ATTRIBUTE_SYSTEM_FILE | // DIRECTORY_ATTRIBUTE_VOLUME_ID | // DIRECTORY_ATTRIBUTE_DIRECTORY | // DIRECTORY_ATTRIBUTE_ARCHIVE // #define DIRECTORY_ATTRIBUTE_LONG_NAME_MASK 0x3F #define FAT_NAME_LEN 8 #define FAT_EXT_LEN 3 // // FAT_SHORT_NAME_LEN name len = // name + ext + 1 for the point // #define FAT_SHORT_NAME_LEN (FAT_NAME_LEN+FAT_EXT_LEN+1) #define FAT_LONG_NAME_LEN 64 // // Some stuff for HD and CD, DRIVE_INFO Flags // // #define FLAG_FAT_IS_CDROM 0x0001 #define FLAG_FAT_IS_ZIP 0x0002 // // DiskSize to SectorPerCluster table // typedef struct { DWORD DiskSize; BYTE SecPerClusVal; } DSKSZTOSECPERCLUS; typedef struct _FAT32FileDataTime { unsigned Seconds:5; unsigned Minute:6; unsigned Hour:5; unsigned Day:5; unsigned Month:4; unsigned Year:7; } FAT32_FILEDATETIME, *PFAT32_FILEDATETIME; typedef struct _FAT32DirectoryEntry { BYTE Name[FAT_NAME_LEN]; BYTE Extension[FAT_EXT_LEN]; BYTE Attribute; BYTE Reserved[8]; WORD HighCluster; FAT32_FILEDATETIME Date; WORD LowCluster; DWORD FileSize; } FAT32_DIRECTORY_ENTRY; typedef struct _FAT32DirectoryEntryLong { BYTE Order; WORD Name1[5]; BYTE Attribute; BYTE Type; BYTE Chksum; WORD Name2[6]; WORD LowCluster; WORD Name3[2]; } FAT32_DIRECTORY_ENTRY_LONG; typedef struct _FAT32FileSystemInformation { DWORD FirstSignature; BYTE Reserved1[480]; DWORD FSInfoSignature; DWORD NumberOfFreeClusters; DWORD MostRecentlyAllocatedCluster; BYTE Reserved2[12]; BYTE Reserved3[2]; WORD Signature; } FAT32_FSINFO; typedef struct _FAT32PartitionEntry { BYTE BootInd; BYTE FirstHead; BYTE FirstSector; BYTE FirstTrack; BYTE FileSystem; BYTE LastHead; BYTE LastSector; BYTE LastTrack; DWORD StartSectors; DWORD NumSectors; } FAT32_PARTITION_ENTRY; typedef struct _FAT32PartionTable { BYTE LoadInstruction[446]; FAT32_PARTITION_ENTRY Partition[4]; WORD Signature; /* AA55 */ } FAT32_PARTITION_TABLE; typedef struct _bpbfat16 { BYTE DrvNum; BYTE Reserved1; BYTE BootSig; DWORD VollID; BYTE VolLab[11]; BYTE FilSysType[8]; BYTE Reserved2[28]; } BPBFAT16; typedef struct _bpbfat32 { DWORD FATSz32; // xxx WORD ExtFlags; // 0 WORD FSVer; // must 0 DWORD RootClus; // WORD FSInfo; // typically 1 WORD BkBootSec; // typically 6 BYTE Reserved[12]; // set all to zero BYTE DrvNum; // must 0x80 BYTE Reserved1; // set all to zero BYTE BootSig; // must 0x29 DWORD VollID; // xxx BYTE VolLab[11]; // "abcdefghijk" BYTE FilSysType[8]; // "FAT32 " } BPBFAT32; typedef union _bpboffset36 { BPBFAT16 FAT16; BPBFAT32 FAT32; } BPBOFFSET36; typedef struct _FAT32BootRecord { BYTE JumpBoot[3]; // 0xeb, 0x58, 0x90 BYTE OEMName[8]; // "MSWIN4.1" WORD BytsPerSec; // must 512 BYTE SecPerClus; // 8 for 4K cluster WORD RsvdSecCnt; // typically 32 for FAT32 BYTE NumFATs; // always 2 WORD RootEntCnt; // must 0 for FAT32 WORD TotSec16; // must 0 for FAT32 BYTE Media; // must 0xf8 WORD FATSz16; // 0 for FAT32 WORD SecPerTrk; // 63 for FAT32 WORD NumHeads; // 255 for FAT32 DWORD HiddSec; // 63 for FAT32 DWORD TotSec32; // xxx BPBOFFSET36 Off36; BYTE Reserved[420]; WORD Signature; // must 0xAA55 } FAT32_BOOT_RECORD, *PFAT32_BOOT_RECORD; typedef struct _fat_entry_table16 { WORD aEntry[256]; } FAT_ENTRY_TABLE16; typedef struct _fat_entry_table32 { DWORD aEntry[128]; } FAT_ENTRY_TABLE32; typedef union _fat_dir_table { FAT32_DIRECTORY_ENTRY aShort[16]; FAT32_DIRECTORY_ENTRY_LONG aLong[16]; } FAT_DIR_TABLE; typedef struct _drive_info { BYTE bIsFAT32; BYTE bDevice; BYTE bSectorsPerCluster; BYTE bFlags; WORD wSectorSize; DWORD dwRootDirSectors; DWORD dwFirstRootDirSector; DWORD dwRootCluster; DWORD dwFAT1StartSector; DWORD dwFAT2StartSector; DWORD dwCluster2StartSector; DWORD dwClusterSize; } DRIVE_INFO; typedef struct _fhandle { DWORD dwFileSize; DWORD dwStartCluster; DWORD dwReadCluster; DWORD dwFilePointer; /* total file pointer */ DWORD dwClusterPointer; /* cluster read pointer */ int nLastError; int nEOF; DRIVE_INFO *pDrive; } FHANDLE; #if (IDE_SUPPORT_ATAPI == 1) // // Some ATAPI stuff // typedef struct _atapi_pvd { BYTE descr_type; BYTE magic[5]; BYTE descr_ver; BYTE unused; BYTE sysid[32]; BYTE volid[32]; BYTE zeros1[8]; BYTE seknum[8]; BYTE zeros2[32]; BYTE volsetsize[4]; BYTE volseqnum[4]; BYTE seksize[4]; BYTE pathtablen[8]; DWORD firstsek_LEpathtab1_LE; DWORD firstsek_LEpathtab2_LE; BYTE firstsek_BEpathtab1_BE[4]; BYTE firstsek_BEpathtab2_BE[4]; BYTE rootdir[34]; BYTE volsetid[128]; BYTE pubid[128]; BYTE dataprepid[128]; BYTE appid[128]; BYTE copyr[37]; BYTE abstractfileid[37]; BYTE bibliofileid[37]; BYTE creationdate[17]; BYTE modify[17]; BYTE expire[17]; BYTE effective[17]; BYTE filestruc_ver; BYTE zero; BYTE app_use[512]; BYTE res[653]; } ATAPI_PVD; // // Descriptor values // #define ATAPI_BOOT_RECORD 0x00 #define ATAPI_PVD_DESC 0x01 #define ATAPI_SVD_DESC 0x02 #define ATAPI_VDST_DESC 0xFF typedef struct _ATAPIDirectoryRecord { BYTE bRecordSize; BYTE bShouldBeZero; DWORD dwFirstSector; DWORD dwNotUse1; DWORD dwEntrySize; DWORD dwNotUse2; BYTE bDate[7]; BYTE bFlags; BYTE bInterleaveInfo[2]; BYTE bVolumeSequenceNumber[4]; BYTE bIdentifierLength; BYTE bName[1]; } ATAPI2_DIRECTORY_RECORD; #endif /*==========================================================*/ /* DEFINE: Definition of all local Data */ /*==========================================================*/ static int gnIsInit = FALSE; static BYTE *pSectorBuffer = NULL; static char *pLongName1 = NULL; static char *pLongName2 = NULL; static DRIVE_INFO sDriveInfo[FAT_MAX_DRIVE]; static HANDLE hFATSemaphore; /*==========================================================*/ /* DEFINE: Definition of all local Procedures */ /*==========================================================*/ /************************************************************/ /* FATLock */ /************************************************************/ void FATLock(void) { NutEventWait(&hFATSemaphore, 0); } /************************************************************/ /* FATFree */ /************************************************************/ void FATFree(void) { NutEventPost(&hFATSemaphore); } /************************************************************/ /* FATSemaInit */ /************************************************************/ void FATSemaInit(void) { NutEventPost(&hFATSemaphore); } /************************************************************/ /* GetFirstSectorOfCluster */ /************************************************************/ static DWORD GetFirstSectorOfCluster(DRIVE_INFO * pDrive, DWORD dwCluster) { DWORD dwSector; if (pDrive->bFlags & FLAG_FAT_IS_CDROM) { dwSector = dwCluster; } else { dwSector = (dwCluster - 2) * pDrive->bSectorsPerCluster; dwSector += pDrive->dwCluster2StartSector; } return (dwSector); } /************************************************************/ /* GetNextCluster */ /************************************************************/ static DWORD GetNextCluster(DRIVE_INFO * pDrive, DWORD dwCluster) { DWORD dwNextCluster; DWORD dwSector; DWORD dwIndex; FAT_ENTRY_TABLE16 *pFatTable16; FAT_ENTRY_TABLE32 *pFatTable32; if (pDrive->bFlags & FLAG_FAT_IS_CDROM) { dwNextCluster = dwCluster + 1; } else { if (pDrive->bIsFAT32 == TRUE) { // // (IDE_SECTOR_SIZE / sizeof(long)) == 128 // dwSector = (dwCluster / 128) + pDrive->dwFAT1StartSector; dwIndex = dwCluster % 128; IDEReadSectors(pDrive->bDevice, pSectorBuffer, dwSector, 1); pFatTable32 = (FAT_ENTRY_TABLE32 *) pSectorBuffer; dwNextCluster = (pFatTable32->aEntry[dwIndex] & FAT32_CLUSTER_MASK); if ((dwNextCluster == FAT32_CLUSTER_EOF) || (dwNextCluster == FAT32_CLUSTER_ERROR)) { dwNextCluster = 0; } } else { /* FAT16 */ // // (IDE_SECTOR_SIZE / sizeof(word)) == 256 // dwSector = (dwCluster / 256) + pDrive->dwFAT1StartSector; dwIndex = dwCluster % 256; IDEReadSectors(pDrive->bDevice, pSectorBuffer, dwSector, 1); pFatTable16 = (FAT_ENTRY_TABLE16 *) pSectorBuffer; dwNextCluster = (pFatTable16->aEntry[dwIndex] & FAT16_CLUSTER_MASK); if ((dwNextCluster == FAT16_CLUSTER_EOF) || (dwNextCluster == FAT16_CLUSTER_ERROR)) { dwNextCluster = 0; } } /* endif pDrive->bIsFAT32 */ } return (dwNextCluster); } /************************************************************/ /* GetLongChar */ /************************************************************/ static char GetLongChar(WORD wValue) { BYTE Value; Value = (BYTE) (wValue & 0x00FF); if (Value == 0xFF) { Value = 0; } if (Value != 0) { Value = toupper(Value); } return ((char) Value); } #if (IDE_SUPPORT_ATAPI == 1) /************************************************************/ /* CheckATAPIName */ /* */ /* Some values are not allowed in a "ATAPIname", */ /* Therefore we will change the name here. */ /************************************************************/ static char *CheckATAPIName(char *pLongName) { char *pRead; pRead = pLongName; while (*pRead != 0) { switch (*pRead) { case '-': *pRead = '_'; break; case ' ': *pRead = '_'; break; } pRead++; } return (pLongName); } /************************************************************/ /* RemoveChar */ /* */ /* Houston, we have a problem.... */ /* Some chars are not allowed in a name, in the case of */ /* ATAPI, the filename test.pic.gif is stored as */ /* testpic.gif. For a directory, we must remove all '.' */ /* Therefore we must correct the name :o( */ /************************************************************/ static char *RemoveChar(char *pLongName, BYTE bDir) { char *pRead; char *pWrite; BYTE bPointCount; pRead = pLongName; pWrite = pLongName; if (bDir == TRUE) { while (*pRead != 0) { if (*pRead != '.') { *pWrite = *pRead; pWrite++; } pRead++; } *pWrite = 0; } else { // // 1. search all points in the name // pRead = pLongName; bPointCount = 0; while (*pRead != 0) { if (*pRead == '.') { bPointCount++; } pRead++; } if (bPointCount >= 2) { // // Remove the points... // bPointCount--; pRead = pLongName; pWrite = pLongName; while (*pRead != 0) { if ((*pRead == '.') && (bPointCount)) { bPointCount--; } else { *pWrite = *pRead; pWrite++; } pRead++; } *pWrite = 0; } } return (pLongName); } /************************************************************/ /* FindFileATAPI */ /* */ /* Find a file by a given name pLongName. */ /* For ATAPI, we have NO cluster. A cluster is the same */ /* as a sector. */ /************************************************************/ static DWORD FindFileATAPI(DRIVE_INFO * pDrive, FAT32_DIRECTORY_ENTRY * pSearchEntry, char *pLongName, DWORD dwDirCluster, DWORD * pFileSize, int nIsLongName) { DWORD dwSector; DWORD dwDirSector; DWORD dwMaxDirSector; WORD wByteCount; BYTE bLongNameLen; BYTE bSearchFlags; ATAPI2_DIRECTORY_RECORD *pDirEntry; dwSector = 0; dwDirSector = dwDirCluster; dwMaxDirSector = pDrive->dwFirstRootDirSector + pDrive->dwRootDirSectors - 1; bSearchFlags = 0; if (pSearchEntry->Attribute == DIRECTORY_ATTRIBUTE_DIRECTORY) { bSearchFlags = 0x02; pLongName = RemoveChar(pLongName, TRUE); } else { pLongName = RemoveChar(pLongName, FALSE); } // // ' ' and '-' are not allowed in the name, change it... // pLongName = CheckATAPIName(pLongName); bLongNameLen = strlen(pLongName); while (dwDirCluster <= dwMaxDirSector) { IDEReadSectors(pDrive->bDevice, pSectorBuffer, dwDirCluster, 1); wByteCount = 0; pDirEntry = (ATAPI2_DIRECTORY_RECORD *) & pSectorBuffer[0]; while (wByteCount < pDrive->dwClusterSize) { if (pDirEntry->bRecordSize == 0) { break; } else { // // Check pDirEntry // if (((pDirEntry->bFlags & 0x03) == bSearchFlags) && (pDirEntry->bIdentifierLength >= bLongNameLen) && (strncmp((char*)&pDirEntry->bName[0], pLongName, bLongNameLen) == 0)) { // // We have found the file :-) // dwSector = pDirEntry->dwFirstSector; *pFileSize = pDirEntry->dwEntrySize; // Sorry for the return here :-( return (dwSector); } wByteCount += pDirEntry->bRecordSize; pDirEntry = (ATAPI2_DIRECTORY_RECORD *) & pSectorBuffer[wByteCount]; } } dwDirCluster++; } return (dwSector); } #endif /************************************************************/ /* FindFile */ /* */ /* Find a file by a given name pLongName. */ /* */ /* It is possible that a SHORT name like "enlogo.gif" */ /* is stored as a LONG name. I have seen this */ /* nasty behaviour by Win98. Therefore I will check */ /* the long name too, even if nIsLongName is FALSE. */ /************************************************************/ static DWORD FindFile(DRIVE_INFO * pDrive, FAT32_DIRECTORY_ENTRY * pSearchEntry, char *pLongName, DWORD dwDirCluster, DWORD * pFileSize, int nIsLongName) { int i, x; BYTE bError; int nNameLen; int nMaxLen; BYTE bFound; BYTE bEndLoop; BYTE bOrder; BYTE bMaxOrder; int nDirMaxSector; DWORD dwSector; DWORD dwNewCluster; FAT32_DIRECTORY_ENTRY *pDirEntryShort; FAT32_DIRECTORY_ENTRY_LONG *pDirEntryLong; char *pDirName = 0; FAT_DIR_TABLE *pDirTable; bError = FALSE; *pFileSize = 0; dwNewCluster = 0; nNameLen = strlen(pLongName); bMaxOrder = (BYTE) ((nNameLen + 12) / 13); nMaxLen = (int) (bMaxOrder * 13); if (nMaxLen >= (FAT_LONG_NAME_LEN - 1)) { bError = TRUE; } bOrder = (BYTE) (0x40 | bMaxOrder); if (bOrder == 0xE5) { // // I do not know what should I do if the bOrder is 0xe5. // This is a sign for a "empty" entry. // bError = TRUE; } if (bError == FALSE) { bFound = FALSE; bEndLoop = FALSE; while ((bEndLoop == FALSE) && (dwDirCluster != 0)) { dwSector = GetFirstSectorOfCluster(pDrive, dwDirCluster); nDirMaxSector = (int) pDrive->bSectorsPerCluster; // // Test for special case dwDirCluster and FAT16. // if ((dwDirCluster == 1) && (pDrive->bIsFAT32 == FALSE)) { dwSector = pDrive->dwFirstRootDirSector; nDirMaxSector = (int) pDrive->dwRootDirSectors; } // // One cluster has SecPerCluster sectors. // for (i = 0; i < nDirMaxSector; i++) { IDEReadSectors(pDrive->bDevice, pSectorBuffer, dwSector + i, 1); pDirTable = (FAT_DIR_TABLE *) pSectorBuffer; // // And one sector has 16 entries. // // IDE_SECTOR_SIZE / sizeof(FAT32_DIRECTORY_ENTRY) = 16 // for (x = 0; x < 16; x++) { if (bFound == TRUE) { pDirEntryShort = (FAT32_DIRECTORY_ENTRY *) & pDirTable->aShort[x]; dwNewCluster = pDirEntryShort->HighCluster; dwNewCluster = (dwNewCluster << 16) | (DWORD) pDirEntryShort->LowCluster; *pFileSize = pDirEntryShort->FileSize; bEndLoop = TRUE; break; } // // Check for valid entry. // pDirEntryShort = (FAT32_DIRECTORY_ENTRY *) & pDirTable->aShort[x]; pDirEntryLong = (FAT32_DIRECTORY_ENTRY_LONG *) & pDirTable->aLong[x]; if (nIsLongName == FALSE) { // // Check if it could be a short name. If Win2000 tell us it // is a short name, it is true. But with Win98 we must // test both, short and long... // if ((pDirEntryShort->Name[0] != 0xE5) && (pDirEntryShort->Name[0] != 0x00)) { if (memcmp(pDirEntryShort, pSearchEntry, 11) == 0) { // // Check for the correct attribute, this is done with // the '&' and not with the memcmp. // With the '&' it is possible to find a hidden archive too :-) // if ((pDirEntryShort->Attribute & pSearchEntry->Attribute) == pSearchEntry->Attribute) { dwNewCluster = pDirEntryShort->HighCluster; dwNewCluster = (dwNewCluster << 16) | (DWORD) pDirEntryShort->LowCluster; *pFileSize = pDirEntryShort->FileSize; bEndLoop = TRUE; break; } /* endif Attribute */ } /* endif test Name+Ext */ } /* endif Name[0] != 0xe5, 0x00 */ } /* endif nIsLongName == FALSE */ if ((pDirEntryLong->Attribute == DIRECTORY_ATTRIBUTE_LONG_NAME) && (pDirEntryLong->Order == bOrder)) { // // Next bOrder is bOrder-- // if (bOrder & 0x40) { bOrder &= ~0x40; // // Get the space for the name. // pDirName = pLongName2; // // Set the end of string. // pDirName[nMaxLen--] = 0; } pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name3[1]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name3[0]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[5]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[4]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[3]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[2]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[1]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[0]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[4]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[3]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[2]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[1]); pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[0]); bOrder--; if (bOrder == 0) { // // Now compare the name. // bOrder = (BYTE) (0x40 | bMaxOrder); nMaxLen = (int) (bMaxOrder * 13); if (memcmp(pLongName, pDirName, strlen(pLongName)) == 0) { // // The next entry will be the correct entry. // bFound = TRUE; } } } /* pDirEntryLong->Order == bOrder */ } /* endfor x<16 */ if (bEndLoop == TRUE) { break; } } if (bEndLoop == FALSE) { // // No file found in this cluster, get the next one. // dwDirCluster = GetNextCluster(pDrive, dwDirCluster); } } /* endwhile */ } /* endif bError == FALSE */ return (dwNewCluster); } #if (IDE_SUPPORT_ATAPI == 1) /************************************************************/ /* MountATAPI */ /************************************************************/ static int MountATAPI(int nDrive) { int nError = IDE_OK; DWORD dwSector; DWORD dwPVDSector; DRIVE_INFO *pDrive; ATAPI_PVD *pPVD; pDrive = &sDriveInfo[nDrive]; dwSector = ATAPI_START_SEARCH_SECTOR; dwPVDSector = 0; while (dwSector <= ATAPI_MAX_SEARCH_SECTOR) { nError = IDEReadSectors(nDrive, pSectorBuffer, dwSector, 1); if (nError != IDE_OK) { break; } else { if ((pSectorBuffer[0] == ATAPI_PVD_DESC) && (pSectorBuffer[1] == 'C') && (pSectorBuffer[2] == 'D') && (pSectorBuffer[3] == '0') && (pSectorBuffer[4] == '0') && (pSectorBuffer[5] == '1')) { dwPVDSector = dwSector; } if ((pSectorBuffer[0] == ATAPI_VDST_DESC) && (pSectorBuffer[1] == 'C') && (pSectorBuffer[2] == 'D') && (pSectorBuffer[3] == '0') && (pSectorBuffer[4] == '0') && (pSectorBuffer[5] == '1')) { break; } } dwSector++; } if (dwPVDSector != 0) { IDEReadSectors(nDrive, pSectorBuffer, dwPVDSector, 1); pPVD = (ATAPI_PVD *) pSectorBuffer; pDrive->bSectorsPerCluster = 1; pDrive->dwFirstRootDirSector = *(DWORD *) & pPVD->rootdir[2]; pDrive->dwFAT1StartSector = pPVD->firstsek_LEpathtab1_LE; pDrive->dwFAT2StartSector = pPVD->firstsek_LEpathtab2_LE; pDrive->dwRootDirSectors = pDrive->dwFAT1StartSector - pDrive->dwFirstRootDirSector; pDrive->dwRootCluster = pDrive->dwFirstRootDirSector; pDrive->dwClusterSize = (DWORD) pDrive->wSectorSize; //nError = IDEATAPISetCDSpeed(nDrive, 0xFFFF); } return (nError); } #endif /************************************************************/ /* MountIDE */ /************************************************************/ static int MountIDE(int nDrive) { int nError; int i; DWORD dwSector; DWORD dwFATSz; DWORD dwRootDirSectors; FAT32_PARTITION_TABLE *pPartitionTable; FAT32_BOOT_RECORD *pBootRecord; DRIVE_INFO *pDrive; nError = IDE_OK; i = nDrive; pDrive = &sDriveInfo[nDrive]; dwSector = 0; if (pDrive->bFlags & FLAG_FAT_IS_ZIP) { dwSector = ZIP_DRIVE_BR_SECTOR; } else { // // Try to find a PartitionTable. // nError = IDEReadSectors(nDrive, pSectorBuffer, 0, 1); if (nError == IDE_OK) { pPartitionTable = (FAT32_PARTITION_TABLE *) pSectorBuffer; if (pPartitionTable->Signature == FAT_SIGNATURE) { if (pPartitionTable->Partition[0].NumSectors) { // // We found a PartitionTable, read BootRecord. // dwSector = pPartitionTable->Partition[0].StartSectors; } } } } if (dwSector != 0) { IDEReadSectors(i, pSectorBuffer, dwSector, 1); pBootRecord = (FAT32_BOOT_RECORD *) pSectorBuffer; // // Test valid BootRecord. // if (pBootRecord->Signature == FAT_SIGNATURE) { pDrive->bSectorsPerCluster = pBootRecord->SecPerClus; if (pBootRecord->FATSz16 != 0) { dwFATSz = pBootRecord->FATSz16; pDrive->bIsFAT32 = FALSE; pDrive->dwRootCluster = 1; /* special value, see */ /* FindFile */ } else { dwFATSz = pBootRecord->Off36.FAT32.FATSz32; pDrive->bIsFAT32 = TRUE; pDrive->dwRootCluster = pBootRecord->Off36.FAT32.RootClus; } dwRootDirSectors = ((pBootRecord->RootEntCnt * 32) + (pBootRecord->BytsPerSec - 1)) / pBootRecord->BytsPerSec; sDriveInfo[i].dwFAT1StartSector = pBootRecord->HiddSec + pBootRecord->RsvdSecCnt; pDrive->dwFAT2StartSector = pDrive->dwFAT1StartSector + dwFATSz; sDriveInfo[i].dwCluster2StartSector = pBootRecord->HiddSec + pBootRecord->RsvdSecCnt + (pBootRecord->NumFATs * dwFATSz) + dwRootDirSectors; pDrive->dwClusterSize = pBootRecord->SecPerClus * pDrive->wSectorSize; pDrive->dwRootDirSectors = dwRootDirSectors; pDrive->dwFirstRootDirSector = pDrive->dwFAT2StartSector + dwFATSz; } /* endif pBootRecord->Signature */ } /* endif dwSector != 0 */ return (nError); } /*==========================================================*/ /* DEFINE: All code exported by the NUTDEVICE */ /*==========================================================*/ /************************************************************/ /* FATMountDrive */ /************************************************************/ static int FATMountDrive(int nDrive) { BYTE i; int nError; nError = IDE_OK; FATLock(); nDrive = nDrive; if (pLongName1 == NULL) { pLongName1 = (char *) NutHeapAlloc(FAT_LONG_NAME_LEN); } if (pLongName2 == NULL) { pLongName2 = (char *) NutHeapAlloc(FAT_LONG_NAME_LEN); } if (pSectorBuffer == NULL) { pSectorBuffer = (BYTE *) NutHeapAlloc(MAX_SECTOR_SIZE); } if ((pSectorBuffer != NULL) && (pLongName1 != NULL) && (pLongName2 != NULL)) { for (i = 0; i < FAT_MAX_DRIVE; i++) { memset((BYTE *) & sDriveInfo[i], 0x00, sizeof(DRIVE_INFO)); sDriveInfo[i].bDevice = i; sDriveInfo[i].wSectorSize = IDEGetSectorSize(i); if (IDEIsCDROMDevice(i) == TRUE) { sDriveInfo[i].bFlags |= FLAG_FAT_IS_CDROM; } if (IDEIsZIPDevice(i) == TRUE) { sDriveInfo[i].bFlags |= FLAG_FAT_IS_ZIP; } switch (sDriveInfo[i].wSectorSize) { case IDE_SECTOR_SIZE:{ nError = MountIDE(i); break; } #if (IDE_SUPPORT_ATAPI == 1) case ATAPI_SECTOR_SIZE:{ nError = MountATAPI(i); break; } #endif default:{ nError = IDE_ERROR; break; } } } /* endfor i= IDE_DRIVE_C) && (nDrive <= IDE_DRIVE_D)) { pDrive = &sDriveInfo[nDrive]; pDrive->bSectorsPerCluster = 0; } else { nError = FAT_ERROR; } FATFree(); return (nError); } /************************************************************/ /* CFMount */ /************************************************************/ static void CFMount(int nDrive) { BYTE *pSectorBuffer; pSectorBuffer = (BYTE *) NutHeapAlloc(MAX_SECTOR_SIZE); if (pSectorBuffer != NULL) { FATMountDrive(nDrive); NutHeapFree(pSectorBuffer); } } /************************************************************/ /* CFUnMount */ /************************************************************/ static void CFUnMount(int nDrive) { FATUnMountDrive(nDrive); } /************************************************************/ /* FATInit */ /************************************************************/ static int FATInit(NUTDEVICE * pDevice) { int nError; int nIDEMode; BYTE *pSectorBuffer; nError = NUTDEV_OK; nIDEMode = IDE_HARDDISK; pSectorBuffer = NULL; if (gnIsInit == FALSE) { gnIsInit = TRUE; // // Get the mode. // switch (pDevice->dev_base) { case FAT_MODE_IDE_HD: nIDEMode = IDE_HARDDISK; break; case FAT_MODE_IDE_HD_7MHZ: nIDEMode = IDE_HARDDISK_7MHZ; break; case FAT_MODE_IDE_CF: nIDEMode = IDE_COMPACT_FLASH; break; case FAT_MODE_MEM_CF: nIDEMode = MEM_8BIT_COMPACT_FLASH; break; default: nError = NUTDEV_ERROR; break; } if (nError == NUTDEV_OK) { // // Init my semaphore. // FATSemaInit(); pSectorBuffer = (BYTE *) NutHeapAlloc(MAX_SECTOR_SIZE); if (pSectorBuffer != NULL) { IDEInit(0, nIDEMode, CFMount, CFUnMount); IDEMountDevice(IDE_DRIVE_C, pSectorBuffer); FATMountDrive(IDE_DRIVE_C); NutHeapFree(pSectorBuffer); } /* endif pSectorBuffer != NULL */ } /* endif nError == NUTDEV_OK */ } /* endif gnIsInit == FALSE */ return (nError); } /************************************************************/ /* FATFileOpen */ /* */ /* Opens an existing file for reading. */ /* */ /* Parameters: pName points to a string that specifies the */ /* name of the file to open. The name must */ /* exactly match the full pathname of the file.*/ /* */ /* Returns: A pointer to a FILE structure that can be */ /* used to read the file and retrieve */ /* information about the file. */ /* */ /* A return value of -1 indicates an error. */ /************************************************************/ static NUTFILE *FATFileOpen(NUTDEVICE * pDevice, const char *pName, int nMode, int nAccess) { int i, x; int nError; int nEndWhile; DWORD dwFileSize; DWORD dwCluster; FHANDLE *hFile; DRIVE_INFO *pDrive; FAT32_DIRECTORY_ENTRY sDirEntry; NUTFILE *hNUTFile; int nLongName; char *pLongName; char *pShortName; char *pExtension; // // If the user has forgotten to call NUTDeviceOpen, // we must call FATInit. // if (gnIsInit == FALSE) { FATInit(pDevice); } FATLock(); pDrive = NULL; nError = TRUE; nLongName = FALSE; pLongName = (char *) pLongName1; // // hFile is our FAT-Handle. // hFile = NULL; // // hNUTFile is the.... correct, NUT handle. // hNUTFile = (NUTFILE *) NUTDEV_ERROR; if (pDevice != NULL) { switch (pDevice->dev_name[4]) { case 'C':{ pDrive = &sDriveInfo[IDE_DRIVE_C]; break; } case 'D':{ pDrive = &sDriveInfo[IDE_DRIVE_D]; break; } } } if ((pDrive != NULL) && (pDrive->bSectorsPerCluster != 0) && (pName[0] != 0)) { // // Create a new file handle. // hFile = (FHANDLE *) NutHeapAlloc(sizeof(FHANDLE)); if ((pDrive->dwFAT1StartSector) && (hFile != NULL) && (*pName != '.')) { memset(hFile, 0x00, sizeof(FHANDLE)); // // Start by the ROOT dir... // dwCluster = pDrive->dwRootCluster; // // If the first char a "/", jump over, e.g. "/index.html" // // if (*pName == '/') { pName++; } nEndWhile = FALSE; while (nEndWhile == FALSE) { /* master loop */ nLongName = FALSE; // // Get Name // i = 0; while ((*pName != '/') && (*pName != '\\') && (*pName != 0)) { if (i >= (FAT_LONG_NAME_LEN - 1)) { nEndWhile = TRUE; break; } pLongName[i] = toupper(*pName); i++; pName++; } /* endwhile Name */ if (nEndWhile == FALSE) { pLongName[i] = 0; // // Check if it is a Long Directory Entry. // Yes, I know that 'i' is the length of the string. // But the code is easier to read with the next strlen. // if (strlen(pLongName) <= FAT_SHORT_NAME_LEN) { // // It could be a ShortName, but "abc.defg" is possible // and this is a long name too. Therfore we need some tests. // pExtension = strchr(pLongName, '.'); if (pExtension == NULL) { if (strlen(pLongName) > FAT_NAME_LEN) { nLongName = TRUE; } else { nLongName = FALSE; } } else { // // Check the length of the extensions. // pExtension++; /* jump over the '.' */ if (strlen(pExtension) > 3) { nLongName = TRUE; } } } else { /* Len > FAT_SHORT_NAME_LEN */ // // Now we have a LongName, sure. // See the "nasty Win98" in FindFile :-) // nLongName = TRUE; } // // Here we knows, if we have a LongName or ShortName. // if (nLongName == FALSE) { // // ShortName // pShortName = pLongName; memset(&sDirEntry, 0x00, sizeof(FAT32_DIRECTORY_ENTRY)); memset(sDirEntry.Name, 0x20, FAT_NAME_LEN); memset(sDirEntry.Extension, 0x20, FAT_EXT_LEN); // // Get the name // i = 0; while ((pShortName[i] != '.') && (pShortName[i] != 0)) { sDirEntry.Name[i] = pShortName[i]; i++; } // // And the extension // if (pShortName[i] == '.') { i++; /* jump over the '.' */ x = 0; while (pShortName[i] != 0) { sDirEntry.Extension[x] = pShortName[i]; i++; x++; } } } // // The file could be a long or short one. // I have seen that Win98 store the short filename // in a long one :-( // switch (*pName) { // // The file is an ARCHIVE // case 0:{ nEndWhile = TRUE; sDirEntry.Attribute = DIRECTORY_ATTRIBUTE_ARCHIVE; if (pDrive->bFlags & FLAG_FAT_IS_CDROM) { dwCluster = FindFileATAPI(pDrive, &sDirEntry, pLongName, dwCluster, &dwFileSize, nLongName); } else { #if (IDE_SUPPORT_ATAPI == 1) dwCluster = FindFile(pDrive, &sDirEntry, pLongName, dwCluster, &dwFileSize, nLongName); #else dwCluster = 0; #endif } if (dwCluster != 0) { hFile->dwFileSize = dwFileSize; hFile->dwStartCluster = dwCluster; hFile->dwReadCluster = dwCluster; hFile->dwFilePointer = 0; hFile->dwClusterPointer = 0; hFile->pDrive = pDrive; hFile->nLastError = FAT_OK; hFile->nEOF = FALSE; nError = FALSE; } break; } /* endcase 0 */ // // The file is a DIRECTORY // case '/': case '\\':{ pName++; /* jump over the char */ sDirEntry.Attribute = DIRECTORY_ATTRIBUTE_DIRECTORY; if (pDrive->bFlags & FLAG_FAT_IS_CDROM) { dwCluster = FindFileATAPI(pDrive, &sDirEntry, pLongName, dwCluster, &dwFileSize, nLongName); } else { #if (IDE_SUPPORT_ATAPI == 1) dwCluster = FindFile(pDrive, &sDirEntry, pLongName, dwCluster, &dwFileSize, nLongName); #else dwCluster = 0; #endif } if (dwCluster != 0) { // // The new Cluster is the Cluster of the directory // } else { nEndWhile = TRUE; } break; } /* endcase / \ */ default:{ nEndWhile = TRUE; break; } } /* end switch */ } /* endif nEndWhile == FALSE */ } /* end while */ } /* endif pDrive->dwFAT1StartSector */ if (nError == TRUE) { // // We found no file, therefore we can delete our FAT-Handle // if (hFile != NULL) { NutHeapFree(hFile); } } else { // // We have found a FILE and can create a NUT-Handle. // hNUTFile = NutHeapAlloc(sizeof(NUTFILE)); if (hNUTFile != NULL) { hNUTFile->nf_dev = pDevice; hNUTFile->nf_fcb = hFile; } else { // // Error, no mem for the NUT-Handle, therefore we // can delete our FAT-Handle too. // NutHeapFree(hFile); } } } /* endif pName[0] != 0 */ FATFree(); return (hNUTFile); } /************************************************************/ /* FATFileClose */ /* */ /* Close a previously opened file. */ /* */ /* Parameters: hNUTFile Identifies the file to close. */ /* This pointer must have been created by */ /* calling FAT32FileOpen(). */ /* */ /* Returns: 0 if the function is successfully closed, */ /* -1 otherwise. */ /************************************************************/ static int FATFileClose(NUTFILE * hNUTFile) { int nError; FHANDLE *hFile; nError = NUTDEV_ERROR; FATLock(); if (hNUTFile != NULL) { hFile = (FHANDLE *) hNUTFile->nf_fcb; if (hFile != NULL) { // // Clear our FAT-Handle // NutHeapFree(hFile); } // // Clear the NUT-Handle // NutHeapFree(hNUTFile); nError = NUTDEV_OK; } FATFree(); return (nError); } /************************************************************/ /* FATFileSize */ /* */ /* Retrieve the size of a file. */ /* */ /* Parameters: pFile Identifies the file to query. */ /* This pointer must have been created by */ /* calling FAT32FileOpen(). */ /* */ /* Returns: The number of bytes in this file or */ /* -1 if an error occured */ /************************************************************/ long FATFileSize(NUTFILE * hNUTFile) { long lSize; FHANDLE *hFile; FATLock(); lSize = NUTDEV_ERROR; if (hNUTFile != NULL) { hFile = (FHANDLE *) hNUTFile->nf_fcb; if (hFile != NULL) { lSize = hFile->dwFileSize; } } FATFree(); return (lSize); } #if 0 /************************************************************/ /* FATFileSeek */ /* */ /* Move the file pointer to a new position. */ /* It points to the next byte to be read from a file. */ /* The file pointer is automatically incremented for */ /* each byte read. When the file is opened, it is at */ /* position 0, the beginning of the file. */ /* */ /* Parameters: pFile Identifies the file to seek. */ /* This pointer must have been created by */ /* calling FAT32FileOpen(). */ /* */ /* lPos Specifies the new absolute position */ /* of the file pointer. */ /* */ /* Returns: 0 if the function is successful, */ /* -1 otherwise. */ /************************************************************/ int FATFileSeek(FILE * pFile, long lPos) { int nError = NUTDEV_ERROR; FHANDLE *hFile; FATLock(); hFile = (FHANDLE *) pFile; // // We must do some work here... // nError = FAT32_ERROR; FATFree(); return (nError); } #endif /************************************************************/ /* FATFileRead */ /* */ /* Read data from a file. */ /* */ /* Parameters: pFile Identifies the file to read from. */ /* This pointer must have been created by */ /* calling FAT32FileOpen(). */ /* */ /* pData Points to the buffer that receives */ /* the data. */ /* */ /* nSize Specifies the number of bytes to */ /* read from the file. */ /* */ /* Returns: The number of bytes read from the file or */ /* -1 if an error occured. */ /************************************************************/ int FATFileRead(NUTFILE * hNUTFile, void *pData, int nSize) { int nError; int nBytesRead; int nBytesToRead; FHANDLE *hFile; DRIVE_INFO *pDrive; BYTE *pByte; DWORD dwReadSector; DWORD dwSector; int nSectorCount; int nSectorOffset; WORD wSectorSize; nBytesRead = 0; FATLock(); hFile = NULL; nError = NUTDEV_ERROR; if (hNUTFile != NULL) { hFile = (FHANDLE *) hNUTFile->nf_fcb; } if ((hFile != NULL) && (nSize != 0)) { if (hFile->dwFilePointer < hFile->dwFileSize) { if ((hFile->dwFilePointer + nSize) > hFile->dwFileSize) { nSize = (int) (hFile->dwFileSize - hFile->dwFilePointer); } pDrive = (DRIVE_INFO *) hFile->pDrive; pByte = (BYTE *) pData; nBytesRead = nSize; wSectorSize = pDrive->wSectorSize; while (nSize) { dwSector = GetFirstSectorOfCluster(pDrive, hFile->dwReadCluster); nSectorCount = hFile->dwClusterPointer / wSectorSize; nSectorOffset = hFile->dwClusterPointer % wSectorSize; // // (Sector + SectorCount) is the sector to read // SectorOffset is the start position in the sector itself // dwReadSector = dwSector + nSectorCount; nError = IDEReadSectors(pDrive->bDevice, pSectorBuffer, dwReadSector, 1); if (nError == IDE_OK) { // // Find the size we can read from ONE sector // if (nSize > (int) wSectorSize) { nBytesToRead = wSectorSize; } else { nBytesToRead = nSize; } // // Test inside a sector // if ((nSectorOffset + nBytesToRead) > (int) wSectorSize) { nBytesToRead = wSectorSize - nSectorOffset; } memcpy(pByte, &pSectorBuffer[nSectorOffset], nBytesToRead); pByte += nBytesToRead; hFile->dwFilePointer += nBytesToRead; hFile->dwClusterPointer += nBytesToRead; // // Check for EOF if (hFile->dwFilePointer >= hFile->dwFileSize) { hFile->nEOF = TRUE; } if (hFile->dwClusterPointer >= pDrive->dwClusterSize) { // // We must switch to the next cluster // hFile->dwReadCluster = GetNextCluster(pDrive, hFile->dwReadCluster); hFile->dwClusterPointer = 0; } nSize -= nBytesToRead; } else { /* IDEReadSectors Error */ nBytesRead = 0; hFile->nLastError = FAT_ERROR_IDE; break; } /* endif nError == IDE_OK */ } /* endwhile */ } else { /* reached the EOF */ hFile->nLastError = FAT_ERROR_EOF; } /* endif hFile->dwFilePointer < hFile->dwFileSize */ } /* endif (hFile != NULL) && (nSize != 0) */ FATFree(); return (nBytesRead); } static int FATFileWrite(NUTFILE * hNUTFile, const void *pData, int nSize) { int nError; nError = NUTDEV_ERROR; return (nError); } #ifdef __HARVARD_ARCH__ static int FATFileWriteP(NUTFILE * hNUTFile, PGM_P pData, int nSize) { int nError; nError = NUTDEV_ERROR; return (nError); } #endif // // FATC Device information structure. // NUTDEVICE devFATC = { 0, /* Pointer to next device. */ /* Unique device name. */ {'F', 'A', 'T', '_', 'C', 0, 0, 0, 0} , IFTYP_STREAM, /* Type of device. */ 2, /* Base address. */ 0, /* First interrupt number. */ 0, /* Interface control block. */ 0, /* Driver control block. */ FATInit, /* Driver initialization routine. */ 0, /* Driver specific control function. */ FATFileRead, /* Driver specific read function. */ FATFileWrite, /* Driver specific write function. */ #ifdef __HARVARD_ARCH__ FATFileWriteP, /* Driver specific write_p function. */ #endif FATFileOpen, /* Driver specific open function. */ FATFileClose, /* Driver specific close function. */ FATFileSize, /* Driver specific size function. */ NULL, /* Driver specific select function. */ };