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

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