Sorting source tree a bit
[tpg/acess2.git] / KernelLand / Modules / Storage / ATA / main.c
diff --git a/KernelLand/Modules/Storage/ATA/main.c b/KernelLand/Modules/Storage/ATA/main.c
new file mode 100644 (file)
index 0000000..41228d8
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * Acess2 IDE Harddisk Driver
+ * - main.c
+ */
+#define DEBUG  0
+#define VERSION        0x0032
+#include <acess.h>
+#include <modules.h>
+#include <vfs.h>
+#include <fs_devfs.h>
+#include <api_drv_common.h>
+#include <api_drv_disk.h>
+#include "common.h"
+
+// === MACROS ===
+#define IO_DELAY()     do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
+
+// === PROTOTYPES ===
+ int   ATA_Install(char **Arguments);
+void   ATA_SetupPartitions(void);
+void   ATA_SetupVFS(void);
+ int   ATA_ScanDisk(int Disk);
+void   ATA_ParseGPT(int Disk);
+void   ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length);
+Uint16 ATA_GetBasePort(int Disk);
+// Filesystem Interface
+char   *ATA_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node      *ATA_FindDir(tVFS_Node *Node, const char *Name);
+Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int   ATA_IOCtl(tVFS_Node *Node, int Id, void *Data);
+// Read/Write Interface/Quantiser
+Uint   ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk);
+Uint   ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, i386ATA, ATA_Install, NULL, "PCI", NULL);
+tVFS_NodeType  gATA_RootNodeType = {
+       .TypeName = "ATA Root Node",
+       .ReadDir = ATA_ReadDir,
+       .FindDir = ATA_FindDir
+       };
+tVFS_NodeType  gATA_DiskNodeType = {
+       .TypeName = "ATA Volume",
+       .Read = ATA_ReadFS,
+       .Write = ATA_WriteFS,
+       .IOCtl = ATA_IOCtl
+       };
+tDevFS_Driver  gATA_DriverInfo = {
+       NULL, "ata",
+       {
+               .NumACLs = 1,
+               .Size = -1,
+               .Flags = VFS_FFLAG_DIRECTORY,
+               .ACLs = &gVFS_ACL_EveryoneRX,
+               .Type = &gATA_RootNodeType
+       }
+};
+tATA_Disk      gATA_Disks[MAX_ATA_DISKS];
+ int   giATA_NumNodes;
+tVFS_Node      **gATA_Nodes;
+
+// === CODE ===
+/**
+ * \brief Initialise the ATA driver
+ */
+int ATA_Install(char **Arguments)
+{
+       int     ret;
+
+       ret = ATA_SetupIO();
+       if(ret) return ret;
+
+       ATA_SetupPartitions();
+
+       ATA_SetupVFS();
+
+       if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 )
+               return MODULE_ERR_MISC;
+
+       return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Scan all disks, looking for partitions
+ */
+void ATA_SetupPartitions(void)
+{
+        int    i;
+       for( i = 0; i < MAX_ATA_DISKS; i ++ )
+       {
+               if( !ATA_ScanDisk(i) ) {
+                       gATA_Disks[i].Name[0] = '\0';   // Mark as unused
+                       continue;
+               }
+       }
+}
+
+/**
+ * \brief Sets up the ATA drivers VFS information and registers with DevFS
+ */
+void ATA_SetupVFS(void)
+{
+        int    i, j, k;
+
+       // Count number of nodes needed
+       giATA_NumNodes = 0;
+       for( i = 0; i < MAX_ATA_DISKS; i++ )
+       {
+               if(gATA_Disks[i].Name[0] == '\0')       continue;       // Ignore
+               giATA_NumNodes ++;
+               giATA_NumNodes += gATA_Disks[i].NumPartitions;
+       }
+
+       // Allocate Node space
+       gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) );
+
+       // Set nodes
+       k = 0;
+       for( i = 0; i < MAX_ATA_DISKS; i++ )
+       {
+               if(gATA_Disks[i].Name[0] == '\0')       continue;       // Ignore
+               gATA_Nodes[ k++ ] = &gATA_Disks[i].Node;
+               for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ )
+                       gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node;
+       }
+
+       gATA_DriverInfo.RootNode.Size = giATA_NumNodes;
+}
+
+/**
+ * \brief Scan a disk, getting the size and any paritions
+ * \param Disk Disk ID to scan
+ */
+int ATA_ScanDisk(int Disk)
+{
+       tVFS_Node       *node;
+       tMBR    mbr;
+
+       ENTER("iDisk", Disk);
+       
+       // Get the disk size
+       gATA_Disks[ Disk ].Sectors = ATA_GetDiskSize(Disk);
+       if(gATA_Disks[ Disk ].Sectors == 0)
+       {
+               LEAVE('i', 0);
+               return 0;
+       }
+
+       LOG("gATA_Disks[ %i ].Sectors = 0x%x", Disk, gATA_Disks[ Disk ].Sectors);
+
+       // Create Name
+       gATA_Disks[ Disk ].Name[0] = 'A'+Disk;
+       gATA_Disks[ Disk ].Name[1] = '\0';
+
+       #if 1
+       {
+               Uint64  val = gATA_Disks[ Disk ].Sectors / 2;
+               char    *units = "KiB";
+               if( val > 4*1024 ) {
+                       val /= 1024;
+                       units = "MiB";
+               }
+               else if( val > 4*1024 ) {
+                       val /= 1024;
+                       units = "GiB";
+               }
+               else if( val > 4*1024 ) {
+                       val /= 1024;
+                       units = "TiB";
+               }
+               Log_Notice("ATA", "Disk %s: 0x%llx Sectors (%lli %s)",
+                       gATA_Disks[ Disk ].Name, gATA_Disks[ Disk ].Sectors, val, units);
+       }
+       #endif
+
+       // Get pointer to vfs node and populate it
+       node = &gATA_Disks[ Disk ].Node;
+       node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE;
+       node->NumACLs = 0;      // Means Superuser only can access it
+       node->Inode = (Disk << 8) | 0xFF;
+       node->ImplPtr = gATA_Disks[ Disk ].Name;
+
+       node->ATime = node->MTime = node->CTime = now();
+
+       node->Type = &gATA_DiskNodeType;
+
+       // --- Scan Partitions ---
+       LOG("Reading MBR");
+       // Read Boot Sector
+       if( ATA_ReadDMA( Disk, 0, 1, &mbr ) != 0 ) {
+               Log_Warning("ATA", "Error in reading MBR on %i", Disk);
+               LEAVE('i', 0);
+               return 0;
+       }
+
+       // Check for a GPT table
+       if(mbr.Parts[0].SystemID == 0xEE)
+               ATA_ParseGPT(Disk);
+       else    // No? Just parse the MBR
+               ATA_ParseMBR(Disk, &mbr);
+       
+       #if DEBUG >= 2
+       ATA_ReadDMA( Disk, 1, 1, &mbr );
+       Debug_HexDump("ATA_ScanDisk", &mbr, 512);
+       #endif
+
+       LEAVE('i', 1);
+       return 1;
+}
+
+/**
+ * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
+ * \brief Fills a parition's information structure
+ */
+void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
+{
+       ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length);
+       Part->Start = Start;
+       Part->Length = Length;
+       Part->Name[0] = 'A'+Disk;
+       if(Num >= 10) {
+               Part->Name[1] = '1'+Num/10;
+               Part->Name[2] = '1'+Num%10;
+               Part->Name[3] = '\0';
+       } else {
+               Part->Name[1] = '1'+Num;
+               Part->Name[2] = '\0';
+       }
+       Part->Node.NumACLs = 0; // Only root can read/write raw block devices
+       Part->Node.Inode = (Disk << 8) | Num;
+       Part->Node.ImplPtr = Part->Name;
+
+       Part->Node.Type = &gATA_DiskNodeType;
+       Log_Notice("ATA", "Partition %s at 0x%llx+0x%llx", Part->Name, Part->Start, Part->Length);
+       LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node);
+       LEAVE('-');
+}
+
+/**
+ * \fn void ATA_ParseGPT(int Disk)
+ * \brief Parses the GUID Partition Table
+ */
+void ATA_ParseGPT(int Disk)
+{
+       ///\todo Support GPT Disks
+       Warning("GPT Disks are currently unsupported (Disk %i)", Disk);
+}
+
+/**
+ * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos)
+ */
+char *ATA_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
+{
+       if(Pos >= giATA_NumNodes || Pos < 0)    return NULL;
+       return strdup( gATA_Nodes[Pos]->ImplPtr );
+}
+
+/**
+ * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name)
+ */
+tVFS_Node *ATA_FindDir(tVFS_Node *UNUSED(Node), const char *Name)
+{
+        int    part;
+       tATA_Disk       *disk;
+       
+       // Check first character
+       if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS)
+               return NULL;
+       disk = &gATA_Disks[Name[0]-'A'];
+       // Raw Disk
+       if(Name[1] == '\0') {
+               if( disk->Sectors == 0 && disk->Name[0] == '\0')
+                       return NULL;
+               return &disk->Node;
+       }
+
+       // Partitions
+       if(Name[1] < '0' || '9' < Name[1])      return NULL;
+       if(Name[2] == '\0') {   // <= 9
+               part = Name[1] - '0';
+               part --;
+               return &disk->Partitions[part].Node;
+       }
+       // > 9
+       if('0' > Name[2] || '9' < Name[2])      return NULL;
+       if(Name[3] != '\0')     return NULL;
+
+       part = (Name[1] - '0') * 10;
+       part += Name[2] - '0';
+       part --;
+       return &disk->Partitions[part].Node;
+
+}
+
+/**
+ * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ */
+Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+        int    disk = Node->Inode >> 8;
+        int    part = Node->Inode & 0xFF;
+
+       ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+       // Raw Disk Access
+       if(part == 0xFF)
+       {
+               if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) {
+                       LEAVE('i', 0);
+                       return 0;
+               }
+               if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
+                       Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
+       }
+       // Partition
+       else
+       {
+               if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) {
+                       LEAVE('i', 0);
+                       return 0;
+               }
+               if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
+                       Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
+               Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
+       }
+
+       {
+               int ret = DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk);
+               //Log("ATA_ReadFS: disk=%i, Offset=%lli, Length=%lli", disk, Offset, Length);
+               //Debug_HexDump("ATA_ReadFS", Buffer, Length);
+               LEAVE('i', ret);
+               return ret;
+       }
+}
+
+/**
+ * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+ */
+Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+        int    disk = Node->Inode >> 8;
+        int    part = Node->Inode & 0xFF;
+
+       // Raw Disk Access
+       if(part == 0xFF)
+       {
+               if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE )
+                       return 0;
+               if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
+                       Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
+       }
+       // Partition
+       else
+       {
+               if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
+                       return 0;
+               if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
+                       Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
+               Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
+       }
+
+       Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer);
+       Debug_HexDump("ATA_WriteFS", Buffer, Length);
+       return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk);
+}
+
+const char     *csaATA_IOCtls[] = {DRV_IOCTLNAMES, DRV_DISK_IOCTLNAMES, NULL};
+/**
+ * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief IO Control Funtion
+ */
+int ATA_IOCtl(tVFS_Node *UNUSED(Node), int Id, void *Data)
+{
+       switch(Id)
+       {
+       BASE_IOCTLS(DRV_TYPE_DISK, "i386ATA", VERSION, csaATA_IOCtls);
+       
+       case DISK_IOCTL_GETBLOCKSIZE:
+               return 512;     
+       
+       default:
+               return 0;
+       }
+       return 0;
+}
+
+// --- Disk Access ---
+/**
+ * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
+ */
+Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
+{
+        int    ret;
+       Uint    offset;
+       Uint    done = 0;
+
+       // Pass straight on to ATA_ReadDMAPage if we can
+       if(Count <= MAX_DMA_SECTORS)
+       {
+               ret = ATA_ReadDMA(Disk, Address, Count, Buffer);
+               if(ret == 0)    return 0;
+               return Count;
+       }
+
+       // Else we will have to break up the transfer
+       offset = 0;
+       while(Count > MAX_DMA_SECTORS)
+       {
+               ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
+               // Check for errors
+               if(ret != 1)    return done;
+               // Change Position
+               done += MAX_DMA_SECTORS;
+               Count -= MAX_DMA_SECTORS;
+               offset += MAX_DMA_SECTORS*SECTOR_SIZE;
+       }
+
+       ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset);
+       if(ret != 1)    return 0;
+       return done+Count;
+}
+
+/**
+ * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk)
+ */
+Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk)
+{
+        int    ret;
+       Uint    offset;
+       Uint    done = 0;
+
+       // Pass straight on to ATA_WriteDMA, if we can
+       if(Count <= MAX_DMA_SECTORS)
+       {
+               ret = ATA_WriteDMA(Disk, Address, Count, Buffer);
+               if(ret == 0)    return 0;
+               return Count;
+       }
+
+       // Else we will have to break up the transfer
+       offset = 0;
+       while(Count > MAX_DMA_SECTORS)
+       {
+               ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
+               // Check for errors
+               if(ret != 1)    return done;
+               // Change Position
+               done += MAX_DMA_SECTORS;
+               Count -= MAX_DMA_SECTORS;
+               offset += MAX_DMA_SECTORS*SECTOR_SIZE;
+       }
+
+       ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset);
+       if(ret != 1)    return 0;
+       return done+Count;
+}

UCC git Repository :: git.ucc.asn.au