revert between 56095 -> 55830 in arch
[AROS.git] / rom / hidds / pci / pciclass.c
blobf02bf58c700a86e4494691be3dac831fffe54825
1 /*
2 Copyright © 2004-2018, 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 static OOP_Object *FindBridge(OOP_Class *cl, OOP_Object *drv, UBYTE bus);
21 static void AssignIRQ(OOP_Class *cl, OOP_Object *drv,
22 struct MinList *irq_routing, OOP_Object *pcidev);
24 /*
25 Returns 0 for no device, 1 for non-multi device and 2 for
26 a multifunction device
28 cl points to the base pci class which is used to extract static data
29 o points to the driver class which is used to read from config space
31 static int isPCIDeviceAvailable(OOP_Class *cl, OOP_Object *o, UBYTE bus, UBYTE dev, UBYTE sub)
33 UWORD Vend;
34 UBYTE Type;
36 Vend = HIDD_PCIDriver_ReadConfigWord(o, NULL, bus, dev, sub, PCICS_VENDOR);
38 if ((Vend == 0xffff) || (Vend == 0x0000))
40 /* 0xffff is an invalid vendor ID, and so is 0x0000
41 * (Well, actually 0x0000 belongs to Gammagraphx, but this really
42 * clashes with multifunc device scanning, so lets just hope nobody
43 * has a card from them :) )
46 return 0;
49 Type = HIDD_PCIDriver_ReadConfigByte(o, NULL, bus, dev, sub, PCICS_HEADERTYPE);
51 if ((Type & PCIHT_MULTIFUNC) == PCIHT_MULTIFUNC)
52 return 2;
54 return 1;
57 static OOP_Object *InsertDevice(OOP_Class *cl, ULONG *highBus, struct TagItem *devtags)
59 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
60 OOP_Object *pcidev;
61 IPTR bridge, secbus;
63 pcidev = OOP_NewObject(pciBase->psd.pciDeviceClass, NULL, devtags);
64 if (pcidev)
66 OOP_GetAttr(pcidev, aHidd_PCIDevice_isBridge, &bridge);
67 if (bridge)
69 OOP_GetAttr(pcidev, aHidd_PCIDevice_SecBus, &secbus);
70 if (secbus > *highBus)
71 *highBus = secbus;
75 * Device class is our private and derived from rootclass.
76 * This makes casting to struct Node * safe.
78 ObtainSemaphore(&pciBase->psd.dev_lock);
79 ADDTAIL(&pciBase->psd.devices, pcidev);
80 ReleaseSemaphore(&pciBase->psd.dev_lock);
82 return pcidev;
86 * PCI::SetUpDriver(OOP_Object *driverObject)
88 * A new PCI hardware driver is being added to the PCI subsystem.
89 * The PCI bus handled through driver added is scanned, and all available
90 * PCI devices are added to the device chain.
92 BOOL PCI__HW__SetUpDriver(OOP_Class *cl, OOP_Object *o,
93 struct pHW_SetUpDriver *msg)
95 OOP_Object *drv = msg->driverObject;
96 ULONG highBus = 0;
97 ULONG bus, dev, sub, type;
99 struct MinList *irq_routing;
101 struct TagItem devtags[] =
103 { aHidd_PCIDevice_Bus , 0 },
104 { aHidd_PCIDevice_Dev , 0 },
105 { aHidd_PCIDevice_Sub , 0 },
106 { aHidd_PCIDevice_Driver , (IPTR)drv },
107 { aHidd_PCIDevice_ExtendedConfig, 0 },
108 { TAG_DONE , 0 }
111 OOP_GetAttr(drv, aHidd_PCIDriver_IRQRoutingTable, (IPTR *)&irq_routing);
113 D(bug("[PCI] Adding Driver 0x%p class 0x%p\n", drv, OOP_OCLASS(drv)));
115 D(bug("[PCI] driver's IRQ routing table at 0x%p\n", irq_routing));
118 * Scan the whole PCI bus looking for devices available
119 * There is no need for semaphore protected list operations at this
120 * point, because the driver is still not public.
122 for (bus = 0; bus <= highBus; bus++)
124 D(bug("[PCI] Scanning bus %d\n",bus));
126 devtags[0].ti_Data = bus;
128 for (dev=0; dev < 32; dev++)
130 devtags[1].ti_Data = dev;
131 devtags[2].ti_Data = 0;
133 /* Knock knock! Is any device here? */
134 type = isPCIDeviceAvailable(cl, drv, bus, dev, 0);
136 switch(type)
138 /* Regular device */
139 case 1:
140 devtags[4].ti_Data = HIDD_PCIDriver_HasExtendedConfig(drv, bus, dev, 0);
141 InsertDevice(cl, &highBus, devtags);
142 break;
144 /* Cool! Multifunction device, search subfunctions then */
145 case 2:
146 devtags[4].ti_Data = HIDD_PCIDriver_HasExtendedConfig(drv, bus, dev, 0);
147 InsertDevice(cl, &highBus, devtags);
149 for (sub=1; sub < 8; sub++)
151 devtags[2].ti_Data = sub;
152 if (isPCIDeviceAvailable(cl, drv, bus, dev, sub)) {
153 devtags[4].ti_Data = HIDD_PCIDriver_HasExtendedConfig(drv, bus, dev, sub);
154 InsertDevice(cl, &highBus, devtags);
157 break;
162 if (irq_routing != NULL)
164 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
165 OOP_Object *pcidev;
167 D(bug("[PCI] Checking IRQ routing for newly added devices\n"));
169 ForeachNode(&pciBase->psd.devices, pcidev)
171 AssignIRQ(cl, drv, irq_routing, pcidev);
175 /* Successful, add the driver to the end of drivers list */
176 return TRUE;
179 /* Assign an IRQ to a device according to the routing table */
180 static void AssignIRQ(OOP_Class *cl, OOP_Object *drv,
181 struct MinList *irq_routing, OOP_Object *pcidev)
183 IPTR d, line;
184 OOP_Object *bridge;
185 BOOL irq_found = FALSE;
186 IPTR bus, dev;
187 struct PCI_IRQRoutingEntry *e;
189 OOP_GetAttr(pcidev, aHidd_PCIDevice_Driver, &d);
190 OOP_GetAttr(pcidev, aHidd_PCIDevice_IRQLine, &line);
192 if (d == (IPTR)drv && line != 0)
194 /* For the first loop iteration, it's simpler to consider the device
195 * it's own bridge! */
196 bridge = pcidev;
198 while (!irq_found)
200 OOP_GetAttr(bridge, aHidd_PCIDevice_Bus, &bus);
201 OOP_GetAttr(bridge, aHidd_PCIDevice_Dev, &dev);
203 D(bug("[PCI] Looking for routing for device %02x"
204 " and INT%c on bus %d\n", dev, 'A' + line - 1, bus));
206 ForeachNode(irq_routing, e)
208 if ((e->re_PCIBusNum == bus) && (e->re_PCIDevNum == dev)
209 && (e->re_IRQPin == line))
211 struct TagItem attr[] =
213 {aHidd_PCIDevice_INTLine, e->re_IRQ},
214 {TAG_DONE, 0UL}
217 D(bug("[PCI] Got a match. Setting INTLine to %d\n",
218 e->re_IRQ));
219 OOP_SetAttrs(pcidev, attr);
220 irq_found = TRUE;
224 if (!irq_found)
226 D(bug("[PCI] No match on bus %d. Trying parent bridge...\n",
227 bus));
229 /* We have to look for a routing entry that matches the
230 * parent bridge instead, so first find the bridge */
231 bridge = FindBridge(cl, drv, bus);
232 OOP_GetAttr(bridge, aHidd_PCIDevice_Bus, &bus);
234 /* Swizzle the INT pin as we traverse up to the parent
235 * bridge */
236 D(bug("[PCI] Swizzling the IRQPin from INT%c to INT%c\n",
237 'A' + line - 1, 'A' + (line - 1 + dev) % 4));
238 line = (line - 1 + dev) % 4 + 1;
244 /* Find the bridge that links to the given secondary bus */
245 static OOP_Object *FindBridge(OOP_Class *cl, OOP_Object *drv, UBYTE bus)
247 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
248 OOP_Object *bridge = NULL, *pcidev;
249 IPTR secbus, d, is_bridge;
251 ForeachNode(&pciBase->psd.devices, pcidev)
253 OOP_GetAttr(pcidev, aHidd_PCIDevice_Driver, &d);
254 OOP_GetAttr(pcidev, aHidd_PCIDevice_isBridge, &is_bridge);
256 if (d == (IPTR)drv && is_bridge)
258 OOP_GetAttr(pcidev, aHidd_PCIDevice_SecBus, &secbus);
260 if (secbus == bus)
263 IPTR bbus, dev, sub;
264 OOP_GetAttr(pcidev, aHidd_PCIDevice_Bus, &bbus);
265 OOP_GetAttr(pcidev, aHidd_PCIDevice_Dev, &dev);
266 OOP_GetAttr(pcidev, aHidd_PCIDevice_Sub, &sub);
267 bug("[PCI] Found PCI-PCI bridge at %x:%02x.%x (%p)\n",
268 bbus, dev, sub, pcidev);
270 bridge = pcidev;
275 return bridge;
278 static const UBYTE attrTable[] =
280 aoHidd_PCIDevice_VendorID,
281 aoHidd_PCIDevice_ProductID,
282 aoHidd_PCIDevice_RevisionID,
283 aoHidd_PCIDevice_Interface,
284 aoHidd_PCIDevice_Class,
285 aoHidd_PCIDevice_SubClass,
286 aoHidd_PCIDevice_SubsystemVendorID,
287 aoHidd_PCIDevice_SubsystemID,
288 aoHidd_PCIDevice_Driver
291 /*****************************************************************************************
293 NAME
294 moHidd_PCI_EnumDevices
296 SYNOPSIS
297 void OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_EnumDrivers *Msg);
299 void HIDD_PCI_EnumDevices(OOP_Object *obj, struct Hook *callback,
300 const struct TagItem *requirements);
302 LOCATION
303 CLID_Hidd_PCI
305 FUNCTION
306 This method calls the callback hook for every PCI device in the system
307 that meets requirements specified (or every device if tags=NULL). It
308 iterates not only through one PCI bus, but instead through all buses
309 managed by all drivers present in the system.
311 INPUTS
312 obj - A PCI subsystem object.
313 callback - A user-supplied hook which will be called for every device.
314 requirements - A TagList specifying search parameters.
316 The hook will be called with the following parameters:
317 AROS_UFHA(struct Hook *, hook , A0)
318 - A pointer to hook structure itself
319 AROS_UFHA(OOP_Object * , deviceObject, A2)
320 - A PCI device object
321 AROS_UFHA(APTR , unused , A1)
322 - Not used
324 The following tags are accepted as search parameters:
325 tHidd_PCI_VendorID - vendor ID
326 tHidd_PCI_ProductID - product ID
327 tHidd_PCI_RevisionID - revision ID
328 tHidd_PCI_Interface - PCI interface ID
329 tHidd_PCI_Class - PCI class ID
330 tHidd_PCI_SubClass - PCI subclass ID
331 tHidd_PCI_SubsystemVendorID - subsystem vendor ID
332 tHidd_PCI_SubsystemID - subsystem ID
333 tHidd_PCI_Driver - a pointer to bus driver object [V4]
335 RESULT
336 None.
338 NOTES
340 EXAMPLE
342 BUGS
344 SEE ALSO
346 INTERNALS
348 *****************************************************************************************/
350 void PCI__Hidd_PCI__EnumDevices(OOP_Class *cl, OOP_Object *o, struct pHidd_PCI_EnumDevices *msg)
352 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
353 struct TagItem *tstate = (struct TagItem *)msg->requirements;
354 struct TagItem *tag;
355 IPTR matchVal[sizeof(attrTable)];
356 ULONG i;
357 OOP_Object *dev;
358 BOOL ok;
360 for (i = 0; i < sizeof(attrTable); i++)
362 matchVal[i] = ~0;
365 /* Get requirements */
366 while ((tag = NextTagItem(&tstate)))
368 ULONG idx = tag->ti_Tag - TAG_USER;
370 if (idx < sizeof(attrTable))
371 matchVal[idx] = tag->ti_Data;
374 /* Lock devices list for shared use */
375 ObtainSemaphoreShared(&pciBase->psd.dev_lock);
377 /* For every device in the system... */
378 ForeachNode(&pciBase->psd.devices, dev)
380 /* check the requirements with its properties */
381 ok = TRUE;
383 for (i = 0; i < sizeof(attrTable); i++)
385 if (matchVal[i] != ~0)
387 IPTR value;
389 OOP_GetAttr(dev, pciBase->psd.hiddPCIDeviceAB + attrTable[i], &value);
390 ok &= (value == matchVal[i]);
394 /* If requirements met, call Hook */
395 if (ok)
397 CALLHOOKPKT(msg->callback, dev, NULL);
401 ReleaseSemaphore(&pciBase->psd.dev_lock);
404 BOOL PCI__HW__RemoveDriver(OOP_Class *cl, OOP_Object *o, struct pHW_RemoveDriver *msg)
406 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
407 OOP_Object *dev, *next, *drv;
408 IPTR disallow = 0;
410 D(bug("[PCI] Removing hardware driver 0x%p\n", msg->driverObject));
413 * Get exclusive lock on devices list.
414 * If we cannot do this, then either enumeration is running or
415 * another driver is being added. We simply cannot remove the driver
416 * in this case.
417 * Well, in the latter case we actually could remove our driver, but
418 * i believe this is extremely rare situation.
420 if (!AttemptSemaphore(&pciBase->psd.dev_lock))
421 return FALSE;
424 * Now we can check if we can remove our devices.
425 * We think we can remove them if nobody has owned any of them.
426 * Drivers which behave badly will not own devices, or they will
427 * defer owning after enumeration loop has ended. So, removing a
428 * driver is still very dangerous.
429 * This can be improved if we implement map/unmnap and
430 * AddInterrupt/RemoveInterrupt accounting in our drivers. The
431 * driver would allow to expunge itself only if its internal counter
432 * of used resources is zero.
433 * PCI API wrappers (like prometheus.library) also build their
434 * own reflection of devices list, so we will have to implement either
435 * some ways to disable expunging, or (better) to get notifications
436 * about devices list being updated. With this notification we can
437 * have full hotplug support.
439 ForeachNode(&pciBase->psd.devices, dev)
441 OOP_GetAttr(dev, aHidd_PCIDevice_Driver, (IPTR *)&drv);
442 if (drv == msg->driverObject)
444 IPTR owner;
446 OOP_GetAttr(dev, aHidd_PCIDevice_Owner, &owner);
447 disallow |= owner;
451 if (disallow)
453 ReleaseSemaphore(&pciBase->psd.dev_lock);
454 D(bug("[PCI] PCI::RemoveDriver() failed, driver in use\n"));
455 return FALSE;
458 ForeachNodeSafe(&pciBase->psd.devices, dev, next)
460 REMOVE(dev);
461 OOP_DisposeObject(dev);
464 ReleaseSemaphore(&pciBase->psd.dev_lock);
465 D(bug("[PCI] PCI::RemHardwareDriver() succeeded\n"));
466 return OOP_DoSuperMethod(cl, o, &msg->mID);
469 /*****************************************************************************************
471 NAME
472 moHidd_PCI_AddHardwareDriver
474 SYNOPSIS
475 OOP_Object *OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_AddHardwareDriver *Msg);
477 OOP_Object *HIDD_PCI_AddHardwareDriver(OOP_Object *obj, OOP_Class *driverClass);
479 LOCATION
480 CLID_Hidd_PCI
482 FUNCTION
483 Creates a bus driver object and registers it in the system.
485 Since V4 this interface is obsolete and deprecated. Use moHW_AddDriver
486 method in order to install the driver.
488 INPUTS
489 obj - A PCI subsystem object.
490 driverClass - A pointer to OOP class of the driver. In order to create an object
491 of some previously registered public class, use
492 oop.library/OOP_FindClass().
494 RESULT
495 None.
497 NOTES
499 EXAMPLE
501 BUGS
503 SEE ALSO
504 moHidd_PCI_RemHardwareDriver
506 INTERNALS
508 *****************************************************************************************/
510 void PCI__Hidd_PCI__AddHardwareDriver(OOP_Class *cl, OOP_Object *o,
511 struct pHidd_PCI_AddHardwareDriver *msg)
513 HW_AddDriver(o, msg->driverClass, NULL);
516 AROS_UFH3(static BOOL, searchFunc,
517 AROS_UFHA(struct Hook *, h, A0),
518 AROS_UFHA(OOP_Object *, driverObject, A2),
519 AROS_UFHA(OOP_Class *, driverClass, A1))
521 AROS_USERFUNC_INIT
523 if (OOP_OCLASS(driverObject) == driverClass)
525 h->h_Data = driverObject;
526 return TRUE;
529 return FALSE;
531 AROS_USERFUNC_EXIT
534 /*****************************************************************************************
536 NAME
537 moHidd_PCI_RemHardwareDriver
539 SYNOPSIS
540 void OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_RemHardwareDriver *Msg);
542 void HIDD_PCI_RemHardwareDriver(OOP_Object *obj, OOP_Class *driverClass);
544 LOCATION
545 CLID_Hidd_PCI
547 FUNCTION
548 Unregisters and disposes bus driver objects of the given class.
550 Since V4 this interface is obsolete and deprecated. Use moHW_RemoveDriver
551 method in order to remove drivers.
553 INPUTS
554 obj - A PCI subsystem object.
555 driverClass - A pointer to a driver class.
557 RESULT
558 None
560 NOTES
562 EXAMPLE
564 BUGS
566 SEE ALSO
567 moHidd_PCI_AddHardwareDriver
569 INTERNALS
571 *****************************************************************************************/
573 BOOL PCI__Hidd_PCI__RemHardwareDriver(OOP_Class *cl, OOP_Object *o,
574 struct pHidd_PCI_RemHardwareDriver *msg)
576 BOOL ok = FALSE;
577 struct Hook searchHook =
579 .h_Entry = (HOOKFUNC)searchFunc
583 * A very stupid and slow algorithm.
584 * Find a driver using Enum method, remember it, then remove.
585 * Repeat until search succeeds.
586 * We cannot remove drivers inside enumeration hook because EnumDrivers
587 * locks internal objects list in shared mode. RemoveDriver locks the
588 * same list in exclusive mode, and it's impossible to change semaphore's
589 * mode on the fly.
593 searchHook.h_Data = NULL;
595 HW_EnumDrivers(o, &searchHook, msg->driverClass);
597 if (searchHook.h_Data)
599 ok = HW_RemoveDriver(o, searchHook.h_Data);
600 if (!ok)
601 break;
603 } while (searchHook.h_Data);
605 return ok;
608 OOP_Object *PCI__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
610 struct pcibase *pciBase = (struct pcibase *)cl->UserData;
611 struct pci_staticdata *psd = &pciBase->psd;
613 if (!psd->pciObject)
615 struct TagItem new_tags[] =
617 {aHW_ClassName, (IPTR)"PCI Local Bus"},
618 {TAG_DONE , 0 }
620 struct pRoot_New new_msg =
622 .mID = msg->mID,
623 .attrList = new_tags
626 psd->pciObject = (OOP_Object *)OOP_DoSuperMethod(cl, o, &new_msg.mID);
628 return psd->pciObject;
631 VOID PCI__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)