- there are usb tapes (#222978)
[opensuse:hwinfo.git] / src / hd / usb.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <sys/ioctl.h>
10
11 #include "hd.h"
12 #include "hd_int.h"
13 #include "hddb.h"
14 #include "usb.h"
15
16 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
17  * usb
18  *
19  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
20  */
21
22 #define IOCNR_GET_DEVICE_ID             1
23 #define IOCNR_GET_BUS_ADDRESS           5
24 #define IOCNR_GET_VID_PID               6
25
26 /* Get device_id string: */
27 #define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
28 /* Get two-int array: [0]=bus number, [1]=device address: */
29 #define LPIOC_GET_BUS_ADDRESS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_BUS_ADDRESS, len)
30 /* Get two-int array: [0]=vendor ID, [1]=product ID: */
31 #define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len)
32
33
34 static void get_usb_devs(hd_data_t *hd_data);
35 static void set_class_entries(hd_data_t *hd_data, hd_t *hd, usb_t *usb);
36 static void add_input_dev(hd_data_t *hd_data, char *name);
37 static void get_input_devs(hd_data_t *hd_data);
38 static void get_printer_devs(hd_data_t *hd_data);
39 static void read_usb_lp(hd_data_t *hd_data, hd_t *hd);
40 static void get_serial_devs(hd_data_t *hd_data);
41
42 void hd_scan_sysfs_usb(hd_data_t *hd_data)
43 {
44   if(!hd_probe_feature(hd_data, pr_usb)) return;
45
46   hd_data->module = mod_usb;
47
48   /* some clean-up */
49   remove_hd_entries(hd_data);
50   hd_data->proc_usb = free_str_list(hd_data->proc_usb);
51   hd_data->usb = NULL;
52
53   PROGRESS(1, 0, "sysfs drivers");
54   
55   hd_sysfs_driver_list(hd_data);
56
57   PROGRESS(2, 0, "usb");
58
59   get_usb_devs(hd_data);
60
61   PROGRESS(3, 1, "joydev mod");
62   load_module(hd_data, "joydev");
63     
64   PROGRESS(3, 2, "evdev mod");
65   load_module(hd_data, "evdev");
66
67   PROGRESS(3, 3, "input");
68   get_input_devs(hd_data);
69
70   PROGRESS(3, 4, "lp");
71   get_printer_devs(hd_data);
72
73   PROGRESS(3, 5, "serial");
74   get_serial_devs(hd_data);
75
76 }
77
78
79 void get_usb_devs(hd_data_t *hd_data)
80 {
81   uint64_t ul0;
82   unsigned u1, u2, u3;
83   hd_t *hd, *hd1;
84   usb_t *usb;
85   str_list_t *sl, *usb_devs = NULL;
86   char *s, *s1, *t;
87   hd_res_t *res;
88   size_t l;
89
90   struct sysfs_bus *sf_bus;
91   struct dlist *sf_dev_list;
92   struct sysfs_device *sf_dev;
93   struct sysfs_device *sf_dev_2;
94
95   sf_bus = sysfs_open_bus("usb");
96
97   if(!sf_bus) {
98     ADD2LOG("sysfs: no such bus: usb\n");
99     return;
100   }
101
102   sf_dev_list = sysfs_get_bus_devices(sf_bus);
103
104   if(sf_dev_list) dlist_for_each_data(sf_dev_list, sf_dev, struct sysfs_device) {
105     if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "bNumInterfaces"), &ul0, 0)) {
106       add_str_list(&usb_devs, sf_dev->path);
107       ADD2LOG("  usb dev: %s\n", hd_sysfs_id(sf_dev->path));
108     }
109   }
110
111   if(sf_dev_list) dlist_for_each_data(sf_dev_list, sf_dev, struct sysfs_device) {
112     ADD2LOG(
113       "  usb device: name = %s, bus_id = %s, bus = %s\n    path = %s\n",
114       sf_dev->name,
115       sf_dev->bus_id,
116       sf_dev->bus,
117       hd_sysfs_id(sf_dev->path)
118     );
119
120     if(
121       hd_attr_uint(sysfs_get_device_attr(sf_dev, "bInterfaceNumber"), &ul0, 16)
122     ) {
123       hd = add_hd_entry(hd_data, __LINE__, 0);
124
125       hd->detail = new_mem(sizeof *hd->detail);
126       hd->detail->type = hd_detail_usb;
127       hd->detail->usb.data = usb = new_mem(sizeof *usb);
128
129       hd->sysfs_id = new_str(hd_sysfs_id(sf_dev->path));
130       hd->sysfs_bus_id = new_str(sf_dev->bus_id);
131
132       hd->bus.id = bus_usb;
133       hd->func = ul0;
134
135       usb->ifdescr = ul0;
136
137       if((s = hd_attr_str(sysfs_get_device_attr(sf_dev, "modalias")))) {
138         s = canon_str(s, strlen(s));
139         ADD2LOG("    modalias = \"%s\"\n", s);
140         if(s && *s) {
141           hd->modalias = s;
142           s = NULL;
143         }
144         s = free_mem(s);
145       }
146
147       ADD2LOG("    bInterfaceNumber = %u\n", hd->func);
148
149       if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "bInterfaceClass"), &ul0, 16)) {
150         usb->i_cls = ul0;
151         ADD2LOG("    bInterfaceClass = %u\n", usb->i_cls);
152       }
153
154       if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "bInterfaceSubClass"), &ul0, 16)) {
155         usb->i_sub = ul0;
156         ADD2LOG("    bInterfaceSubClass = %u\n", usb->i_sub);
157       }
158
159       if(hd_attr_uint(sysfs_get_device_attr(sf_dev, "bInterfaceProtocol"), &ul0, 16)) {
160         usb->i_prot = ul0;
161         ADD2LOG("    bInterfaceProtocol = %u\n", usb->i_prot);
162       }
163
164       /* device has longest matching sysfs id */
165       u2 = strlen(sf_dev->path);
166       s = NULL;
167       for(u3 = 0, sl = usb_devs; sl; sl = sl->next) {
168         u1 = strlen(sl->str);
169         if(u1 > u3 && u1 <= u2 && !strncmp(sf_dev->path, sl->str, u1)) {
170           u3 = u1;
171           s = sl->str;
172         }
173       }
174
175       if(s) {
176         ADD2LOG("    if: %s @ %s\n", hd->sysfs_bus_id, hd_sysfs_id(s));
177         sf_dev_2 = sysfs_open_device_path(s);
178         if(sf_dev_2) {
179
180           if(hd_attr_uint(sysfs_get_device_attr(sf_dev_2, "bDeviceClass"), &ul0, 16)) {
181             usb->d_cls = ul0;
182             ADD2LOG("    bDeviceClass = %u\n", usb->d_cls);
183           }
184
185           if(hd_attr_uint(sysfs_get_device_attr(sf_dev_2, "bDeviceSubClass"), &ul0, 16)) {
186             usb->d_sub = ul0;
187             ADD2LOG("    bDeviceSubClass = %u\n", usb->d_sub);
188           }
189
190           if(hd_attr_uint(sysfs_get_device_attr(sf_dev_2, "bDeviceProtocol"), &ul0, 16)) {
191             usb->d_prot = ul0;
192             ADD2LOG("    bDeviceProtocol = %u\n", usb->d_prot);
193           }
194
195           if(hd_attr_uint(sysfs_get_device_attr(sf_dev_2, "idVendor"), &ul0, 16)) {
196             usb->vendor = ul0;
197             ADD2LOG("    idVendor = 0x%04x\n", usb->vendor);
198           }
199
200           if(hd_attr_uint(sysfs_get_device_attr(sf_dev_2, "idProduct"), &ul0, 16)) {
201             usb->device = ul0;
202             ADD2LOG("    idProduct = 0x%04x\n", usb->device);
203           }
204
205           if((s = hd_attr_str(sysfs_get_device_attr(sf_dev_2, "manufacturer")))) {
206             usb->manufact = canon_str(s, strlen(s));
207             ADD2LOG("    manufacturer = \"%s\"\n", usb->manufact);
208           }
209
210           if((s = hd_attr_str(sysfs_get_device_attr(sf_dev_2, "product")))) {
211             usb->product = canon_str(s, strlen(s));
212             ADD2LOG("    product = \"%s\"\n", usb->product);
213           }
214
215           if((s = hd_attr_str(sysfs_get_device_attr(sf_dev_2, "serial")))) {
216             usb->serial = canon_str(s, strlen(s));
217             ADD2LOG("    serial = \"%s\"\n", usb->serial);
218           }
219
220           if(hd_attr_uint(sysfs_get_device_attr(sf_dev_2, "bcdDevice"), &ul0, 16)) {
221             usb->rev = ul0;
222             ADD2LOG("    bcdDevice = %04x\n", usb->rev);
223           }
224
225           if((s = hd_attr_str(sysfs_get_device_attr(sf_dev_2, "speed")))) {
226             s = canon_str(s, strlen(s));
227             if(!strcmp(s, "1.5")) usb->speed = 15*100000;
228             else if(!strcmp(s, "12")) usb->speed = 12*1000000;
229             else if(!strcmp(s, "480")) usb->speed = 480*1000000;
230             ADD2LOG("    speed = \"%s\"\n", s);
231             s = free_mem(s);
232           }
233
234           sysfs_close_device(sf_dev_2);
235         }
236       }
237
238       if(usb->vendor || usb->device) {
239         hd->vendor.id = MAKE_ID(TAG_USB, usb->vendor);
240         hd->device.id = MAKE_ID(TAG_USB, usb->device);
241       }
242
243       if(usb->manufact) hd->vendor.name = new_str(usb->manufact);
244       if(usb->product) hd->device.name = new_str(usb->product);
245       if(usb->serial) hd->serial = new_str(usb->serial);
246
247       if(usb->rev) str_printf(&hd->revision.name, 0, "%x.%02x", usb->rev >> 8, usb->rev & 0xff);
248
249       if(usb->speed) {
250         res = add_res_entry(&hd->res, new_mem(sizeof *res));
251         res->baud.type = res_baud;
252         res->baud.speed = usb->speed;
253       }
254
255       s = hd_sysfs_find_driver(hd_data, hd->sysfs_id, 1);
256       if(s) add_str_list(&hd->drivers, s);
257
258       set_class_entries(hd_data, hd, usb);
259
260       if(!hd_data->scanner_db) {
261         hd_data->scanner_db = hd_module_list(hd_data, 1);
262       }
263
264       if(
265         hd->drivers &&
266         search_str_list(hd_data->scanner_db, hd->drivers->str)
267       ) {
268         hd->base_class.id = bc_scanner;
269       }
270
271       // ###### FIXME
272       if(hd->base_class.id == bc_modem) {
273         hd->unix_dev_name = new_str("/dev/ttyACM0");
274       }
275
276     }
277   }
278
279   sysfs_close_bus(sf_bus);
280
281   /* connect usb devices to each other */
282   for(hd = hd_data->hd; hd; hd = hd->next) {
283     if(hd->module == hd_data->module && hd->sysfs_id) {
284
285       s = new_str(hd->sysfs_id);
286       t = strrchr(s, '/');
287       if(t) *t = 0;
288
289       /* parent has longest matching sysfs id */
290       u2 = strlen(s);
291       for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
292         if(hd1->sysfs_id) {
293           s1 = new_str(hd1->sysfs_id);
294
295           if(hd1->module == hd_data->module) {
296             t = strrchr(s1, ':');
297             if(t) *t = 0;
298             l = strlen(s1);
299             if(l > 2 && s1[l-2] == '-' && s1[l-1] == '0') {
300               /* root hub */
301               s1[l-2] = 0 ;
302             }
303           }
304
305           u1 = strlen(s1);
306           if(u1 > u3 && u1 <= u2 && !strncmp(s, s1, u1)) {
307             u3 = u1;
308             hd->attached_to = hd1->idx;
309           }
310
311           s1 = free_mem(s1);
312         }
313       }
314
315       s = free_mem(s);
316     }
317   }
318
319   /* remove some entries */
320   for(hd = hd_data->hd; hd; hd = hd->next) {
321     if(
322       hd->module == hd_data->module &&
323       hd->sysfs_id &&
324       !hd->tag.remove
325     ) {
326
327       s = new_str(hd->sysfs_id);
328       t = strrchr(s, ':');
329       if(t) *t = 0;
330
331       for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
332         if(
333           hd1 != hd &&
334           hd1->module == hd_data->module &&
335           hd1->sysfs_id &&
336           !hd1->tag.remove &&
337           hd1->base_class.id == hd->base_class.id
338         ) {
339
340           s1 = new_str(hd1->sysfs_id);
341           t = strrchr(s1, ':');
342           if(t) *t = 0;
343
344           /* same usb device */
345           if(!strcmp(s, s1)) {
346             hd1->tag.remove = 1;
347             ADD2LOG("removed: %s\n", hd1->sysfs_id);
348           }
349
350           s1 = free_mem(s1);
351         }
352       }
353
354       s = free_mem(s);
355     }
356   }
357
358   remove_tagged_hd_entries(hd_data);
359
360
361 }
362
363
364 void set_class_entries(hd_data_t *hd_data, hd_t *hd, usb_t *usb)
365 {
366   int cls, sub, prot;
367   unsigned u;
368
369   if(usb->d_cls) {
370     cls = usb->d_cls; sub = usb->d_sub; prot = usb->d_prot;
371   }
372   else {
373     cls = usb->i_cls; sub = usb->i_sub; prot = usb->i_prot;
374   }
375
376   switch(cls) {
377     case 2:
378       if(usb->i_sub == 6 && usb->i_prot == 0) {
379         hd->base_class.id = bc_network;
380         hd->sub_class.id = 0x91;
381       }
382       else if(usb->i_sub == 2 && usb->i_prot >= 1 && usb->i_prot <= 6) {
383         hd->base_class.id = bc_modem;
384       }
385       break;
386
387     case 3:
388       if(sub == 1 && prot == 1) {
389         hd->base_class.id = bc_keyboard;
390         hd->sub_class.id = sc_keyboard_kbd;
391         break;
392       }
393       if(sub == 1 && prot == 2) {
394         if(!(
395           (usb->vendor == 0x056a && usb->device == 0x0022)      /* Wacom Tablet */
396 //          || (usb->vendor == 0x08ca && usb->device == 0x0020) /* AIPTEK APT-6000U tablet */
397         )) {
398           hd->base_class.id = bc_mouse;
399           hd->sub_class.id = sc_mou_usb;
400           hd->compat_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
401           hd->compat_device.id = MAKE_ID(TAG_SPECIAL, 0x001);
402         }
403         break;
404       }
405       break;
406
407     case 6:
408       if(sub == 1 && prot == 1) { /* PTP camera */
409         hd->base_class.id = bc_camera;
410         hd->sub_class.id = sc_camera_digital;
411         break;
412       }
413       break;
414
415     case 7:
416       hd->base_class.id = bc_printer;
417       break;
418
419     case 8:
420       hd->base_class.id = bc_storage_device;
421       switch(sub) {
422         case 1:         /* flash devices & removable media */
423         case 5:
424         case 6:
425           hd->sub_class.id = sc_sdev_disk;
426           break;
427         case 2:
428           hd->sub_class.id = sc_sdev_cdrom;
429           break;
430         case 3:
431           hd->sub_class.id = sc_sdev_tape;
432           break;
433         case 4:
434           hd->sub_class.id = sc_sdev_floppy;
435           break;
436         default:
437           hd->sub_class.id = sc_sdev_other;
438       }
439       break;
440
441     case 9:
442       hd->base_class.id = bc_hub;
443       break;
444
445     case 0xe0:
446       if(sub == 1 && prot == 1) {
447         hd->base_class.id = bc_bluetooth;
448         hd->sub_class.id = 0;
449       }
450       break;
451
452     case 0xff:
453       /* hp psc 2100, 2200, 2150, officejet 6100 */
454       if(
455         sub == 0xcc &&
456         (
457           usb->vendor == 0x03f0 &&
458           (
459             usb->device == 0x2811 ||
460             usb->device == 0x2911 ||
461             usb->device == 0x2a11 ||
462             usb->device == 0x2d11
463           )
464         )
465       ) {
466         hd->base_class.id = bc_scanner;
467         hd->sub_class.id = 1;
468       }
469       break;
470   }
471
472   if((u = device_class(hd_data, hd->vendor.id, hd->device.id))) {
473     hd->base_class.id = u >> 8;
474     hd->sub_class.id = u & 0xff;
475   }
476
477   /* FIXME: hack for bt isdn box */
478   if(
479     hd->vendor.id == MAKE_ID(TAG_USB, 0x057c) &&
480     hd->device.id == MAKE_ID(TAG_USB, 0x2200)
481   ) {
482     hd_set_hw_class(hd, hw_bluetooth);
483   }
484
485 }
486
487
488 void add_input_dev(hd_data_t *hd_data, char *name)
489 {
490   hd_t *hd;
491   char *s, *t;
492   hd_dev_num_t dev_num = { };
493   unsigned u1, u2;
494
495   struct sysfs_class_device *sf_cdev;
496   struct sysfs_device *sf_dev;
497
498   if(!(sf_cdev = sysfs_open_class_device_path(name))) return;
499
500   ADD2LOG(
501     "  input: name = %s, path = %s\n",
502     sf_cdev->name,
503     hd_sysfs_id(sf_cdev->path)
504   );
505
506   if(!strncmp(sf_cdev->name, "ts", sizeof "ts" - 1)) {
507     sysfs_close_class_device(sf_cdev);
508     return;
509   }
510
511   if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "dev")))) {
512     if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
513       dev_num.type = 'c';
514       dev_num.major = u1;
515       dev_num.minor = u2;
516       dev_num.range = 1;
517     }
518     ADD2LOG("    dev = %u:%u\n", u1, u2);
519   }
520
521   sf_dev = sysfs_get_classdev_device(sf_cdev);
522   if(sf_dev) {
523     s = hd_sysfs_id(sf_dev->path);
524
525     ADD2LOG(
526       "    input device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
527       sf_dev->bus,
528       sf_dev->bus_id,
529       sf_dev->driver_name,
530       s
531     );
532
533     for(hd = hd_data->hd; hd; hd = hd->next) {
534       if(
535         hd->module == hd_data->module &&
536         hd->sysfs_id &&
537         s &&
538         !strcmp(s, hd->sysfs_id)
539       ) {
540         t = NULL;
541         str_printf(&t, 0, "/dev/input/%s", sf_cdev->name);
542
543         if(strncmp(sf_cdev->name, "mouse", sizeof "mouse" - 1)) {
544           hd->unix_dev_name = t;
545           hd->unix_dev_num = dev_num;
546         }
547         else {
548           hd->unix_dev_name2 = t;
549           hd->unix_dev_num2 = dev_num;
550
551           dev_num.major = 13;
552           dev_num.minor = 63;
553           hd->unix_dev_name = new_str(DEV_MICE);
554           hd->unix_dev_num = dev_num;
555
556           // make it a mouse, #216091
557           if(hd->base_class.id == bc_none) {
558             hd->base_class.id = bc_mouse;
559             hd->sub_class.id = sc_mou_usb;
560             hd->compat_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
561             hd->compat_device.id = MAKE_ID(TAG_SPECIAL, 0x001);
562           }
563         }
564       }
565     }
566   }
567
568   sysfs_close_class_device(sf_cdev);
569 }
570
571
572 void get_input_devs(hd_data_t *hd_data)
573 {
574   struct sysfs_link *sf_link;
575   struct sysfs_directory *sf_dir, *sf_subdir;
576   struct dlist *sf_list;
577
578   sf_dir = sysfs_open_directory("/sys/class/input");
579
580   if(!sf_dir) {
581     ADD2LOG("sysfs: no such class: input\n");
582     return;
583   }
584
585   /*
586    * A bit tricky: if there are links, assume newer sysfs layout with compat
587    * symlinks; if not, assume old layout with directories.
588    */
589
590   sysfs_read_dir_links(sf_dir);
591   if(!sf_dir->links) sysfs_read_dir_subdirs(sf_dir);
592
593   if((sf_list = sf_dir->links)) dlist_for_each_data(sf_list, sf_link, struct sysfs_link) {
594     add_input_dev(hd_data, sf_link->target);
595   }
596   else if((sf_list = sf_dir->subdirs)) dlist_for_each_data(sf_list, sf_subdir, struct sysfs_directory) {
597     add_input_dev(hd_data, sf_subdir->path);
598   }
599
600   sysfs_close_directory(sf_dir);
601 }
602
603
604 void get_printer_devs(hd_data_t *hd_data)
605 {
606   hd_t *hd;
607   char *s, *t;
608   hd_dev_num_t dev_num = { };
609   unsigned u1, u2;
610
611   struct sysfs_class *sf_class;
612   struct sysfs_class_device *sf_cdev;
613   struct sysfs_device *sf_dev;
614   struct dlist *sf_cdev_list;
615
616   sf_class = sysfs_open_class("usb");
617
618   if(!sf_class) {
619     ADD2LOG("sysfs: no such class: usb\n");
620     return;
621   }
622
623   sf_cdev_list = sysfs_get_class_devices(sf_class);
624   if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
625     if(strncmp(sf_cdev->name, "lp", 2)) continue;
626
627     ADD2LOG(
628       "  usb: name = %s, path = %s\n",
629       sf_cdev->name,
630       hd_sysfs_id(sf_cdev->path)
631     );
632
633     if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "dev")))) {
634       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
635         dev_num.type = 'c';
636         dev_num.major = u1;
637         dev_num.minor = u2;
638         dev_num.range = 1;
639       }
640       ADD2LOG("    dev = %u:%u\n", u1, u2);
641     }
642
643     sf_dev = sysfs_get_classdev_device(sf_cdev);
644     if(sf_dev) {
645       s = hd_sysfs_id(sf_dev->path);
646
647       ADD2LOG(
648         "    usb device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
649         sf_dev->bus,
650         sf_dev->bus_id,
651         sf_dev->driver_name,
652         s
653       );
654
655       for(hd = hd_data->hd; hd; hd = hd->next) {
656         if(
657           hd->module == hd_data->module &&
658           hd->sysfs_id &&
659           s &&
660           !strcmp(s, hd->sysfs_id)
661         ) {
662           t = NULL;
663           str_printf(&t, 0, "/dev/usb/%s", sf_cdev->name);
664
665           hd->unix_dev_name = t;
666           hd->unix_dev_num = dev_num;
667
668           read_usb_lp(hd_data, hd);
669         }
670       }
671     }
672   }
673
674   sysfs_close_class(sf_class);
675 }
676
677
678 #define MATCH_FIELD(field, var) \
679   if(!strncasecmp(sl->str, field, sizeof field - 1)) var = sl->str + sizeof field - 1
680
681 /*
682  * assign /dev/usb/lp* to usb printers.
683  */
684 void read_usb_lp(hd_data_t *hd_data, hd_t *hd)
685 {
686   char *s;
687   char buf[1024];
688   int fd, two_ints[2];
689   unsigned bus;
690   str_list_t *sl0, *sl;
691   char *vend, *prod, *serial, *descr;
692
693   if((fd = open(hd->unix_dev_name, O_RDWR)) < 0) return;
694
695   if(ioctl(fd, LPIOC_GET_BUS_ADDRESS(sizeof two_ints), two_ints) == -1) {
696     close(fd);
697     return;
698   }
699   
700   ADD2LOG("  usb/lp: bus = %d, dev_nr = %d\n", two_ints[0], two_ints[1]);
701   bus = ((two_ints[0] & 0xff) << 8) + (two_ints[1] & 0xff);
702
703   if(ioctl(fd, LPIOC_GET_VID_PID(sizeof two_ints), two_ints) != -1) {
704     /* just for the record */
705     ADD2LOG("  usb/lp: vend = 0x%04x, prod = 0x%04x\n", two_ints[0], two_ints[1]);
706   }
707
708   memset(buf, 0, sizeof buf);
709   if(!ioctl(fd, LPIOC_GET_DEVICE_ID(sizeof buf), buf)) {
710     buf[sizeof buf - 1] = 0;
711     s = canon_str(buf + 2, sizeof buf - 3);
712     ADD2LOG("  usb/lp: \"%s\"\n", s);
713     sl0 = hd_split(';', s);
714     free_mem(s);
715     vend = prod = serial = descr = NULL;
716     for(sl = sl0; sl; sl = sl->next) {
717       MATCH_FIELD("MFG:", vend);
718       MATCH_FIELD("MANUFACTURER:", vend);
719       MATCH_FIELD("MDL:", prod);
720       MATCH_FIELD("MODEL:", prod);
721       MATCH_FIELD("DES:", descr);
722       MATCH_FIELD("DESCRIPTION:", descr);
723       MATCH_FIELD("SERN:", serial);
724       MATCH_FIELD("SERIALNUMBER:", serial);
725     }
726     ADD2LOG(
727       "  usb/lp: vend = %s, prod = %s, descr = %s, serial = %s\n",
728       vend ?: "", prod ?: "", descr ?: "", serial ?: ""
729     );
730     if(descr) {
731       str_printf(&hd->model, 0, "%s", descr);
732     }
733     if(vend && prod) {
734       str_printf(&hd->sub_vendor.name, 0, "%s", vend);
735       str_printf(&hd->sub_device.name, 0, "%s", prod);
736     }
737     if(serial && !hd->serial) {
738       hd->serial = new_str(serial);
739     }
740
741     free_str_list(sl0);
742   }
743
744   close(fd);
745 }
746 #undef MATCH_FIELD
747
748
749 void get_serial_devs(hd_data_t *hd_data)
750 {
751   hd_t *hd;
752   char *s, *t;
753   hd_dev_num_t dev_num = { };
754   unsigned u1, u2;
755
756   struct sysfs_class *sf_class;
757   struct sysfs_class_device *sf_cdev;
758   struct sysfs_device *sf_dev;
759   struct dlist *sf_cdev_list;
760
761   sf_class = sysfs_open_class("tty");
762
763   if(!sf_class) {
764     ADD2LOG("sysfs: no such class: tty\n");
765     return;
766   }
767
768   sf_cdev_list = sysfs_get_class_devices(sf_class);
769   if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
770     if(strncmp(sf_cdev->name, "ttyUSB", 6)) continue;
771
772     ADD2LOG(
773       "  usb: name = %s, path = %s\n",
774       sf_cdev->name,
775       hd_sysfs_id(sf_cdev->path)
776     );
777
778     if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "dev")))) {
779       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
780         dev_num.type = 'c';
781         dev_num.major = u1;
782         dev_num.minor = u2;
783         dev_num.range = 1;
784       }
785       ADD2LOG("    dev = %u:%u\n", u1, u2);
786     }
787
788     sf_dev = sysfs_get_classdev_device(sf_cdev);
789     if(sf_dev) {
790       s = hd_sysfs_id(sf_dev->path);
791
792       if((t = strrchr(s, '/')) && !strncmp(t + 1, "ttyUSB", sizeof "ttyUSB" - 1)) *t =0;
793
794       ADD2LOG(
795         "    usb device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
796         sf_dev->bus,
797         sf_dev->bus_id,
798         sf_dev->driver_name,
799         s
800       );
801
802       for(hd = hd_data->hd; hd; hd = hd->next) {
803         if(
804           hd->module == hd_data->module &&
805           hd->sysfs_id &&
806           s &&
807           !strcmp(s, hd->sysfs_id)
808         ) {
809           t = NULL;
810           str_printf(&t, 0, "/dev/%s", sf_cdev->name);
811
812           hd->unix_dev_name = t;
813           hd->unix_dev_num = dev_num;
814
815           hd->base_class.id = bc_comm;
816           hd->sub_class.id = sc_com_ser;
817           hd->prog_if.id = 0x80;
818         }
819       }
820     }
821   }
822
823   sysfs_close_class(sf_class);
824 }
825
826