added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / arch / common / ata.device / ata_init.c
blob19a274e80b58385f9c7401cd9074cf9404b532ac
1 /*
2 Copyright © 2004-2007, The AROS Development Team. All rights reserved
3 $Id$
5 Desc:
6 Lang: English
7 */
8 /*
9 * CHANGELOG:
10 * DATE NAME ENTRY
11 * ---------- ------------------ -------------------------------------------------------------------
12 * 2008-01-25 T. Wiszkowski Rebuilt, rearranged and partially fixed 60% of the code here
13 * Enabled implementation to scan for other PCI IDE controllers
14 * Implemented ATAPI Packet Support for both read and write
15 * Corrected ATAPI DMA handling
16 * Fixed major IDE enumeration bugs severely handicapping transfers with more than one controller
17 * Compacted source and implemented major ATA support procedure
18 * Improved DMA and Interrupt management
19 * Removed obsolete code
20 * 2008-01-26 T. Wiszkowski Added 'nodma' flag for ata driver
21 * Moved variables out of global scope
22 * Replaced static variables
23 * 2008-02-08 T. Wiszkowski Fixed DMA accesses for direct scsi devices,
24 * Corrected IO Areas to allow ATA to talk to PCI controllers
25 * 2008-02-24 T. Wiszkowski Corrected unit open function
28 #define DEBUG 0
29 #include <aros/debug.h>
31 #include <aros/symbolsets.h>
33 #include <exec/types.h>
34 #include <exec/exec.h>
35 #include <exec/resident.h>
36 #include <exec/tasks.h>
37 #include <exec/memory.h>
38 #include <exec/nodes.h>
39 #include <utility/utility.h>
40 #include <oop/oop.h>
41 #include <libraries/expansion.h>
42 #include <libraries/configvars.h>
44 #include <dos/bptr.h>
45 #include <dos/filehandler.h>
46 #include <string.h>
48 #include <proto/exec.h>
49 #include <proto/timer.h>
50 #include <proto/bootloader.h>
51 #include <proto/expansion.h>
53 #include <oop/oop.h>
54 #include <hidd/pci.h>
55 #include <proto/oop.h>
58 #include "ata.h"
59 #include LC_LIBDEFS_FILE
62 typedef struct
64 struct ataBase *ATABase;
65 UWORD CurrentBus;
66 UWORD PredefBus;
67 } EnumeratorArgs;
69 /* Add a bootnode using expansion.library */
70 BOOL AddVolume(ULONG StartCyl, ULONG EndCyl, struct ata_Unit *unit)
72 struct ExpansionBase *ExpansionBase;
73 struct DeviceNode *devnode;
74 IPTR *pp;
75 static int volnum;
76 TEXT dosdevname[4] = "HD0", *handler;
77 UWORD len;
79 ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",
80 40L);
82 if (ExpansionBase)
84 pp = AllocMem(24*sizeof(IPTR), MEMF_PUBLIC | MEMF_CLEAR);
86 if (pp)
88 /* This should be dealt with using some sort of volume manager or such. */
89 switch (unit->au_DevType)
91 case DG_DIRECT_ACCESS:
92 break;
93 case DG_CDROM:
94 dosdevname[0] = 'C';
95 break;
96 default:
97 D(bug("IDE: AddVolume called on unknown devicetype\n"));
99 dosdevname[2] += volnum;
100 pp[0] = (IPTR)dosdevname;
101 pp[1] = (IPTR)MOD_NAME_STRING;
102 pp[2] = unit->au_UnitNum;
103 pp[DE_TABLESIZE + 4] = DE_BOOTBLOCKS;
104 pp[DE_SIZEBLOCK + 4] = 1 << (unit->au_SectorShift - 2);
105 pp[DE_NUMHEADS + 4] = unit->au_Heads;
106 pp[DE_SECSPERBLOCK + 4] = 1;
107 pp[DE_BLKSPERTRACK + 4] = unit->au_Sectors;
108 pp[DE_RESERVEDBLKS + 4] = 2;
109 pp[DE_LOWCYL + 4] = StartCyl;
110 pp[DE_HIGHCYL + 4] = EndCyl;
111 pp[DE_NUMBUFFERS + 4] = 10;
112 pp[DE_BUFMEMTYPE + 4] = MEMF_PUBLIC | MEMF_CHIP;
113 pp[DE_MAXTRANSFER + 4] = 0x00200000;
114 pp[DE_MASK + 4] = 0x7FFFFFFE;
115 pp[DE_BOOTPRI + 4] = ((!unit->au_DevType) ? 0 : 10);
116 pp[DE_DOSTYPE + 4] = 0x444F5301;
117 pp[DE_BOOTBLOCKS + 4] = 2;
118 devnode = MakeDosNode(pp);
120 if (devnode)
122 if(unit->au_DevType == DG_DIRECT_ACCESS)
123 handler = "afs.handler";
124 else
125 handler = "cdrom.handler";
126 len = strlen(handler);
127 if ((devnode->dn_Handler =
128 MKBADDR(AllocMem(AROS_BSTR_MEMSIZE4LEN(len),
129 MEMF_PUBLIC | MEMF_CLEAR
134 CopyMem(handler, AROS_BSTR_ADDR(devnode->dn_Handler), len);
135 AROS_BSTR_setstrlen(devnode->dn_Handler, len);
137 D(bug("-Adding volume %s with SC=%d, EC=%d\n",
138 &(devnode->dn_Ext.dn_AROS.dn_DevName[0]), StartCyl, EndCyl));
139 AddBootNode(pp[DE_BOOTPRI + 4], 0, devnode, 0);
140 D(bug("done\n"));
142 volnum++;
144 return TRUE;
149 CloseLibrary((struct Library *)ExpansionBase);
152 return FALSE;
156 * PCI BUS ENUMERATOR
157 * collect ALL ata/ide capable devices (including SATA and other) and spawn consecutive tasks
160 static
161 AROS_UFH3(void, Enumerator,
162 AROS_UFHA(struct Hook *, hook, A0),
163 AROS_UFHA(OOP_Object *, Device, A2),
164 AROS_UFHA(APTR, message,A1))
166 AROS_USERFUNC_INIT
169 * static list of io/irqs that we can handle
171 static struct __bus
173 ULONG port;
174 ULONG alt;
175 UBYTE irq;
176 } Buses[] =
178 {0x1f0, 0x3f0, 14},
179 {0x170, 0x370, 15},
180 {0x168, 0x368, 10},
181 {0x1e8, 0x3e8, 11},
185 * ata bus - this is going to be created and linked to the master list here
187 struct ata_Bus *ab;
190 * parameters we will want to acquire
192 IPTR ProductID,
193 VendorID,
194 DMABase,
195 INTLine,
196 IOBase,
197 IOAlt;
200 * the PCI Attr Base
202 OOP_AttrBase HiddPCIDeviceAttrBase = OOP_ObtainAttrBase(IID_Hidd_PCIDevice);
205 * new parameters for every device:
206 * - allow bus mastering
208 struct TagItem attrs[] =
210 { aHidd_PCIDevice_isMaster, TRUE },
211 { TAG_DONE, 0UL }
215 * enumerator params
217 EnumeratorArgs *a = (EnumeratorArgs*)hook->h_Data;
220 * temporary variables
222 int x;
225 * obtain more or less useful data
227 OOP_GetAttr(Device, aHidd_PCIDevice_ProductID, &ProductID);
228 OOP_GetAttr(Device, aHidd_PCIDevice_VendorID, &VendorID);
229 OOP_GetAttr(Device, aHidd_PCIDevice_Base4, &DMABase);
231 if (a->ATABase->ata_NoDMA)
232 DMABase = 0;
235 * we can have as many as four ports assigned to this device
237 for (x=0; x<2; x++)
240 * obtain base and interrupt
242 switch (x)
244 case 0:
245 OOP_GetAttr(Device, aHidd_PCIDevice_Base0, &IOBase);
246 OOP_GetAttr(Device, aHidd_PCIDevice_Base1, &IOAlt);
247 break;
248 case 1:
249 OOP_GetAttr(Device, aHidd_PCIDevice_Base2, &IOBase);
250 OOP_GetAttr(Device, aHidd_PCIDevice_Base3, &IOAlt);
251 break;
253 OOP_GetAttr(Device, aHidd_PCIDevice_INTLine, &INTLine);
256 * see if IO Base is valid. otherwise pick device from static list
257 * (this most likely means the device is right there)
259 if (IOBase == 0)
261 if (a->PredefBus < (sizeof(Buses) / sizeof(Buses[0])))
264 * collect IOBase and interrupt from the above list
266 IOBase = Buses[a->PredefBus].port;
267 IOAlt = Buses[a->PredefBus].alt;
268 INTLine = Buses[a->PredefBus].irq;
269 a->PredefBus++;
271 else
273 IOBase = 0;
274 IOAlt = 0;
275 DMABase = 0;
276 INTLine = 0;
277 bug("[ATA ] Found more controllers\n");
279 * we're all done. no idea what else they want from us
281 continue;
285 D(bug("[ATA.scanbus] IDE device %04x:%04x - IO: %x:%x DMA: %x\n", ProductID, VendorID, IOBase, IOAlt, DMABase));
288 * initialize structure
290 ab = (struct ata_Bus*) AllocVecPooled(a->ATABase->ata_MemPool, sizeof(struct ata_Bus));
291 if (ab == NULL)
292 return;
294 ab->ab_Base = a->ATABase;
295 ab->ab_Port = IOBase;
296 ab->ab_Alt = IOAlt;
297 ab->ab_Irq = INTLine;
298 ab->ab_Dev[0] = DEV_NONE;
299 ab->ab_Dev[1] = DEV_NONE;
300 ab->ab_Flags = 0;
301 ab->ab_SleepySignal = 0;
302 ab->ab_BusNum = a->CurrentBus++;
303 ab->ab_Waiting = 0;
304 ab->ab_Timeout = 0;
305 ab->ab_Units[0] = 0;
306 ab->ab_Units[1] = 0;
307 ab->ab_IntHandler = (HIDDT_IRQ_Handler *)AllocVecPooled(a->ATABase->ata_MemPool, sizeof(HIDDT_IRQ_Handler));
308 D(bug("[ATA ] Analysing bus %d, units %d and %d\n", ab->ab_BusNum, ab->ab_BusNum<<1, (ab->ab_BusNum<<1)+1));
311 * allocate DMA PRD
313 ab->ab_PRD = AllocVecPooled(a->ATABase->ata_MemPool, (PRD_MAX+1) * 2 * sizeof(struct PRDEntry));
314 if ((0x10000 - ((ULONG)ab->ab_PRD & 0xffff)) < PRD_MAX * sizeof(struct PRDEntry))
315 ab->ab_PRD = (void*)((((IPTR)ab->ab_PRD)+0xffff) &~ 0xffff);
317 InitSemaphore(&ab->ab_Lock);
320 * scan bus - try to locate all devices
322 ata_ScanBus(ab);
323 if (ab->ab_Dev[0] > DEV_UNKNOWN)
325 ab->ab_Units[0] = AllocVecPooled(a->ATABase->ata_MemPool, sizeof(struct ata_Unit));
326 ab->ab_Units[0]->au_DMAPort = (DMABase != 0 ? DMABase + (x<<3) : 0);
328 if (ab->ab_Dev[1] > DEV_UNKNOWN)
330 ab->ab_Units[1] = AllocVecPooled(a->ATABase->ata_MemPool, sizeof(struct ata_Unit));
331 ab->ab_Units[1]->au_DMAPort = (DMABase != 0 ? DMABase + (x<<3) : 0);
334 D(bug("[ATA ] Bus %ld: Unit 0 - %x, Unit 1 - %x\n", ab->ab_BusNum, ab->ab_Dev[0], ab->ab_Dev[1]));
337 * start things up :)
338 * note: this happens no matter there are devices or not
339 * sort of almost-ready-for-hotplug ;)
341 AddTail((struct List*)&a->ATABase->ata_Buses, (struct Node*)ab);
343 ata_InitBusTask(ab);
347 * check dma status
349 if (DMABase != 0)
350 D(bug("[ATA ] Bus0 status says %02x, Bus1 status says %02x\n", inb(DMABase + 2), inb(DMABase + 10)));
352 OOP_SetAttrs(Device, attrs);
353 OOP_ReleaseAttrBase(IID_Hidd_PCIDevice);
355 AROS_USERFUNC_EXIT
359 void ata_Scan(struct ataBase *base)
361 OOP_Object *pci;
363 D(bug("[ATA--] Enumerating devices\n"));
365 pci = OOP_NewObject(NULL, CLID_Hidd_PCI, NULL);
367 if (pci)
369 EnumeratorArgs Args=
371 base,
376 struct Hook FindHook = {
377 h_Entry: (IPTR (*)())Enumerator,
378 h_Data: &Args
381 struct TagItem Requirements[] = {
382 {tHidd_PCI_Class, 0x01},
383 {tHidd_PCI_SubClass, 0x01},
384 {TAG_DONE, 0x00}
388 struct pHidd_PCI_EnumDevices enummsg = {
389 mID: OOP_GetMethodID(IID_Hidd_PCI, moHidd_PCI_EnumDevices),
390 callback: &FindHook,
391 requirements: (struct TagItem *)&Requirements,
392 }, *msg = &enummsg;
394 OOP_DoMethod(pci, (OOP_Msg)msg);
396 OOP_DisposeObject(pci);
401 Here shall we start. Make function static as it shouldn't be visible from
402 outside.
404 static int ata_init(LIBBASETYPEPTR LIBBASE)
406 struct BootLoaderBase *BootLoaderBase;
409 * I've decided to use memory pools again. Alloc everything needed from
410 * a pool, so that we avoid memory fragmentation.
412 LIBBASE->ata_MemPool = CreatePool(MEMF_CLEAR | MEMF_PUBLIC | MEMF_SEM_PROTECTED , 8192, 4096);
413 if (LIBBASE->ata_MemPool == NULL)
414 return FALSE;
417 * store library pointer so we can use it later
419 LIBBASE->ata_32bit = FALSE;
420 LIBBASE->ata_NoDMA = FALSE;
423 * start initialization:
424 * obtain kernel parameters
426 D(bug("[ATA--] ata.device initialization\n"));
427 BootLoaderBase = OpenResource("bootloader.resource");
428 D(bug("[ATA--] BootloaderBase = %p\n", BootLoaderBase));
429 if (BootLoaderBase != NULL)
431 struct List *list;
432 struct Node *node;
434 list = (struct List *)GetBootInfo(BL_Args);
435 if (list)
437 ForeachNode(list, node)
439 if (strncmp(node->ln_Name, "ATA=", 4) == 0)
441 if (strstr(node->ln_Name, "32bit"))
443 D(bug("[ATA ] Using 32-bit IO transfers\n"));
444 LIBBASE->ata_32bit = TRUE;
446 if (strstr(node->ln_Name, "nodma"))
448 D(bug("[ATA ] Disabled DMA transfers\n"));
449 LIBBASE->ata_NoDMA = TRUE;
457 * Initialize BUS list
459 LIBBASE->ata_Buses.mlh_Head = (struct MinNode*) &LIBBASE->ata_Buses.mlh_Tail;
460 LIBBASE->ata_Buses.mlh_Tail = NULL;
461 LIBBASE->ata_Buses.mlh_TailPred = (struct MinNode*) &LIBBASE->ata_Buses.mlh_Head;
463 ata_Scan(LIBBASE);
465 /* Try to setup daemon task looking for diskchanges */
466 ata_InitDaemonTask(LIBBASE);
467 return TRUE;
470 static int open
472 LIBBASETYPEPTR LIBBASE,
473 struct IORequest *iorq,
474 ULONG unitnum,
475 ULONG flags
479 * device location
481 ULONG bus, dev;
484 * Assume it failed
486 iorq->io_Error = IOERR_OPENFAIL;
489 * actual bus
491 struct ata_Bus *b = (struct ata_Bus*)LIBBASE->ata_Buses.mlh_Head;
494 * Extract bus and device numbers
496 bus = unitnum >> 1; // 0xff00 >> 8
497 dev = (unitnum & 0x1); // 0x00ff
500 * locate bus
502 while (bus--)
504 b = (struct ata_Bus*)b->ab_Node.mln_Succ;
505 if (b == NULL)
506 return FALSE;
509 if (b->ab_Node.mln_Succ == NULL)
510 return FALSE;
513 * locate unit
515 if (b->ab_Units[dev] == NULL)
516 return FALSE;
519 * set up iorequest
521 iorq->io_Device = &LIBBASE->ata_Device;
522 iorq->io_Unit = &b->ab_Units[dev]->au_Unit;
523 iorq->io_Error = 0;
525 b->ab_Units[dev]->au_Unit.unit_OpenCnt++;
527 return TRUE;
530 /* Close given device */
531 static int close
533 LIBBASETYPEPTR LIBBASE,
534 struct IORequest *iorq
537 struct ata_Unit *unit = (struct ata_Unit *)iorq->io_Unit;
539 /* First of all make the important fields of struct IORequest invalid! */
540 iorq->io_Unit = (struct Unit *)~0;
542 /* Decrease use counters of unit */
543 unit->au_Unit.unit_OpenCnt--;
545 return TRUE;
548 ADD2INITLIB(ata_init, 0)
549 ADD2OPENDEV(open, 0)
550 ADD2CLOSEDEV(close, 0)
551 ADD2LIBS("irq.hidd", 0, static struct Library *, __irqhidd)