Changes to the module loader to handle specific errors from modules
[tpg/acess2.git] / Kernel / drv / pci.c
1 /*\r
2  * AcessOS/AcessBasic v0.1\r
3  * PCI Bus Driver\r
4  */\r
5 #define DEBUG   0\r
6 #include <acess.h>\r
7 #include <modules.h>\r
8 #include <vfs.h>\r
9 #include <fs_devfs.h>\r
10 #include <drv_pci.h>\r
11 \r
12 #define LIST_DEVICES    1\r
13 \r
14 // === STRUCTURES ===\r
15 typedef struct s_pciDevice {\r
16         Uint16  bus, slot, fcn;\r
17         Uint16  vendor, device;\r
18         union {\r
19                 struct {Uint8 class, subclass;};\r
20                 Uint16  oc;\r
21         };\r
22         Uint16  revision;\r
23         Uint32  ConfigCache[256/4];\r
24         char    Name[8];\r
25         tVFS_Node       Node;\r
26 } t_pciDevice;\r
27 \r
28 // === CONSTANTS ===\r
29 #define SPACE_STEP      5\r
30 #define MAX_RESERVED_PORT       0xD00\r
31 \r
32 // === PROTOTYPES ===\r
33  int    PCI_Install(char **Arguments);\r
34 char    *PCI_ReadDirRoot(tVFS_Node *node, int pos);\r
35 tVFS_Node       *PCI_FindDirRoot(tVFS_Node *node, char *filename);\r
36 Uint64  PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer);\r
37  \r
38  int    PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn);\r
39  int    PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx);\r
40  int    PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev);\r
41 Uint8   PCI_GetIRQ(int id);\r
42 Uint32  PCI_GetBAR0(int id);\r
43 Uint32  PCI_GetBAR1(int id);\r
44 Uint32  PCI_GetBAR3(int id);\r
45 Uint32  PCI_GetBAR4(int id);\r
46 Uint32  PCI_GetBAR5(int id);\r
47 Uint16  PCI_AssignPort(int id, int bar, int count);\r
48 \r
49  int    PCI_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, t_pciDevice *info);\r
50 Uint32  PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
51 void    PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data);\r
52 Uint16  PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
53 Uint8   PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
54 \r
55 // === GLOBALS ===\r
56 //MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL);\r
57  int    giPCI_BusCount = 1;\r
58  int    giPCI_InodeHandle = -1;\r
59  int    giPCI_DeviceCount = 0;\r
60 t_pciDevice     *gPCI_Devices = NULL;\r
61 tDevFS_Driver   gPCI_DriverStruct = {\r
62         NULL, "pci",\r
63         {\r
64         .Flags = VFS_FFLAG_DIRECTORY,\r
65         .Size = -1,\r
66         .NumACLs = 1,\r
67         .ACLs = &gVFS_ACL_EveryoneRX,\r
68         .ReadDir = PCI_ReadDirRoot,\r
69         .FindDir = PCI_FindDirRoot\r
70         }\r
71 };\r
72  Uint32 *gaPCI_PortBitmap = NULL;\r
73  \r
74 // === CODE ===\r
75 /**\r
76  * \fn int PCI_Install()\r
77  * \brief Scan the PCI Bus for devices\r
78  */\r
79 int PCI_Install(char **Arguments)\r
80 {\r
81          int    bus, dev, fcn, i;\r
82          int    space = 0;\r
83         t_pciDevice     devInfo;\r
84         void    *tmpPtr = NULL;\r
85         \r
86         // Build Portmap\r
87         gaPCI_PortBitmap = malloc( 1 << 13 );\r
88         memset( gaPCI_PortBitmap, 0, 1 << 13 );\r
89         for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )\r
90                 gaPCI_PortBitmap[i] = -1;\r
91         for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )\r
92                 gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;\r
93         \r
94         // Scan Busses\r
95         for( bus = 0; bus < giPCI_BusCount; bus++ )\r
96         {\r
97                 for( dev = 0; dev < 10; dev++ ) // 10 Devices per bus\r
98                 {\r
99                         for( fcn = 0; fcn < 8; fcn++ )  // 8 functions per device\r
100                         {\r
101                                 // Check if the device/function exists\r
102                                 if(!PCI_EnumDevice(bus, dev, fcn, &devInfo))\r
103                                 {\r
104                                         continue;\r
105                                 }\r
106                                 \r
107                                 if(giPCI_DeviceCount == space)\r
108                                 {\r
109                                         space += SPACE_STEP;\r
110                                         tmpPtr = realloc(gPCI_Devices, space*sizeof(t_pciDevice));\r
111                                         if(tmpPtr == NULL)\r
112                                                 break;\r
113                                         gPCI_Devices = tmpPtr;\r
114                                 }\r
115                                 if(devInfo.oc == PCI_OC_PCIBRIDGE)\r
116                                 {\r
117                                         #if LIST_DEVICES\r
118                                         Log("[PCI ] Bridge @ %i,%i:%i (0x%x:0x%x)",\r
119                                                 bus, dev, fcn, devInfo.vendor, devInfo.device);\r
120                                         #endif\r
121                                         giPCI_BusCount++;\r
122                                 }\r
123                                 devInfo.Node.Inode = giPCI_DeviceCount;\r
124                                 memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(t_pciDevice));\r
125                                 giPCI_DeviceCount ++;\r
126                                 #if LIST_DEVICES\r
127                                 Log("[PCI ] Device %i,%i:%i => 0x%x:0x%x",\r
128                                         bus, dev, fcn, devInfo.vendor, devInfo.device);\r
129                                 #endif\r
130                                 \r
131                                 // WTF is this for?\r
132                                 if(fcn == 0) {\r
133                                         if( !(devInfo.ConfigCache[3] & 0x800000) )\r
134                                                 break;\r
135                                 }\r
136                         }\r
137                         if(tmpPtr != gPCI_Devices)\r
138                                 break;\r
139                 }\r
140                 if(tmpPtr != gPCI_Devices)\r
141                         break;\r
142         }\r
143         \r
144         if(giPCI_DeviceCount == 0)\r
145                 return MODULE_ERR_NOTNEEDED;\r
146         \r
147         tmpPtr = realloc(gPCI_Devices, giPCI_DeviceCount*sizeof(t_pciDevice));\r
148         if(tmpPtr == NULL)\r
149                 return MODULE_ERR_MALLOC;\r
150         gPCI_Devices = tmpPtr;\r
151         \r
152         // Complete Driver Structure    \r
153         gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
154         \r
155         // And add to DevFS\r
156         DevFS_AddDevice(&gPCI_DriverStruct);\r
157         \r
158         return MODULE_ERR_OK;\r
159 }\r
160 \r
161 /**\r
162  * \fn char *PCI_ReadDirRoot(tVFS_Node *Node, int Pos)\r
163  * \brief Read from Root of PCI Driver\r
164 */\r
165 char *PCI_ReadDirRoot(tVFS_Node *Node, int Pos)\r
166 {\r
167         ENTER("pNode iPos", Node, Pos);\r
168         if(Pos < 0 || Pos >= giPCI_DeviceCount) {\r
169                 LEAVE('n');\r
170                 return NULL;\r
171         }\r
172         \r
173         LEAVE('s', gPCI_Devices[Pos].Name);\r
174         return strdup( gPCI_Devices[Pos].Name );\r
175 }\r
176 /**\r
177  * \fn tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename)\r
178  */\r
179 tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename)\r
180 {\r
181          int    bus,slot,fcn;\r
182          int    i;\r
183         // Validate Filename (Pointer and length)\r
184         if(!filename || strlen(filename) != 7)\r
185                 return NULL;\r
186         // Check for spacers\r
187         if(filename[2] != '.' || filename[5] != ':')\r
188                 return NULL;\r
189         \r
190         // Get Information\r
191         if(filename[0] < '0' || filename[0] > '9')      return NULL;\r
192         bus = (filename[0] - '0')*10;\r
193         if(filename[1] < '0' || filename[1] > '9')      return NULL;\r
194         bus += filename[1] - '0';\r
195         if(filename[3] < '0' || filename[3] > '9')      return NULL;\r
196         slot = (filename[3] - '0')*10;\r
197         if(filename[4] < '0' || filename[4] > '9')      return NULL;\r
198         slot += filename[4] - '0';\r
199         if(filename[6] < '0' || filename[6] > '9')      return NULL;\r
200         fcn = filename[6] - '0';\r
201         \r
202         // Find Match\r
203         for(i=0;i<giPCI_DeviceCount;i++)\r
204         {\r
205                 if(gPCI_Devices[i].bus != bus)          continue;\r
206                 if(gPCI_Devices[i].slot != slot)        continue;\r
207                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
208                 \r
209                 return &gPCI_Devices[i].Node;\r
210         }\r
211         \r
212         // Error Return\r
213         return NULL;\r
214 }\r
215 \r
216 /**\r
217  * \fn Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
218  */\r
219 Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
220 {       \r
221         if( pos + length > 256 )        return 0;\r
222         \r
223         memcpy(\r
224                 buffer,\r
225                 (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
226                 length);\r
227         \r
228         return length;\r
229 }\r
230 \r
231 // --- Kernel Code Interface ---\r
232 /**\r
233  \fn int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn)\r
234  \brief Counts the devices with the specified codes\r
235  \param vendor  Vendor ID\r
236  \param device  Device ID\r
237  \param fcn     Function ID\r
238 */\r
239 int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn)\r
240 {\r
241         int i, ret=0;\r
242         for(i=0;i<giPCI_DeviceCount;i++)\r
243         {\r
244                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
245                 if(gPCI_Devices[i].device != device)    continue;\r
246                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
247                 ret ++;\r
248         }\r
249         return ret;\r
250 }\r
251 \r
252 /**\r
253  \fn int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx)\r
254  \brief Gets the ID of the specified PCI device\r
255  \param vendor  Vendor ID\r
256  \param device  Device ID\r
257  \param fcn     Function IDs\r
258  \param idx     Number of matching entry wanted\r
259 */\r
260 int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx)\r
261 {\r
262         int i, j=0;\r
263         for(i=0;i<giPCI_DeviceCount;i++)\r
264         {\r
265                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
266                 if(gPCI_Devices[i].device != device)    continue;\r
267                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
268                 if(j == idx)    return i;\r
269                 j ++;\r
270         }\r
271         return -1;\r
272 }\r
273 \r
274 /**\r
275  * \fn int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev)\r
276  * \brief Gets the ID of a device by it's class code\r
277  * \param class Class Code\r
278  * \param mask  Mask for class comparison\r
279  * \param prev  ID of previous device (-1 for no previous)\r
280  */\r
281 int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev)\r
282 {\r
283          int    i;\r
284         // Check if prev is negative (meaning get first)\r
285         if(prev < 0)    i = 0;\r
286         else    i = prev+1;\r
287         \r
288         for( ; i < giPCI_DeviceCount; i++ )\r
289         {\r
290                 if((gPCI_Devices[i].oc & mask) != class)        continue;\r
291                 return i;\r
292         }\r
293         return -1;\r
294 }\r
295 \r
296 /**\r
297  \fn Uint8 PCI_GetIRQ(int id)\r
298 */\r
299 Uint8 PCI_GetIRQ(int id)\r
300 {\r
301         if(id < 0 || id >= giPCI_DeviceCount)\r
302                 return 0;\r
303         return gPCI_Devices[id].ConfigCache[15];\r
304         //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
305 }\r
306 \r
307 /**\r
308  \fn Uint32 PCI_GetBAR0(int id)\r
309 */\r
310 Uint32 PCI_GetBAR0(int id)\r
311 {\r
312         if(id < 0 || id >= giPCI_DeviceCount)\r
313                 return 0;\r
314         return gPCI_Devices[id].ConfigCache[4];\r
315 }\r
316 \r
317 /**\r
318  \fn Uint32 PCI_GetBAR1(int id)\r
319 */\r
320 Uint32 PCI_GetBAR1(int id)\r
321 {\r
322         if(id < 0 || id >= giPCI_DeviceCount)\r
323                 return 0;\r
324         return gPCI_Devices[id].ConfigCache[5];\r
325 }\r
326 \r
327 /**\r
328  \fn Uint32 PCI_GetBAR2(int id)\r
329 */\r
330 Uint32 PCI_GetBAR2(int id)\r
331 {\r
332         if(id < 0 || id >= giPCI_DeviceCount)\r
333                 return 0;\r
334         return gPCI_Devices[id].ConfigCache[6];\r
335 }\r
336 \r
337 /**\r
338  \fn Uint32 PCI_GetBAR3(int id)\r
339 */\r
340 Uint32 PCI_GetBAR3(int id)\r
341 {\r
342         if(id < 0 || id >= giPCI_DeviceCount)\r
343                 return 0;\r
344         return gPCI_Devices[id].ConfigCache[7];\r
345 }\r
346 \r
347 /**\r
348  \fn Uint32 PCI_GetBAR4(int id)\r
349 */\r
350 Uint32 PCI_GetBAR4(int id)\r
351 {\r
352         if(id < 0 || id >= giPCI_DeviceCount)\r
353                 return 0;\r
354         return gPCI_Devices[id].ConfigCache[8];\r
355 }\r
356 \r
357 /**\r
358  \fn Uint32 PCI_GetBAR5(int id)\r
359 */\r
360 Uint32 PCI_GetBAR5(int id)\r
361 {\r
362         if(id < 0 || id >= giPCI_DeviceCount)\r
363                 return 0;\r
364         return gPCI_Devices[id].ConfigCache[9];\r
365 }\r
366 \r
367 Uint16 PCI_AssignPort(int id, int bar, int count)\r
368 {\r
369         Uint16  portVals;\r
370          int    gran=0;\r
371          int    i, j;\r
372         t_pciDevice     *dev;\r
373         \r
374         //LogF("PCI_AssignPort: (id=%i,bar=%i,count=%i)\n", id, bar, count);\r
375         \r
376         if(id < 0 || id >= giPCI_DeviceCount)   return 0;\r
377         if(bar < 0 || bar > 5)  return 0;\r
378         \r
379         dev = &gPCI_Devices[id];\r
380         \r
381         PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, -1 );\r
382         portVals = PCI_CfgReadDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4 );\r
383         dev->ConfigCache[4+bar] = portVals;\r
384         //LogF(" PCI_AssignPort: portVals = 0x%x\n", portVals);\r
385         \r
386         // Check for IO port\r
387         if( !(portVals & 1) )   return 0;\r
388         \r
389         // Mask out final bit\r
390         portVals &= ~1;\r
391         \r
392         // Get Granuality\r
393         __asm__ __volatile__ ("bsf %%eax, %%ecx" : "=c" (gran) : "a" (portVals) );\r
394         gran = 1 << gran;\r
395         //LogF(" PCI_AssignPort: gran = 0x%x\n", gran);\r
396         \r
397         // Find free space\r
398         portVals = 0;\r
399         for( i = 0; i < 1<<16; i += gran )\r
400         {\r
401                 for( j = 0; j < count; j ++ )\r
402                 {\r
403                         if( gaPCI_PortBitmap[ (i+j)>>5 ] & 1 << ((i+j)&0x1F) )\r
404                                 break;\r
405                 }\r
406                 if(j == count) {\r
407                         portVals = i;\r
408                         break;\r
409                 }\r
410         }\r
411         \r
412         if(portVals)\r
413         {\r
414                 for( j = 0; j < count; j ++ )\r
415                 {\r
416                         if( gaPCI_PortBitmap[ (portVals+j)>>5 ] |= 1 << ((portVals+j)&0x1F) )\r
417                                 break;\r
418                 }\r
419                 PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, portVals|1 );\r
420                 dev->ConfigCache[4+bar] = portVals|1;\r
421         }\r
422         \r
423         // Return\r
424         //LogF("PCI_AssignPort: RETURN 0x%x\n", portVals);\r
425         return portVals;\r
426 }\r
427 \r
428 /**\r
429  * \fn int      PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info)\r
430  */\r
431 int     PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info)\r
432 {\r
433         Uint16  vendor;\r
434          int    i;\r
435         Uint32  addr;\r
436         \r
437         vendor = PCI_CfgReadWord(bus, slot, fcn, 0x0|0);\r
438         if(vendor == 0xFFFF)    // Invalid Device\r
439                 return 0;\r
440                 \r
441         info->bus = bus;\r
442         info->slot = slot;\r
443         info->fcn = fcn;\r
444         info->vendor = vendor;\r
445         info->device = PCI_CfgReadWord(bus, slot, fcn, 0x0|2);\r
446         info->revision = PCI_CfgReadWord(bus, slot, fcn, 0x8|0);\r
447         info->oc = PCI_CfgReadWord(bus, slot, fcn, 0x8|2);\r
448         \r
449         // Load Config Bytes\r
450         addr = 0x80000000 | ((Uint)bus<<16) | ((Uint)slot<<11) | ((Uint)fcn<<8);\r
451         for(i=0;i<256/4;i++)\r
452         {\r
453                 #if 1\r
454                 outd(0xCF8, addr);\r
455                 info->ConfigCache[i] = ind(0xCFC);\r
456                 addr += 4;\r
457                 #else\r
458                 info->ConfigCache[i] = PCI_CfgReadDWord(bus, slot, fcn, i*4);\r
459                 #endif\r
460         }\r
461         \r
462         //#if LIST_DEVICES\r
463         //Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
464         //Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
465         //Log("Class: 0x%04x", info->oc);\r
466         //#endif\r
467         \r
468         // Make node name\r
469         info->Name[0] = '0' + bus/10;\r
470         info->Name[1] = '0' + bus%10;\r
471         info->Name[2] = '.';\r
472         info->Name[3] = '0' + slot/10;\r
473         info->Name[4] = '0' + slot%10;\r
474         info->Name[5] = ':';\r
475         info->Name[6] = '0' + fcn;\r
476         info->Name[7] = '\0';\r
477         \r
478         // Create VFS Node\r
479         memset( &info->Node, 0, sizeof(tVFS_Node) );\r
480         info->Node.Size = 256;\r
481         \r
482         info->Node.NumACLs = 1;\r
483         info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
484         \r
485         info->Node.Read = PCI_ReadDevice;\r
486         \r
487         return 1;\r
488 }\r
489 \r
490 Uint32 PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
491 {\r
492         Uint32  address;\r
493         Uint32  data;\r
494         \r
495         bus &= 0xFF;    // 8 Bits\r
496         dev &= 0x1F;    // 5 Bits\r
497         func &= 0x7;    // 3 Bits\r
498         offset &= 0xFF; // 8 Bits\r
499         \r
500         address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
501         outd(0xCF8, address);\r
502         \r
503         data = ind(0xCFC);\r
504         return (Uint32)data;\r
505 }\r
506 void PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data)\r
507 {\r
508         Uint32  address;\r
509         \r
510         bus &= 0xFF;    // 8 Bits\r
511         dev &= 0x1F;    // 5 Bits\r
512         func &= 0x7;    // 3 Bits\r
513         offset &= 0xFF; // 8 Bits\r
514         \r
515         address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
516         outd(0xCF8, address);\r
517         outd(0xCFC, data);\r
518 }\r
519 Uint16 PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
520 {\r
521         Uint32  data;\r
522         \r
523         bus &= 0xFF;    // 8 Bits\r
524         dev &= 0x1F;    // 5 Bits\r
525         func &= 0x7;    // 3 Bits\r
526         offset &= 0xFF; // 8 Bits\r
527         \r
528         //LogF("PCI_CfgReadWord: (bus=0x%x,dev=0x%x,func=%x,offset=0x%x)\n", bus, dev, func, offset);\r
529         \r
530         outd(0xCF8,\r
531                 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC) );\r
532         \r
533         data = ind(0xCFC);\r
534         data >>= (offset&2)*8;  //Allow Access to Upper Word\r
535         //LogF("PCI_CfgReadWord: RETURN 0x%x\n", data&0xFFFF);\r
536         return (Uint16)data;\r
537 }\r
538 \r
539 Uint8 PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
540 {\r
541         Uint32  address;\r
542         Uint32  data;\r
543         \r
544         bus &= 0xFF;    // 8 Bits\r
545         dev &= 0x1F;    // 4 Bits\r
546         func &= 0x7;    // 3 Bits\r
547         offset &= 0xFF; // 8 Bits\r
548         \r
549         address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
550         outd(0xCF8, address);\r
551         \r
552         data = ind(0xCFC);\r
553         data >>= (offset&3)*8;  //Allow Access to Upper Word\r
554         return (Uint8)data;\r
555 }\r
556 \r
557 \r
558 // === EXPORTS ===\r
559 //*\r
560 EXPORT(PCI_CountDevices);\r
561 EXPORT(PCI_GetDevice);\r
562 EXPORT(PCI_GetDeviceByClass);\r
563 EXPORT(PCI_AssignPort);\r
564 EXPORT(PCI_GetIRQ);\r
565 //*/\r

UCC git Repository :: git.ucc.asn.au