This commit was manufactured by cvs2svn to create tag
[opensuse:hwinfo.git] / src / hd / block.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <ctype.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/ioctl.h>
11 #include <linux/iso_fs.h>
12
13 #include "hd.h"
14 #include "hd_int.h"
15 #include "hddb.h"
16 #include "block.h"
17
18 #ifndef SCSI_IOCTL_SEND_COMMAND
19 #define SCSI_IOCTL_SEND_COMMAND 1
20 #endif
21
22 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
23  * block device stuff
24  *
25  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26  */
27
28 static void get_block_devs(hd_data_t *hd_data);
29 static void add_partitions(hd_data_t *hd_data, hd_t *hd, char *path);
30 static void add_cdrom_info(hd_data_t *hd_data, hd_t *hd);
31 static void add_other_sysfs_info(hd_data_t *hd_data, hd_t *hd, struct sysfs_device *sf_dev);
32 static void add_ide_sysfs_info(hd_data_t *hd_data, hd_t *hd, struct sysfs_device *sf_dev);
33 static void add_scsi_sysfs_info(hd_data_t *hd_data, hd_t *hd, struct sysfs_device *sf_dev);
34 static void read_partitions(hd_data_t *hd_data);
35 static void read_cdroms(hd_data_t *hd_data);
36 static cdrom_info_t *new_cdrom_entry(cdrom_info_t **ci);
37 static cdrom_info_t *get_cdrom_entry(cdrom_info_t *ci, int n);
38 static void get_scsi_tape(hd_data_t *hd_data);
39 static void get_generic_scsi_devs(hd_data_t *hd_data);
40
41
42 void hd_scan_sysfs_block(hd_data_t *hd_data)
43 {
44   if(!hd_probe_feature(hd_data, pr_block)) return;
45
46   hd_data->module = mod_block;
47
48   /* some clean-up */
49   remove_hd_entries(hd_data);
50
51   hd_data->disks = free_str_list(hd_data->disks);
52   hd_data->partitions = free_str_list(hd_data->partitions);
53   hd_data->cdroms = free_str_list(hd_data->cdroms);
54
55   if(hd_probe_feature(hd_data, pr_block_mods)) {
56     PROGRESS(1, 0, "block modules");
57     load_module(hd_data, "ide_cd");
58     load_module(hd_data, "sr_mod");
59     load_module(hd_data, "sd_mod");
60     load_module(hd_data, "st");
61   }
62
63   PROGRESS(2, 0, "sysfs drivers");
64
65   hd_sysfs_driver_list(hd_data);
66
67   PROGRESS(3, 0, "cdrom");
68
69   read_cdroms(hd_data);
70
71   PROGRESS(4, 0, "partition");
72
73   read_partitions(hd_data);
74
75   PROGRESS(5, 0, "get sysfs block dev data");
76
77   get_block_devs(hd_data);
78
79   if(hd_data->cdrom) {
80     ADD2LOG("oops: cdrom list not empty\n");
81   }
82 }
83
84
85 void get_block_devs(hd_data_t *hd_data)
86 {
87   str_list_t *sl;
88   char *s, *t;
89   unsigned u1, u2, u3;
90   uint64_t ul0;
91   hd_t *hd, *hd1;
92   hd_dev_num_t dev_num;
93
94   struct sysfs_bus *sf_bus;
95   struct sysfs_class *sf_class;
96   struct sysfs_class_device *sf_cdev;
97   struct sysfs_device *sf_dev;
98   struct dlist *sf_cdev_list;
99   struct dlist *sf_ide_list = NULL;
100   struct sysfs_device *sf_ide;
101
102   sf_bus = sysfs_open_bus("ide");
103   if(sf_bus) {
104     sf_ide_list = sysfs_get_bus_devices(sf_bus);
105     if(sf_ide_list) dlist_for_each_data(sf_ide_list, sf_ide, struct sysfs_device) {
106       ADD2LOG(
107         "  ide: bus_id = %s path = %s\n",
108         sf_ide->bus_id,
109         hd_sysfs_id(sf_ide->path)
110       );
111     }
112   }
113
114   sf_class = sysfs_open_class("block");
115
116   if(!sf_class) {
117     ADD2LOG("sysfs: no such class: block\n");
118     return;
119   }
120
121   sf_cdev_list = sysfs_get_class_devices(sf_class);
122   if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
123     ADD2LOG(
124       "  block: name = %s, path = %s\n",
125       sf_cdev->name,
126       hd_sysfs_id(sf_cdev->path)
127     );
128
129     memset(&dev_num, 0, sizeof dev_num);
130
131     if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "dev")))) {
132       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
133         dev_num.type = 'b';
134         dev_num.major = u1;
135         dev_num.minor = u2;
136         dev_num.range = 1;
137       }
138       ADD2LOG("    dev = %u:%u\n", u1, u2);
139     }
140
141     if(hd_attr_uint(sysfs_get_classdev_attr(sf_cdev, "range"), &ul0, 0)) {
142       dev_num.range = ul0;
143       ADD2LOG("    range = %u\n", dev_num.range);
144     }
145
146     sf_dev = sysfs_get_classdev_device(sf_cdev);
147     if(sf_dev) {
148       ADD2LOG(
149         "    block device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
150         sf_dev->bus,
151         sf_dev->bus_id,
152         sf_dev->driver_name,
153         hd_sysfs_id(sf_dev->path)
154       );
155     }
156
157     hd = NULL;
158
159     /* check if disk is DASD and has already been found by s390.c */
160     if(sf_dev && sf_dev->driver_name && strstr(sf_dev->driver_name,"dasd"))
161     {
162       char bid[9];
163       hd_res_t* res;
164       //fprintf(stderr,"dn %s bi %s\n",sf_dev->driver_name,sf_dev->bus_id);
165       for(hd=hd_data->hd;hd;hd=hd->next)
166       {
167         //fprintf(stderr,"bcid %d\n",hd->base_class.id);
168         if(hd->base_class.id == bc_storage_device
169            && hd->detail
170            && hd->detail->ccw.type == hd_detail_ccw)
171         {
172           for(res=hd->res;res;res=res->next)
173           {
174             if(res->io.type==res_io)
175             {
176               sprintf(bid,"%01x.%01x.%04x",
177                       hd->detail->ccw.data->lcss >> 8,
178                       hd->detail->ccw.data->lcss & 0xff,
179                       (unsigned short)res->io.base);
180               //fprintf(stderr,"bid %s\n",bid);
181               if (strcmp(bid,sf_dev->bus_id)==0) goto out;
182             }
183           }
184         }
185       }
186       hd=NULL;
187       out:;
188     }
189     else if((sl = search_str_list(hd_data->disks, hd_sysfs_name2_dev(sf_cdev->name)))) {
190       hd = add_hd_entry(hd_data, __LINE__, 0);
191       hd->sub_class.id = sc_sdev_disk;
192     }
193     else if((sl = search_str_list(hd_data->cdroms, hd_sysfs_name2_dev(sf_cdev->name)))) {
194       hd = add_hd_entry(hd_data, __LINE__, 0);
195       hd->sub_class.id = sc_sdev_cdrom;
196     }
197     else if(sf_dev && sf_dev->bus && !strcmp(sf_dev->bus, "scsi")) {
198       hd = add_hd_entry(hd_data, __LINE__, 0);
199       hd->sub_class.id = sc_sdev_other;
200     }
201
202     if(hd) {
203       str_printf(&hd->unix_dev_name, 0, "/dev/%s", hd_sysfs_name2_dev(sf_cdev->name));
204
205       hd->base_class.id = bc_storage_device;
206
207       hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev->path));
208
209       if(sf_dev) hd->sysfs_device_link = new_str(hd_sysfs_id(sf_dev->path));
210
211       hd->unix_dev_num = dev_num;
212
213       hd->bus.id = bus_none;
214
215       if(sf_dev) {
216         if(sf_dev->bus) {
217           if(!strcmp(sf_dev->bus, "ide")) hd->bus.id = bus_ide;
218           else if(!strcmp(sf_dev->bus, "scsi")) hd->bus.id = bus_scsi;
219         }
220         hd->sysfs_bus_id = new_str(sf_dev->bus_id);
221       }
222
223       if(sf_dev && (s = hd_sysfs_id(sf_dev->path))) {
224
225         /* parent has longest matching sysfs id */
226         u2 = strlen(s);
227         for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
228           if(hd1->sysfs_id) {
229             u1 = strlen(hd1->sysfs_id);
230             if(u1 > u3 && u1 <= u2 && !strncmp(s, hd1->sysfs_id, u1)) {
231               u3 = u1;
232               hd->attached_to = hd1->idx;
233             }
234           }
235         }
236
237         /* find longest matching sysfs id we have a driver for */
238         s = new_str(s);
239         t = strrchr(s, '/');
240         if(t) *t = 0;
241         t = hd_sysfs_find_driver(hd_data, s, 0);
242         if(t) {
243           add_str_list(&hd->drivers, t);
244         }
245         s = free_mem(s);
246
247         /* look for ide-scsi handled devices */
248         if(hd->bus.id == bus_scsi) {
249           if(sf_ide_list) dlist_for_each_data(sf_ide_list, sf_ide, struct sysfs_device) {
250             if(
251               strcmp(sf_dev->path, sf_ide->path) &&
252               !strncmp(sf_dev->path, sf_ide->path, strlen(sf_ide->path)) &&
253               sscanf(sf_ide->bus_id, "%u.%u", &u1, &u2) == 2
254             ) {
255               str_printf(&hd->unix_dev_name2, 0, "/dev/hd%c", 'a' + (u1 << 1) + u2);
256             }
257           }
258         }
259       }
260
261       if(hd->bus.id == bus_ide) {
262         add_ide_sysfs_info(hd_data, hd, sf_dev);
263       }
264       else if(hd->bus.id == bus_scsi) {
265         add_scsi_sysfs_info(hd_data, hd, sf_dev);
266       }
267       else {
268         add_other_sysfs_info(hd_data, hd, sf_dev);
269       }
270
271       
272       if(
273         sf_dev &&
274         sf_dev->driver_name &&
275         *sf_dev->driver_name &&
276         strcmp(sf_dev->driver_name, "unknown")
277       ) {
278         add_str_list(&hd->drivers, sf_dev->driver_name);
279       }
280
281       if(hd->sub_class.id == sc_sdev_cdrom) {
282         add_cdrom_info(hd_data, hd);
283       }
284
285       if(
286         hd->sub_class.id == sc_sdev_disk &&
287         hd_probe_feature(hd_data, pr_block_part)
288       ) {
289         add_partitions(hd_data, hd, sf_cdev->path);
290       }
291
292     }
293
294   }
295
296   sysfs_close_class(sf_class);
297
298   sysfs_close_bus(sf_bus);
299 }
300
301
302 /*
303  * Find driver for sysfs_id.
304  *
305  * Return driver for id (exact = 1) or longest matching id (exact = 0).
306  */
307 char *hd_sysfs_find_driver(hd_data_t *hd_data, char *sysfs_id, int exact)
308 {
309   hd_sysfsdrv_t *sf;
310   char *t;
311   unsigned u1, u2, u3;
312
313   if(!sysfs_id || !*sysfs_id) return NULL;
314
315   t = NULL;
316
317   if(exact) {
318     for(sf = hd_data->sysfsdrv; sf; sf = sf->next) {
319       if(!strcmp(sysfs_id, sf->device)) {
320         t = sf->driver;
321         break;
322       }
323     }
324   }
325   else {
326     u2 = strlen(sysfs_id);
327     u3 = 0;
328     for(sf = hd_data->sysfsdrv; sf; sf = sf->next) {
329       u1 = strlen(sf->device);
330       if(u1 > u3 && u1 <= u2 && !strncmp(sysfs_id, sf->device, u1)) {
331         u3 = u1;
332         t = sf->driver;
333       }
334     }
335   }
336
337   return t;
338 }
339
340
341 void add_partitions(hd_data_t *hd_data, hd_t *hd, char *path)
342 {
343   hd_t *hd1;
344   str_list_t *sl;
345   char *s;
346   size_t len;
347
348   s = hd->unix_dev_name + sizeof "/dev/" - 1;
349   len = strlen(s);
350   for(sl = hd_data->partitions; sl; sl = sl->next) {
351     if(!strncmp(sl->str, s, len)) {
352       hd1 = add_hd_entry(hd_data, __LINE__, 0);
353       hd1->base_class.id = bc_partition;
354       str_printf(&hd1->unix_dev_name, 0, "/dev/%s", sl->str);
355       hd1->attached_to = hd->idx;
356
357       str_printf(&hd1->sysfs_id, 0, "%s/%s", hd->sysfs_id, hd_sysfs_dev2_name(sl->str));
358     }
359   }
360 }
361
362
363 void add_cdrom_info(hd_data_t *hd_data, hd_t *hd)
364 {
365   cdrom_info_t *ci, **prev;
366
367   hd->detail = free_hd_detail(hd->detail);
368   hd->detail = new_mem(sizeof *hd->detail);
369   hd->detail->type = hd_detail_cdrom;
370
371   for(ci = *(prev = &hd_data->cdrom); ci; ci = *(prev = &ci->next)) {
372     if(!strcmp(hd->unix_dev_name + sizeof "/dev/" - 1, ci->name)) {
373       hd->detail->cdrom.data = ci;
374       *prev = ci->next;
375       hd->detail->cdrom.data->next = NULL;
376       break;
377     }
378   }
379
380   if((ci = hd->detail->cdrom.data)) {
381     /* update prog_if: cdr, cdrw, ... */
382     if(
383       /* ###### FIXME: dosn't work anyway: ide-scsi doesn't support sysfs */
384       hd->bus.id == bus_scsi &&
385       !search_str_list(hd->drivers, "ide-scsi")         /* could be ide, though */
386     ) {
387       /* scsi devs lie */
388       if(ci->dvd && (ci->cdrw || ci->dvdr || ci->dvdram)) {
389         ci->dvd = ci->dvdr = ci->dvdram = 0;
390       }
391       ci->dvdr = ci->dvdram = 0;
392       ci->cdr = ci->cdrw = 0;
393       if(hd->prog_if.id == pif_cdr) ci->cdr = 1;
394     }
395
396     /* trust ide info */
397     if(ci->dvd) {
398       hd->is.dvd = 1;
399       hd->prog_if.id = pif_dvd;
400     }
401     if(ci->cdr) {
402       hd->is.cdr = 1;
403       hd->prog_if.id = pif_cdr;
404     }
405     if(ci->cdrw) {
406       hd->is.cdrw = 1;
407       hd->prog_if.id = pif_cdrw;
408     }
409     if(ci->dvdr) {
410       hd->is.dvdr = 1;
411       hd->prog_if.id = pif_dvdr;
412     }
413     if(ci->dvdram) {
414       hd->is.dvdram = 1;
415       hd->prog_if.id = pif_dvdram;
416     }
417   }
418
419   if(
420     hd_probe_feature(hd_data, pr_block_cdrom) &&
421     hd_report_this(hd_data, hd)
422   ) {
423     hd_read_cdrom_info(hd_data, hd);
424   }
425 }
426
427
428 void add_other_sysfs_info(hd_data_t *hd_data, hd_t *hd, struct sysfs_device *sf_dev)
429 {
430   hd_res_t *geo, *size;
431   int fd;
432   unsigned u0, u1;
433   char c, *pr_str;
434
435   if(hd->sysfs_id) {
436     if(
437       sscanf(hd->sysfs_id, "/block/cciss!c%ud%u", &u0, &u1) == 2
438     ) {
439       hd->slot = (u0 << 8) + u1;
440       str_printf(&hd->device.name, 0, "CCISS disk %u/%u", u0, u1);
441     }
442     else if(
443       sscanf(hd->sysfs_id, "/block/ida!c%ud%u", &u0, &u1) == 2
444     ) {
445       hd->slot = (u0 << 8) + u1;
446       str_printf(&hd->device.name, 0, "SMART Array %u/%u", u0, u1);
447     }
448     else if(
449       sscanf(hd->sysfs_id, "/block/rd!c%ud%u", &u0, &u1) == 2
450     ) {
451       hd->slot = (u0 << 8) + u1;
452       str_printf(&hd->device.name, 0, "DAC960 RAID Array %u/%u", u0, u1);
453     }
454     else if(
455       sscanf(hd->sysfs_id, "/block/i2o!hd%c", &c) == 1 &&
456       c >= 'a'
457     ) {
458       hd->slot = c - 'a';
459       str_printf(&hd->device.name, 0, "I2O disk %u", hd->slot);
460     }
461     else if(
462       sscanf(hd->sysfs_id, "/block/dasd%c", &c) == 1 &&
463       c >= 'a'
464     ) {
465       hd->slot = c - 'a';
466       hd->device.name = new_str("S390 Disk");
467     }
468   }
469
470
471   pr_str = NULL;
472
473   if(
474     hd->unix_dev_name &&
475     hd->sub_class.id == sc_sdev_disk
476   ) {
477     PROGRESS(5, 0, hd->unix_dev_name);
478     fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
479     if(fd >= 0) {
480
481       str_printf(&pr_str, 0, "%s geo", hd->unix_dev_name);
482       PROGRESS(5, 1, pr_str);
483
484       if(hd_getdisksize(hd_data, hd->unix_dev_name, fd, &geo, &size) == 1)
485         /* (low-level) unformatted disk */
486         hd->is.notready=1;
487         
488       if(geo) add_res_entry(&hd->res, geo);
489       if(size) add_res_entry(&hd->res, size);
490
491       close(fd);
492     }
493   }
494
495   pr_str = free_mem(pr_str);
496
497 }
498
499
500 void add_ide_sysfs_info(hd_data_t *hd_data, hd_t *hd, struct sysfs_device *sf_dev)
501 {
502   char *fname = NULL, buf[256], *dev_name, *s;
503   unsigned u0, u1, u2;
504   str_list_t *sl, *sl0;
505   hd_res_t *res;
506   FILE *f;
507
508   if(hd->sysfs_bus_id && sscanf(hd->sysfs_bus_id, "%u.%u", &u0, &u1) == 2) {
509     /* host.master/slave */
510     hd->slot = (u0 << 1) + u1;
511   }
512
513   if(
514     hd->unix_dev_name &&
515     strlen(hd->unix_dev_name) > 5
516   ) {
517     dev_name = hd->unix_dev_name + 5;
518
519     str_printf(&fname, 0, PROC_IDE "/%s/media", dev_name);
520     if((sl = read_file(fname, 0, 1))) {
521
522       if(strstr(sl->str, "floppy"))
523         hd->sub_class.id = sc_sdev_floppy;
524       else if(strstr(sl->str, "cdrom"))
525         hd->sub_class.id = sc_sdev_cdrom;
526       else if(strstr(sl->str, "tape"))
527         hd->sub_class.id = sc_sdev_tape;
528
529       free_str_list(sl);
530     }
531
532     str_printf(&fname, 0, PROC_IDE "/%s/model", dev_name);
533     if((sl = read_file(fname, 0, 1))) {
534       hd->vendor.name = canon_str(sl->str, strlen(sl->str));
535       if((s = strchr(hd->vendor.name, ' '))) {
536         hd->device.name = canon_str(s, strlen(s));
537         if(*hd->device.name) {
538           *s = 0;
539         }
540         else {
541           hd->device.name = free_mem(hd->device.name);
542         }
543       }
544       if(!hd->device.name) {
545         hd->device.name = hd->vendor.name;
546         hd->vendor.name = NULL;
547       }
548
549       free_str_list(sl);
550     }
551
552     str_printf(&fname, 0, PROC_IDE "/%s/driver", dev_name);
553     if((sl = read_file(fname, 0, 1))) {
554       if((s = strchr(sl->str, ' '))) *s = 0;
555       s = canon_str(sl->str, strlen(sl->str));
556       add_str_list(&hd->drivers, s);
557       s = free_mem(s);
558       free_str_list(sl);
559     }
560
561
562     str_printf(&fname, 0, PROC_IDE "/%s/geometry", dev_name);
563     if((sl0 = read_file(fname, 0, 2))) {
564       for(sl = sl0; sl; sl = sl->next) {
565         if(sscanf(sl->str, " physical %u / %u / %u", &u0, &u1, &u2) == 3) {
566           if(u0 || u1 || u2) {
567             res = add_res_entry(&hd->res, new_mem(sizeof *res));
568             res->disk_geo.type = res_disk_geo;
569             res->disk_geo.cyls = u0;
570             res->disk_geo.heads = u1;
571             res->disk_geo.sectors = u2;
572           }
573           continue;
574         }
575
576         if(sscanf(sl->str, " logical %u / %u / %u", &u0, &u1, &u2) == 3) {
577           res = add_res_entry(&hd->res, new_mem(sizeof *res));
578           res->disk_geo.type = res_disk_geo;
579           res->disk_geo.cyls = u0;
580           res->disk_geo.heads = u1;
581           res->disk_geo.sectors = u2;
582           res->disk_geo.logical = 1;
583         }
584       }
585       free_str_list(sl0);
586     }
587
588     str_printf(&fname, 0, PROC_IDE "/%s/capacity", dev_name);
589     if((sl = read_file(fname, 0, 1))) {
590       if(sscanf(sl->str, "%u", &u0) == 1 && u0 != 0x7fffffff) {
591         res = add_res_entry(&hd->res, new_mem(sizeof *res));
592         res->size.type = res_size;
593         res->size.unit = size_unit_sectors;
594         res->size.val1 = u0;
595         res->size.val2 = 512;           // ####### FIXME: sector size?
596       }
597       free_str_list(sl);
598     }
599
600     str_printf(&fname, 0, PROC_IDE "/%s/cache", dev_name);
601     if((sl = read_file(fname, 0, 1))) {
602       if(sscanf(sl->str, "%u", &u0) == 1 && u0) {
603         res = add_res_entry(&hd->res, new_mem(sizeof *res));
604         res->cache.type = res_cache;
605         res->cache.size = u0;
606       }
607       free_str_list(sl);
608     }
609
610     str_printf(&fname, 0, PROC_IDE "/%s/identify", dev_name);
611     if((f = fopen(fname, "r"))) {
612       u1 = 0;
613       memset(buf, sizeof buf, 0);
614       while(u1 < sizeof buf - 1 && fscanf(f, "%x", &u0) == 1) {
615         buf[u1++] = u0 >> 8; buf[u1++] = u0;
616       }
617       fclose(f);
618
619       /* ok, we now have the ATA/ATAPI ident block */
620
621       if(buf[0x14] || buf[0x15]) {      /* has serial id */
622         hd->serial = canon_str(buf + 0x14, 20);
623       }
624       if(buf[0x2e] || buf[0x2f]) {      /* has revision id */
625         hd->revision.name = canon_str(buf + 0x2e, 8);
626       }
627     }
628
629     free_mem(fname);
630   }
631 }
632
633
634 void add_scsi_sysfs_info(hd_data_t *hd_data, hd_t *hd, struct sysfs_device *sf_dev)
635 {
636   hd_t *hd1;
637   char *s, *t, *cs, *pr_str;
638   unsigned u0, u1, u2, u3;
639   int fd, k;
640   unsigned char scsi_cmd_buf[0x300];
641   unsigned char *uc;
642   scsi_t *scsi;
643   hd_res_t *geo, *size;
644   uint64_t ul0;
645
646   hd->detail = new_mem(sizeof *hd->detail);
647   hd->detail->type = hd_detail_scsi;
648   hd->detail->scsi.data = scsi = new_mem(sizeof *scsi);
649
650   if(hd->sysfs_bus_id && sscanf(hd->sysfs_bus_id, "%u:%u:%u:%u", &u0, &u1, &u2, &u3) == 4) {
651     /* host:channel:id:lun */
652     hd->slot = (u0 << 8) + (u1 << 4) + u2;
653     hd->func = u3;
654   }
655
656   if((s = hd_attr_str(sysfs_get_device_attr(sf_dev, "vendor")))) {
657     cs = canon_str(s, strlen(s));
658     ADD2LOG("    vendor = %s\n", cs);
659     if(*cs) {
660       hd->vendor.name = cs;
661     }
662     else {
663       free_mem(cs);
664     }
665   }
666
667   if((s = hd_attr_str(sysfs_get_device_attr(sf_dev, "model")))) {
668     cs = canon_str(s, strlen(s));
669     ADD2LOG("    model = %s\n", cs);
670     if(*cs) {
671       hd->device.name = cs;
672     }
673     else {
674       free_mem(cs);
675     }
676   }
677
678   if((s = hd_attr_str(sysfs_get_device_attr(sf_dev, "rev")))) {
679     cs = canon_str(s, strlen(s));
680     ADD2LOG("    rev = %s\n", cs);
681     if(*cs) {
682       hd->revision.name = cs;
683     }
684     else {
685       free_mem(cs);
686     }
687   }
688
689   if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "type"), &ul0, 0)) {
690     ADD2LOG("    type = %u\n", (unsigned) ul0);
691     if(ul0 == 6 /* scanner */) {
692       hd->sub_class.id = sc_sdev_scanner;
693     }
694     else if(ul0 == 3 /* processor */ && hd->vendor.name) {
695       if(
696         !strncmp(hd->vendor.name, "HP", sizeof "HP" - 1) ||
697         !strncmp(hd->vendor.name, "EPSON", sizeof "EPSON" - 1)
698       ) {
699         hd->sub_class.id = sc_sdev_scanner;
700       }
701     }
702
703     if(
704       hd->base_class.id == bc_storage_device &&
705       hd->sub_class.id == sc_sdev_other
706     ) {
707       switch(ul0) {
708         case 0:
709           hd->sub_class.id = sc_sdev_disk;
710           break;
711
712         case 5:
713           hd->sub_class.id = sc_sdev_cdrom;
714           break;
715       }
716     }
717
718   }
719
720   /* s390: wwpn & fcp lun */
721   if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "wwpn"), &ul0, 0)) {
722     ADD2LOG("    wwpn = 0x%016"PRIx64"\n", ul0);
723     scsi->wwpn = ul0;
724
725     /* it's a bit of a hack, actually */
726     t = new_str(hd_sysfs_id(sf_dev->path));
727     if(t) {
728       if((s = strrchr(t, '/'))) *s = 0;
729       if((s = strrchr(t, '/'))) *s = 0;
730       if((s = strrchr(t, '/'))) {
731         scsi->controller_id = new_str(s + 1);
732       }
733     }
734     t = free_mem(t);
735   }
736
737   if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "fcp_lun"), &ul0, 0)) {
738     ADD2LOG("    fcp_lun = 0x%016"PRIx64"\n", ul0);
739     scsi->fcp_lun = ul0;
740   }
741
742   /* ppc: get rom id */
743   if((hd1 = hd_get_device_by_idx(hd_data, hd->attached_to)) && hd1->rom_id) {
744     str_printf(&hd->rom_id, 0, "%s/@%u", hd1->rom_id, (hd->slot & 0xf));
745   }
746
747   pr_str = NULL;
748
749   if(
750     hd_report_this(hd_data, hd) &&
751     hd->unix_dev_name &&
752     hd->sub_class.id == sc_sdev_cdrom &&
753     hd_data->in_vmware != 1             /* VMWare doesn't like it */
754   ) {
755     PROGRESS(5, 0, hd->unix_dev_name);
756     fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
757     if(fd >= 0) {
758
759       str_printf(&pr_str, 0, "%s cache", hd->unix_dev_name);
760       PROGRESS(5, 1, pr_str);
761
762       memset(scsi_cmd_buf, 0, sizeof scsi_cmd_buf);
763       // ###### FIXME: smaller!
764       *((unsigned *) (scsi_cmd_buf + 4)) = sizeof scsi_cmd_buf - 0x100;
765       scsi_cmd_buf[8 + 0] = 0x1a;
766       scsi_cmd_buf[8 + 2] = 0x08;
767       scsi_cmd_buf[8 + 4] = 0xff;
768
769       k = ioctl(fd, SCSI_IOCTL_SEND_COMMAND, scsi_cmd_buf);
770
771       if(k) {
772         ADD2LOG("%s status(0x1a:8) 0x%x\n", hd->unix_dev_name, k);
773       }
774       else {
775         uc = scsi_cmd_buf + 8 + 4 + scsi_cmd_buf[8 + 3] + 2;
776         scsi->cache = uc[0];
777         ADD2LOG("  scsi cache: 0x%02x\n", uc[0]);
778     
779         if((scsi->cache & 4)) {
780           hd->prog_if.id = pif_cdr;
781         }
782       }
783
784       close(fd);
785     }
786   }
787
788
789   if(
790     hd_report_this(hd_data, hd) &&
791     hd->unix_dev_name &&
792     hd->sub_class.id == sc_sdev_disk
793   ) {
794     PROGRESS(5, 0, hd->unix_dev_name);
795     fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
796     if(fd >= 0) {
797
798       str_printf(&pr_str, 0, "%s geo", hd->unix_dev_name);
799       PROGRESS(5, 1, pr_str);
800
801       if(hd_getdisksize(hd_data, hd->unix_dev_name, fd, &geo, &size) == 1)
802         /* (low-level) unformatted disk */
803         hd->is.notready=1;
804       
805       if(geo) add_res_entry(&hd->res, geo);
806       if(size) add_res_entry(&hd->res, size);
807
808       str_printf(&pr_str, 0, "%s serial", hd->unix_dev_name);
809       PROGRESS(5, 2, pr_str);
810
811       memset(scsi_cmd_buf, 0, sizeof scsi_cmd_buf);
812       // ###### FIXME: smaller!
813       *((unsigned *) (scsi_cmd_buf + 4)) = sizeof scsi_cmd_buf - 0x100;
814       scsi_cmd_buf[8 + 0] = 0x12;
815       scsi_cmd_buf[8 + 1] = 0x01;
816       scsi_cmd_buf[8 + 2] = 0x80;
817       scsi_cmd_buf[8 + 4] = 0xff;
818
819       k = ioctl(fd, SCSI_IOCTL_SEND_COMMAND, scsi_cmd_buf);
820
821       if(k) {
822         ADD2LOG("%s status(0x12) 0x%x\n", scsi->dev_name, k);
823       }
824       else {
825         if((hd->serial = canon_str(scsi_cmd_buf + 8 + 4, scsi_cmd_buf[8 + 3]))) {
826           if(!*hd->serial) hd->serial = free_mem(hd->serial);
827         }
828       }
829
830       close(fd);
831     }
832   }
833
834   pr_str = free_mem(pr_str);
835
836
837   if(
838     hd->base_class.id == bc_storage_device &&
839     hd->sub_class.id == sc_sdev_scanner
840   ) {
841     hd->base_class.id = bc_scanner;
842   }
843
844   // ###### FIXME: usb-storage: disk vs. floppy?
845  
846 }
847
848
849 void read_partitions(hd_data_t *hd_data)
850 {
851   str_list_t *sl, *sl0, *pl0 = NULL;
852   char buf[256], *s1, *name, *base;
853   char *last_base = new_str(" ");
854   char *last_name = new_str(" ");
855   int l, is_disk;
856
857   if(!(sl0 = read_file(PROC_PARTITIONS, 2, 0))) return;
858
859   if(hd_data->debug) {
860     ADD2LOG("----- "PROC_PARTITIONS" -----\n");
861     for(sl = sl0; sl; sl = sl->next) {
862       ADD2LOG("  %s", sl->str);
863     }
864     ADD2LOG("----- "PROC_PARTITIONS" end -----\n");
865   }
866
867   for(sl = sl0; sl; sl = sl->next) {
868     *buf = 0;
869     if(sscanf(sl->str, "%*s %*s %*s %255s", buf) > 0) {
870       if(*buf) add_str_list(&pl0, buf);
871     }
872   }
873
874   free_str_list(sl0);
875
876   for(is_disk = 1, sl = pl0; sl; sl = sl->next) {
877     base = sl->str;
878     l = strlen(base);
879     if(!l) continue;
880
881     s1 = base + l - 1;
882     while(isdigit(*s1) && s1 > base) s1--;
883     if(s1 == base) continue;
884
885     name = new_str(base);
886     s1[1] = 0;
887
888     if(!strcmp(last_base, base)) {
889       if(!strcmp(last_name, base)) is_disk = 0;
890     }
891     else {
892       is_disk = strncmp(last_name, base, strlen(last_name)) ? 1 : 0;
893     }
894
895     if(!search_str_list(hd_data->cdroms, name)) {
896       if(
897         strncmp(name, "loop", sizeof "loop" - 1) &&
898         (
899           hd_data->flags.list_md ||
900           (
901             strncmp(name, "md", sizeof "md" - 1) &&
902             strncmp(name, "dm-", sizeof "dm-" - 1)
903           )
904         )
905       ) {
906         add_str_list(is_disk ? &hd_data->disks : &hd_data->partitions, name);
907       }
908     }
909     free_mem(last_base);
910     free_mem(last_name);
911
912     last_base = new_str(base);
913     last_name = name; name = NULL;
914   }
915
916   free_mem(last_base);
917   free_mem(last_name);
918
919   free_str_list(pl0);
920
921   if(hd_data->debug) {
922     ADD2LOG("disks:\n");
923     for(sl = hd_data->disks; sl; sl = sl->next) ADD2LOG("  %s\n", sl->str);
924     ADD2LOG("partitions:\n");
925     for(sl = hd_data->partitions; sl; sl = sl->next) ADD2LOG("  %s\n", sl->str);
926   }
927 }
928
929
930 /*
931  * Read iso9660/el torito info, if there is a CD inserted.
932  * Returns NULL if nothing was found
933  */
934 cdrom_info_t *hd_read_cdrom_info(hd_data_t *hd_data, hd_t *hd)
935 {
936   int fd;
937   char *s;
938   cdrom_info_t *ci;
939   struct iso_primary_descriptor iso_desc;
940   unsigned char sector[0x800];
941   unsigned et;
942   unsigned u0, u1, u2;
943
944 #ifdef LIBHD_MEMCHECK
945   {
946     if(libhd_log)
947       fprintf(libhd_log, "; %s\t%p\t%p\n", __FUNCTION__, CALLED_FROM(hd_read_cdrom_info, hd_data), hd_data);
948   }
949 #endif
950
951   /* free existing entry */
952   if(hd->detail && hd->detail->type != hd_detail_cdrom) {
953     hd->detail = free_hd_detail(hd->detail);
954   }
955
956   if(!hd->detail) {
957     hd->detail = new_mem(sizeof *hd->detail);
958     hd->detail->type = hd_detail_cdrom;
959     hd->detail->cdrom.data = new_mem(sizeof *hd->detail->cdrom.data);
960   }
961
962   ci = hd->detail->cdrom.data;
963
964   hd->is.notready = 0;
965
966   if((fd = open(hd->unix_dev_name, O_RDONLY)) < 0) {
967     /* we are here if there is no CD in the drive */
968     hd->is.notready = 1;
969     return NULL;
970   }
971
972   ci->iso9660.ok = 0;
973   if(
974     lseek(fd, 0x8000, SEEK_SET) >= 0 &&
975     read(fd, &iso_desc, sizeof iso_desc) == sizeof iso_desc
976   ) {
977     ci->cdrom = 1;
978     if(!memcmp(iso_desc.id, "CD001", 5)) {
979       ci->iso9660.ok = 1;
980       /* now, fill in the fields */
981       s = canon_str(iso_desc.volume_id, sizeof iso_desc.volume_id);
982       if(!*s) s = free_mem(s);
983       ci->iso9660.volume = s;
984
985       s = canon_str(iso_desc.publisher_id, sizeof iso_desc.publisher_id);
986       if(!*s) s = free_mem(s);
987       ci->iso9660.publisher = s;
988
989       s = canon_str(iso_desc.preparer_id, sizeof iso_desc.preparer_id);
990       if(!*s) s = free_mem(s);
991       ci->iso9660.preparer = s;
992
993       s = canon_str(iso_desc.application_id, sizeof iso_desc.application_id);
994       if(!*s) s = free_mem(s);
995       ci->iso9660.application = s;
996
997       s = canon_str(iso_desc.creation_date, sizeof iso_desc.creation_date);
998       if(!*s) s = free_mem(s);
999       ci->iso9660.creation_date = s;
1000     }
1001   }
1002
1003   if(
1004     ci->iso9660.ok &&
1005     lseek(fd, 0x8800, SEEK_SET) >= 0 &&
1006     read(fd, &sector, sizeof sector) == sizeof sector
1007   ) {
1008     if(
1009       sector[0] == 0 && sector[6] == 1 &&
1010       !memcmp(sector + 1, "CD001", 5) &&
1011       !memcmp(sector + 7, "EL TORITO SPECIFICATION", 23)
1012     ) {
1013       et = sector[0x47] + (sector[0x48] << 8) + (sector[0x49] << 16) + (sector[0x4a] << 24);
1014       ADD2LOG("  %s: el torito boot catalog at 0x%04x\n", ci->name, et);
1015       if(
1016         lseek(fd, et * 0x800, SEEK_SET) >= 0 &&
1017         read(fd, &sector, sizeof sector) == sizeof sector &&
1018         sector[0] == 1
1019       ) {
1020         ci->el_torito.ok = 1;
1021         ci->el_torito.catalog = et;
1022         ci->el_torito.platform = sector[1];
1023         s = canon_str(sector + 4, 24);
1024         if(!*s) s = free_mem(s);
1025         ci->el_torito.id_string = s;
1026         ci->el_torito.bootable = sector[0x20] == 0x88 ? 1 : 0;
1027         ci->el_torito.media_type = sector[0x21];
1028         ADD2LOG("    media type: %u\n",  ci->el_torito.media_type);
1029         ci->el_torito.load_address = (sector[0x22] + (sector[0x23] << 8)) << 4;
1030         ADD2LOG("    load address: 0x%04x\n",  ci->el_torito.load_address);
1031 #if 0
1032         if(ci->el_torito.platform == 0 && ci->el_torito.load_address == 0)
1033           ci->el_torito.load_address = 0x7c00;
1034 #endif
1035         ci->el_torito.load_count = sector[0x26] + (sector[0x27] << 8);
1036         ci->el_torito.start = sector[0x28] + (sector[0x29] << 8) + (sector[0x2a] << 16) + (sector[0x2b] << 24);
1037         if(ci->el_torito.media_type >= 1 && ci->el_torito.media_type <= 3) {
1038           ci->el_torito.geo.c = 80;
1039           ci->el_torito.geo.h = 2;
1040         }
1041         switch(ci->el_torito.media_type) {
1042           case 1:
1043             ci->el_torito.geo.s = 15;
1044             break;
1045           case 2:
1046             ci->el_torito.geo.s = 18;
1047             break;
1048           case 3:
1049             ci->el_torito.geo.s = 36;
1050             break;
1051         }
1052         if(
1053           lseek(fd, ci->el_torito.start * 0x800, SEEK_SET) >= 0 &&
1054           read(fd, &sector, sizeof sector) == sizeof sector
1055         ) {
1056           if(ci->el_torito.media_type == 4) {
1057             /* ##### we should go on and read the 1st partition sector in this case... */
1058             ci->el_torito.geo.h = (unsigned) sector[0x1be + 5] + 1;
1059             ci->el_torito.geo.s = sector[0x1be + 6] & 0x3f;
1060             ci->el_torito.geo.c = sector[0x1be + 7] + (((unsigned) sector[0x1be + 6] >> 6) << 8);
1061           }
1062           if(
1063             sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa &&
1064             sector[0x0b] == 0 && sector[0x0c] == 2 &&
1065             sector[0x0e] == 1 && sector[0x0f] == 0
1066           ) {
1067             u0 = sector[0x13] + (sector[0x14] << 8);    /* partition size */
1068             u1 = sector[0x18] + (sector[0x19] << 8);    /* sectors per track */
1069             u2 = sector[0x1a] + (sector[0x1b] << 8);    /* heads */
1070             u0 = u0 ? u0 : sector[0x20] + (sector[0x21] << 8) + (sector[0x22] << 16) + ((unsigned) sector[0x23] << 24);
1071             if(sector[0x26] == 0x29) {
1072               s = canon_str(sector + 0x2b, 11);
1073               if(!*s) s = free_mem(s);
1074               ci->el_torito.label = s;
1075             }
1076             if(!ci->el_torito.label) {
1077               s = canon_str(sector + 3, 8);
1078               if(!*s) s = free_mem(s);
1079               ci->el_torito.label = s;
1080             }
1081             if(
1082               (ci->el_torito.media_type == 0 || ci->el_torito.media_type > 3) &&
1083               u0 && u1 && u2
1084             ) {
1085               ci->el_torito.geo.h = u2;
1086               ci->el_torito.geo.s = u1;
1087               ci->el_torito.geo.size = u0;
1088               ci->el_torito.geo.c = ci->el_torito.geo.size / (u1 * u2);
1089             }
1090           }
1091         }
1092
1093         ci->el_torito.geo.size = ci->el_torito.geo.s * ci->el_torito.geo.c * ci->el_torito.geo.h;
1094       }
1095     }
1096   }
1097
1098   close(fd);
1099
1100   return ci;
1101 }
1102
1103
1104 /*
1105  * Read the list of CDROM devices known to the kernel. The info is taken
1106  * from /proc/sys/dev/cdrom/info.
1107  */
1108 void read_cdroms(hd_data_t *hd_data)
1109 {
1110   char *s, *t, *v;
1111   str_list_t *sl, *sl0;
1112   cdrom_info_t *ci;
1113   int i, line, entries = 0;
1114   unsigned val;
1115
1116   if(!(sl0 = read_file(PROC_CDROM_INFO, 2, 0))) return;
1117
1118   if((hd_data->debug & HD_DEB_CDROM)) {
1119     ADD2LOG("----- "PROC_CDROM_INFO" -----\n");
1120     for(sl = sl0; sl; sl = sl->next) {
1121       if(*sl->str != '\n') ADD2LOG("%s", sl->str);
1122     }
1123     ADD2LOG("----- "PROC_CDROM_INFO" end -----\n");
1124   }
1125
1126   for(sl = sl0; sl; sl = sl->next) {
1127     if(
1128       (line = 0, strstr(sl->str, "drive name:") == sl->str) ||
1129       (line++, strstr(sl->str, "drive speed:") == sl->str) ||
1130       (line++, strstr(sl->str, "Can write CD-R:") == sl->str) ||
1131       (line++, strstr(sl->str, "Can write CD-RW:") == sl->str) ||
1132       (line++, strstr(sl->str, "Can read DVD:") == sl->str) ||
1133       (line++, strstr(sl->str, "Can write DVD-R:") == sl->str) ||
1134       (line++, strstr(sl->str, "Can write DVD-RAM:") == sl->str)
1135     ) {
1136       s = strchr(sl->str, ':') + 1;
1137       i = 0;
1138       while((t = strsep(&s, " \t\n"))) {
1139         if(!*t) continue;
1140         i++;
1141         switch(line) {
1142           case 0:       /* drive name */
1143             ci = new_cdrom_entry(&hd_data->cdrom);
1144             entries++;
1145             add_str_list(&hd_data->cdroms, t);
1146             ci->name = new_str(t);
1147             break;
1148
1149           case 1:       /* drive speed */
1150           case 2:       /* Can write CD-R */
1151           case 3:       /* Can write CD-RW */
1152           case 4:       /* Can read DVD */
1153           case 5:       /* Can write DVD-R */
1154           case 6:       /* Can write DVD-RAM */
1155             ci = get_cdrom_entry(hd_data->cdrom, entries - i);
1156             if(ci) {
1157               val = strtoul(t, &v, 10);
1158               if(!*v) {
1159                 switch(line) {
1160                   case 1:
1161                     ci->speed = val;
1162                     break;
1163                   case 2:
1164                     ci->cdr = val;
1165                     break;
1166                   case 3:
1167                     ci->cdrw = val;
1168                     break;
1169                   case 4:
1170                     ci->dvd = val;
1171                     break;
1172                   case 5:
1173                     ci->dvdr = val;
1174                     break;
1175                   case 6:
1176                     ci->dvdram = val;
1177                     break;
1178                 }
1179               }
1180             }
1181             break;
1182         }
1183       }
1184     }  
1185   }
1186
1187   free_str_list(sl0);
1188 }
1189
1190
1191 /* add new entries at the _start_ of the list */
1192 cdrom_info_t *new_cdrom_entry(cdrom_info_t **ci)
1193 {
1194   cdrom_info_t *new_ci = new_mem(sizeof *new_ci);
1195
1196   new_ci->next = *ci;
1197   return *ci = new_ci;
1198 }
1199
1200
1201 /* return nth entry */
1202 cdrom_info_t *get_cdrom_entry(cdrom_info_t *ci, int n)
1203 {
1204   if(n < 0) return NULL;
1205
1206   while(n--) {
1207     if(!ci) return NULL;
1208     ci = ci->next;
1209   }
1210
1211   return ci;
1212 }
1213
1214
1215 /*
1216  * Add generic scsi devs.
1217  */
1218 void hd_scan_sysfs_scsi(hd_data_t *hd_data)
1219 {
1220   if(!hd_probe_feature(hd_data, pr_scsi)) return;
1221
1222   hd_data->module = mod_scsi;
1223
1224   /* some clean-up */
1225   remove_hd_entries(hd_data);
1226
1227   PROGRESS(1, 0, "scsi tape");
1228
1229   get_scsi_tape(hd_data);
1230
1231   PROGRESS(2, 0, "scsi generic");
1232
1233   get_generic_scsi_devs(hd_data);
1234 }
1235
1236
1237 void get_scsi_tape(hd_data_t *hd_data)
1238 {
1239   char *s, *t;
1240   unsigned u1, u2, u3;
1241   uint64_t ul0;
1242   hd_t *hd, *hd1;
1243   hd_dev_num_t dev_num;
1244
1245   struct sysfs_class *sf_class;
1246   struct sysfs_class_device *sf_cdev;
1247   struct sysfs_device *sf_dev;
1248   struct dlist *sf_cdev_list;
1249
1250   sf_class = sysfs_open_class("scsi_tape");
1251
1252   if(!sf_class) {
1253     ADD2LOG("sysfs: no such class: scsi_tape\n");
1254     return;
1255   }
1256
1257   sf_cdev_list = sysfs_get_class_devices(sf_class);
1258   if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
1259     ADD2LOG(
1260       "  scsi tape: name = %s, path = %s\n",
1261       sf_cdev->name,
1262       hd_sysfs_id(sf_cdev->path)
1263     );
1264
1265     memset(&dev_num, 0, sizeof dev_num);
1266
1267     if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "dev")))) {
1268       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
1269         dev_num.type = 'c';
1270         dev_num.major = u1;
1271         dev_num.minor = u2;
1272         dev_num.range = 1;
1273       }
1274       ADD2LOG("    dev = %u:%u\n", u1, u2);
1275     }
1276
1277     if(hd_attr_uint(sysfs_get_classdev_attr(sf_cdev, "range"), &ul0, 0)) {
1278       dev_num.range = ul0;
1279       ADD2LOG("    range = %u\n", dev_num.range);
1280     }
1281
1282     sf_dev = sysfs_get_classdev_device(sf_cdev);
1283     if(sf_dev) {
1284       s = hd_sysfs_id(sf_dev->path);
1285       ADD2LOG(
1286         "    scsi device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
1287         sf_dev->bus,
1288         sf_dev->bus_id,
1289         sf_dev->driver_name,
1290         s
1291       );
1292
1293       for(hd = hd_data->hd; hd; hd = hd->next) {
1294         if(
1295           hd->module == hd_data->module &&
1296           hd->sysfs_device_link &&
1297           hd->base_class.id == bc_storage_device &&
1298           hd->sub_class.id == sc_sdev_tape &&
1299           s &&
1300           !strcmp(hd->sysfs_device_link, s)
1301         ) break;
1302       }
1303
1304       if(!hd) {
1305         hd = add_hd_entry(hd_data, __LINE__, 0);
1306         hd->base_class.id = bc_storage_device;
1307         hd->sub_class.id = sc_sdev_tape;
1308
1309         hd->bus.id = bus_scsi;
1310
1311         hd->sysfs_device_link = new_str(s);
1312
1313         hd->sysfs_bus_id = new_str(sf_dev->bus_id);
1314
1315         /* parent has longest matching sysfs id */
1316         u2 = strlen(s);
1317         for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
1318           if(hd1->sysfs_id) {
1319             u1 = strlen(hd1->sysfs_id);
1320             if(u1 > u3 && u1 <= u2 && !strncmp(s, hd1->sysfs_id, u1)) {
1321               u3 = u1;
1322               hd->attached_to = hd1->idx;
1323             }
1324           }
1325         }
1326
1327         /* find longest matching sysfs id we have a driver for */
1328         s = new_str(s);
1329         t = strrchr(s, '/');
1330         if(t) *t = 0;
1331         t = hd_sysfs_find_driver(hd_data, s, 0);
1332         if(t) {
1333           add_str_list(&hd->drivers, t);
1334         }
1335         s = free_mem(s);
1336
1337         if(
1338           sf_dev->driver_name &&
1339           *sf_dev->driver_name &&
1340           strcmp(sf_dev->driver_name, "unknown")
1341         ) {
1342           add_str_list(&hd->drivers, sf_dev->driver_name);
1343         }
1344
1345         add_scsi_sysfs_info(hd_data, hd, sf_dev);
1346       }
1347
1348       s = hd_sysfs_name2_dev(sf_cdev->name);
1349
1350       if(!hd->unix_dev_name || strlen(s) + sizeof "/dev/" - 1 < strlen(hd->unix_dev_name)) {
1351         str_printf(&hd->unix_dev_name, 0, "/dev/%s", s);
1352         hd->unix_dev_num = dev_num;
1353         free_mem(hd->sysfs_id);
1354         hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev->path));
1355       }
1356     }
1357   }
1358
1359   sysfs_close_class(sf_class);
1360 }
1361
1362
1363 void get_generic_scsi_devs(hd_data_t *hd_data)
1364 {
1365   char *s, *t;
1366   unsigned u1, u2, u3;
1367   uint64_t ul0;
1368   hd_t *hd, *hd1;
1369   hd_dev_num_t dev_num;
1370
1371   struct sysfs_class *sf_class;
1372   struct sysfs_class_device *sf_cdev;
1373   struct sysfs_device *sf_dev;
1374   struct dlist *sf_cdev_list;
1375
1376   sf_class = sysfs_open_class("scsi_generic");
1377
1378   if(!sf_class) {
1379     ADD2LOG("sysfs: no such class: scsi_generic\n");
1380     return;
1381   }
1382
1383   sf_cdev_list = sysfs_get_class_devices(sf_class);
1384   if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
1385     ADD2LOG(
1386       "  scsi: name = %s, path = %s\n",
1387       sf_cdev->name,
1388       hd_sysfs_id(sf_cdev->path)
1389     );
1390
1391     memset(&dev_num, 0, sizeof dev_num);
1392
1393     if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "dev")))) {
1394       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
1395         dev_num.type = 'c';
1396         dev_num.major = u1;
1397         dev_num.minor = u2;
1398         dev_num.range = 1;
1399       }
1400       ADD2LOG("    dev = %u:%u\n", u1, u2);
1401     }
1402
1403     if(hd_attr_uint(sysfs_get_classdev_attr(sf_cdev, "range"), &ul0, 0)) {
1404       dev_num.range = ul0;
1405       ADD2LOG("    range = %u\n", dev_num.range);
1406     }
1407
1408     sf_dev = sysfs_get_classdev_device(sf_cdev);
1409     if(sf_dev) {
1410       s = hd_sysfs_id(sf_dev->path);
1411
1412       ADD2LOG(
1413         "    scsi device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
1414         sf_dev->bus,
1415         sf_dev->bus_id,
1416         sf_dev->driver_name,
1417         s
1418       );
1419     }
1420
1421     for(hd = hd_data->hd; hd; hd = hd->next) {
1422       if(
1423         hd->sysfs_device_link &&
1424         hd->bus.id == bus_scsi &&
1425         s &&
1426         !strcmp(hd->sysfs_device_link, s)
1427       ) break;
1428     }
1429
1430     if(hd) {
1431       if(!hd->unix_dev_name2) {
1432         str_printf(&hd->unix_dev_name2, 0, "/dev/%s", hd_sysfs_name2_dev(sf_cdev->name));
1433         hd->unix_dev_num2 = dev_num;
1434       }
1435     }
1436
1437     hd = NULL;
1438
1439     if(
1440       sf_dev &&
1441       sf_dev->driver_name &&
1442       !strcmp(sf_dev->driver_name, "unknown")
1443     ) {
1444       hd = add_hd_entry(hd_data, __LINE__, 0);
1445       hd->base_class.id = bc_storage_device;
1446       hd->sub_class.id = sc_sdev_other;
1447
1448       str_printf(&hd->unix_dev_name, 0, "/dev/%s", hd_sysfs_name2_dev(sf_cdev->name));
1449
1450       hd->bus.id = bus_scsi;
1451
1452       hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev->path));
1453
1454       hd->unix_dev_num = dev_num;
1455
1456       if(sf_dev) hd->sysfs_bus_id = new_str(sf_dev->bus_id);
1457
1458       if(sf_dev && (s = hd_sysfs_id(sf_dev->path))) {
1459
1460         /* parent has longest matching sysfs id */
1461         u2 = strlen(s);
1462         for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
1463           if(hd1->sysfs_id) {
1464             u1 = strlen(hd1->sysfs_id);
1465             if(u1 > u3 && u1 <= u2 && !strncmp(s, hd1->sysfs_id, u1)) {
1466               u3 = u1;
1467               hd->attached_to = hd1->idx;
1468             }
1469           }
1470         }
1471
1472         /* find longest matching sysfs id we have a driver for */
1473         s = new_str(s);
1474         t = strrchr(s, '/');
1475         if(t) *t = 0;
1476         t = hd_sysfs_find_driver(hd_data, s, 0);
1477         if(t) {
1478           add_str_list(&hd->drivers, t);
1479         }
1480         s = free_mem(s);
1481
1482       }
1483
1484       add_scsi_sysfs_info(hd_data, hd, sf_dev);
1485
1486     }
1487
1488   }
1489
1490   sysfs_close_class(sf_class);
1491 }
1492
1493