This commit was manufactured by cvs2svn to create tag
[opensuse:hwinfo.git] / src / hd / manual.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <dirent.h>
6 #include <ctype.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9
10 #include "hd.h"
11 #include "hd_int.h"
12 #include "manual.h"
13 #include "hddb.h"
14
15 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
16  *
17  * hardware in /var/lib/hardware/
18  *
19  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
20  */
21
22 typedef struct {
23   int key;
24   char *value;
25 } hash_t;
26
27 /* corresponds to hd_status_value_t */
28 static hash_t status_names[] = {
29   { status_no,      "no"      },
30   { status_yes,     "yes"     },
31   { status_unknown, "unknown" },
32   { status_new,     "new"     },
33   { 0,              NULL      }
34 };
35
36 /* corresponds to hd_hw_item_t */
37 static hash_t hw_items[] = {
38   { hw_sys,           "system"              },
39   { hw_cpu,           "cpu"                 },
40   { hw_keyboard,      "keyboard"            },
41   { hw_braille,       "braille"             },
42   { hw_mouse,         "mouse"               },
43   { hw_joystick,      "joystick"            },
44   { hw_printer,       "printer"             },
45   { hw_scanner,       "scanner"             },
46   { hw_chipcard,      "chipcard"            },
47   { hw_monitor,       "monitor"             },
48   { hw_tv,            "tv card"             },
49   { hw_display,       "graphics card"       },
50   { hw_framebuffer,   "framebuffer"         },
51   { hw_camera,        "camera"              },
52   { hw_sound,         "sound"               },
53   { hw_storage_ctrl,  "storage"             },
54   { hw_network_ctrl,  "network"             },
55   { hw_isdn,          "isdn adapter"        },
56   { hw_modem,         "modem"               },
57   { hw_network,       "network interface"   },
58   { hw_disk,          "disk"                },
59   { hw_partition,     "partition"           },
60   { hw_cdrom,         "cdrom"               },
61   { hw_floppy,        "floppy"              },
62   { hw_manual,        "manual"              },
63   { hw_usb_ctrl,      "usb controller"      },
64   { hw_usb,           "usb"                 },
65   { hw_bios,          "bios"                },
66   { hw_pci,           "pci"                 },
67   { hw_isapnp,        "isapnp"              },
68   { hw_bridge,        "bridge"              },
69   { hw_hub,           "hub"                 },
70   { hw_scsi,          "scsi"                },
71   { hw_ide,           "ide"                 },
72   { hw_memory,        "memory"              },
73   { hw_dvb,           "dvb card"            },
74   { hw_pcmcia,        "pcmcia"              },
75   { hw_pcmcia_ctrl,   "pcmcia controller"   },
76   { hw_ieee1394,      "firewire"            },
77   { hw_ieee1394_ctrl, "firewire controller" },
78   { hw_hotplug,       "hotplug"             },
79   { hw_hotplug_ctrl,  "hotplug controller"  },
80   { hw_zip,           "zip"                 },
81   { hw_pppoe,         "pppoe"               },
82   { hw_wlan,          "wlan card"           },
83   { hw_dsl,           "DSL adapter"         },
84   { hw_block,         "block device"        },
85   { hw_tape,          "tape"                },
86   { hw_vbe,           "vesa bios"           },
87   { hw_unknown,       "unknown"             },
88   { 0,                NULL                  }
89 };
90
91 typedef enum {
92   hw_id_unique = 1, hw_id_parent, hw_id_child, hw_id_hwclass, hw_id_model,
93   hw_id_configured, hw_id_available, hw_id_needed, hw_id_cfgstring, hw_id_active
94 } hw_id_t;
95
96 #ifndef LIBHD_TINY
97
98 #define MAN_SECT_GENERAL        "General"
99 #define MAN_SECT_STATUS         "Status"
100 #define MAN_SECT_HARDWARE       "Hardware"
101
102 static hash_t hw_ids_general[] = {
103   { hw_id_unique,     "UniqueID"   },
104   { hw_id_parent,     "ParentID"   },
105   { hw_id_child,      "ChildIDs"   },
106   { hw_id_hwclass,    "HWClass"    },
107   { hw_id_model,      "Model"      },
108   { 0,                NULL         }
109 };
110
111 static hash_t hw_ids_status[] = {
112   { hw_id_configured, "Configured"   },
113   { hw_id_available,  "Available"    },
114   { hw_id_needed,     "Needed"       },
115   { hw_id_cfgstring,  "ConfigString" },
116   { hw_id_active,     "Active"       },
117   { 0,                NULL           }
118 };
119
120 /* structure elements from hd_t */
121 typedef enum {
122   hwdi_bus = 1, hwdi_slot, hwdi_func, hwdi_base_class, hwdi_sub_class,
123   hwdi_prog_if, hwdi_dev, hwdi_vend, hwdi_sub_dev, hwdi_sub_vend, hwdi_rev,
124   hwdi_compat_dev, hwdi_compat_vend, hwdi_dev_name, hwdi_vend_name,
125   hwdi_sub_dev_name, hwdi_sub_vend_name, hwdi_rev_name, hwdi_serial,
126   hwdi_unix_dev_name, hwdi_unix_dev_name2, hwdi_unix_dev_names, hwdi_rom_id,
127   hwdi_broken, hwdi_usb_guid, hwdi_res_mem, hwdi_res_phys_mem, hwdi_res_io,
128   hwdi_res_irq, hwdi_res_dma, hwdi_res_size, hwdi_res_baud, hwdi_res_cache,
129   hwdi_res_disk_geo, hwdi_res_monitor, hwdi_res_framebuffer, hwdi_features,
130   hwdi_hotplug, hwdi_class_list, hwdi_drivers, hwdi_sysfs_id,
131   hwdi_sysfs_busid, hwdi_sysfs_link
132 } hw_hd_items_t;
133
134 static hash_t hw_ids_hd_items[] = {
135   { hwdi_bus,             "Bus"              },
136   { hwdi_slot,            "Slot"             },
137   { hwdi_func,            "Function"         },
138   { hwdi_base_class,      "BaseClass"        },
139   { hwdi_sub_class,       "SubClass"         },
140   { hwdi_prog_if,         "ProgIF"           },
141   { hwdi_dev,             "DeviceID"         },
142   { hwdi_vend,            "VendorID"         },
143   { hwdi_sub_dev,         "SubDeviceID"      },
144   { hwdi_sub_vend,        "SubVendorID"      },
145   { hwdi_rev,             "RevisionID"       },
146   { hwdi_compat_dev,      "CompatDeviceID"   },
147   { hwdi_compat_vend,     "CompatVendorID"   },
148   { hwdi_dev_name,        "DeviceName"       },
149   { hwdi_vend_name,       "VendorName"       },
150   { hwdi_sub_dev_name,    "SubDeviceName"    },
151   { hwdi_sub_vend_name,   "SubVendorName"    },
152   { hwdi_rev_name,        "RevisionName"     },
153   { hwdi_serial,          "Serial"           },
154   { hwdi_unix_dev_name,   "UnixDevice"       },
155   { hwdi_unix_dev_name2,  "UnixDeviceAlt"    },
156   { hwdi_unix_dev_names,  "UnixDeviceList"   },
157   { hwdi_rom_id,          "ROMID"            },
158   { hwdi_broken,          "Broken"           },
159   { hwdi_usb_guid,        "USBGUID"          },
160   { hwdi_res_phys_mem,    "Res.PhysMemory"   },
161   { hwdi_res_mem,         "Res.Memory"       },
162   { hwdi_res_io,          "Res.IO"           },
163   { hwdi_res_irq,         "Res.Interrupts"   },
164   { hwdi_res_dma,         "Res.DMA"          },
165   { hwdi_res_size,        "Res.Size"         },
166   { hwdi_res_baud,        "Res.Baud"         },
167   { hwdi_res_cache,       "Res.Cache"        },
168   { hwdi_res_disk_geo,    "Res.DiskGeometry" },
169   { hwdi_res_monitor,     "Res.Monitor"      },
170   { hwdi_res_framebuffer, "Res.Framebuffer"  },
171   { hwdi_features,        "Features"         },
172   { hwdi_hotplug,         "Hotplug"          },
173   { hwdi_class_list,      "HWClassList"      },
174   { hwdi_drivers,         "Drivers"          },
175   { hwdi_sysfs_id,        "SysfsID"          },
176   { hwdi_sysfs_busid,     "SysfsBusID"       },
177   { hwdi_sysfs_link,      "SysfsLink"        },
178   { 0,                    NULL               }
179 };
180 #endif
181
182 static char *key2value(hash_t *hash, int id);
183
184 #ifndef LIBHD_TINY
185
186 static int value2key(hash_t *hash, char *str);
187 static void dump_manual(hd_data_t *hd_data);
188 static unsigned str2id(char *str);
189 static void manual2hd(hd_data_t *hd_data, hd_manual_t *entry, hd_t *hd);
190 static void hd2manual(hd_t *hd, hd_manual_t *entry);
191
192 void hd_scan_manual(hd_data_t *hd_data)
193 {
194   DIR *dir;
195   struct dirent *de;
196   hd_manual_t *entry, **entry_next;
197   int i;
198   hd_t *hd, *hd1;
199
200   if(!hd_probe_feature(hd_data, pr_manual)) return;
201
202   hd_data->module = mod_manual;
203
204   /* some clean-up */
205   remove_hd_entries(hd_data);
206
207   hd_data->manual = hd_free_manual(hd_data->manual);
208   entry_next = &hd_data->manual;
209
210   if((dir = opendir(HARDWARE_UNIQUE_KEYS))) {
211     i = 0;
212     while((de = readdir(dir))) {
213       if(*de->d_name == '.') continue;
214       PROGRESS(1, ++i, "read");
215       if((entry = hd_manual_read_entry(hd_data, de->d_name))) {
216         ADD2LOG("  got %s\n", entry->unique_id);
217         *entry_next = entry;
218         entry_next = &entry->next;
219       }
220     }
221     closedir(dir);
222   }
223
224   /* for compatibility: read old files, too */
225   if((dir = opendir(HARDWARE_DIR))) {
226     i = 0;
227     while((de = readdir(dir))) {
228       if(*de->d_name == '.') continue;
229       for(entry = hd_data->manual; entry; entry = entry->next) {
230         if(entry->unique_id && !strcmp(entry->unique_id, de->d_name)) break;
231       }
232       if(entry) continue;
233       PROGRESS(2, ++i, "read");
234       if((entry = hd_manual_read_entry(hd_data, de->d_name))) {
235         ADD2LOG("  got %s\n", entry->unique_id);
236         *entry_next = entry;
237         entry_next = &entry->next;
238       }
239     }
240     closedir(dir);
241   }
242
243   if(hd_data->debug) dump_manual(hd_data);
244
245   /* initialize some useful status value */
246   for(hd = hd_data->hd; hd; hd = hd->next) {
247     if(
248       !hd->status.configured &&
249       !hd->status.available &&
250       !hd->status.needed &&
251       !hd->status.active &&
252       !hd->status.invalid
253     ) {
254       hd->status.configured = status_new;
255       hd->status.available = hd->module == mod_manual ? status_unknown : status_yes;
256       hd->status.needed = status_no;
257       hd->status.active = status_unknown;
258     }
259   }
260
261   for(entry = hd_data->manual; entry; entry = entry->next) {
262
263     for(hd = hd_data->hd; hd; hd = hd->next) {
264       if(hd->unique_id && !strcmp(hd->unique_id, entry->unique_id)) break;
265     }
266
267     if(hd) {
268       /* just update config status */
269       hd->status = entry->status;
270       hd->status.available = status_yes;
271
272       hd->config_string = new_str(entry->config_string);
273     }
274     else {
275       /* add new entry */
276       hd = add_hd_entry(hd_data, __LINE__, 0);
277
278       manual2hd(hd_data, entry, hd);
279
280       if(hd->status.available != status_unknown) hd->status.available = status_no;
281
282       if(hd->parent_id) {
283         for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
284           if(hd1->unique_id && !strcmp(hd1->unique_id, hd->parent_id)) {
285             hd->attached_to = hd1->idx;
286             break;
287           }
288         }
289       }
290     }
291   }
292
293 }
294
295
296 void hd_scan_manual2(hd_data_t *hd_data)
297 {
298   hd_t *hd, *hd1;
299
300   /* check if it's necessary to reconfigure this hardware */
301   for(hd = hd_data->hd; hd; hd = hd->next) {
302     hd->status.reconfig = status_no;
303
304     if(hd->status.needed != status_yes) continue;
305
306     if(hd->status.available == status_no) {
307       hd->status.reconfig = status_yes;
308       continue;
309     }
310
311     if(hd->status.available != status_unknown) continue;
312
313     for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
314       if(hd1 == hd) continue;
315
316       if(
317         hd1->hw_class == hd->hw_class &&
318         hd1->status.configured == status_new &&
319         hd1->status.available == status_yes
320       ) break;
321     }
322
323     if(hd1) hd->status.reconfig = status_yes;
324   }
325 }
326
327
328 int value2key(hash_t *hash, char *str)
329 {
330   for(; hash->value; hash++) {
331     if(!strcmp(hash->value, str)) break;
332   }
333
334   return hash->key;
335 }
336
337 #endif
338
339 char *key2value(hash_t *hash, int id)
340 {
341   for(; hash->value; hash++) {
342     if(hash->key == id) break;
343   }
344
345   return hash->value;
346 }
347
348 char *hd_hw_item_name(hd_hw_item_t item)
349 {
350   return key2value(hw_items, item);
351 }
352
353
354 #ifndef LIBHD_TINY
355
356 char *hd_status_value_name(hd_status_value_t status)
357 {
358   return key2value(status_names, status);
359 }
360
361 /*
362  * read an entry
363  */
364 hd_manual_t *hd_manual_read_entry(hd_data_t *hd_data, const char *id)
365 {
366   char path[PATH_MAX];
367   int i, j, line;
368   str_list_t *sl, *sl0;
369   hd_manual_t *entry;
370   hash_t *sect;
371   char *s, *s1, *s2;
372   int err = 0;
373
374   snprintf(path, sizeof path, "%s/%s", HARDWARE_UNIQUE_KEYS, id);
375
376   if(!(sl0 = read_file(path, 0, 0))) {
377     /* try old location, too */
378     snprintf(path, sizeof path, "%s/%s", HARDWARE_DIR, id);
379     if(!(sl0 = read_file(path, 0, 0))) return NULL;
380   }
381
382   entry = new_mem(sizeof *entry);
383
384   // default list: no valid entries
385   sect = hw_ids_general + sizeof hw_ids_general / sizeof *hw_ids_general - 1;
386
387   for(line = 1, sl = sl0; sl; sl = sl->next, line++) {
388     s = sl->str;
389     while(isspace(*s)) s++;
390     if(!*s || *s == '#' || *s == ';') continue; /* empty lines & comments */
391
392     s2 = s;
393     s1 = strsep(&s2, "=");
394
395     if(!s2 && *s == '[') {
396       s2 = s + 1;
397       s1 = strsep(&s2, "]");
398       if(s1) {
399         if(!strcmp(s1, MAN_SECT_GENERAL)) {
400           sect = hw_ids_general;
401           continue;
402         }
403         if(!strcmp(s1, MAN_SECT_STATUS)) {
404           sect = hw_ids_status;
405           continue;
406         }
407         if(!strcmp(s1, MAN_SECT_HARDWARE)) {
408           sect = NULL;
409           continue;
410         }
411       }
412       s2 = NULL;
413     }
414
415     if(!s2) {
416       ADD2LOG("  %s: invalid line %d\n", id, line);
417       err = 1;
418       break;
419     }
420
421     if(sect) {
422       i = value2key(sect, s1);
423       if(!i) {
424         ADD2LOG("  %s: invalid line %d\n", id, line);
425         err = 1;
426         break;
427       }
428       s = canon_str(s2, strlen(s2));
429       switch(i) {
430         case hw_id_unique:
431           entry->unique_id = s;
432           s = NULL;
433           break;
434
435         case hw_id_parent:
436           entry->parent_id = s;
437           s = NULL;
438           break;
439
440         case hw_id_child:
441           entry->child_ids = s;
442           s = NULL;
443           break;
444
445         case hw_id_hwclass:
446           j = value2key(hw_items, s);
447           entry->hw_class = j;
448           if(!j) err = 1;
449           break;
450
451         case hw_id_model:
452           entry->model = s;
453           s = NULL;
454           break;
455
456         case hw_id_configured:
457           j = value2key(status_names, s);
458           entry->status.configured = j;
459           if(!j) err = 1;
460           break;
461
462         case hw_id_available:
463           j = value2key(status_names, s);
464           entry->status.available_orig =
465           entry->status.available = j;
466           if(!j) err = 1;
467           break;
468
469         case hw_id_needed:
470           j = value2key(status_names, s);
471           entry->status.needed = j;
472           if(!j) err = 1;
473           break;
474
475         case hw_id_active:
476           j = value2key(status_names, s);
477           entry->status.active = j;
478           if(!j) err = 1;
479           break;
480
481         case hw_id_cfgstring:
482           entry->config_string = s;
483           s = NULL;
484           break;
485
486         default:
487           err = 1;
488       }
489
490       free_mem(s);
491
492       if(err) {
493         ADD2LOG("  %s: invalid line %d\n", id, line);
494         break;
495       }
496     }
497     else {
498       add_str_list(&entry->key, s1);
499       s = canon_str(s2, strlen(s2));
500       add_str_list(&entry->value, s);
501       free_mem(s);
502     }
503   }
504
505   free_str_list(sl0);
506
507   /*
508    * do some basic consistency checks
509    */
510
511   if(!entry->unique_id || strcmp(entry->unique_id, id)) {
512     ADD2LOG("  %s: unique id does not match file name\n", id);
513     err = 1;
514   }
515
516   /*
517    * if the status info is completely missing, fake some:
518    * new hardware, not autodetectable, not needed
519    */
520   if(
521     !entry->status.configured &&
522     !entry->status.available &&
523     !entry->status.needed &&
524     !entry->status.invalid
525   ) {
526     entry->status.configured = status_new;
527     entry->status.available = status_unknown;
528     entry->status.needed = status_no;
529   }
530
531   if(!entry->status.active) entry->status.active = status_unknown;
532
533   if(
534     !entry->status.configured ||
535     !entry->status.available ||
536     !entry->status.needed ||
537     !entry->status.active
538   ) {
539     ADD2LOG("  %s: incomplete status\n", id);
540     err = 1;
541   }
542
543   if(!entry->hw_class) {
544     ADD2LOG("  %s: no class\n", id);
545     err = 1;
546   }
547
548   if(!entry->model) {
549     ADD2LOG("  %s: no model\n", id);
550     err = 1;
551   }
552
553   if(err) {
554     entry = hd_free_manual(entry);
555   }
556
557   return entry;
558 }
559
560
561 /*
562  * write an entry
563  */
564
565 int hd_manual_write_entry(hd_data_t *hd_data, hd_manual_t *entry)
566 {
567   FILE *f;
568   char path[PATH_MAX];
569   int error = 0;
570   struct stat sbuf;
571   str_list_t *sl1, *sl2;
572
573   if(!entry) return 0;
574   if(!entry->unique_id || entry->status.invalid) return 1;
575
576   snprintf(path, sizeof path, "%s/%s", HARDWARE_UNIQUE_KEYS, entry->unique_id);
577
578   if(!(f = fopen(path, "w"))) {
579     /* maybe we have to create the HARDWARE_UNIQUE_KEYS directory first... */
580
581     if(lstat(HARDWARE_DIR, &sbuf)) {
582       mkdir(HARDWARE_DIR, 0755);
583     }
584
585     if(lstat(HARDWARE_UNIQUE_KEYS, &sbuf)) {
586       mkdir(HARDWARE_UNIQUE_KEYS, 0755);
587     }
588
589     if(!(f = fopen(path, "w"))) return 2;
590   }
591
592   fprintf(f, "[%s]\n", MAN_SECT_GENERAL);
593
594   if(
595     !fprintf(f, "%s=%s\n",
596       key2value(hw_ids_general, hw_id_unique),
597       entry->unique_id
598     )
599   ) error = 3;
600
601   if(
602     entry->parent_id &&
603     !fprintf(f, "%s=%s\n",
604       key2value(hw_ids_general, hw_id_parent),
605       entry->parent_id
606     )
607   ) error = 3;
608
609   if(
610     entry->child_ids &&
611     !fprintf(f, "%s=%s\n",
612       key2value(hw_ids_general, hw_id_child),
613       entry->child_ids
614     )
615   ) error = 3;
616
617   if(
618     (entry->hw_class && key2value(hw_items, entry->hw_class)) &&
619     !fprintf(f, "%s=%s\n",
620       key2value(hw_ids_general, hw_id_hwclass),
621       key2value(hw_items, entry->hw_class)
622     )
623   ) error = 3;
624
625   if(
626     entry->model &&
627     !fprintf(f, "%s=%s\n",
628       key2value(hw_ids_general, hw_id_model),
629       entry->model
630     )
631   ) error = 3;
632
633   fprintf(f, "\n[%s]\n", MAN_SECT_STATUS);
634
635   if(
636     (entry->status.configured && key2value(status_names, entry->status.configured)) &&
637     !fprintf(f, "%s=%s\n",
638       key2value(hw_ids_status, hw_id_configured),
639       key2value(status_names, entry->status.configured)
640     )
641   ) error = 4;
642
643   if(
644     (entry->status.available && key2value(status_names, entry->status.available)) &&
645     !fprintf(f, "%s=%s\n",
646       key2value(hw_ids_status, hw_id_available),
647       key2value(status_names, entry->status.available)
648     )
649   ) error = 4;
650
651   if(
652     (entry->status.needed && key2value(status_names, entry->status.needed)) &&
653     !fprintf(f, "%s=%s\n",
654       key2value(hw_ids_status, hw_id_needed),
655       key2value(status_names, entry->status.needed)
656     )
657   ) error = 4;
658
659   if(
660     (entry->status.active && key2value(status_names, entry->status.active)) &&
661     !fprintf(f, "%s=%s\n",
662       key2value(hw_ids_status, hw_id_active),
663       key2value(status_names, entry->status.active)
664     )
665   ) error = 4;
666
667   if(
668     entry->config_string &&
669     !fprintf(f, "%s=%s\n",
670       key2value(hw_ids_status, hw_id_cfgstring),
671       entry->config_string
672     )
673   ) error = 4;
674
675   fprintf(f, "\n[%s]\n", MAN_SECT_HARDWARE);
676
677   for(
678     sl1 = entry->key, sl2 = entry->value;
679     sl1 && sl2;
680     sl1 = sl1->next, sl2 = sl2->next
681   ) {
682     if(!fprintf(f, "%s=%s\n", sl1->str, sl2->str)) {
683       error = 5;
684       break;
685     }
686   }
687
688   fputs("\n", f);
689
690   fclose(f);
691
692   /* remove old file */
693   if(!error) {
694     snprintf(path, sizeof path, "%s/%s", HARDWARE_DIR, entry->unique_id);
695     unlink(path);
696   }
697
698   return error;
699 }
700
701
702 void dump_manual(hd_data_t *hd_data)
703 {
704   hd_manual_t *entry;
705   static const char *txt = "manually configured hardware";
706   str_list_t *sl1, *sl2;
707
708   if(!hd_data->manual) return;
709
710   ADD2LOG("----- %s -----\n", txt);
711   for(entry = hd_data->manual; entry; entry = entry->next) {
712     ADD2LOG("  %s=%s\n",
713       key2value(hw_ids_general, hw_id_unique),
714       entry->unique_id
715     );
716     if(entry->parent_id)
717       ADD2LOG("    %s=%s\n",
718         key2value(hw_ids_general, hw_id_parent),
719         entry->parent_id
720       );
721     if(entry->child_ids)
722       ADD2LOG("    %s=%s\n",
723         key2value(hw_ids_general, hw_id_child),
724         entry->child_ids
725       );
726     ADD2LOG("    %s=%s\n",
727       key2value(hw_ids_general, hw_id_hwclass),
728       key2value(hw_items, entry->hw_class)
729     );
730     ADD2LOG("    %s=%s\n",
731       key2value(hw_ids_general, hw_id_model),
732       entry->model
733     );
734     ADD2LOG("    %s=%s\n",
735       key2value(hw_ids_status, hw_id_configured),
736       key2value(status_names, entry->status.configured)
737     );
738     ADD2LOG("    %s=%s\n",
739       key2value(hw_ids_status, hw_id_available),
740       key2value(status_names, entry->status.available)
741     );
742     ADD2LOG("    %s=%s\n",
743       key2value(hw_ids_status, hw_id_needed),
744       key2value(status_names, entry->status.needed)
745     );
746     ADD2LOG("    %s=%s\n",
747       key2value(hw_ids_status, hw_id_active),
748       key2value(status_names, entry->status.active)
749     );
750     if(entry->config_string)
751       ADD2LOG("    %s=%s\n",
752         key2value(hw_ids_status, hw_id_cfgstring),
753         entry->config_string
754       );
755     for(
756       sl1 = entry->key, sl2 = entry->value;
757       sl1 && sl2;
758       sl1 = sl1->next, sl2 = sl2->next
759     ) {
760       ADD2LOG("    %s=%s\n", sl1->str, sl2->str);
761     }
762   }
763   ADD2LOG("----- %s end -----\n", txt);
764 }
765
766
767 unsigned str2id(char *str)
768 {
769   unsigned id;
770   unsigned tag = 0;
771
772   if(strlen(str) == 3) return name2eisa_id(str);
773
774   switch(*str) {
775     case 'p':
776       tag = TAG_PCI; str++; break;
777
778     case 'r':
779       str++; break;
780
781     case 'u':
782       tag = TAG_USB; str++; break;
783
784     case 's':
785       tag = TAG_SPECIAL; str++; break;
786
787     case 'P':
788       tag = TAG_PCMCIA; str++; break;
789
790   }
791
792   id = strtoul(str, &str, 16);
793   if(*str) return 0;
794
795   return MAKE_ID(tag, ID_VALUE(id));
796 }
797
798
799 /*
800  * move info from hd_manual_t to hd_t
801  */
802 void manual2hd(hd_data_t *hd_data, hd_manual_t *entry, hd_t *hd)
803 {
804   str_list_t *sl1, *sl2;
805   hw_hd_items_t item;
806   unsigned tag, u0, u1, u2, u3, u4;
807   hd_res_t *res;
808   uint64_t u64_0, u64_1;
809   char *s;
810   int i;
811
812   if(!hd || !entry) return;
813
814   hd->unique_id = new_str(entry->unique_id);
815   hd->parent_id = new_str(entry->parent_id);
816   hd->child_ids = hd_split(',', entry->child_ids);
817   hd->model = new_str(entry->model);
818   hd->hw_class = entry->hw_class;
819
820   hd->config_string = new_str(entry->config_string);
821
822   hd->status = entry->status;
823
824   for(
825     sl1 = entry->key, sl2 = entry->value;
826     sl1 && sl2;
827     sl1 = sl1->next, sl2 = sl2->next
828   ) {
829     switch(item = value2key(hw_ids_hd_items, sl1->str)) {
830       case hwdi_bus:
831         hd->bus.id = strtoul(sl2->str, NULL, 0);
832         break;
833
834       case hwdi_slot:
835         hd->slot = strtoul(sl2->str, NULL, 0);
836         break;
837
838       case hwdi_func:
839         hd->func = strtoul(sl2->str, NULL, 0);
840         break;
841
842       case hwdi_base_class:
843         hd->base_class.id = strtoul(sl2->str, NULL, 0);
844         break;
845
846       case hwdi_sub_class:
847         hd->sub_class.id = strtoul(sl2->str, NULL, 0);
848         break;
849
850       case hwdi_prog_if:
851         hd->prog_if.id = strtoul(sl2->str, NULL, 0);
852         break;
853
854       case hwdi_dev:
855         hd->device.id = str2id(sl2->str);
856         break;
857
858       case hwdi_vend:
859         hd->vendor.id = str2id(sl2->str);
860         break;
861
862       case hwdi_sub_dev:
863         hd->sub_device.id = str2id(sl2->str);
864         break;
865
866       case hwdi_sub_vend:
867         hd->sub_vendor.id = str2id(sl2->str);
868         break;
869
870       case hwdi_rev:
871         hd->revision.id = strtoul(sl2->str, NULL, 0);
872         break;
873
874       case hwdi_compat_dev:
875         hd->compat_device.id = str2id(sl2->str);
876         break;
877
878       case hwdi_compat_vend:
879         hd->compat_vendor.id = str2id(sl2->str);
880         break;
881
882       case hwdi_dev_name:
883         hd->device.name = new_str(sl2->str);
884         break;
885
886       case hwdi_vend_name:
887         hd->vendor.name = new_str(sl2->str);
888         break;
889
890       case hwdi_sub_dev_name:
891         hd->sub_device.name = new_str(sl2->str);
892         break;
893
894       case hwdi_sub_vend_name:
895         hd->sub_vendor.name = new_str(sl2->str);
896         break;
897
898       case hwdi_rev_name:
899         hd->revision.name = new_str(sl2->str);
900         break;
901
902       case hwdi_serial:
903         hd->serial = new_str(sl2->str);
904         break;
905
906       case hwdi_unix_dev_name:
907         hd->unix_dev_name = new_str(sl2->str);
908         break;
909
910       case hwdi_unix_dev_name2:
911         hd->unix_dev_name2 = new_str(sl2->str);
912         break;
913
914       case hwdi_unix_dev_names:
915         hd->unix_dev_names = hd_split(' ', sl2->str);
916         break;
917
918       case hwdi_drivers:
919         hd->drivers = hd_split('|', sl2->str);
920         break;
921
922       case hwdi_sysfs_id:
923         hd->sysfs_id = new_str(sl2->str);
924         break;
925
926       case hwdi_sysfs_busid:
927         hd->sysfs_bus_id = new_str(sl2->str);
928         break;
929
930       case hwdi_sysfs_link:
931         hd->sysfs_device_link = new_str(sl2->str);
932         break;
933
934       case hwdi_rom_id:
935         hd->rom_id = new_str(sl2->str);
936         break;
937
938       case hwdi_broken:
939         hd->broken = strtoul(sl2->str, NULL, 0);
940         break;
941
942       case hwdi_usb_guid:
943         hd->usb_guid = new_str(sl2->str);
944         break;
945
946       case hwdi_hotplug:
947         hd->hotplug = strtol(sl2->str, NULL, 0);
948         break;
949
950       case hwdi_class_list:
951         for(
952           u0 = 0, s = sl2->str;
953           u0 < sizeof hd->hw_class_list / sizeof *hd->hw_class_list;
954           u0++
955         ) {
956           if(*s && s[1] && (i = hex(s, 2)) >= 0) {
957             hd->hw_class_list[u0] = i;
958             s += 2;
959           }
960           else {
961             break;
962           }
963         }
964         break;
965
966       case hwdi_res_mem:
967         res = add_res_entry(&hd->res, new_mem(sizeof *res));
968         res->any.type = res_mem;
969         if(sscanf(sl2->str, "0x%"SCNx64",0x%"SCNx64",%u,%u,%u", &u64_0, &u64_1, &u0, &u1, &u2) == 5) {
970           res->mem.base = u64_0;
971           res->mem.range = u64_1;
972           res->mem.enabled = u0;
973           res->mem.access = u1;
974           res->mem.prefetch = u2;
975         }
976         break;
977
978       case hwdi_res_phys_mem:
979         res = add_res_entry(&hd->res, new_mem(sizeof *res));
980         res->any.type = res_phys_mem;
981         if(sscanf(sl2->str, "0x%"SCNx64"", &u64_0) == 1) {
982           res->phys_mem.range = u64_0;
983         }
984         break;
985
986       case hwdi_res_io:
987         res = add_res_entry(&hd->res, new_mem(sizeof *res));
988         res->any.type = res_io;
989         if(sscanf(sl2->str, "0x%"SCNx64",0x%"SCNx64",%u,%u", &u64_0, &u64_1, &u0, &u1) == 4) {
990           res->io.base = u64_0;
991           res->io.range = u64_1;
992           res->io.enabled = u0;
993           res->io.access = u1;
994         }
995         break;
996
997       case hwdi_res_irq:
998         res = add_res_entry(&hd->res, new_mem(sizeof *res));
999         res->any.type = res_irq;
1000         if(sscanf(sl2->str, "%u,%u,%u", &u0, &u1, &u2) == 3) {
1001           res->irq.base = u0;
1002           res->irq.triggered = u1;
1003           res->irq.enabled = u2;
1004         }
1005         break;
1006
1007       case hwdi_res_dma:
1008         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1009         res->any.type = res_dma;
1010         if(sscanf(sl2->str, "%u,%u", &u0, &u1) == 2) {
1011           res->dma.base = u0;
1012           res->dma.enabled = u1;
1013         }
1014         break;
1015
1016       case hwdi_res_size:
1017         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1018         res->any.type = res_size;
1019         if(sscanf(sl2->str, "%u,%u,%u", &u0, &u1, &u2) == 3) {
1020           res->size.unit = u0;
1021           res->size.val1 = u1;
1022           res->size.val2 = u2;
1023         }
1024         break;
1025
1026       case hwdi_res_baud:
1027         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1028         res->any.type = res_baud;
1029         if(sscanf(sl2->str, "%u,%u,%u,%u,%u", &u0, &u1, &u2, &u3, &u4) == 5) {
1030           res->baud.speed = u0;
1031           res->baud.bits = u1;
1032           res->baud.stopbits = u2;
1033           res->baud.parity = (char) u3;
1034           res->baud.handshake = (char) u4;
1035         }
1036         break;
1037
1038       case hwdi_res_cache:
1039         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1040         res->any.type = res_cache;
1041         if(sscanf(sl2->str, "%u", &u0) == 1) {
1042           res->cache.size = u0;
1043         }
1044         break;
1045
1046       case hwdi_res_disk_geo:
1047         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1048         res->any.type = res_disk_geo;
1049         if(sscanf(sl2->str, "%u,%u,%u,%u", &u0, &u1, &u2, &u3) == 4) {
1050           res->disk_geo.cyls = u0;
1051           res->disk_geo.heads = u1;
1052           res->disk_geo.sectors = u2;
1053           res->disk_geo.logical = u3;
1054         }
1055         break;
1056
1057       case hwdi_res_monitor:
1058         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1059         res->any.type = res_monitor;
1060         if(sscanf(sl2->str, "%u,%u,%u,%u", &u0, &u1, &u2, &u3) == 4) {
1061           res->monitor.width = u0;
1062           res->monitor.height = u1;
1063           res->monitor.vfreq = u2;
1064           res->monitor.interlaced = u3;
1065         }
1066         break;
1067
1068       case hwdi_res_framebuffer:
1069         res = add_res_entry(&hd->res, new_mem(sizeof *res));
1070         res->any.type = res_framebuffer;
1071         if(sscanf(sl2->str, "%u,%u,%u,%u,%u", &u0, &u1, &u2, &u3, &u4) == 5) {
1072           res->framebuffer.width = u0;
1073           res->framebuffer.height = u1;
1074           res->framebuffer.bytes_p_line = u2;
1075           res->framebuffer.colorbits = u3;
1076           res->framebuffer.mode = u4;
1077         }
1078         break;
1079
1080       case hwdi_features:
1081         u0 = strtoul(sl2->str, NULL, 0);
1082         if(u0 & (1 << 0)) hd->is.agp = 1;
1083         if(u0 & (1 << 1)) hd->is.isapnp = 1;
1084         if(u0 & (1 << 2)) hd->is.softraiddisk = 1;
1085         if(u0 & (1 << 3)) hd->is.zip = 1;
1086         if(u0 & (1 << 4)) hd->is.cdr = 1;
1087         if(u0 & (1 << 5)) hd->is.cdrw = 1;
1088         if(u0 & (1 << 6)) hd->is.dvd = 1;
1089         if(u0 & (1 << 7)) hd->is.dvdr = 1;
1090         if(u0 & (1 << 8)) hd->is.dvdram = 1;
1091         if(u0 & (1 << 9)) hd->is.pppoe = 1;
1092         if(u0 & (1 << 10)) hd->is.wlan = 1;
1093         break;
1094     }
1095   }
1096
1097   if(hd->device.id || hd->vendor.id) {
1098     tag = ID_TAG(hd->device.id);
1099     tag = tag ? tag : ID_TAG(hd->vendor.id);
1100     tag = tag ? tag : TAG_PCI;
1101     hd->device.id = MAKE_ID(tag, ID_VALUE(hd->device.id));
1102     hd->vendor.id = MAKE_ID(tag, ID_VALUE(hd->vendor.id));
1103   }
1104
1105   if(hd->sub_device.id || hd->sub_vendor.id) {
1106     tag = ID_TAG(hd->sub_device.id);
1107     tag = tag ? tag : ID_TAG(hd->sub_vendor.id);
1108     tag = tag ? tag : TAG_PCI;
1109     hd->sub_device.id = MAKE_ID(tag, ID_VALUE(hd->sub_device.id));
1110     hd->sub_vendor.id = MAKE_ID(tag, ID_VALUE(hd->sub_vendor.id));
1111   }
1112
1113   if(hd->compat_device.id || hd->compat_vendor.id) {
1114     tag = ID_TAG(hd->compat_device.id);
1115     tag = tag ? tag : ID_TAG(hd->compat_vendor.id);
1116     tag = tag ? tag : TAG_PCI;
1117     hd->compat_device.id = MAKE_ID(tag, ID_VALUE(hd->compat_device.id));
1118     hd->compat_vendor.id = MAKE_ID(tag, ID_VALUE(hd->compat_vendor.id));
1119   }
1120
1121   if(hd->status.available == status_unknown) hd->is.manual = 1;
1122
1123   /* create some entries, if missing */
1124
1125   if(!hd->device.id && !hd->vendor.id && !hd->device.name) {
1126     hd->device.name = new_str(hd->model);
1127   }
1128
1129   if(hd->hw_class && !hd->base_class.id) {
1130     switch(hd->hw_class) {
1131       case hw_cdrom:
1132         hd->base_class.id = bc_storage_device;
1133         hd->sub_class.id = sc_sdev_cdrom;
1134         break;
1135
1136       case hw_mouse:
1137         hd->base_class.id = bc_mouse;
1138         hd->sub_class.id = sc_mou_other;
1139         break;
1140
1141       default:
1142         break;
1143     }
1144   }
1145
1146   hddb_add_info(hd_data, hd);
1147 }
1148
1149
1150 void hd2manual(hd_t *hd, hd_manual_t *entry)
1151 {
1152   char *s, *t;
1153   hd_res_t *res;
1154   str_list_t *sl;
1155   unsigned u;
1156
1157   if(!hd || !entry) return;
1158
1159   entry->unique_id = new_str(hd->unique_id);
1160   entry->parent_id = new_str(hd->parent_id);
1161   entry->child_ids = hd_join(",", hd->child_ids);
1162   entry->model = new_str(hd->model);
1163   entry->hw_class = hd->hw_class;
1164
1165   entry->config_string = new_str(hd->config_string);
1166
1167   entry->status = hd->status;
1168
1169   if(
1170     !entry->status.configured &&
1171     !entry->status.available &&
1172     !entry->status.needed &&
1173     !entry->status.active &&
1174     !entry->status.invalid
1175   ) {
1176     entry->status.configured = status_new;
1177     entry->status.available = hd->module == mod_manual ? status_unknown : status_yes;
1178     entry->status.needed = status_no;
1179     entry->status.active = status_unknown;
1180   }
1181
1182   s = NULL;
1183
1184   if(hd->broken) {
1185     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_broken));
1186     str_printf(&s, 0, "0x%x", hd->broken);
1187     add_str_list(&entry->value, s);
1188   }
1189
1190   if(hd->bus.id) {
1191     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_bus));
1192     str_printf(&s, 0, "0x%x", hd->bus.id);
1193     add_str_list(&entry->value, s);
1194   }
1195
1196   if(hd->slot) {
1197     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_slot));
1198     str_printf(&s, 0, "0x%x", hd->slot);
1199     add_str_list(&entry->value, s);
1200   }
1201
1202   if(hd->func) {
1203     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_func));
1204     str_printf(&s, 0, "0x%x", hd->func);
1205     add_str_list(&entry->value, s);
1206   }
1207
1208   if(hd->base_class.id) {
1209     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_base_class));
1210     str_printf(&s, 0, "0x%x", hd->base_class.id);
1211     add_str_list(&entry->value, s);
1212   }
1213
1214   if(hd->sub_class.id) {
1215     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sub_class));
1216     str_printf(&s, 0, "0x%x", hd->sub_class.id);
1217     add_str_list(&entry->value, s);
1218   }
1219
1220   if(hd->prog_if.id) {
1221     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_prog_if));
1222     str_printf(&s, 0, "0x%x", hd->prog_if.id);
1223     add_str_list(&entry->value, s);
1224   }
1225
1226   if(hd->device.id || hd->vendor.id) {
1227     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_vend));
1228     add_str_list(&entry->value, vend_id2str(hd->vendor.id));
1229     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_dev));
1230     str_printf(&s, 0, "%04x", ID_VALUE(hd->device.id));
1231     add_str_list(&entry->value, s);
1232   }
1233
1234   if(hd->sub_device.id || hd->sub_vendor.id) {
1235     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sub_vend));
1236     add_str_list(&entry->value, vend_id2str(hd->sub_vendor.id));
1237     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sub_dev));
1238     str_printf(&s, 0, "%04x", ID_VALUE(hd->sub_device.id));
1239     add_str_list(&entry->value, s);
1240   }
1241
1242   if(hd->revision.id) {
1243     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_rev));
1244     str_printf(&s, 0, "0x%x", hd->revision.id);
1245     add_str_list(&entry->value, s);
1246   }
1247
1248   if(hd->compat_device.id || hd->compat_vendor.id) {
1249     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_compat_vend));
1250     add_str_list(&entry->value, vend_id2str(hd->compat_vendor.id));
1251     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_compat_dev));
1252     str_printf(&s, 0, "%04x", ID_VALUE(hd->compat_device.id));
1253     add_str_list(&entry->value, s);
1254   }
1255
1256   if(hd->device.name) {
1257     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_dev_name));
1258     add_str_list(&entry->value, hd->device.name);
1259   }
1260
1261   if(hd->vendor.name) {
1262     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_vend_name));
1263     add_str_list(&entry->value, hd->vendor.name);
1264   }
1265
1266   if(hd->sub_device.name) {
1267     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sub_dev_name));
1268     add_str_list(&entry->value, hd->sub_device.name);
1269   }
1270
1271   if(hd->sub_vendor.name) {
1272     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sub_vend_name));
1273     add_str_list(&entry->value, hd->sub_vendor.name);
1274   }
1275
1276   if(hd->revision.name) {
1277     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_rev_name));
1278     add_str_list(&entry->value, hd->revision.name);
1279   }
1280
1281   if(hd->serial) {
1282     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_serial));
1283     add_str_list(&entry->value, hd->serial);
1284   }
1285
1286   if(hd->unix_dev_name) {
1287     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_unix_dev_name));
1288     add_str_list(&entry->value, hd->unix_dev_name);
1289   }
1290
1291   if(hd->unix_dev_name2) {
1292     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_unix_dev_name2));
1293     add_str_list(&entry->value, hd->unix_dev_name2);
1294   }
1295
1296   if(hd->unix_dev_names) {
1297     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_unix_dev_names));
1298     s = free_mem(s);
1299     s = hd_join(" ", hd->unix_dev_names);
1300     add_str_list(&entry->value, s);
1301   }
1302
1303   if(hd->drivers) {
1304     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_drivers));
1305     s = free_mem(s);
1306     s = hd_join("|", hd->drivers);
1307     add_str_list(&entry->value, s);
1308   }
1309
1310   if(hd->sysfs_id) {
1311     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sysfs_id));
1312     add_str_list(&entry->value, hd->sysfs_id);
1313   }
1314
1315   if(hd->sysfs_bus_id) {
1316     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sysfs_busid));
1317     add_str_list(&entry->value, hd->sysfs_bus_id);
1318   }
1319
1320   if(hd->sysfs_device_link) {
1321     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_sysfs_link));
1322     add_str_list(&entry->value, hd->sysfs_device_link);
1323   }
1324
1325   if(hd->rom_id) {
1326     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_rom_id));
1327     add_str_list(&entry->value, hd->rom_id);
1328   }
1329
1330   if(hd->usb_guid) {
1331     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_usb_guid));
1332     add_str_list(&entry->value, hd->usb_guid);
1333   }
1334
1335   if(hd->hotplug) {
1336     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_hotplug));
1337     str_printf(&s, 0, "%d", hd->hotplug);
1338     add_str_list(&entry->value, s);
1339   }
1340
1341   s = free_mem(s);
1342   for(u = 0; u < sizeof hd->hw_class_list / sizeof *hd->hw_class_list; u++) {
1343     str_printf(&s, -1, "%02x", hd->hw_class_list[u]);
1344   }
1345   add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_class_list));
1346   add_str_list(&entry->value, s);
1347
1348   u = 0;
1349   if(hd->is.agp)          u |= 1 << 0;
1350   if(hd->is.isapnp)       u |= 1 << 1;
1351   if(hd->is.softraiddisk) u |= 1 << 2;
1352   if(hd->is.zip)          u |= 1 << 3;
1353   if(hd->is.cdr)          u |= 1 << 4;
1354   if(hd->is.cdrw)         u |= 1 << 5;
1355   if(hd->is.dvd)          u |= 1 << 6;
1356   if(hd->is.dvdr)         u |= 1 << 7;
1357   if(hd->is.dvdram)       u |= 1 << 8;
1358   if(hd->is.pppoe)        u |= 1 << 9;
1359   if(hd->is.wlan)         u |= 1 << 10;
1360   
1361   if(u) {
1362     add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_features));
1363     str_printf(&s, 0, "0x%x", u);
1364     add_str_list(&entry->value, s);
1365   }
1366
1367   for(res = hd->res; res; res = res->next) {
1368     sl = NULL;
1369     switch(res->any.type) {
1370       case res_mem:
1371         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_mem));
1372         str_printf(&s, 0, "0x%"PRIx64"", res->mem.base);
1373         add_str_list(&sl, s);
1374         str_printf(&s, 0, "0x%"PRIx64"", res->mem.range);
1375         add_str_list(&sl, s);
1376         str_printf(&s, 0, "%u", res->mem.enabled);
1377         add_str_list(&sl, s);
1378         str_printf(&s, 0, "%u", res->mem.access);
1379         add_str_list(&sl, s);
1380         str_printf(&s, 0, "%u", res->mem.prefetch);
1381         add_str_list(&sl, s);
1382         break;
1383
1384       case res_phys_mem:
1385         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_phys_mem));
1386         str_printf(&s, 0, "0x%"PRIx64"", res->phys_mem.range);
1387         add_str_list(&sl, s);
1388         break;
1389
1390       case res_io:
1391         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_io));
1392         str_printf(&s, 0, "0x%"PRIx64"", res->io.base);
1393         add_str_list(&sl, s);
1394         str_printf(&s, 0, "0x%"PRIx64"", res->io.range);
1395         add_str_list(&sl, s);
1396         str_printf(&s, 0, "%u", res->io.enabled);
1397         add_str_list(&sl, s);
1398         str_printf(&s, 0, "%u", res->io.access);
1399         add_str_list(&sl, s);
1400         break;
1401
1402       case res_irq:
1403         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_irq));
1404         str_printf(&s, 0, "%u", res->irq.base);
1405         add_str_list(&sl, s);
1406         str_printf(&s, 0, "%u", res->irq.triggered);
1407         add_str_list(&sl, s);
1408         str_printf(&s, 0, "%u", res->irq.enabled);
1409         add_str_list(&sl, s);
1410         break;
1411
1412       case res_dma:
1413         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_dma));
1414         str_printf(&s, 0, "%u", res->dma.base);
1415         add_str_list(&sl, s);
1416         str_printf(&s, 0, "%u", res->dma.enabled);
1417         add_str_list(&sl, s);
1418         break;
1419
1420       case res_size:
1421         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_size));
1422         str_printf(&s, 0, "%u", res->size.unit);
1423         add_str_list(&sl, s);
1424         str_printf(&s, 0, "%"PRIu64, res->size.val1);
1425         add_str_list(&sl, s);
1426         str_printf(&s, 0, "%"PRIu64, res->size.val2);
1427         add_str_list(&sl, s);
1428         break;
1429
1430       case res_baud:
1431         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_baud));
1432         str_printf(&s, 0, "%u", res->baud.speed);
1433         add_str_list(&sl, s);
1434         str_printf(&s, 0, "%u", res->baud.bits);
1435         add_str_list(&sl, s);
1436         str_printf(&s, 0, "%u", res->baud.stopbits);
1437         add_str_list(&sl, s);
1438         str_printf(&s, 0, "0x%02x", (unsigned) res->baud.parity);
1439         add_str_list(&sl, s);
1440         str_printf(&s, 0, "0x%02x", (unsigned) res->baud.handshake);
1441         add_str_list(&sl, s);
1442         break;
1443
1444       case res_cache:
1445         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_cache));
1446         str_printf(&s, 0, "%u", res->cache.size);
1447         add_str_list(&sl, s);
1448         break;
1449
1450       case res_disk_geo:
1451         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_disk_geo));
1452         str_printf(&s, 0, "%u", res->disk_geo.cyls);
1453         add_str_list(&sl, s);
1454         str_printf(&s, 0, "%u", res->disk_geo.heads);
1455         add_str_list(&sl, s);
1456         str_printf(&s, 0, "%u", res->disk_geo.sectors);
1457         add_str_list(&sl, s);
1458         str_printf(&s, 0, "%u", res->disk_geo.logical);
1459         add_str_list(&sl, s);
1460         break;
1461
1462       case res_monitor:
1463         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_monitor));
1464         str_printf(&s, 0, "%u", res->monitor.width);
1465         add_str_list(&sl, s);
1466         str_printf(&s, 0, "%u", res->monitor.height);
1467         add_str_list(&sl, s);
1468         str_printf(&s, 0, "%u", res->monitor.vfreq);
1469         add_str_list(&sl, s);
1470         str_printf(&s, 0, "%u", res->monitor.interlaced);
1471         add_str_list(&sl, s);
1472         break;
1473
1474       case res_framebuffer:
1475         add_str_list(&entry->key, key2value(hw_ids_hd_items, hwdi_res_framebuffer));
1476         str_printf(&s, 0, "%u", res->framebuffer.width);
1477         add_str_list(&sl, s);
1478         str_printf(&s, 0, "%u", res->framebuffer.height);
1479         add_str_list(&sl, s);
1480         str_printf(&s, 0, "%u", res->framebuffer.bytes_p_line);
1481         add_str_list(&sl, s);
1482         str_printf(&s, 0, "%u", res->framebuffer.colorbits);
1483         add_str_list(&sl, s);
1484         str_printf(&s, 0, "%u", res->framebuffer.mode);
1485         add_str_list(&sl, s);
1486         break;
1487
1488       default:
1489         break;
1490     }
1491     /* keep entry->key & entry->value symmetrical! */
1492     if(sl) {
1493       t = hd_join(",", sl);
1494       add_str_list(&entry->value, t);
1495       free_mem(t);
1496       free_str_list(sl);
1497     }
1498   }
1499
1500   free_mem(s);
1501 }
1502
1503
1504 hd_t *hd_read_config(hd_data_t *hd_data, const char *id)
1505 {
1506   hd_t *hd = NULL;
1507   hd_manual_t *entry;
1508
1509   hddb_init(hd_data);
1510
1511   entry = hd_manual_read_entry(hd_data, id);
1512
1513   if(entry) {
1514     hd = new_mem(sizeof *hd);
1515     hd->module = hd_data->module;
1516     hd->line = __LINE__;
1517     hd->tag.freeit = 1;         /* make it a 'stand alone' entry */
1518     manual2hd(hd_data, entry, hd);
1519     hd_free_manual(entry);
1520   }
1521
1522   return hd;
1523 }
1524
1525
1526 int hd_write_config(hd_data_t *hd_data, hd_t *hd)
1527 {
1528   int err = 0;
1529   hd_manual_t *entry;
1530
1531   if(!hd_report_this(hd_data, hd)) return 0;
1532
1533   entry = new_mem(sizeof *entry);
1534
1535   hd2manual(hd, entry);
1536
1537   err = entry->unique_id ? hd_manual_write_entry(hd_data, entry) : 5;
1538
1539   hd_free_manual(entry);
1540
1541   return err;
1542 }
1543
1544
1545 #endif  /* LIBHD_TINY */
1546