Libraries/ld-acess - APPEND and TRUNCATE flags (unimplimented)
[tpg/acess2.git] / KernelLand / 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 #include <drv_pci_int.h>\r
12 \r
13 #define USE_PORT_BITMAP 0\r
14 #define LIST_DEVICES    1\r
15 \r
16 // === STRUCTURES ===\r
17 typedef struct sPCIDevice\r
18 {\r
19         Uint16  bus, slot, fcn;\r
20         Uint16  vendor, device;\r
21         Uint32  class;  // Class:Subclass:ProgIf\r
22         Uint8   revision;\r
23         Uint32  ConfigCache[256/4];\r
24         char    Name[8];\r
25         tVFS_Node       Node;\r
26 } tPCIDevice;\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  int    PCI_ScanBus(int ID, int bFill);\r
35  \r
36  int    PCI_int_ReadDirRoot(tVFS_Node *node, int pos, char Dest[FILENAME_MAX]);\r
37 tVFS_Node       *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename);\r
38 Uint32  PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset);\r
39 size_t  PCI_int_ReadDevice(tVFS_Node *node, off_t Offset, size_t Length, void *buffer);\r
40  int    PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info);\r
41 \r
42 // === GLOBALS ===\r
43 MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL);\r
44  int    giPCI_BusCount = 1;\r
45  int    giPCI_InodeHandle = -1;\r
46  int    giPCI_DeviceCount = 0;\r
47 tPCIDevice      *gPCI_Devices = NULL;\r
48 tVFS_NodeType   gPCI_RootNodeType = {\r
49         .TypeName = "PCI Root Node",\r
50         .ReadDir = PCI_int_ReadDirRoot,\r
51         .FindDir = PCI_int_FindDirRoot\r
52 };\r
53 tVFS_NodeType   gPCI_DevNodeType = {\r
54         .TypeName = "PCI Dev Node",\r
55         .Read = PCI_int_ReadDevice\r
56 };\r
57 tDevFS_Driver   gPCI_DriverStruct = {\r
58         NULL, "pci",\r
59         {\r
60         .Flags = VFS_FFLAG_DIRECTORY,\r
61         .Size = -1,\r
62         .NumACLs = 1,\r
63         .ACLs = &gVFS_ACL_EveryoneRX,\r
64         .Type = &gPCI_RootNodeType\r
65         }\r
66 };\r
67 #if USE_PORT_BITMAP\r
68 Uint32  *gaPCI_PortBitmap = NULL;\r
69 #endif\r
70 Uint32  gaPCI_BusBitmap[256/32];\r
71  \r
72 // === CODE ===\r
73 /**\r
74  * \brief Scan the PCI Bus for devices\r
75  * \param Arguments     Boot-time parameters\r
76  */\r
77 int PCI_Install(char **Arguments)\r
78 {\r
79          int    ret, bus;\r
80         void    *tmpPtr;\r
81         \r
82         #if USE_PORT_BITMAP\r
83         // Build Portmap\r
84         gaPCI_PortBitmap = malloc( 1 << 13 );\r
85         if( !gaPCI_PortBitmap ) {\r
86                 Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13);\r
87                 return MODULE_ERR_MALLOC;\r
88         }\r
89         memset( gaPCI_PortBitmap, 0, 1 << 13 );\r
90          int    i;\r
91         for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )\r
92                 gaPCI_PortBitmap[i] = -1;\r
93         for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )\r
94                 gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;\r
95         #endif  \r
96 \r
97         // Scan Bus (Bus 0, Don't fill gPCI_Devices)\r
98         for( bus = 0; bus < giPCI_BusCount; bus ++ )\r
99         {\r
100                 ret = PCI_ScanBus(bus, 0);\r
101                 if(ret != MODULE_ERR_OK)        return ret;\r
102         }\r
103                 \r
104         if(giPCI_DeviceCount == 0) {\r
105                 Log_Notice("PCI", "No devices were found");\r
106                 return MODULE_ERR_NOTNEEDED;\r
107         }\r
108         \r
109         // Allocate device buffer\r
110         tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice));\r
111         if(tmpPtr == NULL) {\r
112                 Log_Warning("PCI", "Malloc ERROR");\r
113                 return MODULE_ERR_MALLOC;\r
114         }\r
115         gPCI_Devices = tmpPtr;\r
116         \r
117         Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount);\r
118         \r
119         // Reset counts\r
120         giPCI_DeviceCount = 0;\r
121         giPCI_BusCount = 1;\r
122         memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap));\r
123         // Rescan, filling the PCI device array\r
124         for( bus = 0; bus < giPCI_BusCount; bus ++ )\r
125         {\r
126                 PCI_ScanBus(bus, 1);\r
127         }\r
128         \r
129         // Complete Driver Structure\r
130         gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
131         \r
132         // And add to DevFS\r
133         DevFS_AddDevice(&gPCI_DriverStruct);\r
134         \r
135         return MODULE_ERR_OK;\r
136 }\r
137 \r
138 /**\r
139  * \brief Scans a specific PCI Bus\r
140  * \param BusID PCI Bus ID to scan\r
141  * \param bFill Fill the \a gPCI_Devices array?\r
142  */\r
143 int PCI_ScanBus(int BusID, int bFill)\r
144 {\r
145          int    dev, fcn;\r
146         tPCIDevice      devInfo;\r
147         \r
148         if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) )\r
149                 return MODULE_ERR_OK;\r
150         \r
151         gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32));\r
152         \r
153         for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus\r
154         {\r
155                 for( fcn = 0; fcn < 8; fcn++ )  // Max 8 functions per device\r
156                 {\r
157                         // Check if the device/function exists\r
158                         if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))\r
159                                 continue;\r
160                         \r
161                         if(devInfo.class == PCI_OC_PCIBRIDGE)\r
162                         {\r
163                                 #if LIST_DEVICES\r
164                                 if( !bFill )\r
165                                         Log_Log("PCI", "Bridge @ %i,%i:%i (0x%x:0x%x)",\r
166                                                 BusID, dev, fcn, devInfo.vendor, devInfo.device);\r
167                                 #endif\r
168                                 //TODO: Handle PCI-PCI Bridges\r
169                                 //PCI_ScanBus(devInfo.???, bFill);\r
170                                 giPCI_BusCount ++;\r
171                         }\r
172                         else\r
173                         {\r
174                                 #if LIST_DEVICES\r
175                                 if( !bFill )\r
176                                         Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",\r
177                                                 BusID, dev, fcn, devInfo.class,\r
178                                                 devInfo.vendor, devInfo.device, devInfo.revision);\r
179                                 #endif\r
180                         }\r
181                         \r
182                         if( bFill ) {\r
183                                 devInfo.Node.Inode = giPCI_DeviceCount;\r
184                                 memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice));\r
185                         }\r
186                         giPCI_DeviceCount ++;\r
187                         \r
188                         // If bit 23 of (soemthing) is set, there are sub-functions\r
189                         if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )\r
190                                 break;\r
191                 }\r
192         }\r
193         \r
194         return MODULE_ERR_OK;\r
195 }\r
196 \r
197 /**\r
198  * \brief Read from Root of PCI Driver\r
199 */\r
200 int PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])\r
201 {\r
202         ENTER("pNode iPos", Node, Pos);\r
203         if(Pos < 0 || Pos >= giPCI_DeviceCount) {\r
204                 LEAVE_RET('i', -EINVAL);\r
205         }\r
206         \r
207         LOG("Name = %s", gPCI_Devices[Pos].Name);\r
208         strncpy(Dest, gPCI_Devices[Pos].Name, FILENAME_MAX);\r
209         LEAVE_RET('i', 0);\r
210 }\r
211 /**\r
212  */\r
213 tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename)\r
214 {\r
215          int    bus,slot,fcn;\r
216          int    i;\r
217         // Validate Filename (Pointer and length)\r
218         if(!filename || strlen(filename) != 7)\r
219                 return NULL;\r
220         // Check for spacers\r
221         if(filename[2] != '.' || filename[5] != ':')\r
222                 return NULL;\r
223         \r
224         // Get Information\r
225         if(filename[0] < '0' || filename[0] > '9')      return NULL;\r
226         bus = (filename[0] - '0')*10;\r
227         if(filename[1] < '0' || filename[1] > '9')      return NULL;\r
228         bus += filename[1] - '0';\r
229         if(filename[3] < '0' || filename[3] > '9')      return NULL;\r
230         slot = (filename[3] - '0')*10;\r
231         if(filename[4] < '0' || filename[4] > '9')      return NULL;\r
232         slot += filename[4] - '0';\r
233         if(filename[6] < '0' || filename[6] > '9')      return NULL;\r
234         fcn = filename[6] - '0';\r
235         \r
236         // Find Match\r
237         for(i=0;i<giPCI_DeviceCount;i++)\r
238         {\r
239                 if(gPCI_Devices[i].bus != bus)          continue;\r
240                 if(gPCI_Devices[i].slot != slot)        continue;\r
241                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
242                 \r
243                 return &gPCI_Devices[i].Node;\r
244         }\r
245         \r
246         // Error Return\r
247         return NULL;\r
248 }\r
249 \r
250 /**\r
251  * \brief Read the PCI configuration space of a device\r
252  */\r
253 size_t PCI_int_ReadDevice(tVFS_Node *node, off_t pos, size_t length, void *buffer)\r
254 {       \r
255         if( pos + length > 256 )        return 0;\r
256         \r
257         memcpy(\r
258                 buffer,\r
259                 (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
260                 length);\r
261         \r
262         return length;\r
263 }\r
264 \r
265 // --- Kernel Code Interface ---\r
266 /**\r
267  * \brief Counts the devices with the specified codes\r
268  * \param vendor        Vendor ID\r
269  * \param device        Device ID\r
270  */\r
271 int PCI_CountDevices(Uint16 vendor, Uint16 device)\r
272 {\r
273         int i, ret=0;\r
274         for(i=0;i<giPCI_DeviceCount;i++)\r
275         {\r
276                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
277                 if(gPCI_Devices[i].device != device)    continue;\r
278                 ret ++;\r
279         }\r
280         return ret;\r
281 }\r
282 \r
283 /**\r
284  * \brief Gets the ID of the specified PCI device\r
285  * \param vendor        Vendor ID\r
286  * \param device        Device ID\r
287  * \param idx   Number of matching entry wanted\r
288  */\r
289 tPCIDev PCI_GetDevice(Uint16 vendor, Uint16 device, 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(j == idx)    return i;\r
297                 j ++;\r
298         }\r
299         return -1;\r
300 }\r
301 \r
302 /**\r
303  * \brief Gets the ID of a device by it's class code\r
304  * \param class Class Code\r
305  * \param mask  Mask for class comparison\r
306  * \param prev  ID of previous device (-1 for no previous)\r
307  */\r
308 tPCIDev PCI_GetDeviceByClass(Uint32 class, Uint32 mask, tPCIDev prev)\r
309 {\r
310          int    i;\r
311         // Check if prev is negative (meaning get first)\r
312         if(prev < 0)    i = 0;\r
313         else    i = prev+1;\r
314         \r
315         for( ; i < giPCI_DeviceCount; i++ )\r
316         {\r
317                 if((gPCI_Devices[i].class & mask) == class)\r
318                         return i;\r
319         }\r
320         return -1;\r
321 }\r
322 \r
323 int PCI_GetDeviceInfo(tPCIDev ID, Uint16 *Vendor, Uint16 *Device, Uint32 *Class)\r
324 {\r
325         tPCIDevice      *dev = &gPCI_Devices[ID];\r
326         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
327         \r
328         if(Vendor)      *Vendor = dev->vendor;\r
329         if(Device)      *Device = dev->device;\r
330         if(Class)       *Class = dev->class;\r
331         return 0;\r
332 }\r
333 \r
334 int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision)\r
335 {\r
336         tPCIDevice      *dev = &gPCI_Devices[ID];\r
337         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
338         \r
339         if(Revision)    *Revision = dev->revision;\r
340         return 0;\r
341 }\r
342 \r
343 int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID)\r
344 {\r
345         tPCIDevice      *dev = &gPCI_Devices[ID];\r
346         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
347         \r
348         if(SubsystemVendor)     *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF;\r
349         if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16;\r
350 \r
351         return 0;\r
352 }\r
353 \r
354 Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset)\r
355 {\r
356         Bus &= 0xFF;\r
357         Slot &= 0x1F;\r
358         Fcn &= 7;\r
359         Offset &= 0xFC;\r
360         return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC);\r
361 }\r
362 \r
363 Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size)\r
364 {\r
365         tPCIDevice      *dev;\r
366         Uint32  dword, addr;\r
367         \r
368         if( ID < 0 || ID >= giPCI_DeviceCount ) return 0;\r
369         if( Offset < 0 || Offset > 256 )        return 0;\r
370 \r
371         // TODO: Should I support non-aligned reads?\r
372         if( Offset & (Size - 1) )       return 0;\r
373 \r
374         dev = &gPCI_Devices[ID];\r
375         addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
376 \r
377         dword = PCI_CfgReadDWord(addr);\r
378         gPCI_Devices[ID].ConfigCache[Offset/4] = dword;\r
379         switch( Size )\r
380         {\r
381         case 1: return (dword >> (8 * (Offset&3))) & 0xFF;\r
382         case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF;\r
383         case 4: return dword;\r
384         default:\r
385                 return 0;\r
386         }\r
387 }\r
388 \r
389 void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value)\r
390 {\r
391         tPCIDevice      *dev;\r
392         Uint32  dword, addr;\r
393          int    shift;\r
394         if( ID < 0 || ID >= giPCI_DeviceCount ) return ;\r
395         if( Offset < 0 || Offset > 256 )        return ;\r
396         \r
397         dev = &gPCI_Devices[ID];\r
398         addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
399 \r
400         if(Size != 4)\r
401                 dword = PCI_CfgReadDWord(addr);\r
402         switch(Size)\r
403         {\r
404         case 1:\r
405                 shift = (Offset&3)*8;\r
406                 dword &= ~(0xFF << shift);\r
407                 dword |= Value << shift;\r
408                 break;\r
409         case 2:\r
410                 shift = (Offset&2)*8;\r
411                 dword &= ~(0xFFFF << shift);\r
412                 dword |= Value << shift;\r
413                 break;\r
414         case 4:\r
415                 dword = Value;\r
416                 break;\r
417         default:\r
418                 return;\r
419         }\r
420         PCI_CfgWriteDWord(addr, dword);\r
421 }\r
422 \r
423 /**\r
424  * \brief Get the IRQ assigned to a device\r
425  */\r
426 Uint8 PCI_GetIRQ(tPCIDev id)\r
427 {\r
428         if(id < 0 || id >= giPCI_DeviceCount)\r
429                 return 0;\r
430         return gPCI_Devices[id].ConfigCache[15] & 0xFF;\r
431         //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
432 }\r
433 \r
434 /**\r
435  * \brief Read the a BAR (base address register) from the PCI config space\r
436  */\r
437 Uint32 PCI_GetBAR(tPCIDev id, int BARNum)\r
438 {\r
439         if(id < 0 || id >= giPCI_DeviceCount)\r
440                 return 0;\r
441         if(BARNum < 0 || BARNum >= 6)\r
442                 return 0;\r
443         return gPCI_Devices[id].ConfigCache[4+BARNum];\r
444 }\r
445 \r
446 /**\r
447  * \brief Get device information for a slot/function\r
448  */\r
449 int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info)\r
450 {\r
451         Uint32  vendor_dev, tmp;\r
452          int    i;\r
453         Uint32  addr;\r
454         addr = PCI_int_GetBusAddr(bus, slot, fcn, 0);   \r
455 \r
456         vendor_dev = PCI_CfgReadDWord( addr );\r
457         if((vendor_dev & 0xFFFF) == 0xFFFF)     // Invalid Device\r
458                 return 0;\r
459 \r
460         info->ConfigCache[0] = vendor_dev;\r
461         for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )\r
462         {\r
463                 info->ConfigCache[i] = PCI_CfgReadDWord(addr);\r
464         }       \r
465 \r
466         info->bus = bus;\r
467         info->slot = slot;\r
468         info->fcn = fcn;\r
469         info->vendor = vendor_dev & 0xFFFF;\r
470         info->device = vendor_dev >> 16;\r
471         tmp = info->ConfigCache[2];\r
472         info->revision = tmp & 0xFF;\r
473         info->class = tmp >> 8;\r
474         \r
475 //      #if LIST_DEVICES\r
476 //      Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
477 //      Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
478 //      Log("Class: 0x%06x", info->class);\r
479 //      #endif\r
480         \r
481         // Make node name\r
482         info->Name[0] = '0' + bus/10;\r
483         info->Name[1] = '0' + bus%10;\r
484         info->Name[2] = '.';\r
485         info->Name[3] = '0' + slot/10;\r
486         info->Name[4] = '0' + slot%10;\r
487         info->Name[5] = ':';\r
488         info->Name[6] = '0' + fcn;\r
489         info->Name[7] = '\0';\r
490         \r
491         // Create VFS Node\r
492         memset( &info->Node, 0, sizeof(tVFS_Node) );\r
493         info->Node.Size = 256;\r
494         \r
495         info->Node.NumACLs = 1;\r
496         info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
497         \r
498         info->Node.Type = &gPCI_DevNodeType;\r
499         \r
500         return 1;\r
501 }\r
502 \r
503 // === EXPORTS ===\r
504 //*\r
505 EXPORT(PCI_CountDevices);\r
506 EXPORT(PCI_GetDevice);\r
507 EXPORT(PCI_GetDeviceByClass);\r
508 EXPORT(PCI_GetDeviceInfo);\r
509 EXPORT(PCI_GetDeviceVersion);\r
510 EXPORT(PCI_GetDeviceSubsys);\r
511 //EXPORT(PCI_AssignPort);\r
512 EXPORT(PCI_GetBAR);\r
513 EXPORT(PCI_GetIRQ);\r
514 //*/\r

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