118699303709542ac0bee70ee62772e375a93ff1
[profile/ivi/syslinux.git] / com32 / lib / pci / scan.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2006-2007 Erwan Velu - All Rights Reserved
4  *
5  *   Permission is hereby granted, free of charge, to any person
6  *   obtaining a copy of this software and associated documentation
7  *   files (the "Software"), to deal in the Software without
8  *   restriction, including without limitation the rights to use,
9  *   copy, modify, merge, publish, distribute, sublicense, and/or
10  *   sell copies of the Software, and to permit persons to whom
11  *   the Software is furnished to do so, subject to the following
12  *   conditions:
13  *
14  *   The above copyright notice and this permission notice shall
15  *   be included in all copies or substantial portions of the Software.
16  *
17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  *   OTHER DEALINGS IN THE SOFTWARE.
25  *
26  * ----------------------------------------------------------------------- */
27
28 /*
29  * pci.c
30  *
31  * A module to extract pci informations
32  */
33
34 #include <inttypes.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <console.h>
39 #include <sys/pci.h>
40 #include <com32.h>
41 #include <stdbool.h>
42
43 #ifdef DEBUG
44 # define dprintf printf
45 #else
46 # define dprintf(...) ((void)0)
47 #endif
48
49 #define MAX_LINE 512
50
51 /* searching the next char that is not a space */
52 static char *skipspace(char *p)
53 {
54   while (*p && *p <= ' ')
55     p++;
56
57   return p;
58 }
59
60 /* removing any \n found in a string */
61 static void remove_eol(char *string)
62 {
63  int j = strlen(string);
64  int i = 0;
65  for(i = 0; i < j; i++) if(string[i] == '\n') string[i] = 0;
66 }
67
68 /* converting a hexa string into its numerical value*/
69 static int hex_to_int(char *hexa)
70 {
71   return strtoul(hexa, NULL, 16);
72 }
73
74 /* Try to match any pci device to the appropriate kernel module */
75 /* it uses the modules.pcimap from the boot device*/
76 void get_module_name_from_pci_ids(struct pci_device_list *pci_device_list)
77 {
78   char line[MAX_LINE];
79   char module_name[21]; // the module name field is 21 char long
80   char delims[]=" ";    // colums are separated by spaces
81   char vendor_id[16];
82   char product_id[16];
83   char sub_vendor_id[16];
84   char sub_product_id[16];
85   FILE *f;
86   int pci_dev;
87
88   /* Intializing the linux_kernel_module for each pci device to "unknow" */
89   /* adding a pci_dev_info member if needed*/
90   for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
91     struct pci_device *pci_device = &(pci_device_list->pci_device[pci_dev]);
92
93     /* initialize the pci_dev_info structure if it doesn't exist yet. */
94     if (! pci_device->pci_dev_info) {
95       pci_device->pci_dev_info = calloc(1,sizeof (struct pci_device));
96       if (!pci_device->pci_dev_info) {
97         printf("Can't allocate memory\n");
98         return;
99       }
100     }
101     pci_device->pci_dev_info->linux_kernel_module=strdup("unknown");
102   }
103
104   /* Opening the modules.pcimap (ofa linux kernel) from the boot device*/
105   f=fopen("modules.pcimap","r");
106   if (!f)
107     return;
108
109   strcpy(vendor_id,"0000");
110   strcpy(product_id,"0000");
111   strcpy(sub_product_id,"0000");
112   strcpy(sub_vendor_id,"0000");
113
114   /* for each line we found in the modules.pcimap*/
115   while ( fgets(line, sizeof line, f) ) {
116     /*skipping unecessary lines */
117     if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
118         continue;
119
120     char *result = NULL;
121     int field=0;
122
123     /* looking for the next field */
124     result = strtok(line, delims);
125     while( result != NULL ) {
126        /* if the column is larger than 1 char */
127        /* multiple spaces generates some empty fields*/
128        if (strlen(result)>1) {
129          switch (field) {
130          case 0:strcpy(module_name,result); break;
131          case 1:strcpy(vendor_id,result); break;
132          case 2:strcpy(product_id,result); break;
133          case 3:strcpy(sub_vendor_id,result); break;
134          case 4:strcpy(sub_product_id,result); break;
135          }
136          field++;
137        }
138        /* Searching the next field*/
139        result = strtok( NULL, delims );
140    }
141     /* if a pci_device match an entry, fill the linux_kernel_module with
142        the appropriate kernel module */
143     for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
144       struct pci_device *pci_device =
145         &pci_device_list->pci_device[pci_dev];
146
147       if (hex_to_int(vendor_id) == pci_device->vendor &&
148           hex_to_int(product_id) == pci_device->product &&
149           (hex_to_int(sub_product_id) & pci_device->sub_product)
150           == pci_device->sub_product &&
151           (hex_to_int(sub_vendor_id) & pci_device->sub_vendor)
152           == pci_device->sub_vendor)
153         strcpy(pci_device->pci_dev_info->linux_kernel_module,
154                module_name);
155     }
156   }
157  fclose(f);
158 }
159
160 /* Try to match any pci device to the appropriate vendor and product name */
161 /* it uses the pci.ids from the boot device*/
162 void get_name_from_pci_ids(struct pci_device_list *pci_device_list)
163 {
164   char line[MAX_LINE];
165   char *vendor=NULL;
166   char vendor_id[5];
167   char *product=NULL;
168   char product_id[5];
169   char sub_product_id[5];
170   char sub_vendor_id[5];
171   FILE *f;
172   int pci_dev;
173
174  /* Intializing the vendor/product name for each pci device to "unknow" */
175  /* adding a pci_dev_info member if needed*/
176  for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
177     struct pci_device *pci_device = &pci_device_list->pci_device[pci_dev];
178
179     /* initialize the pci_dev_info structure if it doesn't exist yet. */
180     if (! pci_device->pci_dev_info) {
181       pci_device->pci_dev_info = calloc(1,sizeof (struct pci_device));
182
183       if (!pci_device->pci_dev_info) {
184         printf("Can't allocate memory\n");
185         return;
186       }
187     }
188
189     pci_device->pci_dev_info->vendor_name=strdup("unknown");
190     pci_device->pci_dev_info->product_name=strdup("unknown");
191   }
192
193   /* Opening the pci.ids from the boot device*/
194   f=fopen("pci.ids","r");
195   if (!f)
196         return;
197
198   strcpy(vendor_id,"0000");
199   strcpy(product_id,"0000");
200   strcpy(sub_product_id,"0000");
201   strcpy(sub_vendor_id,"0000");
202
203
204   /* for each line we found in the pci.ids*/
205   while ( fgets(line, sizeof line, f) ) {
206
207     /* Skipping uncessary lines */
208     if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 'C') ||
209         (line[0] == 10))
210         continue;
211     /* If the line doesn't start with a tab, it means that's a vendor id */
212     if (line[0] != '\t') {
213
214         /* the 4th first chars are the vendor_id */
215         strncpy(vendor_id,line,4);
216
217         /* the vendor name is the next field*/
218         vendor_id[4]=0;
219         vendor=strdup(skipspace(strstr(line," ")));
220         remove_eol(vendor);
221
222         /* init product_id, sub_product and sub_vendor */
223         strcpy(product_id,"0000");
224         strcpy(sub_product_id,"0000");
225         strcpy(sub_vendor_id,"0000");
226
227         /* ffff is an invalid vendor id */
228         if (strstr(vendor_id,"ffff")) break;
229
230         /* assign the vendor_name to any matching pci device*/
231         for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
232           struct pci_device *pci_device =
233             &pci_device_list->pci_device[pci_dev];
234
235           if (hex_to_int(vendor_id) == pci_device->vendor)
236             pci_device->pci_dev_info->vendor_name=strdup(vendor);
237         }
238     /* if we have a tab + a char, it means this is a product id */
239     } else if ((line[0] == '\t') && (line[1] != '\t')) {
240
241         /* the product name the second field */
242         product=strdup(skipspace(strstr(line," ")));
243         remove_eol(product);
244
245         /* the product id is first field */
246         strncpy(product_id,&line[1],4);
247         product_id[4]=0;
248
249         /* init sub_product and sub_vendor */
250         strcpy(sub_product_id,"0000");
251         strcpy(sub_vendor_id,"0000");
252
253         /* assign the product_name to any matching pci device*/
254         for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
255           struct pci_device *pci_device =
256             &pci_device_list->pci_device[pci_dev];
257           if (hex_to_int(vendor_id) == pci_device->vendor &&
258               hex_to_int(product_id) == pci_device->product)
259             pci_device->pci_dev_info->product_name=strdup(product);
260         }
261
262     /* if we have two tabs, it means this is a sub product */
263     } else if ((line[0] == '\t') && (line[1] == '\t')) {
264
265       /* the product name is last field */
266       product=skipspace(strstr(line," "));
267       product=strdup(skipspace(strstr(product," ")));
268       remove_eol(product);
269
270       /* the sub_vendor id is first field */
271       strncpy(sub_vendor_id,&line[2],4);
272       sub_vendor_id[4]=0;
273
274       /* the sub_vendor id is second field */
275       strncpy(sub_product_id,&line[7],4);
276       sub_product_id[4]=0;
277
278       /* assign the product_name to any matching pci device*/
279       for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
280         struct pci_device *pci_device =
281           &pci_device_list->pci_device[pci_dev];
282
283         if (hex_to_int(vendor_id) == pci_device->vendor &&
284             hex_to_int(product_id) == pci_device->product &&
285             hex_to_int(sub_product_id) == pci_device->sub_product &&
286             hex_to_int(sub_vendor_id) == pci_device->sub_vendor)
287           pci_device->pci_dev_info->product_name=strdup(product);
288       }
289     }
290   }
291   fclose(f);
292 }
293
294 /* searching if any pcidevice match our query */
295 struct match *find_pci_device(struct pci_device_list * pci_device_list,
296                               struct match *list)
297 {
298   int pci_dev;
299   uint32_t did, sid;
300   struct match *m;
301   /* for all matches we have to search */
302   for (m = list; m; m = m->next) {
303           /* for each pci device we know */
304     for (pci_dev = 0; pci_dev < pci_device_list->count; pci_dev++) {
305       struct pci_device *pci_device =
306         &pci_device_list->pci_device[pci_dev];
307
308       /* sid & did are the easiest way to compare devices */
309       /* they are made of vendor/product subvendor/subproduct ids */
310       sid =
311         ((pci_device->sub_product) << 16 | (pci_device->
312                                             sub_vendor));
313       did = ((pci_device->product << 16) | (pci_device->vendor));
314
315       /*if the current device match */
316       if (((did ^ m->did) & m->did_mask) == 0 &&
317           ((sid ^ m->sid) & m->sid_mask) == 0 &&
318           pci_device->revision >= m->rid_min
319           && pci_device->revision <= m->rid_max) {
320         dprintf("PCI Match: Vendor=%04x Product=%04x Sub_vendor=%04x Sub_Product=%04x Release=%02x\n",
321                 pci_device->vendor, pci_device->product,
322                 pci_device->sub_vendor,
323                 pci_device->sub_product,
324                 pci_device->revision);
325         /* returning the matched pci device */
326         return m;
327       }
328     }
329   }
330   return NULL;
331 }
332
333 /* scanning the pci bus to find pci devices */
334 int pci_scan(struct pci_bus_list * pci_bus_list, struct pci_device_list * pci_device_list)
335 {
336   unsigned int bus, dev, func, maxfunc;
337   uint32_t did, sid;
338   uint8_t hdrtype, rid;
339   pciaddr_t a;
340   int cfgtype;
341
342   pci_device_list->count = 0;
343
344 #ifdef DEBUG
345   outl(~0, 0xcf8);
346   printf("Poking at port CF8 = %#08x\n", inl(0xcf8));
347   outl(0, 0xcf8);
348 #endif
349
350   cfgtype = pci_set_config_type(PCI_CFG_AUTO);
351   (void)cfgtype;
352
353   dprintf("PCI configuration type %d\n", cfgtype);
354   dprintf("Scanning PCI Buses\n");
355
356   /* We try to detect 255 buses */
357   for (bus = 0; bus <= MAX_PCI_BUSES; bus++) {
358
359     dprintf("Probing bus 0x%02x... \n", bus);
360
361     pci_bus_list->pci_bus[bus].id = bus;
362     pci_bus_list->pci_bus[bus].pci_device_count = 0;
363     pci_bus_list->count = 0;;
364
365     for (dev = 0; dev <= 0x1f; dev++) {
366       maxfunc = 0;
367       for (func = 0; func <= maxfunc; func++) {
368         a = pci_mkaddr(bus, dev, func, 0);
369
370         did = pci_readl(a);
371
372         if (did == 0xffffffff || did == 0xffff0000 ||
373             did == 0x0000ffff || did == 0x00000000)
374           continue;
375
376         hdrtype = pci_readb(a + 0x0e);
377
378         if (hdrtype & 0x80)
379           maxfunc = 7;  /* Multifunction device */
380
381         rid = pci_readb(a + 0x08);
382         sid = pci_readl(a + 0x2c);
383         struct pci_device *pci_device =
384           &pci_device_list->
385           pci_device[pci_device_list->count];
386         pci_device->product = did >> 16;
387         pci_device->sub_product = sid >> 16;
388         pci_device->vendor = (did << 16) >> 16;
389         pci_device->sub_vendor = (sid << 16) >> 16;
390         pci_device->revision = rid;
391         pci_device_list->count++;
392         dprintf
393           ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n",
394            bus, did, did >> 16, (did << 16) >> 16,
395            sid, rid);
396         /* Adding the detected pci device to the bus */
397         pci_bus_list->pci_bus[bus].
398           pci_device[pci_bus_list->pci_bus[bus].
399                      pci_device_count] = pci_device;
400         pci_bus_list->pci_bus[bus].pci_device_count++;
401       }
402     }
403   }
404
405   /* Detecting pci buses that have pci devices connected */
406   for (bus = 0; bus <= 0xff; bus++) {
407     if (pci_bus_list->pci_bus[bus].pci_device_count > 0) {
408       pci_bus_list->count++;
409     }
410   }
411   return 0;
412 }