2 Copyright © 2004-2018, The AROS Development Team. All rights reserved.
7 #include <aros/debug.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>
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
);
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
)
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 :) )
49 Type
= HIDD_PCIDriver_ReadConfigByte(o
, NULL
, bus
, dev
, sub
, PCICS_HEADERTYPE
);
51 if ((Type
& PCIHT_MULTIFUNC
) == PCIHT_MULTIFUNC
)
57 static OOP_Object
*InsertDevice(OOP_Class
*cl
, ULONG
*highBus
, struct TagItem
*devtags
)
59 struct pcibase
*pciBase
= (struct pcibase
*)cl
->UserData
;
63 pcidev
= OOP_NewObject(pciBase
->psd
.pciDeviceClass
, NULL
, devtags
);
66 OOP_GetAttr(pcidev
, aHidd_PCIDevice_isBridge
, &bridge
);
69 OOP_GetAttr(pcidev
, aHidd_PCIDevice_SecBus
, &secbus
);
70 if (secbus
> *highBus
)
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
);
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
;
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 },
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);
140 devtags
[4].ti_Data
= HIDD_PCIDriver_HasExtendedConfig(drv
, bus
, dev
, 0);
141 InsertDevice(cl
, &highBus
, devtags
);
144 /* Cool! Multifunction device, search subfunctions then */
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
);
162 if (irq_routing
!= NULL
)
164 struct pcibase
*pciBase
= (struct pcibase
*)cl
->UserData
;
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 */
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
)
185 BOOL irq_found
= FALSE
;
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! */
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
},
217 D(bug("[PCI] Got a match. Setting INTLine to %d\n",
219 OOP_SetAttrs(pcidev
, attr
);
226 D(bug("[PCI] No match on bus %d. Trying parent bridge...\n",
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
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
);
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
);
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 /*****************************************************************************************
294 moHidd_PCI_EnumDevices
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);
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.
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)
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]
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
;
355 IPTR matchVal
[sizeof(attrTable
)];
360 for (i
= 0; i
< sizeof(attrTable
); i
++)
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 */
383 for (i
= 0; i
< sizeof(attrTable
); i
++)
385 if (matchVal
[i
] != ~0)
389 OOP_GetAttr(dev
, pciBase
->psd
.hiddPCIDeviceAB
+ attrTable
[i
], &value
);
390 ok
&= (value
== matchVal
[i
]);
394 /* If requirements met, call Hook */
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
;
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
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
))
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
)
446 OOP_GetAttr(dev
, aHidd_PCIDevice_Owner
, &owner
);
453 ReleaseSemaphore(&pciBase
->psd
.dev_lock
);
454 D(bug("[PCI] PCI::RemoveDriver() failed, driver in use\n"));
458 ForeachNodeSafe(&pciBase
->psd
.devices
, dev
, next
)
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 /*****************************************************************************************
472 moHidd_PCI_AddHardwareDriver
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);
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.
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().
504 moHidd_PCI_RemHardwareDriver
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
))
523 if (OOP_OCLASS(driverObject
) == driverClass
)
525 h
->h_Data
= driverObject
;
534 /*****************************************************************************************
537 moHidd_PCI_RemHardwareDriver
540 void OOP_DoMethod(OOP_Object *obj, struct pHidd_PCI_RemHardwareDriver *Msg);
542 void HIDD_PCI_RemHardwareDriver(OOP_Object *obj, OOP_Class *driverClass);
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.
554 obj - A PCI subsystem object.
555 driverClass - A pointer to a driver class.
567 moHidd_PCI_AddHardwareDriver
571 *****************************************************************************************/
573 BOOL
PCI__Hidd_PCI__RemHardwareDriver(OOP_Class
*cl
, OOP_Object
*o
,
574 struct pHidd_PCI_RemHardwareDriver
*msg
)
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
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
);
603 } while (searchHook
.h_Data
);
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
;
615 struct TagItem new_tags
[] =
617 {aHW_ClassName
, (IPTR
)"PCI Local Bus"},
620 struct pRoot_New new_msg
=
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
)