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

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