Initial revision
[opensuse:hwinfo.git] / src / hd / misc.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9
10 #include "hd.h"
11 #include "hd_int.h"
12 #include "misc.h"
13
14 // parameter for gather_resources(, , which)
15 #define W_IO    (1 << 0)
16 #define W_DMA   (1 << 1)
17 #define W_IRQ   (1 << 2)
18
19 static void read_ioports(misc_t *m);
20 static void read_dmas(misc_t *m);
21 static void read_irqs(misc_t *m);
22 static void gather_resources(misc_t *m, hd_res_t **, char *, unsigned);
23 static int active_vga_card(hd_t *);
24
25 static void dump_misc_proc_data(hd_data_t *hd_data);
26 static void dump_misc_data(hd_data_t *hd_data);
27
28 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29  * misc info
30  *
31  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32  */
33
34
35 void hd_scan_misc(hd_data_t *hd_data)
36 {
37   hd_t *hd;
38   hd_res_t *res;
39   int fd;
40   char *s, buf[1];
41   int fd_ser0, fd_ser1;
42
43   if(!(hd_data->probe & (1 << pr_misc))) return;
44
45   hd_data->module = mod_misc;
46
47   /* some clean-up */
48   remove_hd_entries(hd_data);
49   hd_data->misc = NULL;
50
51   PROGRESS(1, 0, "misc data");
52   hd_data->misc = new_mem(sizeof *hd_data->misc);
53
54   /* this is enough to load the module */
55   fd_ser0 = fd_ser1 = -1;
56   if((hd_data->probe & (1 << pr_misc_serial))) {
57     PROGRESS(1, 1, "open serial");
58     fd_ser0 = open("/dev/ttyS0", O_RDONLY | O_NONBLOCK);
59     fd_ser1 = open("/dev/ttyS1", O_RDONLY | O_NONBLOCK);
60     /* keep the devices open until the resources have been read */
61   }
62
63   /* this is enough to load the module */
64   if((hd_data->probe & (1 << pr_misc_par))) {
65     PROGRESS(1, 2, "open parallel");
66     fd = open("/dev/lp0", O_RDONLY | O_NONBLOCK);
67     if(fd >= 0) close(fd);
68   }
69
70   /*
71    * floppy driver resources are allocated only temporarily,
72    * so we access it just before we read the resources
73    */
74   if((hd_data->probe & (1 << pr_misc_floppy))) {
75     /* look for a floppy *device* entry... */
76     for(hd = hd_data->hd; hd; hd = hd->next) {
77       if(hd->base_class == bc_storage_device && hd->sub_class == sc_sdev_floppy) {
78         s = hd->unix_dev_name;
79
80         PROGRESS(1, 3, "open floppy");
81         /* ...then try to read from it... */
82         fd = open(s, O_RDONLY | O_NONBLOCK);
83         if(fd >= 0) {
84           PROGRESS(1, 4, "read floppy");
85           read(fd, buf, sizeof buf);
86           close(fd);
87         }
88
89         break;
90       }
91     }
92
93   }
94
95   PROGRESS(2, 1, "io");
96   read_ioports(hd_data->misc);
97
98   PROGRESS(2, 2, "dma");
99   read_dmas(hd_data->misc);
100
101   PROGRESS(2, 2, "irq");
102   read_irqs(hd_data->misc);
103
104   if((hd_data->debug & HD_DEB_MISC)) dump_misc_proc_data(hd_data);
105
106   if(fd_ser0 >= 0) close(fd_ser0);
107   if(fd_ser1 >= 0) close(fd_ser1);
108
109   /* now create some system generic entries */
110
111   /* FPU */
112   res = NULL;
113   gather_resources(hd_data->misc, &res, "fpu", 0);
114   if(res) {
115     hd = add_hd_entry(hd_data, __LINE__, 0);
116     hd->base_class = bc_internal;
117     hd->sub_class = sc_int_fpu;
118     hd->res = res;
119   }
120
121   /* DMA */
122   res = NULL;
123   gather_resources(hd_data->misc, &res, "dma1", 0);
124   gather_resources(hd_data->misc, &res, "dma2", 0);
125   gather_resources(hd_data->misc, &res, "dma page reg", 0);
126   gather_resources(hd_data->misc, &res, "cascade", W_DMA);
127   if(res) {
128     hd = add_hd_entry(hd_data, __LINE__, 0);
129     hd->base_class = bc_system;
130     hd->sub_class = sc_sys_dma;
131     hd->res = res;
132   }
133
134   /* PIC */
135   res = NULL;
136   gather_resources(hd_data->misc, &res, "pic1", 0);
137   gather_resources(hd_data->misc, &res, "pic2", 0);
138   gather_resources(hd_data->misc, &res, "cascade", W_IRQ);
139   if(res) {
140     hd = add_hd_entry(hd_data, __LINE__, 0);
141     hd->base_class = bc_system;
142     hd->sub_class = sc_sys_pic;
143     hd->res = res;
144   }
145
146   /* timer */
147   res = NULL;
148   gather_resources(hd_data->misc, &res, "timer", 0);
149   if(res) {
150     hd = add_hd_entry(hd_data, __LINE__, 0);
151     hd->base_class = bc_system;
152     hd->sub_class = sc_sys_timer;
153     hd->res = res;
154   }
155
156   /* real time clock */
157   res = NULL;
158   gather_resources(hd_data->misc, &res, "rtc", 0);
159   if(res) {
160     hd = add_hd_entry(hd_data, __LINE__, 0);
161     hd->base_class = bc_system;
162     hd->sub_class = sc_sys_rtc;
163     hd->res = res;
164   }
165
166   /* keyboard */
167   res = NULL;
168   gather_resources(hd_data->misc, &res, "keyboard", 0);
169   if(res) {
170     hd = add_hd_entry(hd_data, __LINE__, 0);
171     hd->base_class = bc_input;
172     hd->sub_class = sc_inp_keyb;
173     hd->res = res;
174   }
175
176   /* parallel port */
177   res = NULL;
178   gather_resources(hd_data->misc, &res, "parport0", 0);
179   if(res) {
180     hd = add_hd_entry(hd_data, __LINE__, 0);
181     hd->base_class = bc_comm;
182     hd->sub_class = sc_com_par;
183     hd->res = res;
184   }
185
186   /* floppy controller */
187   res = NULL;
188   gather_resources(hd_data->misc, &res, "floppy", 0);
189   gather_resources(hd_data->misc, &res, "floppy DIR", 0);
190   if(res) {
191     /* look for an existing entry */
192     for(hd = hd_data->hd; hd; hd = hd->next) {
193       if(hd->base_class == bc_storage && hd->sub_class == sc_sto_floppy) break;
194     }
195
196     /* missing, so create one */
197     if(!hd) {
198       hd = add_hd_entry(hd_data, __LINE__, 0);
199       hd->base_class = bc_storage;
200       hd->sub_class = sc_sto_floppy;
201     }
202
203     hd->res = res;
204   }
205
206   /*
207    * look for PS/2 port
208    *
209    * The catch is, that sometimes /dev/psaux is accessible only for root,
210    * so the open() may fail but there are irq events registered.
211    *
212    */
213   fd = open(DEV_PSAUX, O_RDONLY | O_NONBLOCK);
214   if(fd >= 0) close(fd);
215
216   res = NULL;
217   gather_resources(hd_data->misc, &res, "PS/2 Mouse", 0);
218
219   if(res || fd >= 0) {
220     hd = add_hd_entry(hd_data, __LINE__, 0);
221     hd->base_class = bc_ps2;
222
223     if(res) {
224       hd->res = res;
225     }
226   }
227 }
228
229
230 void hd_scan_misc2(hd_data_t *hd_data)
231 {
232   hd_t *hd, *hd1;
233   misc_t *m;
234   hd_res_t *res, *res1, *res2;
235   int i;
236
237   if(!(hd_data->probe & (1 << pr_misc))) return;
238
239   hd_data->module = mod_misc;
240
241   PROGRESS(5, 0, "misc data");
242
243   /* create some more system generic entries */
244
245   /* IDE */
246
247 // ###### add special ide detail to hd_t!!!
248   res = NULL;
249   gather_resources(hd_data->misc, &res, "ide0", 0);
250   gather_resources(hd_data->misc, &res, "ide1", 0);
251   if(res) {
252     for(hd = hd_data->hd; hd; hd = hd->next) {
253       if(
254         hd->base_class == bc_storage &&
255         hd->sub_class == sc_sto_ide &&
256         have_common_res(hd->res, res)
257       ) break;
258     }
259     if(!hd) {
260       /* eg. non-PCI IDE controller */
261       hd = add_hd_entry(hd_data, __LINE__, 0);
262       hd->base_class = bc_storage;
263       hd->sub_class = sc_sto_ide;
264       /* use join_res to join the i/o ranges of ide0/1 */
265       join_res_io(&hd->res, res);
266       join_res_irq(&hd->res, res);
267       join_res_dma(&hd->res, res);
268       free_res_list(res);
269     }
270     else {
271       /* eg. PCI IDE controller, add resources */
272       join_res_io(&hd->res, res);
273       join_res_irq(&hd->res, res);
274       join_res_dma(&hd->res, res);
275       free_res_list(res);
276     }
277   }
278
279   /* VGA */
280   res = NULL;
281   gather_resources(hd_data->misc, &res, "vga+", 0);
282   if(res) {
283     for(i = 0, hd1 = NULL, hd = hd_data->hd; hd; hd = hd->next) {
284       if(hd->base_class == bc_display && hd->sub_class == sc_dis_vga) {
285         i++;
286         hd1 = hd;
287       }
288     }
289     if(i == 0) {
290       /* non-PCI VGA card */
291       hd = add_hd_entry(hd_data, __LINE__, 0);
292       hd->base_class = bc_display;
293       hd->sub_class = sc_dis_vga;
294       hd->res = res;
295     }
296     else if(i == 1) {
297       /* 1 PCI vga card, add resources */
298       join_res_io(&hd1->res, res);
299       join_res_irq(&hd1->res, res);
300       join_res_dma(&hd1->res, res);
301       free_res_list(res);
302     }
303     else {
304       /* more than 1: look again, now only 'active' cards */
305       for(i = 0, hd1 = NULL, hd = hd_data->hd; hd; hd = hd->next) {
306         if(
307           hd->base_class == bc_display &&
308           hd->sub_class == sc_dis_vga &&
309           active_vga_card(hd)
310         ) {
311           i++;
312           hd1 = hd;
313         }
314       }
315       if(i == 1) {
316         /* 'the' active PCI vga card, add resources */
317         join_res_io(&hd1->res, res);
318         join_res_irq(&hd1->res, res);
319         join_res_dma(&hd1->res, res);
320       }
321       else {
322        /* now, what??? */
323        ADD2LOG("Oopy, could not figure out *the* active display adapter!\n");
324       }
325       free_res_list(res);
326     }
327   }
328
329   /* serial ports */
330   res = NULL;
331   gather_resources(hd_data->misc, &res, "serial(auto)", 0);
332   gather_resources(hd_data->misc, &res, "serial(set)", 0);
333   gather_resources(hd_data->misc, &res, "serial", 0);
334   for(hd = hd_data->hd; hd; hd = hd->next) {
335     if(hd->base_class == bc_comm && hd->sub_class == sc_com_ser) {
336       for(res1 = hd->res; res1; res1 = res1->next) {
337         for(res2 = res; res2; res2 = res2->next) {
338           if(res1->any.type == res2->any.type) {
339             switch(res1->any.type) {
340               case res_irq:
341                 if(res1->irq.base == res2->irq.base) {
342                   res2->any.type = res_any;
343                 }
344                 break;
345               case res_io:
346                 if(
347                   res1->io.base == res2->io.base &&
348                   (!res1->io.range || res1->io.range == res2->io.range)
349                 ) {
350                   res1->io.range = res2->io.range;
351                   res2->any.type = res_any;
352                 }
353                 break;
354               default:          /* gcc -Wall */
355             }
356           }
357         }
358       }
359     }
360   }
361
362   /* if any of the serial resources are unaccounted for, make an extra entry */
363   for(res2 = res; res2; res2 = res2->next) {
364     if(res2->any.type != res_any) {
365       hd = add_hd_entry(hd_data, __LINE__, 0);
366       hd->base_class = bc_comm;
367       hd->sub_class = sc_com_ser;
368       for(; res2; res2 = res2->next) {
369         if(res2->any.type != res_any) {
370           res1 = add_res_entry(&hd->res, new_mem(sizeof *res));
371           *res1 = *res2;
372           res1->next = NULL;
373         }
374       }
375       break;
376     }
377   }
378   free_res_list(res);
379
380   /* go through our list and assign event counts to irq entries */
381   m = hd_data->misc;
382   for(hd = hd_data->hd; hd; hd = hd->next) {
383     for(res = hd->res; res; res = res->next) {
384       if(res->irq.type == res_irq) {
385         for(i = 0; i < m->irq_len; i++) {
386           if(res->irq.base == m->irq[i].irq) {
387             res->irq.triggered = m->irq[i].events;
388             break;
389           }
390         }
391       }
392     }
393   }
394
395   if((hd_data->debug & HD_DEB_MISC)) dump_misc_data(hd_data);
396 }
397
398
399 /*
400  * read /proc/ioports
401  */
402 void read_ioports(misc_t *m)
403 {
404   char buf[100];
405   misc_io_t *r;
406   uint64 u, v;
407   str_list_t *sl;
408
409   if(!(m->proc_io = read_file(PROC_IOPORTS, 0, 0))) return;
410
411   for(sl = m->proc_io; sl; sl = sl->next) {
412     if(sscanf(sl->str, " %"HD_LL"x - %"HD_LL"x : %99[^\n]", &u, &v, buf) == 3) {
413       m->io = add_mem(m->io, sizeof *m->io, m->io_len);
414       r = m->io + m->io_len++;
415       r->addr = u;
416       r->size = v >= u ? v - u + 1 : 0;
417       r->dev = new_str(buf);
418     }
419   }
420 }
421
422 /*
423  * read /proc/dma
424  */
425 void read_dmas(misc_t *m)
426 {
427   char buf[100];
428   misc_dma_t *d;
429   unsigned u;
430   str_list_t *sl;
431
432   if(!(m->proc_dma = read_file(PROC_DMA, 0, 0))) return;
433
434   for(sl = m->proc_dma; sl; sl = sl->next) {
435     if(sscanf(sl->str, " %u : %99[^\n]", &u, buf) == 2) {
436       m->dma = add_mem(m->dma, sizeof *m->dma, m->dma_len);
437       d = m->dma + m->dma_len++;
438       d->channel = u;
439       d->dev = new_str(buf);
440     }
441   }
442 }
443
444
445 /*
446  * read /proc/interrupts
447  *
448  * This is somewhat more tricky, as the irq event counts are done separately
449  * per cpu *and* there may be irq sharing.
450  */
451 void read_irqs(misc_t *m)
452 {
453   char buf[100], buf2[100], *s;
454   misc_irq_t *ir;
455   int i, j;
456   unsigned u, v, k;
457   str_list_t *sl;
458
459   if(!(m->proc_irq = read_file(PROC_INTERRUPTS, 1, 0))) return;
460
461   for(sl = m->proc_irq; sl; sl = sl->next) {
462     /* irq */
463     if(sscanf(sl->str, " %u : %n", &u, &i) < 1) continue;
464     v = 0;
465     j = i;
466     /* add up all event counters */
467     while(j < strlen(sl->str) && sscanf(sl->str + j, " %u %n", &k, &i) >= 1) {
468       v += k;
469       j += i;
470     }
471     /* device driver name string */
472     if(sscanf(sl->str + j, " %*s %99[^\n]", buf) == 1) {
473       m->irq = add_mem(m->irq, sizeof *m->irq, m->irq_len);
474       ir = m->irq + m->irq_len++;
475       ir->irq = u;
476       ir->events = v;
477
478       /* split device driver names (separated by ',') */
479       s = buf;
480       while(*s && sscanf(s, " %99[^,] %n", buf2, &j) >= 1) {
481         ir->dev = add_mem(ir->dev, sizeof *ir->dev, ir->devs);
482         ir->dev[ir->devs++] = new_str(buf2);
483         s += j;
484         if(*s) s++;     /* skip ',' */
485       }
486     }
487   }
488 }
489
490 void gather_resources(misc_t *m, hd_res_t **r, char *name, unsigned which)
491 {
492   int i, j;
493   hd_res_t *res;
494
495   if(!which) which = W_IO | W_DMA | W_IRQ;
496
497   if((which & W_IO)) for(i = 0; i < m->io_len; i++) {
498     if(!strcmp(name, m->io[i].dev)) {
499       res = add_res_entry(r, new_mem(sizeof **r));
500       res->io.type = res_io;
501       res->io.enabled = 1;
502       res->io.base = m->io[i].addr;
503       res->io.range = m->io[i].size;
504       res->io.access = acc_rw;
505       m->io[i].tag++;
506     }
507   }
508
509   if((which & W_DMA)) for(i = 0; i < m->dma_len; i++) {
510     if(!strcmp(name, m->dma[i].dev)) {
511       res = add_res_entry(r, new_mem(sizeof **r));
512       res->dma.type = res_dma;
513       res->dma.enabled = 1;
514       res->dma.base = m->dma[i].channel;
515       m->dma[i].tag++;
516     }
517   }
518
519   if((which & W_IRQ)) for(i = 0; i < m->irq_len; i++) {
520     for(j = 0; j <  m->irq[i].devs; j++) {
521       if(!strcmp(name, m->irq[i].dev[j])) {
522         res = add_res_entry(r, new_mem(sizeof **r));
523         res->irq.type = res_irq;
524         res->irq.enabled = 1;
525         res->irq.base = m->irq[i].irq;
526         res->irq.triggered = m->irq[i].events;
527         m->irq[i].tag++;
528       }
529     }
530   }
531 }
532
533
534 int active_vga_card(hd_t *hd)
535 {
536   hd_res_t *res;
537
538   if(hd->bus != bus_pci) return 1;
539
540   for(res = hd->res; res; res = res->next) {
541     if(
542       (res->mem.type == res_mem && res->mem.enabled) ||
543       (res->io.type == res_io && res->io.enabled)
544     ) return 1;
545   }
546
547   return 0;
548 }
549
550
551 /*
552  * Add some proc info to the global log.
553  */
554 void dump_misc_proc_data(hd_data_t *hd_data)
555 {
556   str_list_t *sl;
557
558   ADD2LOG("----- /proc/ioports -----\n");
559   for(sl = hd_data->misc->proc_io; sl; sl = sl->next) {
560     ADD2LOG("  %s", sl->str);
561   }
562   ADD2LOG("----- /proc/ioports end -----\n");
563
564   ADD2LOG("----- /proc/interrupts -----\n");
565   for(sl = hd_data->misc->proc_irq; sl; sl = sl->next) {
566     ADD2LOG("  %s", sl->str);
567   }
568   ADD2LOG("----- /proc/interrupts end -----\n");
569
570   ADD2LOG("----- /proc/dma -----\n");
571   for(sl = hd_data->misc->proc_dma; sl; sl = sl->next) {
572     ADD2LOG("  %s", sl->str);
573   }
574   ADD2LOG("----- /proc/dma end -----\n");
575
576 }
577
578
579 /*
580  * Add the resource usage to the global log.
581  */
582 void dump_misc_data(hd_data_t *hd_data)
583 {
584   misc_t *m = hd_data->misc;
585   int i, j;
586
587   ADD2LOG("----- misc resources -----\n");
588
589   for(i = 0; i < m->io_len; i++) {
590     ADD2LOG(
591       "i/o:%u 0x%04"HD_LL"x - 0x%04"HD_LL"x (0x%02"HD_LL"x) \"%s\"\n",
592       m->io[i].tag,
593       m->io[i].addr, m->io[i].addr + m->io[i].size - 1,
594       m->io[i].size, m->io[i].dev
595     );
596   }
597
598   for(i = 0; i < m->irq_len; i++) {
599     ADD2LOG(
600       "irq:%u %2u (%9u)",
601       m->irq[i].tag, m->irq[i].irq, m->irq[i].events
602     );
603     for(j = 0; j <  m->irq[i].devs; j++) {
604       ADD2LOG(" \"%s\"", m->irq[i].dev[j]);
605     }
606     ADD2LOG("\n");
607   }
608
609   for(i = 0; i < m->dma_len; i++) {
610     ADD2LOG(
611       "dma:%u %u \"%s\"\n",
612       m->dma[i].tag, m->dma[i].channel, m->dma[i].dev
613     );
614   }
615
616   ADD2LOG("----- misc resources end -----\n");
617 }
618