Broke out the ATA IO functions (Read/WriteDMA into their own file to improve code...
[tpg/acess2.git] / Modules / Storage / ATA / main.c
1 /*
2  * Acess2 IDE Harddisk Driver
3  * - main.c
4  */
5 #define DEBUG   1
6 #define VERSION 0x0032
7 #include <acess.h>
8 #include <modules.h>
9 #include <vfs.h>
10 #include <fs_devfs.h>
11 #include <tpl_drv_common.h>
12 #include <tpl_drv_disk.h>
13 #include "common.h"
14
15 // === MACROS ===
16 #define IO_DELAY()      do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
17
18 // === PROTOTYPES ===
19  int    ATA_Install(char **Arguments);
20 void    ATA_SetupPartitions(void);
21 void    ATA_SetupVFS(void);
22  int    ATA_ScanDisk(int Disk);
23 void    ATA_ParseGPT(int Disk);
24 void    ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length);
25 Uint16  ATA_GetBasePort(int Disk);
26 // Filesystem Interface
27 char    *ATA_ReadDir(tVFS_Node *Node, int Pos);
28 tVFS_Node       *ATA_FindDir(tVFS_Node *Node, char *Name);
29 Uint64  ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
30 Uint64  ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
31  int    ATA_IOCtl(tVFS_Node *Node, int Id, void *Data);
32 // Read/Write Interface/Quantiser
33 Uint    ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk);
34 Uint    ATA_WriteRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk);
35
36 // === GLOBALS ===
37 MODULE_DEFINE(0, VERSION, i386ATA, ATA_Install, NULL, "PCI", NULL);
38 tDevFS_Driver   gATA_DriverInfo = {
39         NULL, "ata",
40         {
41                 .NumACLs = 1,
42                 .Size = -1,
43                 .Flags = VFS_FFLAG_DIRECTORY,
44                 .ACLs = &gVFS_ACL_EveryoneRX,
45                 .ReadDir = ATA_ReadDir,
46                 .FindDir = ATA_FindDir
47         }
48 };
49 tATA_Disk       gATA_Disks[MAX_ATA_DISKS];
50  int    giATA_NumNodes;
51 tVFS_Node       **gATA_Nodes;
52
53 // === CODE ===
54 /**
55  * \brief Initialise the ATA driver
56  */
57 int ATA_Install(char **Arguments)
58 {
59         int     ret;
60
61         ret = ATA_SetupIO();
62         if(ret) return ret;
63
64         ATA_SetupPartitions();
65
66         ATA_SetupVFS();
67
68         if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 )
69                 return MODULE_ERR_MISC;
70
71         return MODULE_ERR_OK;
72 }
73
74 /**
75  * \brief Scan all disks, looking for partitions
76  */
77 void ATA_SetupPartitions(void)
78 {
79          int    i;
80         for( i = 0; i < MAX_ATA_DISKS; i ++ )
81         {
82                 if( !ATA_ScanDisk(i) ) {
83                         gATA_Disks[i].Name[0] = '\0';   // Mark as unused
84                         continue;
85                 }
86         }
87 }
88
89 /**
90  * \brief Sets up the ATA drivers VFS information and registers with DevFS
91  */
92 void ATA_SetupVFS(void)
93 {
94          int    i, j, k;
95
96         // Count number of nodes needed
97         giATA_NumNodes = 0;
98         for( i = 0; i < MAX_ATA_DISKS; i++ )
99         {
100                 if(gATA_Disks[i].Name[0] == '\0')       continue;       // Ignore
101                 giATA_NumNodes ++;
102                 giATA_NumNodes += gATA_Disks[i].NumPartitions;
103         }
104
105         // Allocate Node space
106         gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) );
107
108         // Set nodes
109         k = 0;
110         for( i = 0; i < MAX_ATA_DISKS; i++ )
111         {
112                 if(gATA_Disks[i].Name[0] == '\0')       continue;       // Ignore
113                 gATA_Nodes[ k++ ] = &gATA_Disks[i].Node;
114                 for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ )
115                         gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node;
116         }
117
118         gATA_DriverInfo.RootNode.Size = giATA_NumNodes;
119 }
120
121 /**
122  * \brief Scan a disk, getting the size and any paritions
123  * \param Disk  Disk ID to scan
124  */
125 int ATA_ScanDisk(int Disk)
126 {
127         tVFS_Node       *node;
128         tMBR    mbr;
129
130         ENTER("iDisk", Disk);
131         
132         // Get the disk size
133         gATA_Disks[ Disk ].Sectors = ATA_GetDiskSize(Disk);
134         if(gATA_Disks[ Disk ].Sectors == 0)
135         {
136                 LEAVE('i', 0);
137                 return 0;
138         }
139
140         LOG("gATA_Disks[ %i ].Sectors = 0x%x", Disk, gATA_Disks[ Disk ].Sectors);
141
142         {
143                 Uint64  val = gATA_Disks[ Disk ].Sectors / 2;
144                 char    *units = "KiB";
145                 if( val > 4*1024 ) {
146                         val /= 1024;
147                         units = "MiB";
148                 }
149                 else if( val > 4*1024 ) {
150                         val /= 1024;
151                         units = "GiB";
152                 }
153                 else if( val > 4*1024 ) {
154                         val /= 1024;
155                         units = "TiB";
156                 }
157                 Log_Log("ATA", "Disk %i: 0x%llx Sectors (%lli %s)", Disk,
158                         gATA_Disks[ Disk ].Sectors, val, units);
159         }
160
161         // Create Name
162         gATA_Disks[ Disk ].Name[0] = 'A'+Disk;
163         gATA_Disks[ Disk ].Name[1] = '\0';
164
165         // Get pointer to vfs node and populate it
166         node = &gATA_Disks[ Disk ].Node;
167         node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE;
168         node->NumACLs = 0;      // Means Superuser only can access it
169         node->Inode = (Disk << 8) | 0xFF;
170         node->ImplPtr = gATA_Disks[ Disk ].Name;
171
172         node->ATime = node->MTime
173                 = node->CTime = now();
174
175         node->Read = ATA_ReadFS;
176         node->Write = ATA_WriteFS;
177         node->IOCtl = ATA_IOCtl;
178
179         // --- Scan Partitions ---
180         LOG("Reading MBR");
181         // Read Boot Sector
182         ATA_ReadDMA( Disk, 0, 1, &mbr );
183
184         // Check for a GPT table
185         if(mbr.Parts[0].SystemID == 0xEE)
186                 ATA_ParseGPT(Disk);
187         else    // No? Just parse the MBR
188                 ATA_ParseMBR(Disk, &mbr);
189         
190         #if DEBUG >= 2
191         ATA_ReadDMA( Disk, 1, 1, &mbr );
192         Debug_HexDump("ATA_ScanDisk", &mbr, 512);
193         #endif
194
195         LEAVE('i', 0);
196         return 1;
197 }
198
199 /**
200  * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
201  * \brief Fills a parition's information structure
202  */
203 void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
204 {
205         ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length);
206         Part->Start = Start;
207         Part->Length = Length;
208         Part->Name[0] = 'A'+Disk;
209         if(Num >= 10) {
210                 Part->Name[1] = '1'+Num/10;
211                 Part->Name[2] = '1'+Num%10;
212                 Part->Name[3] = '\0';
213         } else {
214                 Part->Name[1] = '1'+Num;
215                 Part->Name[2] = '\0';
216         }
217         Part->Node.NumACLs = 0; // Only root can read/write raw block devices
218         Part->Node.Inode = (Disk << 8) | Num;
219         Part->Node.ImplPtr = Part->Name;
220
221         Part->Node.Read = ATA_ReadFS;
222         Part->Node.Write = ATA_WriteFS;
223         Part->Node.IOCtl = ATA_IOCtl;
224         Log_Notice("ATA", "Note '%s' at 0x%llx, 0x%llx long", Part->Name, Part->Start, Part->Length);
225         LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node);
226         LEAVE('-');
227 }
228
229 /**
230  * \fn void ATA_ParseGPT(int Disk)
231  * \brief Parses the GUID Partition Table
232  */
233 void ATA_ParseGPT(int Disk)
234 {
235         ///\todo Support GPT Disks
236         Warning("GPT Disks are currently unsupported (Disk %i)", Disk);
237 }
238
239 /**
240  * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos)
241  */
242 char *ATA_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
243 {
244         if(Pos >= giATA_NumNodes || Pos < 0)    return NULL;
245         return strdup( gATA_Nodes[Pos]->ImplPtr );
246 }
247
248 /**
249  * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name)
250  */
251 tVFS_Node *ATA_FindDir(tVFS_Node *UNUSED(Node), char *Name)
252 {
253          int    part;
254         tATA_Disk       *disk;
255         
256         // Check first character
257         if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS)
258                 return NULL;
259         disk = &gATA_Disks[Name[0]-'A'];
260         // Raw Disk
261         if(Name[1] == '\0') {
262                 if( disk->Sectors == 0 && disk->Name[0] == '\0')
263                         return NULL;
264                 return &disk->Node;
265         }
266
267         // Partitions
268         if(Name[1] < '0' || '9' < Name[1])      return NULL;
269         if(Name[2] == '\0') {   // <= 9
270                 part = Name[1] - '0';
271                 part --;
272                 return &disk->Partitions[part].Node;
273         }
274         // > 9
275         if('0' > Name[2] || '9' < Name[2])      return NULL;
276         if(Name[3] != '\0')     return NULL;
277
278         part = (Name[1] - '0') * 10;
279         part += Name[2] - '0';
280         part --;
281         return &disk->Partitions[part].Node;
282
283 }
284
285 /**
286  * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
287  */
288 Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
289 {
290          int    disk = Node->Inode >> 8;
291          int    part = Node->Inode & 0xFF;
292
293         ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
294
295         // Raw Disk Access
296         if(part == 0xFF)
297         {
298                 if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) {
299                         LEAVE('i', 0);
300                         return 0;
301                 }
302                 if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
303                         Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
304         }
305         // Partition
306         else
307         {
308                 if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) {
309                         LEAVE('i', 0);
310                         return 0;
311                 }
312                 if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
313                         Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
314                 Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
315         }
316
317         {
318                 int ret = DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk);
319                 Log("ATA_ReadFS: disk=%i, Offset=%lli, Length=%lli", disk, Offset, Length);
320                 Debug_HexDump("ATA_ReadFS", Buffer, Length);
321                 LEAVE('i', ret);
322                 return ret;
323         }
324 }
325
326 /**
327  * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
328  */
329 Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
330 {
331          int    disk = Node->Inode >> 8;
332          int    part = Node->Inode & 0xFF;
333
334         // Raw Disk Access
335         if(part == 0xFF)
336         {
337                 if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE )
338                         return 0;
339                 if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
340                         Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
341         }
342         // Partition
343         else
344         {
345                 if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
346                         return 0;
347                 if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
348                         Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
349                 Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
350         }
351
352         Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer);
353         Debug_HexDump("ATA_WriteFS", Buffer, Length);
354         return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk);
355 }
356
357 const char      *csaATA_IOCtls[] = {DRV_IOCTLNAMES, DRV_DISK_IOCTLNAMES, NULL};
358 /**
359  * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data)
360  * \brief IO Control Funtion
361  */
362 int ATA_IOCtl(tVFS_Node *UNUSED(Node), int Id, void *Data)
363 {
364         switch(Id)
365         {
366         BASE_IOCTLS(DRV_TYPE_DISK, "i386ATA", VERSION, csaATA_IOCtls);
367         
368         case DISK_IOCTL_GETBLOCKSIZE:
369                 return 512;     
370         
371         default:
372                 return 0;
373         }
374         return 0;
375 }
376
377 // --- Disk Access ---
378 /**
379  * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
380  */
381 Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
382 {
383          int    ret;
384         Uint    offset;
385         Uint    done = 0;
386
387         // Pass straight on to ATA_ReadDMAPage if we can
388         if(Count <= MAX_DMA_SECTORS)
389         {
390                 ret = ATA_ReadDMA(Disk, Address, Count, Buffer);
391                 if(ret == 0)    return 0;
392                 return Count;
393         }
394
395         // Else we will have to break up the transfer
396         offset = 0;
397         while(Count > MAX_DMA_SECTORS)
398         {
399                 ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
400                 // Check for errors
401                 if(ret != 1)    return done;
402                 // Change Position
403                 done += MAX_DMA_SECTORS;
404                 Count -= MAX_DMA_SECTORS;
405                 offset += MAX_DMA_SECTORS*SECTOR_SIZE;
406         }
407
408         ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset);
409         if(ret != 1)    return 0;
410         return done+Count;
411 }
412
413 /**
414  * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
415  */
416 Uint ATA_WriteRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
417 {
418          int    ret;
419         Uint    offset;
420         Uint    done = 0;
421
422         // Pass straight on to ATA_WriteDMA, if we can
423         if(Count <= MAX_DMA_SECTORS)
424         {
425                 ret = ATA_WriteDMA(Disk, Address, Count, Buffer);
426                 if(ret == 0)    return 0;
427                 return Count;
428         }
429
430         // Else we will have to break up the transfer
431         offset = 0;
432         while(Count > MAX_DMA_SECTORS)
433         {
434                 ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
435                 // Check for errors
436                 if(ret != 1)    return done;
437                 // Change Position
438                 done += MAX_DMA_SECTORS;
439                 Count -= MAX_DMA_SECTORS;
440                 offset += MAX_DMA_SECTORS*SECTOR_SIZE;
441         }
442
443         ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset);
444         if(ret != 1)    return 0;
445         return done+Count;
446 }

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