- Define structure pointer to NULL to make the compiler happy
[AROS.git] / rom / hidds / pci / pciclass.c
blob0e87f392500ffd1fda27e51b23fed6448d14589c
1 /*
2 Copyright © 2004-2017, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #define DEBUG 1
7 #include <aros/debug.h>
8 #include <hidd/hidd.h>
9 #include <hidd/pci.h>
10 #include <oop/oop.h>
11 #include <utility/tagitem.h>
12 #include <utility/hooks.h>
14 #include <proto/exec.h>
15 #include <proto/utility.h>
16 #include <proto/oop.h>
18 #include "pci.h"
20 /*
21 Returns 0 for no device, 1 for non-multi device and 2 for
22 a multifunction device
24 cl points to the base pci class which is used to extract static data
25 o points to the driver class which is used to read from config space
27 static int isPCIDeviceAvailable(OOP_Class *cl, OOP_Object *o, UBYTE bus, UBYTE dev, UBYTE sub)
29 UWORD Vend;
30 UBYTE Type;
32 Vend = HIDD_PCIDriver_ReadConfigWord(o, NULL, bus, dev, sub, PCICS_VENDOR);
34 if ((Vend == 0xffff) || (Vend == 0x0000))
36 /* 0xffff is an invalid vendor ID, and so is 0x0000
37 * (Well, actually 0x0000 belongs to Gammagraphx, but this really
38 * clashes with multifunc device scanning, so lets just hope nobody
39 * has a card from them :) )
42 return 0;
45 Type = HIDD_PCIDriver_ReadConfigByte(o, NULL, bus, dev, sub, PCICS_HEADERTYPE);
47 if ((Type & PCIHT_MULTIFUNC) == PCIHT_MULTIFUNC)
48 return 2;
50 return 1;
53 static OOP_Object *InsertDevice(OOP_Class *cl, ULONG *highBus, struct TagItem *devtags)
55 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
56 OOP_Object *pcidev;
57 IPTR bridge, subbus;
59 pcidev = OOP_NewObject(pciBase->psd.pciDeviceClass, NULL, devtags);
60 if (pcidev)
62 OOP_GetAttr(pcidev, aHidd_PCIDevice_isBridge, &bridge);
63 if (bridge)
65 OOP_GetAttr(pcidev, aHidd_PCIDevice_SubBus, &subbus);
66 if (subbus > *highBus)
67 *highBus = subbus;
71 * Device class is our private and derived from rootclass.
72 * This makes casting to struct Node * safe.
74 ObtainSemaphore(&pciBase->psd.dev_lock);
75 ADDTAIL(&pciBase->psd.devices, pcidev);
76 ReleaseSemaphore(&pciBase->psd.dev_lock);
78 return pcidev;
82 * PCI::SetUpDriver(OOP_Object *driverObject)
84 * A new PCI hardware driver is being added to the PCI subsystem.
85 * The PCI bus handled through driver added is scanned, and all available
86 * PCI devices are added to the device chain.
88 BOOL PCI__HW__SetUpDriver(OOP_Class *cl, OOP_Object *o,
89 struct pHW_SetUpDriver *msg)
91 OOP_Object *drv = msg->driverObject;
92 ULONG highBus = 0;
93 ULONG bus, dev, sub, type;
95 struct MinList *irq_routing;
97 struct TagItem devtags[] =
99 { aHidd_PCIDevice_Bus , 0 },
100 { aHidd_PCIDevice_Dev , 0 },
101 { aHidd_PCIDevice_Sub , 0 },
102 { aHidd_PCIDevice_Driver , (IPTR)drv },
103 { aHidd_PCIDevice_ExtendedConfig, 0 },
104 { TAG_DONE , 0 }
107 OOP_GetAttr(drv, aHidd_PCIDriver_IRQRoutingTable, (IPTR *)&irq_routing);
109 D(bug("[PCI] Adding Driver 0x%p class 0x%p\n", drv, OOP_OCLASS(drv)));
111 D(bug("[PCI] drivers IRQ routing table at 0x%p\n", irq_routing));
114 * Scan the whole PCI bus looking for devices available
115 * There is no need for semaphore protected list operations at this
116 * point, because the driver is still not public.
118 for (bus = 0; bus <= highBus; bus++)
120 D(bug("[PCI] Scanning bus %d\n",bus));
122 devtags[0].ti_Data = bus;
124 for (dev=0; dev < 32; dev++)
126 devtags[1].ti_Data = dev;
127 devtags[2].ti_Data = 0;
129 /* Knock knock! Is any device here? */
130 type = isPCIDeviceAvailable(cl, drv, bus, dev, 0);
132 switch(type)
134 /* Regular device */
135 case 1:
136 devtags[4].ti_Data = HIDD_PCIDriver_HasExtendedConfig(drv, bus, dev, 0);
137 InsertDevice(cl, &highBus, devtags);
138 break;
140 /* Cool! Multifunction device, search subfunctions then */
141 case 2:
142 devtags[4].ti_Data = HIDD_PCIDriver_HasExtendedConfig(drv, bus, dev, 0);
143 InsertDevice(cl, &highBus, devtags);
145 for (sub=1; sub < 8; sub++)
147 devtags[2].ti_Data = sub;
148 if (isPCIDeviceAvailable(cl, drv, bus, dev, sub)) {
149 devtags[4].ti_Data = HIDD_PCIDriver_HasExtendedConfig(drv, bus, dev, sub);
150 InsertDevice(cl, &highBus, devtags);
153 break;
158 if (irq_routing)
160 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
161 OOP_Object *pcidev;
163 D(bug("[PCI] Checking IRQ routing for newly added devices\n"));
165 ForeachNode(&pciBase->psd.devices, pcidev)
167 IPTR d;
168 IPTR line;
170 OOP_GetAttr(pcidev, aHidd_PCIDevice_Driver, &d);
171 OOP_GetAttr(pcidev, aHidd_PCIDevice_IRQLine, &line);
172 if (d == (IPTR)drv && line)
174 IPTR bus, dev, sub;
175 struct PCI_IRQRoutingEntry *e;
177 OOP_GetAttr(pcidev, aHidd_PCIDevice_Bus, &bus);
178 OOP_GetAttr(pcidev, aHidd_PCIDevice_Dev, &dev);
179 OOP_GetAttr(pcidev, aHidd_PCIDevice_Sub, &sub);
181 D(bug("[PCI] Device %d:%02x.%02x uses INT%c and may need IRQ fixup\n", bus, dev, sub, 'A' + line - 1));
183 if (bus == 0)
185 ForeachNode(irq_routing, e)
187 if ((e->re_PCIDevNum == dev) &&
188 (e->re_PCIFuncNum == 0xffff || e->re_PCIFuncNum == sub) &&
189 (e->re_IRQPin == line))
191 struct TagItem attr[] =
193 {aHidd_PCIDevice_INTLine, e->re_IRQ},
194 {TAG_DONE, 0UL}
197 D(bug("[PCI] Setting INTLine to %02x\n", e->re_IRQ));
198 OOP_SetAttrs(pcidev, attr);
202 else
204 OOP_Object *pcibus;
205 D(bug("[PCI] Device is on bus %d, looking for it...\n", bus));
207 ForeachNode(&pciBase->psd.devices, pcibus)
209 IPTR d;
210 IPTR isBridge;
212 if (pcibus == pcidev)
213 continue;
215 OOP_GetAttr(pcibus, aHidd_PCIDevice_Driver, &d);
216 OOP_GetAttr(pcibus, aHidd_PCIDevice_isBridge, &isBridge);
218 if (d == (IPTR)drv && isBridge)
220 IPTR subbus;
221 OOP_GetAttr(pcibus, aHidd_PCIDevice_SubBus, &subbus);
223 if (subbus == bus)
225 IPTR bbus, bdev, bsub;
226 struct PCI_IRQRoutingEntry *e;
227 IPTR new_line = 1 + (line - 1 + dev) % 4;
229 OOP_GetAttr(pcibus, aHidd_PCIDevice_Bus, &bbus);
230 OOP_GetAttr(pcibus, aHidd_PCIDevice_Dev, &bdev);
231 OOP_GetAttr(pcibus, aHidd_PCIDevice_Sub, &bsub);
233 D(bug("[PCI] Found match. PCI-PCI bridge at %x:%02x.%02x (%p)\n",
234 bbus, bdev, bsub, pcibus));
236 D(bug("[PCI] Swizzling the IRQPin from INT%c to INT%c\n", 'A' + line - 1,
237 'A' + new_line - 1));
239 ForeachNode(irq_routing, e)
241 if ((e->re_PCIDevNum == bdev) &&
242 (e->re_PCIFuncNum == 0xffff || e->re_PCIFuncNum == bsub) &&
243 (e->re_IRQPin == new_line))
245 struct TagItem attr[] =
247 {aHidd_PCIDevice_INTLine, e->re_IRQ},
248 {TAG_DONE, 0UL}};
250 D(bug("[PCI] Setting INTLine to %02x\n", e->re_IRQ));
251 OOP_SetAttrs(pcidev, attr);
262 /* Succesful, add the driver to the end of drivers list */
263 return TRUE;
266 static const UBYTE attrTable[] =
268 aoHidd_PCIDevice_VendorID,
269 aoHidd_PCIDevice_ProductID,
270 aoHidd_PCIDevice_RevisionID,
271 aoHidd_PCIDevice_Interface,
272 aoHidd_PCIDevice_Class,
273 aoHidd_PCIDevice_SubClass,
274 aoHidd_PCIDevice_SubsystemVendorID,
275 aoHidd_PCIDevice_SubsystemID,
276 aoHidd_PCIDevice_Driver
279 /*****************************************************************************************
281 NAME
282 moHidd_PCI_EnumDevices
284 SYNOPSIS
285 void OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_EnumDrivers *Msg);
287 void HIDD_PCI_EnumDevices(OOP_Object *obj, struct Hook *callback,
288 const struct TagItem *requirements);
290 LOCATION
291 CLID_Hidd_PCI
293 FUNCTION
294 This method calls the callback hook for every PCI device in the system
295 that meets requirements specified (or every device if tags=NULL). It
296 iterates not only through one PCI bus, but instead through all buses
297 managed by all drivers present in the system.
299 INPUTS
300 obj - A PCI subsystem object.
301 callback - A user-supplied hook which will be called for every device.
302 requirements - A TagList specifying search parameters.
304 The hook will be called with the following parameters:
305 AROS_UFHA(struct Hook *, hook , A0)
306 - A pointer to hook structure itself
307 AROS_UFHA(OOP_Object * , deviceObject, A2)
308 - A PCI device object
309 AROS_UFHA(APTR , unused , A1)
310 - Not used
312 The following tags are accepted as search parameters:
313 tHidd_PCI_VendorID - vendor ID
314 tHidd_PCI_ProductID - product ID
315 tHidd_PCI_RevisionID - revision ID
316 tHidd_PCI_Interface - PCI interface ID
317 tHidd_PCI_Class - PCI class ID
318 tHidd_PCI_SubClass - PCI subclass ID
319 tHidd_PCI_SubsystemVendorID - subsystem vendor ID
320 tHidd_PCI_SubsystemID - subsystem ID
321 tHidd_PCI_Driver - a pointer to bus driver object [V4]
323 RESULT
324 None.
326 NOTES
328 EXAMPLE
330 BUGS
332 SEE ALSO
334 INTERNALS
336 *****************************************************************************************/
338 void PCI__Hidd_PCI__EnumDevices(OOP_Class *cl, OOP_Object *o, struct pHidd_PCI_EnumDevices *msg)
340 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
341 struct TagItem *tstate = (struct TagItem *)msg->requirements;
342 struct TagItem *tag;
343 IPTR matchVal[sizeof(attrTable)];
344 ULONG i;
345 OOP_Object *dev;
346 BOOL ok;
348 for (i = 0; i < sizeof(attrTable); i++)
350 matchVal[i] = ~0;
353 /* Get requirements */
354 while ((tag = NextTagItem(&tstate)))
356 ULONG idx = tag->ti_Tag - TAG_USER;
358 if (idx < sizeof(attrTable))
359 matchVal[idx] = tag->ti_Data;
362 /* Lock devices list for shared use */
363 ObtainSemaphoreShared(&pciBase->psd.dev_lock);
365 /* For every device in the system... */
366 ForeachNode(&pciBase->psd.devices, dev)
368 /* check the requirements with its properties */
369 ok = TRUE;
371 for (i = 0; i < sizeof(attrTable); i++)
373 if (matchVal[i] != ~0)
375 IPTR value;
377 OOP_GetAttr(dev, pciBase->psd.hiddPCIDeviceAB + attrTable[i], &value);
378 ok &= (value == matchVal[i]);
382 /* If requirements met, call Hook */
383 if (ok)
385 CALLHOOKPKT(msg->callback, dev, NULL);
389 ReleaseSemaphore(&pciBase->psd.dev_lock);
392 BOOL PCI__HW__RemoveDriver(OOP_Class *cl, OOP_Object *o, struct pHW_RemoveDriver *msg)
394 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
395 OOP_Object *dev, *next, *drv;
396 IPTR disallow = 0;
398 D(bug("[PCI] Removing hardware driver 0x%p\n", msg->driverObject));
401 * Get exclusive lock on devices list.
402 * If we cannot do this, then either enumeration is running or
403 * another driver is being added. We simply cannot remove the driver
404 * in this case.
405 * Well, in the latter case we actually could remove our driver, but
406 * i believe this is extremely rare situation.
408 if (!AttemptSemaphore(&pciBase->psd.dev_lock))
409 return FALSE;
412 * Now we can check if we can remove our devices.
413 * We think we can remove them if nobody has owned any of them.
414 * Drivers which behave badly will not own devices, or they will
415 * defer owning after enumeration loop has ended. So, removing a
416 * driver is still very dangerous.
417 * This can be improved if we implement map/unmnap and
418 * AddInterrupt/RemoveInterrupt accounting in our drivers. The
419 * driver would allow to expunge itself only if its internal counter
420 * of used resources is zero.
421 * PCI API wrappers (like prometheus.library) also build their
422 * own reflection of devices list, so we will have to implement either
423 * some ways to disable expunging, or (better) to get notifications
424 * about devices list being updated. With this notification we can
425 * have full hotplug support.
427 ForeachNode(&pciBase->psd.devices, dev)
429 OOP_GetAttr(dev, aHidd_PCIDevice_Driver, (IPTR *)&drv);
430 if (drv == msg->driverObject)
432 IPTR owner;
434 OOP_GetAttr(dev, aHidd_PCIDevice_Owner, &owner);
435 disallow |= owner;
439 if (disallow)
441 ReleaseSemaphore(&pciBase->psd.dev_lock);
442 D(bug("[PCI] PCI::RemoveDriver() failed, driver in use\n"));
443 return FALSE;
446 ForeachNodeSafe(&pciBase->psd.devices, dev, next)
448 REMOVE(dev);
449 OOP_DisposeObject(dev);
452 ReleaseSemaphore(&pciBase->psd.dev_lock);
453 D(bug("[PCI] PCI::RemHardwareDriver() succeeded\n"));
454 return OOP_DoSuperMethod(cl, o, &msg->mID);
457 /*****************************************************************************************
459 NAME
460 moHidd_PCI_AddHardwareDriver
462 SYNOPSIS
463 OOP_Object *OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_AddHardwareDriver *Msg);
465 OOP_Object *HIDD_PCI_AddHardwareDriver(OOP_Object *obj, OOP_Class *driverClass);
467 LOCATION
468 CLID_Hidd_PCI
470 FUNCTION
471 Creates a bus driver object and registers it in the system.
473 Since V4 this interface is obsolete and deprecated. Use moHW_AddDriver
474 method in order to install the driver.
476 INPUTS
477 obj - A PCI subsystem object.
478 driverClass - A pointer to OOP class of the driver. In order to create an object
479 of some previously registered public class, use
480 oop.library/OOP_FindClass().
482 RESULT
483 None.
485 NOTES
487 EXAMPLE
489 BUGS
491 SEE ALSO
492 moHidd_PCI_RemHardwareDriver
494 INTERNALS
496 *****************************************************************************************/
498 void PCI__Hidd_PCI__AddHardwareDriver(OOP_Class *cl, OOP_Object *o,
499 struct pHidd_PCI_AddHardwareDriver *msg)
501 HW_AddDriver(o, msg->driverClass, NULL);
504 AROS_UFH3(static BOOL, searchFunc,
505 AROS_UFHA(struct Hook *, h, A0),
506 AROS_UFHA(OOP_Object *, driverObject, A2),
507 AROS_UFHA(OOP_Class *, driverClass, A1))
509 AROS_USERFUNC_INIT
511 if (OOP_OCLASS(driverObject) == driverClass)
513 h->h_Data = driverObject;
514 return TRUE;
517 return FALSE;
519 AROS_USERFUNC_EXIT
522 /*****************************************************************************************
524 NAME
525 moHidd_PCI_RemHardwareDriver
527 SYNOPSIS
528 void OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_RemHardwareDriver *Msg);
530 void HIDD_PCI_RemHardwareDriver(OOP_Object *obj, OOP_Class *driverClass);
532 LOCATION
533 CLID_Hidd_PCI
535 FUNCTION
536 Unregisters and disposes bus driver objects of the given class.
538 Since V4 this interface is obsolete and deprecated. Use moHW_RemoveDriver
539 method in order to remove drivers.
541 INPUTS
542 obj - A PCI subsystem object.
543 driverClass - A pointer to a driver class.
545 RESULT
546 None
548 NOTES
550 EXAMPLE
552 BUGS
554 SEE ALSO
555 moHidd_PCI_AddHardwareDriver
557 INTERNALS
559 *****************************************************************************************/
561 BOOL PCI__Hidd_PCI__RemHardwareDriver(OOP_Class *cl, OOP_Object *o,
562 struct pHidd_PCI_RemHardwareDriver *msg)
564 BOOL ok = FALSE;
565 struct Hook searchHook =
567 .h_Entry = (HOOKFUNC)searchFunc
571 * A very stupid and slow algorithm.
572 * Find a driver using Enum method, remember it, then remove.
573 * Repeat until search succeeds.
574 * We cannot remove drivers inside enumeration hook because EnumDrivers
575 * locks internal objects list in shared mode. RemoveDriver locks the
576 * same list in exclusive mode, and it's impossible to change semaphore's
577 * mode on the fly.
581 searchHook.h_Data = NULL;
583 HW_EnumDrivers(o, &searchHook, msg->driverClass);
585 if (searchHook.h_Data)
587 ok = HW_RemoveDriver(o, searchHook.h_Data);
588 if (!ok)
589 break;
591 } while (searchHook.h_Data);
593 return ok;
596 OOP_Object *PCI__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
598 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
599 struct pci_staticdata *psd = &pciBase->psd;
601 if (!psd->pciObject)
603 struct TagItem new_tags[] =
605 {aHW_ClassName, (IPTR)"PCI Local Bus"},
606 {TAG_DONE , 0 }
608 struct pRoot_New new_msg =
610 .mID = msg->mID,
611 .attrList = new_tags
614 psd->pciObject = (OOP_Object *)OOP_DoSuperMethod(cl, o, &new_msg.mID);
616 return psd->pciObject;
619 VOID PCI__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)