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

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