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

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