]> git.ipfire.org Git - ipfire-2.x.git/blame - src/hwinfo/src/hd/prom.c
Signierten GPG-Schluessel importiert.
[ipfire-2.x.git] / src / hwinfo / src / hd / prom.c
CommitLineData
93afd047
MT
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <dirent.h>
6#include <sys/stat.h>
7
8#include "hd.h"
9#include "hd_int.h"
10#include "hddb.h"
11#include "prom.h"
12
13/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
14 * prom info
15 *
16 * Note: make sure that hd_scan_sysfs_pci() has been run!
17 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
18 */
19
20#if defined(__PPC__)
21
22static devtree_t *add_devtree_entry(devtree_t **devtree, devtree_t *new);
23static devtree_t *new_devtree_entry(devtree_t *parent);
24static void read_str(char *path, char *name, char **str);
25static void read_mem(char *path, char *name, unsigned char **mem, unsigned len);
26static void read_int(char *path, char *name, int *val);
27static void read_devtree(hd_data_t *hd_data);
28static void add_pci_prom_devices(hd_data_t *hd_data, hd_t *hd_parent, devtree_t *parent);
29static void add_legacy_prom_devices(hd_data_t *hd_data, devtree_t *dt);
30static int add_prom_display(hd_data_t *hd_data, devtree_t *dt);
31static int add_prom_vscsi(hd_data_t *hd_data, devtree_t *dt);
32static int add_prom_veth(hd_data_t *hd_data, devtree_t *dt);
33static void add_devices(hd_data_t *hd_data);
34static void dump_devtree_data(hd_data_t *hd_data);
35
36static unsigned veth_cnt, vscsi_cnt;
37
38int detect_smp_prom(hd_data_t *hd_data)
39{
40 unsigned cpus;
41 devtree_t *devtree;
42
43 if(!(devtree = hd_data->devtree)) return -1; /* hd_scan_prom() not called */
44
45 for(cpus = 0; devtree; devtree = devtree->next) {
46 if(devtree->device_type && !strcmp(devtree->device_type, "cpu")) cpus++;
47 }
48
49 return cpus > 1 ? cpus : 0;
50}
51
52void hd_scan_prom(hd_data_t *hd_data)
53{
54 hd_t *hd;
55 unsigned char buf[16];
56 FILE *f;
57 prom_info_t *pt;
58
59 if(!hd_probe_feature(hd_data, pr_prom)) return;
60
61 hd_data->module = mod_prom;
62
63 /* some clean-up */
64 remove_hd_entries(hd_data);
65 hd_data->devtree = free_devtree(hd_data);
66
67 veth_cnt = vscsi_cnt = 0;
68
69 PROGRESS(1, 0, "devtree");
70
71 read_devtree(hd_data);
72 if(hd_data->debug) dump_devtree_data(hd_data);
73 add_devices(hd_data);
74
75 PROGRESS(2, 0, "color");
76
77 hd = add_hd_entry(hd_data, __LINE__, 0);
78 hd->base_class.id = bc_internal;
79 hd->sub_class.id = sc_int_prom;
80 hd->detail = new_mem(sizeof *hd->detail);
81 hd->detail->type = hd_detail_prom;
82 hd->detail->prom.data = pt = new_mem(sizeof *pt);
83
84 if((f = fopen(PROC_PROM "/color-code", "r"))) {
85 if(fread(buf, 1, 2, f) == 2) {
86 pt->has_color = 1;
87 pt->color = buf[1];
88 hd_data->color_code = pt->color | 0x10000;
89 ADD2LOG("color-code: 0x%04x\n", (buf[0] << 8) + buf[1]);
90 }
91
92 fclose(f);
93 }
94
95}
96
97/* store a device tree entry */
98devtree_t *add_devtree_entry(devtree_t **devtree, devtree_t *new)
99{
100 while(*devtree) devtree = &(*devtree)->next;
101 return *devtree = new;
102}
103
104/* create a new device tree entry */
105devtree_t *new_devtree_entry(devtree_t *parent)
106{
107 static unsigned idx = 0;
108 devtree_t *devtree = new_mem(sizeof *devtree);
109
110 if(!parent) idx = 0;
111 devtree->idx = ++idx;
112 devtree->parent = parent;
113
114 devtree->interrupt = devtree->class_code =
115 devtree->device_id = devtree->vendor_id =
116 devtree->subdevice_id = devtree->subvendor_id =
117 devtree->revision_id = -1;
118
119 return devtree;
120}
121
122void read_str(char *path, char *name, char **str)
123{
124 char *s = NULL;
125 str_list_t *sl;
126
127 str_printf(&s, 0, "%s/%s", path, name);
128 if((sl = read_file(s, 0, 1))) {
129 *str = sl->str;
130 sl->str = NULL;
131 sl = free_str_list(sl);
132 }
133 free_mem(s);
134}
135
136void read_mem(char *path, char *name, unsigned char **mem, unsigned len)
137{
138 FILE *f;
139 char *s = NULL;
140 unsigned char *m = new_mem(len);
141
142 str_printf(&s, 0, "%s/%s", path, name);
143 if((f = fopen(s, "r"))) {
144 if(fread(m, len, 1, f) == 1) {
145 *mem = m;
146 m = NULL;
147 }
148 fclose(f);
149 }
150 free_mem(s);
151 free_mem(m);
152}
153
154void read_int(char *path, char *name, int *val)
155{
156 unsigned char *p = NULL;
157
158 read_mem(path, name, &p, sizeof (int));
159 if(p) memcpy(val, p, sizeof (int));
160 free_mem(p);
161}
162
163void read_devtree_entry(hd_data_t *hd_data, devtree_t *parent, char *dirname)
164{
165 DIR *dir;
166 struct dirent *de;
167 struct stat sbuf;
168 char *path, *s;
169 devtree_t *devtree, *dt2;
170
171 devtree = add_devtree_entry(&hd_data->devtree, new_devtree_entry(parent));
172
173 devtree->filename = new_str(dirname);
174
175 str_printf(&devtree->path, 0, "%s%s%s",
176 parent ? parent->path : "", parent && *parent->path ? "/" : "", dirname
177 );
178
179 path = 0;
180 str_printf(&path, 0, PROC_PROM "/%s", devtree->path);
181
182 if((dir = opendir(path))) {
183 while((de = readdir(dir))) {
184 if(!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
185 s = NULL;
186 str_printf(&s, 0, "%s/%s", path, de->d_name);
187 if(!lstat(s, &sbuf)) {
188 if(S_ISDIR(sbuf.st_mode)) {
189 /* prom entries don't always have unique names, unfortunately... */
190 for(dt2 = hd_data->devtree; dt2; dt2 = dt2->next) {
191 if(
192 dt2->parent == devtree &&
193 !strcmp(dt2->filename, de->d_name)
194 ) break;
195 }
196 if(!dt2) read_devtree_entry(hd_data, devtree, de->d_name);
197 }
198 }
199 s = free_mem(s);
200 }
201 closedir(dir);
202 }
203
204 read_str(path, "name", &devtree->name);
205 read_str(path, "model", &devtree->model);
206 read_str(path, "device_type", &devtree->device_type);
207 read_str(path, "compatible", &devtree->compatible);
208
209 read_int(path, "interrupts", &devtree->interrupt);
210 read_int(path, "AAPL,interrupts", &devtree->interrupt);
211 read_int(path, "class-code", &devtree->class_code);
212 read_int(path, "vendor-id", &devtree->vendor_id);
213 read_int(path, "device-id", &devtree->device_id);
214 read_int(path, "subsystem-vendor-id", &devtree->subvendor_id);
215 read_int(path, "subsystem-id", &devtree->subdevice_id);
216 read_int(path, "revision-id", &devtree->revision_id);
217
218 read_mem(path, "EDID", &devtree->edid, 0x80);
219 if(!devtree->edid) read_mem(path, "DFP,EDID", &devtree->edid, 0x80);
220
221 if(
222 devtree->class_code != -1 && devtree->vendor_id != -1 &&
223 devtree->device_id != -1
224 ) {
225 devtree->pci = 1;
226 }
227
228 path = free_mem(path);
229}
230
231void read_devtree(hd_data_t *hd_data)
232{
233 read_devtree_entry(hd_data, NULL, "");
234
235}
236
237void add_pci_prom_devices(hd_data_t *hd_data, hd_t *hd_parent, devtree_t *parent)
238{
239 hd_t *hd;
240 hd_res_t *res;
241 devtree_t *dt, *dt2;
242 int irq, floppy_ctrl_idx;
243 unsigned sound_ok = 0, net_ok = 0, scsi_ok = 0;
244 unsigned id;
245 char *s;
246
247 for(dt = hd_data->devtree; dt; dt = dt->next) {
248 if(
249 dt->parent == parent ||
250 (
251 /* special magic to reach some sound chips */
252 dt->parent &&
253 dt->parent->parent == parent &&
254 !dt->parent->pci
255 )
256 ) {
257
258 if(
259 dt->device_type &&
260 (!strcmp(dt->device_type, "block") || !strcmp(dt->device_type, "swim3"))
261 ) {
262 /* block devices */
263
264 s = dt->compatible ? dt->compatible : dt->name;
265 id = 0;
266
267 if(s) {
268 if(strstr(s, "swim3")) {
269 id = MAKE_ID(TAG_SPECIAL, 0x0040);
270 }
271 }
272
273 if(id) {
274 hd = add_hd_entry(hd_data, __LINE__, 0);
275 hd->bus.id = bus_none;
276 hd->base_class.id = bc_storage;
277 hd->sub_class.id = sc_sto_floppy;
278
279 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0401);
280 hd->device.id = id;
281 hd->attached_to = hd_parent->idx;
282 hd->rom_id = new_str(dt->path);
283 if(dt->interrupt) {
284 res = add_res_entry(&hd->res, new_mem(sizeof *res));
285 res->irq.type = res_irq;
286 res->irq.enabled = 1;
287 res->irq.base = dt->interrupt;
288 }
289 floppy_ctrl_idx = hd->idx;
290
291 hd = add_hd_entry(hd_data, __LINE__, 0);
292 hd->base_class.id = bc_storage_device;
293 hd->sub_class.id = sc_sdev_floppy;
294 hd->bus.id = bus_floppy;
295 hd->unix_dev_name = new_str("/dev/fd0");
296
297 res = add_res_entry(&hd->res, new_mem(sizeof *res));
298 res->size.type = res_size;
299 res->size.val1 = str2float("3.5", 2);
300 res->size.unit = size_unit_cinch;
301
302 res = add_res_entry(&hd->res, new_mem(sizeof *res));
303 res->size.type = res_size;
304 res->size.val1 = 2880;
305 res->size.val2 = 0x200;
306 res->size.unit = size_unit_sectors;
307
308 hd->attached_to = floppy_ctrl_idx;
309 }
310 }
311
312 if(
313 !scsi_ok &&
314 dt->device_type &&
315 !strcmp(dt->device_type, "scsi")
316 ) {
317 /* scsi */
318 scsi_ok = 1; /* max. 1 controller */
319
320 s = dt->compatible ? dt->compatible : dt->name;
321 id = 0;
322
323 if(s) {
324 if(strstr(s, "mesh")) { /* mesh || chrp,mesh0 */
325 id = MAKE_ID(TAG_SPECIAL, 0x0030);
326 }
327 else if(!strcmp(s, "53c94")) {
328 id = MAKE_ID(TAG_SPECIAL, 0x0031);
329 }
330 }
331
332 if(id) {
333 hd = add_hd_entry(hd_data, __LINE__, 0);
334 hd->bus.id = bus_none;
335 hd->base_class.id = bc_storage;
336 hd->sub_class.id = sc_sto_scsi;
337
338 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0401);
339 hd->device.id = id;
340 hd->attached_to = hd_parent->idx;
341 hd->rom_id = new_str(dt->path);
342 if(dt->interrupt) {
343 res = add_res_entry(&hd->res, new_mem(sizeof *res));
344 res->irq.type = res_irq;
345 res->irq.enabled = 1;
346 res->irq.base = dt->interrupt;
347 }
348 }
349 }
350
351 if(
352 !net_ok &&
353 dt->device_type &&
354 !strcmp(dt->device_type, "network")
355 ) {
356 /* network */
357 net_ok = 1; /* max. 1 controller */
358
359 s = dt->compatible ? dt->compatible : dt->name;
360 id = 0;
361
362 if(s) {
363 if(!strcmp(s, "mace")) {
364 id = MAKE_ID(TAG_SPECIAL, 0x0020);
365 }
366 else if(!strcmp(s, "bmac")) {
367 id = MAKE_ID(TAG_SPECIAL, 0x0021);
368 }
369 else if(!strcmp(s, "bmac+")) {
370 id = MAKE_ID(TAG_SPECIAL, 0x0022);
371 }
372 }
373
374 if(id) {
375 hd = add_hd_entry(hd_data, __LINE__, 0);
376 hd->bus.id = bus_none;
377 hd->base_class.id = bc_network;
378 hd->sub_class.id = 0; /* ethernet */
379
380 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0401);
381 hd->device.id = id;
382 hd->attached_to = hd_parent->idx;
383 hd->rom_id = new_str(dt->path);
384 if(dt->interrupt) {
385 res = add_res_entry(&hd->res, new_mem(sizeof *res));
386 res->irq.type = res_irq;
387 res->irq.enabled = 1;
388 res->irq.base = dt->interrupt;
389 }
390 }
391 }
392
393 if(
394 !sound_ok &&
395 dt->device_type &&
396 strstr(dt->device_type, "sound") == dt->device_type
397 ) {
398 /* sound */
399 sound_ok = 1; /* max 1 controller */
400
401 for(dt2 = dt; dt2; dt2 = dt2->next) {
402 if(
403 (
404 dt2 == dt ||
405 (dt2->parent && dt2->parent == dt)
406 ) &&
407 (
408 !strcmp(dt2->device_type, "sound") ||
409 !strcmp(dt2->device_type, "soundchip")
410 )
411 ) break;
412 }
413 if(!dt2) dt2 = dt;
414
415 hd = add_hd_entry(hd_data, __LINE__, 0);
416 hd->bus.id = bus_none;
417 hd->base_class.id = bc_multimedia;
418 hd->sub_class.id = sc_multi_audio;
419 hd->attached_to = hd_parent->idx;
420 hd->rom_id = new_str(dt2->path);
421 irq = dt2->interrupt;
422 if(irq <= 1 && dt2->parent && !dt2->parent->pci) {
423 irq = dt2->parent->interrupt;
424 }
425 if(irq > 1) {
426 res = add_res_entry(&hd->res, new_mem(sizeof *res));
427 res->irq.type = res_irq;
428 res->irq.enabled = 1;
429 res->irq.base = irq;
430 }
431
432 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x401); /* Apple */
433 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0010);
434
435 if(dt2->compatible) {
436 if(!strcmp(dt2->compatible, "screamer")) {
437 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0011);
438 }
439 else if(!strcmp(dt2->compatible, "burgundy")) {
440 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0012);
441 }
442 else if(!strcmp(dt2->compatible, "daca")) {
443 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0013);
444 }
445 else if(!strcmp(dt2->compatible, "CRUS,CS4236B")) {
446 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x402); /* IBM */
447 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0014);
448 }
449 }
450 }
451 }
452 }
453}
454
455
456void add_legacy_prom_devices(hd_data_t *hd_data, devtree_t *dt)
457{
458 if(dt->pci) return;
459
460 if(add_prom_display(hd_data, dt)) return;
461 if(add_prom_vscsi(hd_data, dt)) return;
462 if(add_prom_veth(hd_data, dt)) return;
463}
464
465int add_prom_display(hd_data_t *hd_data, devtree_t *dt)
466{
467 hd_t *hd;
468 hd_res_t *res;
469 unsigned id;
470
471 if(
472 dt->device_type &&
473 !strcmp(dt->device_type, "display")
474 ) {
475 /* display devices */
476
477 id = 0;
478
479 if(dt->name) {
480 if(!strcmp(dt->name, "valkyrie")) {
481 id = MAKE_ID(TAG_SPECIAL, 0x3000);
482 }
483 else if(!strcmp(dt->name, "platinum")) {
484 id = MAKE_ID(TAG_SPECIAL, 0x3001);
485 }
486 }
487
488 if(id) {
489 hd = add_hd_entry(hd_data, __LINE__, 0);
490 hd->bus.id = bus_none;
491 hd->base_class.id = bc_display;
492 hd->sub_class.id = sc_dis_other;
493
494 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0401);
495 hd->device.id = id;
496 hd->rom_id = new_str(dt->path);
497 if(dt->interrupt) {
498 res = add_res_entry(&hd->res, new_mem(sizeof *res));
499 res->irq.type = res_irq;
500 res->irq.enabled = 1;
501 res->irq.base = dt->interrupt;
502 }
503
504 return 1;
505 }
506 }
507
508 return 0;
509}
510
511int add_prom_vscsi(hd_data_t *hd_data, devtree_t *dt)
512{
513 hd_t *hd;
514 char *s, *id;
515
516 if(
517 dt->path &&
518 dt->device_type &&
519 !strcmp(dt->device_type, "vscsi")
520 ) {
521 /* vscsi storage */
522
523 if(
524 (s = strstr(dt->path, "v-scsi@")) &&
525 *(id = s + sizeof "v-scsi@" - 1)
526 ) {
527 hd = add_hd_entry(hd_data, __LINE__, 0);
528 hd->bus.id = bus_none;
529 hd->base_class.id = bc_storage;
530 hd->sub_class.id = sc_sto_scsi;
531 hd->slot = veth_cnt++;
532
533 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6001);
534 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x1001);
535 str_printf(&hd->device.name, 0, "Virtual SCSI %d", hd->slot);
536 hd->rom_id = new_str(dt->path);
537
538 str_printf(&hd->sysfs_id, 0, "/devices/vio/%s", id);
539
540 return 1;
541 }
542 }
543
544 return 0;
545}
546
547int add_prom_veth(hd_data_t *hd_data, devtree_t *dt)
548{
549 hd_t *hd;
550 char *s, *id;
551
552 if(
553 dt->path &&
554 dt->device_type &&
555 dt->compatible &&
556 !strcmp(dt->device_type, "network") &&
557 !strcmp(dt->compatible, "IBM,l-lan")
558 ) {
559 /* veth network */
560
561 if(
562 (s = strstr(dt->path, "l-lan@")) &&
563 *(id = s + sizeof "l-lan@" - 1)
564 ) {
565 hd = add_hd_entry(hd_data, __LINE__, 0);
566 hd->bus.id = bus_none;
567 hd->base_class.id = bc_network;
568 hd->sub_class.id = 0x00;
569 hd->slot = veth_cnt++;
570
571 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6001);
572 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x1002);
573 str_printf(&hd->device.name, 0, "Virtual Ethernet card %d", hd->slot);
574 hd->rom_id = new_str(dt->path);
575
576 str_printf(&hd->sysfs_id, 0, "/devices/vio/%s", id);
577
578 return 1;
579 }
580 }
581
582 return 0;
583}
584
585void add_devices(hd_data_t *hd_data)
586{
587 hd_t *hd;
588 devtree_t *dt;
589#if 0
590 hd_res_t *res;
591 unsigned pci_slot = 0, u;
592#endif
593
594 /* remove old assignments */
595 for(hd = hd_data->hd; hd; hd = hd->next) {
596 if(ID_TAG(hd->device.id) == TAG_PCI && ID_TAG(hd->vendor.id) == TAG_PCI) {
597 hd->rom_id = free_mem(hd->rom_id);
598 hd->detail = free_hd_detail(hd->detail);
599 }
600 }
601
602 for(dt = hd_data->devtree; dt; dt = dt->next) {
603 if(dt->pci) {
604 for(hd = hd_data->hd; hd; hd = hd->next) {
605 if(
606 /* do *not* compare class ids */
607 /* It would be better to check the slot numbers instead but
608 * as they are not stored within /proc/device-tree in a consistent
609 * way, we can't do that.
610 */
611 !hd->rom_id &&
612 ID_TAG(hd->device.id) == TAG_PCI &&
613 ID_TAG(hd->vendor.id) == TAG_PCI &&
614 ID_VALUE(hd->device.id) == dt->device_id &&
615 ID_VALUE(hd->vendor.id) == dt->vendor_id &&
616 (dt->subvendor_id == -1 || ID_VALUE(hd->sub_vendor.id) == dt->subvendor_id) &&
617 (dt->subdevice_id == -1 || ID_VALUE(hd->sub_device.id) == dt->subdevice_id) &&
618 hd->revision.id == dt->revision_id
619 ) break;
620 }
621
622 if(hd) {
623 hd->rom_id = new_str(dt->path);
624 hd->detail = new_mem(sizeof *hd->detail);
625 hd->detail->type = hd_detail_devtree;
626 hd->detail->devtree.data = dt;
627 add_pci_prom_devices(hd_data, hd, dt);
628 }
629 }
630 else {
631 add_legacy_prom_devices(hd_data, dt);
632 }
633 }
634}
635
636void dump_devtree_data(hd_data_t *hd_data)
637{
638 unsigned u;
639 devtree_t *devtree;
640
641 devtree = hd_data->devtree;
642 if(!devtree) return;
643
644 ADD2LOG("----- /proc device tree -----\n");
645
646 for(; devtree; devtree = devtree->next) {
647 u = devtree->parent ? devtree->parent->idx : 0;
648 ADD2LOG(" %02u @%02u %s", devtree->idx, u, devtree->path);
649 if(devtree->pci) ADD2LOG(" [pci]");
650 ADD2LOG("\n");
651
652 ADD2LOG(
653 " name \"%s\", model \"%s\", dtype \"%s\", compat \"%s\"\n",
654 devtree->name ? devtree->name : "",
655 devtree->model ? devtree->model : "",
656 devtree->device_type ? devtree->device_type : "",
657 devtree->compatible ? devtree->compatible : ""
658 );
659
660 if(
661 devtree->class_code != -1 || devtree->vendor_id != -1 ||
662 devtree->device_id != -1 || devtree->revision_id != -1 ||
663 devtree->subdevice_id != -1 || devtree->subvendor_id != -1 ||
664 devtree->interrupt != -1
665 ) {
666 ADD2LOG(" ");
667 if(devtree->class_code != -1) ADD2LOG(" class 0x%06x", devtree->class_code);
668 if(devtree->vendor_id != -1) ADD2LOG(" vend 0x%04x", devtree->vendor_id);
669 if(devtree->device_id != -1) ADD2LOG(" dev 0x%04x", devtree->device_id);
670 if(devtree->subvendor_id != -1) ADD2LOG(" svend 0x%04x", devtree->subvendor_id);
671 if(devtree->subdevice_id != -1) ADD2LOG(" sdev 0x%04x", devtree->subdevice_id);
672 if(devtree->revision_id != -1) ADD2LOG(" rev 0x%02x", devtree->revision_id);
673 if(devtree->interrupt != -1) ADD2LOG(" irq %d", devtree->interrupt);
674 ADD2LOG("\n");
675 }
676
677 if(devtree->edid) {
678 ADD2LOG(" EDID record:\n");
679 for(u = 0; u < 0x80; u += 0x10) {
680 ADD2LOG(" %02x ", u);
681 hexdump(&hd_data->log, 1, 0x10, devtree->edid + u);
682 ADD2LOG("\n");
683 }
684 }
685
686 if(devtree->next) ADD2LOG("\n");
687 }
688
689 ADD2LOG("----- /proc device tree end -----\n");
690}
691
692
693#endif /* defined(__PPC__) */
694