Base ATA HIDD classes are embedded into ata.device.
[AROS.git] / rom / devs / ata / ata_init.c
blob5a0581b72b7274009778c7866f612fca4b4c0ac0
1 /*
2 Copyright © 2004-2013, The AROS Development Team. All rights reserved
3 $Id$
5 Desc:
6 Lang: English
7 */
9 #define __OOP_NOMETHODBASES__
11 #include <aros/bootloader.h>
12 #include <aros/debug.h>
13 #include <aros/symbolsets.h>
14 #include <exec/exec.h>
15 #include <exec/resident.h>
16 #include <exec/tasks.h>
17 #include <exec/memory.h>
18 #include <exec/nodes.h>
19 #include <hidd/hidd.h>
20 #include <utility/utility.h>
21 #include <libraries/expansion.h>
22 #include <libraries/configvars.h>
23 #include <dos/bptr.h>
24 #include <dos/dosextens.h>
25 #include <dos/filehandler.h>
27 #include <proto/exec.h>
28 #include <proto/timer.h>
29 #include <proto/bootloader.h>
30 #include <proto/expansion.h>
31 #include <proto/oop.h>
33 #include <string.h>
35 #include "ata.h"
36 #include "timer.h"
38 #include LC_LIBDEFS_FILE
40 /* Add a bootnode using expansion.library */
41 BOOL ata_RegisterVolume(ULONG StartCyl, ULONG EndCyl, struct ata_Unit *unit)
43 struct ExpansionBase *ExpansionBase;
44 struct DeviceNode *devnode;
45 TEXT dosdevname[4] = "HD0";
46 const ULONG IdDOS = AROS_MAKE_ID('D','O','S','\001');
47 const ULONG IdCDVD = AROS_MAKE_ID('C','D','V','D');
49 ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",
50 40L);
52 if (ExpansionBase)
54 IPTR pp[24];
56 /* This should be dealt with using some sort of volume manager or such. */
57 switch (unit->au_DevType)
59 case DG_DIRECT_ACCESS:
60 break;
61 case DG_CDROM:
62 dosdevname[0] = 'C';
63 break;
64 default:
65 D(bug("[ATA>>]:-ata_RegisterVolume called on unknown devicetype\n"));
68 if (unit->au_UnitNum < 10)
69 dosdevname[2] += unit->au_UnitNum % 10;
70 else
71 dosdevname[2] = 'A' - 10 + unit->au_UnitNum;
73 pp[0] = (IPTR)dosdevname;
74 pp[1] = (IPTR)MOD_NAME_STRING;
75 pp[2] = unit->au_UnitNum;
76 pp[DE_TABLESIZE + 4] = DE_BOOTBLOCKS;
77 pp[DE_SIZEBLOCK + 4] = 1 << (unit->au_SectorShift - 2);
78 pp[DE_NUMHEADS + 4] = unit->au_Heads;
79 pp[DE_SECSPERBLOCK + 4] = 1;
80 pp[DE_BLKSPERTRACK + 4] = unit->au_Sectors;
81 pp[DE_RESERVEDBLKS + 4] = 2;
82 pp[DE_LOWCYL + 4] = StartCyl;
83 pp[DE_HIGHCYL + 4] = EndCyl;
84 pp[DE_NUMBUFFERS + 4] = 10;
85 pp[DE_BUFMEMTYPE + 4] = MEMF_PUBLIC | MEMF_31BIT;
86 pp[DE_MAXTRANSFER + 4] = 0x00200000;
87 pp[DE_MASK + 4] = 0x7FFFFFFE;
88 pp[DE_BOOTPRI + 4] = ((unit->au_DevType == DG_DIRECT_ACCESS) ? 0 : 10);
89 pp[DE_DOSTYPE + 4] = ((unit->au_DevType == DG_DIRECT_ACCESS) ? IdDOS : IdCDVD);
90 pp[DE_CONTROL + 4] = 0;
91 pp[DE_BOOTBLOCKS + 4] = 2;
93 devnode = MakeDosNode(pp);
95 if (devnode)
97 D(bug("[ATA>>]:-ata_RegisterVolume: '%b', type=0x%08lx with StartCyl=%d, EndCyl=%d .. ",
98 devnode->dn_Name, pp[DE_DOSTYPE + 4], StartCyl, EndCyl));
100 AddBootNode(pp[DE_BOOTPRI + 4], ADNF_STARTPROC, devnode, NULL);
101 D(bug("done\n"));
103 return TRUE;
106 CloseLibrary((struct Library *)ExpansionBase);
109 return FALSE;
112 static AROS_INTH1(ATAResetHandler,struct ata_Bus *, bus)
114 AROS_INTFUNC_INIT
116 struct ata_Unit *unit;
117 UWORD i;
119 /* Stop DMA */
120 for (i = 0; i < MAX_BUSUNITS; i++)
122 unit = bus->ab_Units[i];
123 if (unit != NULL)
125 if(unit->au_DMAPort != 0)
127 dma_StopDMA(unit);
128 BUS_OUTL(0, dma_PRD, unit->au_DMAPort);
133 /* Disable interrupts */
134 BUS_OUT(0x2, ata_AltControl, bus->ab_Alt);
136 return FALSE;
138 AROS_INTFUNC_EXIT
142 * This routine needs to be called by bus probe code in order to register a device.
143 * IOBase - base address of primary I/O registers on your bus.
144 * IOAlt - base address of secondary I/O register bank. Zero if no secondary bank
145 * is present. (IDE splitter on Amiga(tm), for example).
146 * DMABase - base address of DMA controller on your bus. Zero if DMA is not supported.
147 * Flags - Misc flags
148 * driver - structure holding pointers to I/O functions (for speedup)
149 * driverData - driver-specific data, whatever it needs.
151 * Flags: - ARBF_80Wire
152 * Set if your drive is connected using 80-wire cable. Enables high-speed
153 * UDMA modes (where appropriate).
154 * - ARBF_EarlyInterrupt
155 * Setup interrupt handler before IDE bus probe to catch possible spurious
156 * interrupts (IDE splitter disables access to ata_devcon register)
158 * When a HIDD subsystem is implemented, these parameters will become HIDD attributes.
160 void ata_RegisterBus(IPTR IOBase, IPTR IOAlt, IPTR INTLine, IPTR DMABase, ULONG Flags,
161 const struct ata_BusDriver *driver, APTR driverData, struct ataBase *ATABase)
164 * ata bus - this is going to be created and linked to the master list here
166 struct ata_Bus *ab;
168 UWORD i;
171 * initialize structure
173 ab = (struct ata_Bus*) AllocVecPooled(ATABase->ata_MemPool, sizeof(struct ata_Bus));
174 if (ab == NULL)
175 return;
177 ab->ab_Base = ATABase;
178 ab->ab_Port = IOBase;
179 ab->ab_Alt = IOAlt;
180 ab->ab_IRQ = INTLine;
181 ab->ab_Dev[0] = DEV_NONE;
182 ab->ab_Dev[1] = DEV_NONE;
183 ab->ab_Flags = 0;
184 ab->ab_SleepySignal = 0;
185 ab->ab_BusNum = ATABase->ata__buscount++;
186 ab->ab_Timeout = 0;
187 ab->ab_Units[0] = NULL;
188 ab->ab_Units[1] = NULL;
189 ab->ab_Task = NULL;
190 ab->ab_HandleIRQ = NULL;
191 ab->ab_Driver = driver;
192 ab->ab_DriverData = driverData;
194 D(bug("[ATA>>] ata_RegisterBus: Analysing bus %d, units %d and %d\n", ab->ab_BusNum, ab->ab_BusNum<<1, (ab->ab_BusNum<<1)+1));
195 D(bug("[ATA>>] ata_RegisterBus: IRQ %d, IO: %x:%x, DMA: %x\n", INTLine, IOBase, IOAlt, DMABase));
198 * DMABase is also used for reporting interrupt status, so NoDMA == TRUE
199 * is not equal to DMABase == 0.
201 if (DMABase && (!ATABase->ata_NoDMA))
203 /* Allocate DMA PRD. Due to the nature of PCI bus it must be in 32-bit memory. */
204 ab->ab_PRD = AllocMem((PRD_MAX + 1) * 2 * sizeof(struct PRDEntry), MEMF_PUBLIC|MEMF_CLEAR|MEMF_31BIT);
206 if (ab->ab_PRD)
208 if ((0x10000 - ((IPTR)ab->ab_PRD & 0xffff)) < PRD_MAX * sizeof(struct PRDEntry))
209 ab->ab_PRD = (void*)((((IPTR)ab->ab_PRD)+0xffff) &~ 0xffff);
211 else
213 D(bug("[ATA>>] Failed to allocate DMA PRD! Disabling DMA for the bus.\n"));
214 DMABase = 0;
219 * add reset handler for this bus
221 ab->ab_ResetInt.is_Code = (VOID_FUNC)ATAResetHandler;
222 ab->ab_ResetInt.is_Data = ab;
223 AddResetCallback(&ab->ab_ResetInt);
225 /* catch possible spurious interrupts */
226 if (Flags & ARBF_EarlyInterrupt)
227 ab->ab_Driver->CreateInterrupt(ab);
230 * scan bus - try to locate all devices (disables irq)
232 ata_InitBus(ab);
233 for (i = 0; i < MAX_BUSUNITS; i++)
235 if (ab->ab_Dev[i] > DEV_UNKNOWN)
237 ab->ab_Units[i] = AllocVecPooled(ATABase->ata_MemPool,
238 sizeof(struct ata_Unit));
239 ab->ab_Units[i]->au_DMAPort = DMABase;
240 ab->ab_Units[i]->au_Flags = (Flags & ARBF_80Wire) ? AF_80Wire : 0;
241 ata_init_unit(ab, i);
245 D(bug("[ATA>>] ata_RegisterBus: Bus %ld: Unit 0 - %x, Unit 1 - %x\n", ab->ab_BusNum, ab->ab_Dev[0], ab->ab_Dev[1]));
248 * start things up :)
249 * note: this happens no matter there are devices or not
250 * sort of almost-ready-for-hotplug ;)
252 AddTail((struct List*)&ATABase->ata_Buses, (struct Node*)ab);
256 * This init routine has +127 priority, so it runs after all
257 * bus scanners have done their job.
258 * It initializes all discovered units.
260 static int ata_Scan(struct ataBase *base)
262 struct SignalSemaphore ssem;
263 struct ata_Bus* node;
264 struct Task *parent = FindTask(NULL);
266 D(bug("[ATA--] ata_Scan: Initialising Bus Tasks..\n"));
267 InitSemaphore(&ssem);
268 ForeachNode(&base->ata_Buses, node)
270 NewCreateTask(TASKTAG_PC , BusTaskCode,
271 TASKTAG_NAME , "ATA[PI] Subsystem",
272 TASKTAG_STACKSIZE , STACK_SIZE,
273 TASKTAG_PRI , TASK_PRI,
274 TASKTAG_TASKMSGPORT, &node->ab_MsgPort,
275 TASKTAG_ARG1 , node,
276 TASKTAG_ARG2 , parent,
277 TASKTAG_ARG3 , &ssem,
278 TAG_DONE);
280 /* Initial handshake */
281 Wait(SIGBREAKF_CTRL_C);
285 * wait for all buses to complete their init
287 D(bug("[ATA--] ata_Scan: Waiting for Buses to finish Initialising\n"));
288 ObtainSemaphore(&ssem);
291 * and leave.
293 ReleaseSemaphore(&ssem);
294 D(bug("[ATA--] ata_Scan: Finished\n"));
296 /* Try to setup daemon task looking for diskchanges */
297 NewCreateTask(TASKTAG_PC , DaemonCode,
298 TASKTAG_NAME , "ATA.daemon",
299 TASKTAG_STACKSIZE, STACK_SIZE,
300 TASKTAG_PRI , TASK_PRI - 1, /* The daemon should have a little bit lower Pri as handler tasks */
301 TASKTAG_ARG1 , base,
302 TAG_DONE);
304 return TRUE;
307 /* Keep order the same as order of IDs in struct ataBase! */
308 static CONST_STRPTR attrBaseIDs[] =
310 IID_HW,
311 IID_Hidd_ATABus,
312 NULL
316 Here shall we start. Make function static as it shouldn't be visible from
317 outside.
319 static int ata_init(struct ataBase *ATABase)
321 OOP_Object *hwRoot;
322 OOP_MethodID HWBase;
323 struct BootLoaderBase *BootLoaderBase;
325 D(bug("[ATA--] ata_init: ata.device Initialization\n"));
328 * I've decided to use memory pools again. Alloc everything needed from
329 * a pool, so that we avoid memory fragmentation.
331 ATABase->ata_MemPool = CreatePool(MEMF_CLEAR | MEMF_PUBLIC | MEMF_SEM_PROTECTED , 8192, 4096);
332 if (ATABase->ata_MemPool == NULL)
333 return FALSE;
335 D(bug("[ATA--] ata_init: MemPool @ %p\n", ATABase->ata_MemPool));
337 if (OOP_ObtainAttrBasesArray(&ATABase->hwAttrBase, attrBaseIDs))
338 return FALSE;
340 hwRoot = OOP_NewObject(NULL, CLID_HW_Root, NULL);
341 if (!hwRoot)
342 return FALSE;
344 HWBase = OOP_GetMethodID(IID_HW, 0);
345 if (!HW_AddDriver(hwRoot, ATABase->ataClass, NULL))
346 return FALSE;
348 /* Set default ata.device config options */
349 ATABase->ata_32bit = FALSE;
350 ATABase->ata_NoMulti = FALSE;
351 ATABase->ata_NoDMA = FALSE;
352 ATABase->ata_Poll = FALSE;
353 ATABase->ata_CmdLine = NULL;
356 * start initialization:
357 * obtain kernel parameters
359 BootLoaderBase = OpenResource("bootloader.resource");
360 D(bug("[ATA--] ata_init: BootloaderBase = %p\n", BootLoaderBase));
361 if (BootLoaderBase != NULL)
363 struct List *list;
364 struct Node *node;
366 list = (struct List *)GetBootInfo(BL_Args);
367 if (list)
369 ForeachNode(list, node)
371 if (strncmp(node->ln_Name, "ATA=", 4) == 0)
374 * Remember the entire command line.
375 * Bus drivers (for example PCI one) may want it.
377 ATABase->ata_CmdLine = &node->ln_Name[4];
379 if (strstr(ATABase->ata_CmdLine, "32bit"))
381 D(bug("[ATA ] ata_init: Using 32-bit IO transfers\n"));
382 ATABase->ata_32bit = TRUE;
384 if (strstr(ATABase->ata_CmdLine, "nomulti"))
386 D(bug("[ATA ] ata_init: Disabled multisector transfers\n"));
387 ATABase->ata_NoMulti = TRUE;
389 if (strstr(ATABase->ata_CmdLine, "nodma"))
391 D(bug("[ATA ] ata_init: Disabled DMA transfers\n"));
392 ATABase->ata_NoDMA = TRUE;
394 if (strstr(ATABase->ata_CmdLine, "poll"))
396 D(bug("[ATA ] ata_init: Using polling to detect end of busy state\n"));
397 ATABase->ata_Poll = TRUE;
404 /* Initialize BUS list */
405 NEWLIST(&ATABase->ata_Buses);
407 return TRUE;
410 static int ata_expunge(struct ataBase *ATABase)
412 if (ATABase->ataObj)
415 * CLID_HW is a singletone, you can get it as many times as you want.
416 * Here we save up some space in struct ataBase by obtaining hwclass
417 * object and its MethodBase only when we need it. This happens rarely,
418 * so small performance loss is OK here.
420 OOP_Object *hwRoot = OOP_NewObject(NULL, CLID_HW, NULL);
421 OOP_MethodID HWBase = OOP_GetMethodID(IID_HW, 0);
423 HW_RemoveDriver(hwRoot, ATABase->ataObj);
426 OOP_ReleaseAttrBasesArray(&ATABase->hwAttrBase, attrBaseIDs);
428 return TRUE;
431 static int open
433 LIBBASETYPEPTR LIBBASE,
434 struct IORequest *iorq,
435 ULONG unitnum,
436 ULONG flags
440 * device location
442 ULONG bus, dev;
445 * Assume it failed
447 iorq->io_Error = IOERR_OPENFAIL;
450 * actual bus
452 struct ata_Bus *b = (struct ata_Bus*)LIBBASE->ata_Buses.mlh_Head;
455 * Extract bus and device numbers
457 bus = unitnum >> 1; // 0xff00 >> 8
458 dev = (unitnum & 0x1); // 0x00ff
461 * locate bus
463 while (bus--)
465 b = (struct ata_Bus*)b->ab_Node.mln_Succ;
466 if (b == NULL)
467 return FALSE;
470 if (b->ab_Node.mln_Succ == NULL)
471 return FALSE;
474 * locate unit
476 if (b->ab_Units[dev] == NULL)
477 return FALSE;
480 * set up iorequest
482 iorq->io_Device = &LIBBASE->ata_Device;
483 iorq->io_Unit = &b->ab_Units[dev]->au_Unit;
484 iorq->io_Error = 0;
486 b->ab_Units[dev]->au_Unit.unit_OpenCnt++;
488 return TRUE;
491 /* Close given device */
492 static int close
494 LIBBASETYPEPTR LIBBASE,
495 struct IORequest *iorq
498 struct ata_Unit *unit = (struct ata_Unit *)iorq->io_Unit;
500 /* First of all make the important fields of struct IORequest invalid! */
501 iorq->io_Unit = (struct Unit *)~0;
503 /* Decrease use counters of unit */
504 unit->au_Unit.unit_OpenCnt--;
506 return TRUE;
509 ADD2INITLIB(ata_init, 0)
510 ADD2EXPUNGELIB(ata_expunge, 0)
511 ADD2INITLIB(ata_Scan, 127)
512 ADD2OPENDEV(open, 0)
513 ADD2CLOSEDEV(close, 0)
514 /* vim: set ts=8 sts=4 et : */