Initial revision
[opensuse:hwinfo.git] / src / hd / isapnp.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "hd.h"
6 #include "hd_int.h"
7 #include "hdx.h"
8 #include "isapnp.h"
9
10
11 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
12  * isapnp stuff
13  *
14  * TODO:
15  *   - memory range decoding
16  *   - port range 'guessing' is wrong; cf. SB16 cards
17  *
18  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19  */
20
21 #if defined(__i386__) || defined(__alpha__)
22
23 static void dump_raw_isapnp(hd_data_t *hd_data);
24 static void get_read_port(isapnp_t *);
25 static isapnp_res_t *get_isapnp_res(isapnp_card_t *, int, int);    
26 static unsigned find_io_range(isapnp_card_t *, unsigned);
27 static void dump_pnp_res(hd_data_t *hd_data, isapnp_card_t *);
28
29 void hd_scan_isapnp(hd_data_t *hd_data)
30 {
31   hd_t *hd;
32   int i, j, k;
33   unsigned u;
34   unsigned char *t, *v, *s;
35   isapnp_card_t *c;
36   isapnp_res_t *r;
37   isapnp_dev_t *dev;
38   hd_res_t *res;
39   
40   if(!(hd_data->probe & (1 << pr_isapnp))) return;
41
42   hd_data->module = mod_isapnp;
43
44   /* some clean-up */
45   remove_hd_entries(hd_data);
46   hd_data->isapnp = NULL;
47
48   PROGRESS(1, 0, "read port");
49   
50   hd_data->isapnp = new_mem(sizeof *hd_data->isapnp);
51
52   get_read_port(hd_data->isapnp);
53
54   PROGRESS(2, 0, "get pnp data");
55   
56   hd_data->module = mod_pnpdump;
57   pnpdump(hd_data, 0);
58   hd_data->module = mod_isapnp;
59
60   if((hd_data->debug & HD_DEB_ISAPNP)) dump_raw_isapnp(hd_data);
61
62   PROGRESS(3, 0, "build list");
63
64   if(hd_data->isapnp->cards) {
65     hd = add_hd_entry(hd_data, __LINE__, 0);
66     hd->bus = bus_isa;
67     hd->base_class = bc_internal;
68     hd->sub_class = sc_int_isapnp_if;
69
70     res = add_res_entry(&hd->res, new_mem(sizeof *res));
71     res->io.type = res_io;
72     res->io.enabled = 1;
73     res->io.base = ISAPNP_ADDR_PORT;
74     res->io.range = 1;
75     res->io.access = acc_wo;
76
77     res = add_res_entry(&hd->res, new_mem(sizeof *res));
78     res->io.type = res_io;
79     res->io.enabled = 1;
80     res->io.base = ISAPNP_DATA_PORT;
81     res->io.range = 1;
82     res->io.access = acc_wo;
83
84     res = add_res_entry(&hd->res, new_mem(sizeof *res));
85     res->io.type = res_io;
86     res->io.enabled = 1;
87     res->io.base = hd_data->isapnp->read_port;
88     res->io.range = 1;
89     res->io.access = acc_ro;
90   }
91
92   for(i = 0; i < hd_data->isapnp->cards; i++) {
93     c = hd_data->isapnp->card + i;
94     t = c->serial;
95
96     for(j = 0; j < c->log_devs; j++) {
97       hd = add_hd_entry(hd_data, __LINE__, 0);
98
99       hd->detail = new_mem(sizeof *hd->detail);
100       hd->detail->type = hd_detail_isapnp;
101       dev = hd->detail->isapnp.data = new_mem(sizeof *hd->detail->isapnp.data);
102       dev->card = c;
103       dev->dev = j;
104
105       hd->bus = bus_isa;
106       hd->slot = c->csn;
107       hd->func = j;
108
109       hd->vend = MAKE_EISA_ID((t[0] << 8) + t[1]);
110       hd->dev = MAKE_EISA_ID((t[2] << 8) + t[3]);
111
112       if((u = device_class(hd->vend, hd->dev))) {
113         hd->base_class = u >> 8;
114         hd->sub_class = u & 0xff;
115       }
116
117       if(
118         (ID_VALUE(hd->vend) || ID_VALUE(hd->dev)) &&
119         !device_name(hd->vend, hd->dev)
120       ) {
121         if((r = get_isapnp_res(c, 0, RES_ANSI_NAME))) {
122           s = canon_str(r->data, r->len);
123           add_device_name(hd->vend, hd->dev, 0, s);
124           free_mem(s);
125         }
126       }
127
128       hd->serial = new_mem(11);
129       sprintf(hd->serial, "%u", (t[7] << 24) + (t[6] << 16) + (t[5] << 8)+ t[4]);
130
131       if((c->ldev_regs[j][0] & 1)) {
132         dev->flags |= (1 << isapnp_flag_act);
133       }
134
135       if((r = get_isapnp_res(c, j + 1, RES_LOG_DEV_ID))) {
136         v = r->data;
137         hd->sub_vend = MAKE_EISA_ID((v[0] << 8) + v[1]);
138         hd->sub_dev = MAKE_EISA_ID((v[2] << 8) + v[3]);
139         if(
140           c->log_devs == 1 &&
141           hd->sub_vend == hd->vend &&
142           hd->sub_dev == hd->dev
143         ) {
144           hd->sub_vend = hd->sub_dev = 0;
145         }
146
147         if((u = sub_device_class(hd->vend, hd->dev, hd->sub_vend, hd->sub_dev))) {
148           hd->base_class = u >> 8;
149           hd->sub_class = u & 0xff;
150         }
151
152         if(
153           (ID_VALUE(hd->sub_vend) || ID_VALUE(hd->sub_dev)) &&
154           !sub_device_name(hd->vend, hd->dev, hd->sub_vend, hd->sub_dev)
155         ) {
156           if((r = get_isapnp_res(c, j + 1, RES_ANSI_NAME))) {
157             s = canon_str(r->data, r->len);
158             add_sub_device_name(hd->vend, hd->dev, hd->sub_vend, hd->sub_dev, 0, s);
159             free_mem(s);
160           }
161         }
162       }
163
164       if((r = get_isapnp_res(c, j + 1, RES_COMPAT_DEV_ID))) {
165         v = r->data;
166
167         hd->compat_vend = MAKE_EISA_ID((v[0] << 8) + v[1]);
168         hd->compat_dev = MAKE_EISA_ID((v[2] << 8) + v[3]);
169
170         if(!(hd->base_class || hd->sub_class)) {
171           if((u = device_class(hd->compat_vend, hd->compat_dev))) {
172             hd->base_class = u >> 8;
173             hd->sub_class = u & 0xff;
174           }
175           else if(hd->compat_vend == MAKE_EISA_ID(0x41d0)) {
176             /* 0x41d0 is 'PNP' */
177             switch((hd->compat_dev >> 12) & 0xf) {
178               case   8:
179                 hd->base_class = bc_network;
180                 hd->sub_class = 0x80;
181                 break;
182               case 0xa:
183                 hd->base_class = bc_storage;
184                 hd->sub_class = 0x80;
185                 break;
186               case 0xb:
187                 hd->base_class = bc_multimedia;
188                 hd->sub_class = 0x80;
189                 break;
190               case 0xc:
191               case 0xd:
192                 hd->base_class = bc_modem;
193                 break;
194             }
195           }
196         }
197       }
198
199
200       v = c->ldev_regs[j];
201
202       for(k = 0; k < 8; k++) {
203         u = (v[CFG_IO_HI_BASE - 0x30 + 2*k] << 8) +
204              v[CFG_IO_LO_BASE - 0x30 + 2*k];
205         if(u) {
206           res = add_res_entry(&hd->res, new_mem(sizeof *res));
207           res->io.type = res_io;
208           res->io.enabled = dev->flags & (1 << isapnp_flag_act) ? 1 : 0;
209           res->io.base = u;
210           res->io.range = find_io_range(c, u);
211           res->io.access = acc_rw;
212         }
213       }
214
215       u = v[CFG_IRQ_0 - 0x30] & 15;
216       if(u) {
217         res = add_res_entry(&hd->res, new_mem(sizeof *res));
218         res->irq.type = res_irq;
219         res->irq.enabled = dev->flags & (1 << isapnp_flag_act) ? 1 : 0;
220         res->irq.base = u;
221       }
222
223       u = v[CFG_IRQ_1 - 0x30] & 15;
224       if(u) {
225         res = add_res_entry(&hd->res, new_mem(sizeof *res));
226         res->irq.type = res_irq;
227         res->irq.enabled = dev->flags & (1 << isapnp_flag_act) ? 1 : 0;
228         res->irq.base = u;
229       }
230
231       u = v[CFG_DMA_0 - 0x30] & 7;
232       if(u != 4) {
233         res = add_res_entry(&hd->res, new_mem(sizeof *res));
234         res->dma.type = res_dma;
235         res->dma.enabled = dev->flags & (1 << isapnp_flag_act) ? 1 : 0;
236         res->dma.base = u;
237       }
238
239       u = v[CFG_DMA_1 - 0x30] & 7;
240       if(u != 4) {
241         res = add_res_entry(&hd->res, new_mem(sizeof *res));
242         res->dma.type = res_dma;
243         res->dma.enabled = dev->flags & (1 << isapnp_flag_act) ? 1 : 0;
244         res->dma.base = u;
245       }
246     }
247   }
248
249 }
250
251 unsigned char *add_isapnp_card_res(isapnp_card_t *ic, int len, int type)
252 {
253   ic->res = add_mem(ic->res, sizeof *ic->res, ic->res_len);
254
255   ic->res[ic->res_len].len = len;
256   ic->res[ic->res_len].type = type;
257   ic->res[ic->res_len].data = new_mem(len);
258
259   if(type == RES_LOG_DEV_ID) {  /* logical device id */
260     ic->log_devs++;
261   }
262
263   return ic->res[ic->res_len++].data;
264 }
265
266
267 isapnp_card_t *add_isapnp_card(isapnp_t *ip, int csn)
268 {
269   isapnp_card_t *c;
270
271   ip->card = add_mem(ip->card, sizeof *ip->card, ip->cards);
272   c = ip->card + ip->cards++;
273
274   c->csn = csn;
275   c->serial = new_mem(sizeof *c->serial * 8);
276   c->card_regs = new_mem(sizeof *c->card_regs * 0x30);
277
278   return c;
279 }
280
281
282 void dump_raw_isapnp(hd_data_t *hd_data)
283 {
284   int i, j, k;
285   isapnp_t *p;
286   isapnp_card_t *c;
287
288   p = hd_data->isapnp;
289
290   ADD2LOG("---------- ISA PnP raw data ----------\n");
291   ADD2LOG("isapnp read port: 0x%x\n", p->read_port);
292   for(k = 0; k < p->cards; k++) {
293     c = p->card + k;
294
295     ADD2LOG("card %d (csn %d, %d logical devices)\n", k, c->csn, c->log_devs);
296
297     ADD2LOG("  serial: ");
298     hexdump(&hd_data->log, 0, 8, c->serial);
299     ADD2LOG("\n");
300
301     ADD2LOG("  card_regs: 00: ");
302     for(i = 0; i < 0x30; i += 0x10) {
303       if(i) ADD2LOG("\n             %02x: ", i);
304       hexdump(&hd_data->log, 1, 0x10, c->card_regs + i);
305     }
306     ADD2LOG("\n");
307
308     for(j = 0; j < c->log_devs; j++) {
309       ADD2LOG("  log dev %d: 30: ", j);
310       for(i = 0; i < 0xd0; i += 0x10) {
311         if(i) ADD2LOG("\n             %02x: ", i + 0x30);
312         hexdump(&hd_data->log, 1, 0x10, c->ldev_regs[j] + i);
313       }
314       ADD2LOG("\n");
315     }
316
317     for(i = 0; i < c->res_len; i++) {
318       ADD2LOG("  type 0x%02x, len %2d: ", c->res[i].type, c->res[i].len);
319       hexdump(&hd_data->log, 1, c->res[i].len, c->res[i].data);
320       ADD2LOG("\n");
321     }
322     dump_pnp_res(hd_data, c);
323     if(k != p->cards - 1) ADD2LOG("\n");
324   }
325   ADD2LOG("---------- ISA PnP raw data end ----------\n");
326 }
327
328
329 void get_read_port(isapnp_t *p)
330 {
331   FILE *f;
332   char buf[200];
333   int i = 0;
334
335   p->read_port = 0;
336
337   if(!(f = fopen(ISAPNP_CONF, "r"))) return;
338
339   while(fgets(buf, sizeof buf, f)) {
340     if(sscanf(buf, " ( READPORT %x )", &i) == 1) break;
341   }
342
343   fclose(f);
344
345   p->read_port = i;
346 }
347
348
349 isapnp_res_t *get_isapnp_res(isapnp_card_t *c, int log_dev, int type)
350 {
351   int i;
352   isapnp_res_t *r;
353
354   if(!c->res_len) return NULL;
355
356   for(r = c->res, i = 0; i < c->res_len; i++, r++) {
357     if(r->type == RES_LOG_DEV_ID) log_dev--;
358     if(r->type == type && !log_dev) return r;
359   }
360
361   return NULL;
362 }
363
364
365 unsigned find_io_range(isapnp_card_t *ic, unsigned io_base)
366 {
367   int i, ranges = 0;
368   unsigned range = 0, u0, u1, u2, u3;   /* range = 0 for gcc -Wall */
369   unsigned char *t;
370
371   for(i = 0; i < ic->res_len; i++) {
372     t = ic->res[i].data;
373
374     if(ic->res[i].type == RES_FIXED_IO && ic->res[i].len == 3) {
375       u0 = ((t[1] & 3) << 8) + t[0];
376       u1 = t[2];
377       if(u0 == io_base) {
378         if(!ranges) { range = u1; ranges++; }
379         if(range != u1) ranges++;
380       }
381     }
382
383     if(ic->res[i].type == RES_IO && ic->res[i].len == 7) {
384       u0 = (t[2] << 8) + t[1];
385       u1 = (t[4] << 8) + t[3];
386       u2 = t[5]; u3 = t[6];
387       if(
388         io_base == u0 ||
389         io_base == u1 ||
390         (io_base >= u0 && io_base <= u1 && u2 && !((io_base - u0) % u2))
391       ) {
392         if(!ranges) { range = u3; ranges++; }
393         if(range != u3) ranges++;
394       }
395 //      printf(">>io: %4x-%4x+%2x(%2x)<<\n", u0, u1, u2, t[6]);
396     }
397   }
398
399   return ranges == 1 ? range : 0;
400 }
401
402
403 void dump_pnp_res(hd_data_t *hd_data, isapnp_card_t *c)
404 {
405   int i, j;
406   unsigned char *t;
407   char *s, *df = "";
408   unsigned u0, u1, u2, u3;
409
410   ADD2LOG("  ----\n");
411   for(i = 0; i < c->res_len; i++) {
412     t = c->res[i].data;
413
414     switch(c->res[i].type) {
415       case RES_PNP_VERSION:
416         ADD2LOG("%s  pnp ver %u.%u, %u\n", df, t[0] >> 4, t[0] & 5, t[1]);
417         break;
418
419       case RES_LOG_DEV_ID:
420         s = isa_id2str((t[3] << 24) + (t[2] << 16) + (t[1] << 8)+ t[0]);
421         ADD2LOG("%s  id %s\n", df, s);
422         free_mem(s);
423         break;
424
425       case RES_COMPAT_DEV_ID:
426         s = isa_id2str((t[3] << 24) + (t[2] << 16) + (t[1] << 8)+ t[0]);
427         ADD2LOG("%s  compat id %s\n", df, s);
428         free_mem(s);
429         break;
430
431       case RES_IRQ:
432         u0 = (t[1] << 8) + t[0];
433         ADD2LOG("%s  irq mask", df);
434         for(j = 0; j < 16; j++) if(u0 & (1 << j)) ADD2LOG(" %u", j);
435         ADD2LOG("\n");
436         break;
437
438       case RES_DMA:
439         u0 = t[0];
440         ADD2LOG("%s  dma mask", df);
441         for(j = 0; j < 8; j++) if(u0 & (1 << j)) ADD2LOG(" %u", j);
442         ADD2LOG("\n");
443         break;
444
445       case RES_START_DEP:
446         df = "  ";
447         ADD2LOG("  start DF\n");
448         break;
449
450       case RES_END_DEP:
451         df = "";
452         ADD2LOG("  end DF\n");
453         break;
454
455       case RES_IO:
456         u0 = (t[2] << 8) + t[1];
457         u1 = (t[4] << 8) + t[3];
458         u2 = t[5]; u3 = t[6];
459         ADD2LOG("%s  i/o ports 0x%x-0x%x(0x%x), step 0x%x\n", df, u0, u1, u3, u2);
460         break;
461
462       case RES_FIXED_IO:
463         u0 = ((t[1] & 3) << 8) + t[0];
464         u1 = t[2];
465         ADD2LOG("%s  fixed i/o ports 0x%x(0x%x)\n", df, u0, u1);
466         break;
467
468       case RES_END:
469         df = 0;
470         ADD2LOG("  end\n");
471         break;
472
473       case RES_ANSI_NAME:
474         s = new_mem(c->res[i].len + 1);
475         memcpy(s, t, c->res[i].len);
476         ADD2LOG("%s  name \"%s\"\n", df, s);
477         free_mem(s);
478         break;
479
480       default:
481         ADD2LOG("%s  type 0x%02x, len %2d: ", df, c->res[i].type, c->res[i].len);
482         hexdump(&hd_data->log, 1, c->res[i].len, t);
483         ADD2LOG("\n");
484     }
485   }
486 }
487
488 #endif /* defined(__i386__) || defined(__alpha__) */
489