Modules/AHCI - Reading the boot sector works
[tpg/acess2.git] / KernelLand / Modules / Storage / AHCI / ahci.c
1 /*
2  * Acess2 Kernel - AHCI Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * ahci.c
6  * - Driver core
7  */
8 #define DEBUG   1
9 #define VERSION 0x0001
10 #include <acess.h>
11 #include <timers.h>
12 #include <modules.h>
13 #include <drv_pci.h>
14 #include "ahci.h"
15
16 // === CONSTANTS ===
17 #define MAX_CONTROLLERS 4
18
19 // === PROTOTYPES ===
20  int    AHCI_Install(char **Arguments);
21  int    AHCI_Cleanup(void);
22 // - Hardware init
23  int    AHCI_InitSys(tAHCI_Ctrlr *Ctrlr);
24 void    AHCI_QueryDevice(tAHCI_Ctrlr *Ctrlr, int PortNum);
25 void    AHCI_IRQHandler(int UNUSED(IRQ), void *Data);
26 void    AHCI_int_IRQHandlerPort(tAHCI_Port *Port);
27 // - LVM Interface
28 int     AHCI_ReadSectors(void *Ptr, Uint64 Address, size_t Count, void *Buffer);
29 int     AHCI_WriteSectors(void *Ptr, Uint64 Address, size_t Count, const void *Buffer);
30 // - Low Level
31  int    AHCI_SendLBA28Cmd(tAHCI_Port *Port, int bWrite,
32                 Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data
33                 );
34  int    AHCI_SendLBA48Cmd(tAHCI_Port *Port, int bWrite,
35                 Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data
36                 );
37  int    AHCI_DoFIS(tAHCI_Port *Port, int bWrite,
38                 size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
39                 size_t OutSize, void *OutData
40                 );
41  int    AHCI_WaitForInterrupt(tAHCI_Port *Port, unsigned int Timeout);
42
43 // === GLOABLS ===
44 MODULE_DEFINE(0, VERSION, AHCI, AHCI_Install, AHCI_Cleanup, "LVM", NULL);
45 tAHCI_Ctrlr     gaAHCI_Controllers[MAX_CONTROLLERS];
46 tLVM_VolType    gAHCI_VolumeType = {
47         .Name = "AHCI",
48         .Read = AHCI_ReadSectors,
49         .Write = AHCI_WriteSectors
50         };
51
52 // === CODE ====
53 int AHCI_Install(char **Arguments)
54 {
55          int    rv;
56         LOG("offsetof(struct sAHCI_MemSpace, Ports) = %x", offsetof(struct sAHCI_MemSpace, Ports));
57         ASSERT( offsetof(struct sAHCI_MemSpace, Ports) == 0x100 );
58
59         // 0106XX = Standard, 010400 = RAID
60          int    i = 0;
61          int    id = -1;
62         while( (id = PCI_GetDeviceByClass(0x010601, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
63         {
64                 tAHCI_Ctrlr *ctrlr = &gaAHCI_Controllers[i];
65                 ctrlr->PMemBase = PCI_GetBAR(id, 5);
66                 // 
67                 if( !ctrlr->PMemBase )
68                 {
69                         // ctrlr->PMemBase = PCI_AllocateBAR(id, 5);
70                         // TODO: Allocate space
71                         continue ;
72                 }
73                 // - IO Address?
74                 if( (ctrlr->PMemBase & 0x1FFF) ) {
75                         Log_Warning("AHCI", "Controller %i [PCI %i] is invalid (BAR5=%P)",
76                                 i, id, ctrlr->PMemBase);
77                         continue;
78                 }
79                 
80                 ctrlr->IRQ = PCI_GetIRQ(id);
81                 IRQ_AddHandler(ctrlr->IRQ, AHCI_IRQHandler, ctrlr);
82
83                 // Prepare MMIO (two pages according to the spec)
84                 ctrlr->MMIO = (void*)MM_MapHWPages(ctrlr->PMemBase, 2);
85
86                 LOG("%i [%i]: %P/IRQ%i mapped to %p",
87                         i, id, ctrlr->PMemBase, ctrlr->IRQ, ctrlr->MMIO);
88
89                 LOG(" CAP = %x, PI = %x, VS = %x",
90                         ctrlr->MMIO->CAP, ctrlr->MMIO->PI, ctrlr->MMIO->VS);
91
92                 if( (rv = AHCI_InitSys(ctrlr)) ) {
93                         // Clean up controller's allocations
94                         // TODO: Should an init error cause module unload?
95                         return rv;
96                 }       
97
98                 i ++;
99         }
100         if( id >= 0 ) {
101                 Log_Notice("AHCI", "Only up to %i controllers are supported", MAX_CONTROLLERS);
102         }
103         
104         return 0;
105 }
106
107 int AHCI_Cleanup(void)
108 {
109         return 0;
110 }
111
112 static inline void *AHCI_AllocPage(tAHCI_Ctrlr *Ctrlr, const char *AllocName)
113 {
114         void    *ret;
115         #if PHYS_BITS > 32
116         if( Ctrlr->Supports64Bit )
117                 ret = (void*)MM_AllocDMA(1, 64, NULL);
118         else
119         #endif
120                 ret = (void*)MM_AllocDMA(1, 32, NULL);
121         if( !ret ) {
122                 Log_Error("AHCI", "Unable to allocate page for '%s'", AllocName);
123                 return NULL;
124         }
125         return ret;
126 }
127
128 static inline void AHCI_int_SetAddr(tAHCI_Ctrlr *Ctrlr, volatile Uint32 *Addr, tPAddr PAddr)
129 {
130         Addr[0] = PAddr;
131         #if PHYS_BITS > 32
132         if(Ctrlr->Supports64Bit)
133                 Addr[1] = PAddr >> 32;
134         else
135         #endif
136                 Addr[1] = 0;
137 }
138
139 int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr)
140 {
141         // 1. Set GHC.AE
142         Ctrlr->MMIO->GHC |= AHCI_GHC_AE;
143         // 2. Enumerate ports using PI
144         tTime   basetime = now();
145         for( int i = 0; i < 32; i ++ )
146         {
147                 if( !(Ctrlr->MMIO->PI & (1 << i)) )
148                         continue ;
149                 
150                 Ctrlr->PortCount ++;
151                 
152                 volatile struct s_port  *port = &Ctrlr->MMIO->Ports[i];
153                 // 3. (for each port) Ensure that port is not running
154                 //  - if PxCMD.(ST|CR|FRE|FR) all are clear, port is idle
155                 if( (port->PxCMD & (AHCI_PxCMD_ST|AHCI_PxCMD_CR|AHCI_PxCMD_FRE|AHCI_PxCMD_FR)) == 0 )
156                         continue ;
157                 //  - Idle is set by clearing PxCMD.ST and waiting for .CR to clear (timeout 500ms)
158                 //  - AND .FRE = 0, checking .FR (timeout 500ms again)
159                 port->PxCMD = 0;
160                 basetime = now();
161                 //  > On timeout, port/HBA reset
162         }
163         Ctrlr->Ports = malloc( Ctrlr->PortCount * sizeof(*Ctrlr->Ports) );
164         if( !Ctrlr->Ports ) {
165                 return MODULE_ERR_MALLOC;
166         }
167         // - Process timeouts after all ports have been poked, saves time
168         for( int i = 0, idx = 0; i < 32; i ++ )
169         {
170                 if( !(Ctrlr->MMIO->PI & (1 << i)) )
171                         continue ;
172                 volatile struct s_port  *port = &Ctrlr->MMIO->Ports[i];
173                 Ctrlr->Ports[idx].Ctrlr = Ctrlr;
174                 Ctrlr->Ports[idx].Idx = i;
175                 Ctrlr->Ports[idx].MMIO = port;
176                 idx ++;
177         
178                 while( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) && now()-basetime < 500 )
179                         Time_Delay(10);
180                 
181                 if( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) )
182                 {
183                         Log_Error("AHCI", "Port %i did not return to idle", i);
184                 }
185         }
186         // 4. Read CAP.NCS to get number of command slots
187         Ctrlr->NCS = (Ctrlr->MMIO->CAP & AHCI_CAP_NCS) >> AHCI_CAP_NCS_ofs;
188         // 5. Allocate PxCLB and PxFB for each port (setting PxCMD.FRE once done)
189         struct sAHCI_RcvdFIS    *fis_vpage = NULL;
190         struct sAHCI_CmdHdr     *cl_vpage = NULL;
191         struct sAHCI_CmdTable   *ct_vpage = NULL;
192         for( int i = 0; i < Ctrlr->PortCount; i ++ )
193         {
194                 tAHCI_Port      *port = &Ctrlr->Ports[i];
195                 // CLB First (1 KB alignemnt)
196                 if( ((tVAddr)cl_vpage & 0xFFF) == 0 ) {
197                         cl_vpage = AHCI_AllocPage(Ctrlr, "CLB");
198                         if( !cl_vpage )
199                                 return MODULE_ERR_MALLOC;
200                 }
201                 port->CmdList = cl_vpage;
202                 cl_vpage += 1024/sizeof(*cl_vpage);
203
204                 tPAddr  cl_paddr = MM_GetPhysAddr(port->CmdList);
205                 AHCI_int_SetAddr(Ctrlr, &port->MMIO->PxCLB, cl_paddr);
206
207                 // Received FIS Area
208                 // - If there is space for the FIS in the end of the 1K block, use it
209                 if( Ctrlr->NCS <= (1024-256)/32 )
210                 {
211                         Ctrlr->Ports[i].RcvdFIS = (void*)(cl_vpage - 256/32);
212                 }
213                 else
214                 {
215                         if( ((tVAddr)fis_vpage & 0xFFF) == 0 ) {
216                                 fis_vpage = AHCI_AllocPage(Ctrlr, "FIS");
217                                 if( !fis_vpage )
218                                         return MODULE_ERR_MALLOC;
219                         }
220                         Ctrlr->Ports[i].RcvdFIS = fis_vpage;
221                         fis_vpage ++;
222                 }
223                 tPAddr  fis_paddr = MM_GetPhysAddr(Ctrlr->Ports[i].RcvdFIS);
224                 AHCI_int_SetAddr(Ctrlr, &port->MMIO->PxFB, fis_paddr);
225         
226                 // Command tables
227                 for( int j = 0; j < Ctrlr->NCS; j ++ )
228                 {
229                         if( ((tVAddr)ct_vpage & 0xFFF) == 0 ) {
230                                 ct_vpage = AHCI_AllocPage(Ctrlr, "CmdTab");
231                                 if( !ct_vpage )
232                                         return MODULE_ERR_MALLOC;
233                         }
234                         port->CommandTables[j] = ct_vpage;
235                         port->CmdList[j].Flags = 0;
236                         port->CmdList[j].PRDTL = 0;
237                         port->CmdList[j].PRDBC = 0;
238                         AHCI_int_SetAddr(Ctrlr, &port->CmdList[j].CTBA, MM_GetPhysAddr(ct_vpage));
239                         ct_vpage ++;
240                 }
241                 
242                 LOG("Port #%i: CLB=%p/%P, FB=%p/%P", i,
243                         Ctrlr->Ports[i].CmdList, cl_paddr,
244                         Ctrlr->Ports[i].RcvdFIS, fis_paddr);
245         }
246         
247         // 6. Clear PxSERR with 1 to each implimented bit
248         // > Clear PxIS then IS.IPS before setting PxIE/GHC.IE
249         // 7. Set PxIE with desired interrupts and set GHC.IE
250         for( int i = 0; i < Ctrlr->PortCount; i ++ )
251         {
252                 tAHCI_Port      *port = &Ctrlr->Ports[i];
253                 
254                 port->MMIO->PxSERR = 0x3FF783;
255                 port->MMIO->PxIS = -1;
256                 port->MMIO->PxIE = AHCI_PxIS_CPDS|AHCI_PxIS_DSS|AHCI_PxIS_PSS|AHCI_PxIS_DHRS;
257         }
258         Ctrlr->MMIO->IS = -1;
259         Ctrlr->MMIO->GHC |= AHCI_GHC_IE;
260
261         // Start command engine on all implimented ports
262         for( int i = 0; i < Ctrlr->PortCount; i ++ )
263         {
264                 tAHCI_Port      *port = &Ctrlr->Ports[i];
265                 port->MMIO->PxCMD |= AHCI_PxCMD_ST|AHCI_PxCMD_FRE;
266         }
267         
268         // Detect present ports using:
269         // > PxTFD.STS.BSY = 0, PxTFD.STS.DRQ = 0, and PxSSTS.DET = 3
270         for( int i = 0; i < Ctrlr->PortCount; i ++ )
271         {
272                 if( Ctrlr->Ports[i].MMIO->PxTFD & (AHCI_PxTFD_STS_BSY|AHCI_PxTFD_STS_DRQ) )
273                         continue ;
274                 if( (Ctrlr->Ports[i].MMIO->PxSSTS & AHCI_PxSSTS_DET) != 3 << AHCI_PxSSTS_DET_ofs )
275                         continue ;
276                 
277                 LOG("Port #%i: Connection detected", i);
278                 Ctrlr->Ports[i].bPresent = 1;
279                 AHCI_QueryDevice(Ctrlr, i);
280         }
281         return 0;
282 }
283
284 static inline void _flipChars(char *buffer, size_t pairs)
285 {
286         for( int i = 0; i < pairs; i ++ )
287         {
288                 char tmp = buffer[i*2];
289                 buffer[i*2] = buffer[i*2+1];
290                 buffer[i*2+1] = tmp;
291         }
292 }
293
294 void AHCI_QueryDevice(tAHCI_Ctrlr *Ctrlr, int PortNum)
295 {
296         tAHCI_Port      *const Port = &Ctrlr->Ports[PortNum];
297         tATA_Identify   data;   
298
299         AHCI_SendLBA28Cmd(Port, false, 0, 0, 0, ATA_CMD_IDENTIFY_DEVICE, sizeof(data), &data);
300         AHCI_WaitForInterrupt(Port, 1000);
301         // TODO: Check status from command
302         
303         // TODO: on error, mark device as bad and return
304
305         _flipChars(data.SerialNum, 20/2);
306         _flipChars(data.FirmwareVer, 8/2);
307         _flipChars(data.ModelNumber, 40/2);
308         LOG("data.SerialNum   = '%.20s'", data.SerialNum);
309         LOG("data.FirmwareVer = '%.8s'", data.FirmwareVer);
310         LOG("data.ModelNumber = '%.40s'", data.ModelNumber);
311
312         Uint64  sector_count;
313         if( data.Sectors48 != 0 ) {
314                 // Use LBA48 size
315                 LOG("Size[48] = 0x%X", (Uint64)data.Sectors48);
316                 sector_count = data.Sectors48;
317         }
318         else {
319                 // Use LBA28 size
320                 LOG("Size[28] = 0x%x", data.Sectors28);
321                 sector_count = data.Sectors28;
322         }
323         
324         // Create LVM name
325         char lvmname[4+1+20+1];
326         strcpy(lvmname, "AHCI:");
327         memcpy(lvmname+5, data.SerialNum, 20);
328         for(int i = 20+5; i-- && lvmname[i] == ' '; )
329                 lvmname[i] = '\0';
330         
331         // Register with LVM
332         Port->LVMHandle = LVM_AddVolume(&gAHCI_VolumeType, lvmname, Port, 512, sector_count);
333 }
334
335 void AHCI_IRQHandler(int UNUSED(IRQ), void *Data)
336 {
337         tAHCI_Ctrlr     *Ctrlr = Data;
338         tAHCI_Port      *port = &Ctrlr->Ports[0];
339         
340         Uint32  IS = Ctrlr->MMIO->IS;
341         Uint32  PI = Ctrlr->MMIO->PI;
342         LOG("Ctrlr->MMIO->IS = %x", IS);
343
344         for( int i = 0; i < 32; i ++ )
345         {
346                 if( !(PI & (1 << i)) )
347                         continue ;
348                 if( IS & (1 << i) )
349                 {
350                         AHCI_int_IRQHandlerPort(port);
351                 }
352                 port ++;
353         }       
354
355         Ctrlr->MMIO->IS = IS;
356 }
357
358 void AHCI_int_IRQHandlerPort(tAHCI_Port *Port)
359 {
360         Uint32  PxIS = Port->MMIO->PxIS;
361         LOG("port->MMIO->PxIS = %x", PxIS);
362         Port->LastIS |= PxIS;
363         if( PxIS & AHCI_PxIS_CPDS ) {
364                 // Port change detected
365         }
366
367         if( PxIS & AHCI_PxIS_DHRS ) {
368                 LOG("Port->RcvdFIS->RFIS = {");
369                 LOG(".Status = 0x%02x", Port->RcvdFIS->RFIS.Status);
370                 LOG(".Error = 0x%02x", Port->RcvdFIS->RFIS.Error);
371                 LOG("}");
372         }       
373
374         // Get bitfield of completed commands (Issued but no activity)
375         Uint32  done_commands = Port->IssuedCommands ^ Port->MMIO->PxSACT;
376         LOG("done_commands = %x", done_commands);
377         for( int i = 0; i < 32; i ++ )
378         {
379                 if( !(done_commands & (1 << i)) )
380                         continue;
381                 ASSERT( Port->IssuedCommands & (1 << i) );
382                 // If complete, post a SHORTWAIT
383                 if( Port->CommandThreads[i] ) {
384                         LOG("%i - Poking thread %p", i, Port->CommandThreads[i]);
385                         Threads_PostEvent(Port->CommandThreads[i], THREAD_EVENT_SHORTWAIT);
386                         Port->CommandThreads[i] = NULL;
387                 }
388                 Port->IssuedCommands &= ~(1 << i);
389         }
390         Port->MMIO->PxIS = PxIS;
391 }
392
393 // - LVM Interface
394 int AHCI_ReadSectors(void *Ptr, Uint64 Address, size_t Count, void *Buffer)
395 {
396         tAHCI_Port      *Port = Ptr;
397
398         memset(Buffer, 0xFF, Count*512);
399         
400         ASSERT(Count <= 8096/512);
401         if( (Address+Count) < (1 << 24) )
402                 AHCI_SendLBA28Cmd(Port, 0, 0, Count, Address, ATA_CMD_READDMA28, Count*512, Buffer);
403         else
404                 AHCI_SendLBA48Cmd(Port, 0, 0, Count, Address, ATA_CMD_READDMA48, Count*512, Buffer);
405         if( AHCI_WaitForInterrupt(Port, 1000) )
406                 return 0;
407         //Debug_HexDump("AHCI_ReadSectors", Buffer, Count*512);
408         // TODO: Check status from command
409         return Count;
410 }
411
412 int AHCI_WriteSectors(void *Ptr, Uint64 Address, size_t Count, const void *Buffer)
413 {
414         return 0;
415 }
416
417 // -- Internals
418 int AHCI_int_GetCommandSlot(tAHCI_Port *Port)
419 {
420         Mutex_Acquire(&Port->lCommandSlots);
421         Uint32  PxCI = Port->MMIO->PxCI;
422         Uint32  PxSACT = Port->MMIO->PxSACT;
423         for( int i = 0; i < Port->Ctrlr->NCS; i ++ )
424         {
425                 if( PxCI & (1 << i) )
426                         continue ;
427                 if( PxSACT & (1 << i) )
428                         continue ;
429                 LOG("Allocated slot %i", i);
430                 Port->IssuedCommands |= (1 << i);
431                 return i;
432         }
433         return -1;
434 }
435 void AHCI_int_StartCommand(tAHCI_Port *Port, int Slot)
436 {
437         Port->MMIO->PxCI = 1 << Slot;   
438         Mutex_Release(&Port->lCommandSlots);
439 }
440 void AHCI_int_CancelCommand(tAHCI_Port *Port, int Slot)
441 {
442         // Release command
443         Port->IssuedCommands &= ~(1 << Slot);
444         Mutex_Release(&Port->lCommandSlots);
445 }
446
447 // -- Low Level
448 int AHCI_SendLBA48Cmd(tAHCI_Port *Port, int bWrite,
449         Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data)
450 {
451         struct sSATA_FIS_H2DRegister    regs;
452         
453         regs.Type = SATA_FIS_H2DRegister;
454         regs.Flags = 0x80;      // [7]: Update to command register
455         regs.Command = Cmd;
456         regs.Features = 0;
457         
458         regs.SectorNum = LBA;
459         regs.CylLow = LBA >> 8;
460         regs.CylHigh = LBA >> 16;
461         regs.Dev_Head = 0x40|Dev;       // TODO: Need others?
462         
463         regs.SectorNumExp = LBA >> 24;
464         regs.CylLowExp = LBA >> 32;
465         regs.CylHighExp = LBA >> 40;
466         regs.FeaturesExp = 0;
467         
468         regs.SectorCount = Sectors;
469         regs.SectorCountExp = 0;
470         regs.Control = 0;
471
472         LOG("Sending command %02x with %p+0x%x", Cmd, Data, Size);
473         AHCI_DoFIS(Port, bWrite, sizeof(regs), &regs, 0, NULL, Size, Data);
474
475         return 0;
476 }
477
478 int AHCI_SendLBA28Cmd(tAHCI_Port *Port, int bWrite,
479         Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data)
480 {
481         struct sSATA_FIS_H2DRegister    regs;
482
483         ASSERT(LBA < (1 << 24));
484         
485         regs.Type = SATA_FIS_H2DRegister;
486         regs.Flags = 0x80;      // [7]: Update to command register
487         regs.Command = Cmd;
488         regs.Features = 0;
489         
490         regs.SectorNum = LBA;
491         regs.CylLow = LBA >> 8;
492         regs.CylHigh = LBA >> 16;
493         regs.Dev_Head = 0x40|Dev|(LBA >> 24);
494         
495         regs.SectorNumExp = 0;
496         regs.CylLowExp = 0;
497         regs.CylHighExp = 0;
498         regs.FeaturesExp = 0;
499         
500         regs.SectorCount = Sectors;
501         regs.SectorCountExp = 0;
502         regs.Control = 0;
503
504         LOG("Sending command %02x with %p+0x%x", Cmd, Data, Size);
505         AHCI_DoFIS(Port, bWrite, sizeof(regs), &regs, 0, NULL, Size, Data);
506
507         return 0;
508 }
509
510 int AHCI_ReadFIS(tAHCI_Port *Port,
511         size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
512         size_t InSize, void *InData)
513 {
514         return AHCI_DoFIS(Port, 0, CmdSize, CmdData, PktSize, PktData, InSize, InData);
515 }
516 int AHCI_WriteFIS(tAHCI_Port *Port,
517         size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
518         size_t OutSize, const void *OutData)
519 {
520         return AHCI_DoFIS(Port, 1, CmdSize, CmdData, PktSize, PktData, OutSize, (void*)OutData);
521 }
522
523 int AHCI_DoFIS(tAHCI_Port *Port, int bWrite,
524         size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
525         size_t OutSize, void *OutData)
526 {
527         // 1. Obtain a command slot
528         int slot = AHCI_int_GetCommandSlot(Port);
529         if( slot < 0 ) {
530                 return -1;
531         }
532         ASSERT(slot < 32);
533         struct sAHCI_CmdTable   *cmdt = Port->CommandTables[slot];
534
535         // 2. Fill commands
536         if( CmdSize > 64 ) {
537                 Log_Error("AHCI", "_DoFIS: Command FIS size %i > 64", CmdSize);
538                 goto error;
539         }
540         memcpy(cmdt->CFIS, CmdData, CmdSize);
541         if(PktSize > 16) {
542                 Log_Error("AHCI", "_DoFIS: ATAPI packet size %i > 64", PktSize);
543                 goto error;
544         }
545         memcpy(cmdt->ACMD, PktData, PktSize);
546         
547         // 3. Set pointers
548         size_t  ofs = 0;
549          int    prdtl = 0;
550         while( ofs < OutSize )
551         {
552                 tPAddr  phys = MM_GetPhysAddr( (char*)OutData + ofs );
553                 ASSERT( !(phys & 3) );
554                 // TODO: must be 4 byte aligned, and handle 64-bit addressing
555                 size_t  len = MIN(OutSize - ofs, PAGE_SIZE - (phys % PAGE_SIZE));
556                 ASSERT( !(len & 1) );
557                 ASSERT( len < 4*1024*1024 );
558                 LOG("PRDTL[%i] = %P+%i", prdtl, phys, len);
559                 // TODO: count must be even.
560                 AHCI_int_SetAddr(Port->Ctrlr, &cmdt->PRDT[prdtl].DBA, phys);
561                 cmdt->PRDT[prdtl].DBC = len-1;
562                 prdtl ++;
563                 ofs += len;
564         }
565         ASSERT(ofs == OutSize);
566         cmdt->PRDT[prdtl-1].DBC |= (1<<31);     // Set IOC
567         
568         // TODO: Port multipliers
569         Port->CmdList[slot].PRDTL = prdtl;
570         Port->CmdList[slot].Flags = (bWrite << 6) | (CmdSize / 4);
571         
572         // Prepare interrupt
573         Port->CommandThreads[slot] = Proc_GetCurThread();
574         Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
575
576         // 4. Dispatch
577         AHCI_int_StartCommand(Port, slot);
578
579         return 0;
580 error:
581         AHCI_int_CancelCommand(Port, slot);
582         return -1;
583 }
584
585 int AHCI_WaitForInterrupt(tAHCI_Port *Port, unsigned int Timeout)
586 {
587         // Set up a timeout callback
588         Threads_ClearEvent(THREAD_EVENT_TIMER);
589         tTimer *timeout = Time_AllocateTimer(NULL, NULL);
590         Time_ScheduleTimer(timeout, Timeout);
591         
592         // Wait until an interrupt arrives or the timeout fires
593         Uint32 ev = Threads_WaitEvents(THREAD_EVENT_SHORTWAIT|THREAD_EVENT_TIMER);
594         Time_FreeTimer(timeout);
595
596         if( ev & THREAD_EVENT_TIMER ) {
597                 Log_Notice("AHCI", "Timeout of %i ms exceeded", Timeout);
598                 return 1;
599         }
600         return 0;
601 }
602

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