- some more dir -> link sysfs changes (#303978)
[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  * @defgroup USBint Universal Serial Bus (USB)
18  * @ingroup libhdBUSint
19  * @brief USB scan functions
20  *
21  * @{
22  */
23
24 #define IOCNR_GET_DEVICE_ID             1
25 #define IOCNR_GET_BUS_ADDRESS           5
26 #define IOCNR_GET_VID_PID               6
27
28 /* Get device_id string: */
29 #define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
30 /* Get two-int array: [0]=bus number, [1]=device address: */
31 #define LPIOC_GET_BUS_ADDRESS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_BUS_ADDRESS, len)
32 /* Get two-int array: [0]=vendor ID, [1]=product ID: */
33 #define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len)
34
35
36 static void get_usb_devs(hd_data_t *hd_data);
37 static void set_class_entries(hd_data_t *hd_data, hd_t *hd, usb_t *usb);
38 static void add_input_dev(hd_data_t *hd_data, char *name);
39 static void get_input_devs(hd_data_t *hd_data);
40 static void get_printer_devs(hd_data_t *hd_data);
41 static void read_usb_lp(hd_data_t *hd_data, hd_t *hd);
42 static void get_serial_devs(hd_data_t *hd_data);
43
44 void hd_scan_sysfs_usb(hd_data_t *hd_data)
45 {
46   if(!hd_probe_feature(hd_data, pr_usb)) return;
47
48   hd_data->module = mod_usb;
49
50   /* some clean-up */
51   remove_hd_entries(hd_data);
52   hd_data->proc_usb = free_str_list(hd_data->proc_usb);
53   hd_data->usb = NULL;
54
55   PROGRESS(1, 0, "sysfs drivers");
56   
57   hd_sysfs_driver_list(hd_data);
58
59   PROGRESS(2, 0, "usb");
60
61   get_usb_devs(hd_data);
62
63   PROGRESS(3, 1, "joydev mod");
64   load_module(hd_data, "joydev");
65     
66   PROGRESS(3, 2, "evdev mod");
67   load_module(hd_data, "evdev");
68
69   PROGRESS(3, 3, "input");
70   get_input_devs(hd_data);
71
72   PROGRESS(3, 4, "lp");
73   get_printer_devs(hd_data);
74
75   PROGRESS(3, 5, "serial");
76   get_serial_devs(hd_data);
77
78 }
79
80
81 void get_usb_devs(hd_data_t *hd_data)
82 {
83   uint64_t ul0;
84   unsigned u1, u2, u3;
85   hd_t *hd, *hd1;
86   usb_t *usb;
87   str_list_t *sl, *usb_devs = NULL;
88   char *s, *s1, *t;
89   hd_res_t *res;
90   size_t l;
91   str_list_t *sf_bus, *sf_bus_e;
92   char *sf_dev, *sf_dev_2;
93
94   sf_bus = reverse_str_list(read_dir("/sys/bus/usb/devices", 'l'));
95
96   if(!sf_bus) {
97     ADD2LOG("sysfs: no such bus: usb\n");
98     return;
99   }
100
101   for(sf_bus_e = sf_bus; sf_bus_e; sf_bus_e = sf_bus_e->next) {
102     sf_dev = hd_read_sysfs_link("/sys/bus/usb/devices", sf_bus_e->str);
103
104     if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "bNumInterfaces"), &ul0, 0)) {
105       add_str_list(&usb_devs, sf_dev);
106       ADD2LOG("  usb dev: %s\n", hd_sysfs_id(sf_dev));
107     }
108   }
109
110   for(sf_bus_e = sf_bus; sf_bus_e; sf_bus_e = sf_bus_e->next) {
111     sf_dev = new_str(hd_read_sysfs_link("/sys/bus/usb/devices", sf_bus_e->str));
112
113     ADD2LOG(
114       "  usb device: name = %s\n    path = %s\n",
115       sf_bus_e->str,
116       hd_sysfs_id(sf_dev)
117     );
118
119     if(
120       hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "bInterfaceNumber"), &ul0, 16)
121     ) {
122       hd = add_hd_entry(hd_data, __LINE__, 0);
123
124       hd->detail = new_mem(sizeof *hd->detail);
125       hd->detail->type = hd_detail_usb;
126       hd->detail->usb.data = usb = new_mem(sizeof *usb);
127
128       hd->sysfs_id = new_str(hd_sysfs_id(sf_dev));
129       hd->sysfs_bus_id = new_str(sf_bus_e->str);
130
131       hd->bus.id = bus_usb;
132       hd->func = ul0;
133
134       usb->ifdescr = ul0;
135
136       if((s = get_sysfs_attr_by_path(sf_dev, "modalias"))) {
137         s = canon_str(s, strlen(s));
138         ADD2LOG("    modalias = \"%s\"\n", s);
139         if(s && *s) {
140           hd->modalias = s;
141           s = NULL;
142         }
143         s = free_mem(s);
144       }
145
146       ADD2LOG("    bInterfaceNumber = %u\n", hd->func);
147
148       if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "bInterfaceClass"), &ul0, 16)) {
149         usb->i_cls = ul0;
150         ADD2LOG("    bInterfaceClass = %u\n", usb->i_cls);
151       }
152
153       if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "bInterfaceSubClass"), &ul0, 16)) {
154         usb->i_sub = ul0;
155         ADD2LOG("    bInterfaceSubClass = %u\n", usb->i_sub);
156       }
157
158       if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "bInterfaceProtocol"), &ul0, 16)) {
159         usb->i_prot = ul0;
160         ADD2LOG("    bInterfaceProtocol = %u\n", usb->i_prot);
161       }
162
163       /* device has longest matching sysfs id */
164       u2 = strlen(sf_dev);
165       s = NULL;
166       for(u3 = 0, sl = usb_devs; sl; sl = sl->next) {
167         u1 = strlen(sl->str);
168         if(u1 > u3 && u1 <= u2 && !strncmp(sf_dev, sl->str, u1)) {
169           u3 = u1;
170           s = sl->str;
171         }
172       }
173
174       if(s) {
175         ADD2LOG("    if: %s @ %s\n", hd->sysfs_bus_id, hd_sysfs_id(s));
176         sf_dev_2 = new_str(s);
177         if(sf_dev_2) {
178
179           if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev_2, "bDeviceClass"), &ul0, 16)) {
180             usb->d_cls = ul0;
181             ADD2LOG("    bDeviceClass = %u\n", usb->d_cls);
182           }
183
184           if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev_2, "bDeviceSubClass"), &ul0, 16)) {
185             usb->d_sub = ul0;
186             ADD2LOG("    bDeviceSubClass = %u\n", usb->d_sub);
187           }
188
189           if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev_2, "bDeviceProtocol"), &ul0, 16)) {
190             usb->d_prot = ul0;
191             ADD2LOG("    bDeviceProtocol = %u\n", usb->d_prot);
192           }
193
194           if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev_2, "idVendor"), &ul0, 16)) {
195             usb->vendor = ul0;
196             ADD2LOG("    idVendor = 0x%04x\n", usb->vendor);
197           }
198
199           if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev_2, "idProduct"), &ul0, 16)) {
200             usb->device = ul0;
201             ADD2LOG("    idProduct = 0x%04x\n", usb->device);
202           }
203
204           if((s = get_sysfs_attr_by_path(sf_dev_2, "manufacturer"))) {
205             usb->manufact = canon_str(s, strlen(s));
206             ADD2LOG("    manufacturer = \"%s\"\n", usb->manufact);
207           }
208
209           if((s = get_sysfs_attr_by_path(sf_dev_2, "product"))) {
210             usb->product = canon_str(s, strlen(s));
211             ADD2LOG("    product = \"%s\"\n", usb->product);
212           }
213
214           if((s = get_sysfs_attr_by_path(sf_dev_2, "serial"))) {
215             usb->serial = canon_str(s, strlen(s));
216             ADD2LOG("    serial = \"%s\"\n", usb->serial);
217           }
218
219           if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev_2, "bcdDevice"), &ul0, 16)) {
220             usb->rev = ul0;
221             ADD2LOG("    bcdDevice = %04x\n", usb->rev);
222           }
223
224           if((s = get_sysfs_attr_by_path(sf_dev_2, "speed"))) {
225             s = canon_str(s, strlen(s));
226             if(!strcmp(s, "1.5")) usb->speed = 15*100000;
227             else if(!strcmp(s, "12")) usb->speed = 12*1000000;
228             else if(!strcmp(s, "480")) usb->speed = 480*1000000;
229             ADD2LOG("    speed = \"%s\"\n", s);
230             s = free_mem(s);
231           }
232
233           sf_dev_2 = free_mem(sf_dev_2);
234         }
235       }
236
237       if(usb->vendor || usb->device) {
238         hd->vendor.id = MAKE_ID(TAG_USB, usb->vendor);
239         hd->device.id = MAKE_ID(TAG_USB, usb->device);
240       }
241
242       if(usb->manufact) hd->vendor.name = new_str(usb->manufact);
243       if(usb->product) hd->device.name = new_str(usb->product);
244       if(usb->serial) hd->serial = new_str(usb->serial);
245
246       if(usb->rev) str_printf(&hd->revision.name, 0, "%x.%02x", usb->rev >> 8, usb->rev & 0xff);
247
248       if(usb->speed) {
249         res = add_res_entry(&hd->res, new_mem(sizeof *res));
250         res->baud.type = res_baud;
251         res->baud.speed = usb->speed;
252       }
253
254       s = hd_sysfs_find_driver(hd_data, hd->sysfs_id, 1);
255       if(s) add_str_list(&hd->drivers, s);
256
257       set_class_entries(hd_data, hd, usb);
258
259       if(!hd_data->scanner_db) {
260         hd_data->scanner_db = hd_module_list(hd_data, 1);
261       }
262
263       if(
264         hd->drivers &&
265         search_str_list(hd_data->scanner_db, hd->drivers->str)
266       ) {
267         hd->base_class.id = bc_scanner;
268       }
269
270       // ###### FIXME
271       if(hd->base_class.id == bc_modem) {
272         hd->unix_dev_name = new_str("/dev/ttyACM0");
273       }
274     }
275
276     sf_dev = free_mem(sf_dev);
277   }
278
279   sf_bus = free_str_list(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   char *sf_drv_name, *sf_drv, *bus_name, *bus_id;
495   char *sf_cdev_name, *sf_dev;
496
497   sf_cdev_name = name ? strrchr(name, '/') : NULL;
498   if(sf_cdev_name) sf_cdev_name++;
499
500   ADD2LOG(
501     "  input: name = %s, path = %s\n",
502     sf_cdev_name,
503     hd_sysfs_id(name)
504   );
505
506   if(!strncmp(sf_cdev_name, "ts", sizeof "ts" - 1)) return;
507
508   if((s = get_sysfs_attr_by_path(name, "dev"))) {
509     if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
510       dev_num.type = 'c';
511       dev_num.major = u1;
512       dev_num.minor = u2;
513       dev_num.range = 1;
514     }
515     ADD2LOG("    dev = %u:%u\n", u1, u2);
516   }
517
518   sf_dev = new_str(hd_read_sysfs_link(name, "device"));
519
520   if(sf_dev) {
521     bus_id = sf_dev ? strrchr(sf_dev, '/') : NULL;
522     if(bus_id) bus_id++;
523
524     sf_drv_name = NULL;
525     if((sf_drv = hd_read_sysfs_link(sf_dev, "driver"))) {
526       sf_drv_name = strrchr(sf_drv, '/');
527       if(sf_drv_name) sf_drv_name++;
528       sf_drv_name = new_str(sf_drv_name);
529     }
530
531     bus_name = NULL;
532     if((s = hd_read_sysfs_link(sf_dev, "bus"))) {
533       bus_name = strrchr(s, '/');
534       if(bus_name) bus_name++;
535       bus_name = new_str(bus_name);
536     }
537
538     s = hd_sysfs_id(sf_dev);
539
540     ADD2LOG(
541       "    input device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
542       bus_name,
543       bus_id,
544       sf_drv_name,
545       s
546     );
547
548     for(hd = hd_data->hd; hd; hd = hd->next) {
549       if(
550         hd->module == hd_data->module &&
551         hd->sysfs_id &&
552         s &&
553         !strcmp(s, hd->sysfs_id)
554       ) {
555         t = NULL;
556         str_printf(&t, 0, "/dev/input/%s", sf_cdev_name);
557
558         if(strncmp(sf_cdev_name, "mouse", sizeof "mouse" - 1)) {
559           if(!hd->unix_dev_name) {
560             hd->unix_dev_name = t;
561             hd->unix_dev_num = dev_num;
562           }
563         }
564         else {
565           free_mem(hd->unix_dev_name);
566           free_mem(hd->unix_dev_name2);
567
568           hd->unix_dev_name2 = t;
569           hd->unix_dev_num2 = dev_num;
570
571           dev_num.major = 13;
572           dev_num.minor = 63;
573           hd->unix_dev_name = new_str(DEV_MICE);
574           hd->unix_dev_num = dev_num;
575
576           // make it a mouse, #216091
577           if(hd->base_class.id == bc_none) {
578             hd->base_class.id = bc_mouse;
579             hd->sub_class.id = sc_mou_usb;
580             hd->compat_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
581             hd->compat_device.id = MAKE_ID(TAG_SPECIAL, 0x001);
582           }
583         }
584       }
585     }
586
587     bus_name = free_mem(bus_name);
588     sf_drv_name = free_mem(sf_drv_name);
589   }
590
591   sf_dev = free_mem(sf_dev);
592 }
593
594
595 void get_input_devs(hd_data_t *hd_data)
596 {
597   str_list_t *sf_dir, *sf_dir_e;
598   char *sf_dev = NULL;
599   int is_dir = 0;
600
601   /*
602    * A bit tricky: if there are links, assume newer sysfs layout with compat
603    * symlinks; if not, assume old layout with directories.
604    */
605   sf_dir = reverse_str_list(read_dir("/sys/class/input", 'l'));
606   if(!sf_dir) {
607     sf_dir = reverse_str_list(read_dir("/sys/class/input", 'd'));
608     is_dir = 1;
609   }
610   
611   if(!sf_dir) {
612     ADD2LOG("sysfs: no such class: input\n");
613     return;
614   }
615
616   for(sf_dir_e = sf_dir; sf_dir_e; sf_dir_e = sf_dir_e->next) {
617     if(is_dir) {
618       str_printf(&sf_dev, 0, "/sys/class/input/%s", sf_dir_e->str);
619     }
620     else {
621       sf_dev = new_str(hd_read_sysfs_link("/sys/class/input", sf_dir_e->str));
622     }
623
624     add_input_dev(hd_data, sf_dev);
625
626     sf_dev = free_mem(sf_dev);
627   }
628
629   sf_dir = free_str_list(sf_dir);
630 }
631
632
633 void get_printer_devs(hd_data_t *hd_data)
634 {
635   hd_t *hd;
636   char *s, *t;
637   hd_dev_num_t dev_num = { };
638   unsigned u1, u2;
639   str_list_t *sf_class, *sf_class_e;
640   char *sf_cdev = NULL, *sf_dev;
641   char *sf_drv_name, *sf_drv, *bus_id, *bus_name;
642
643   sf_class = reverse_str_list(read_dir("/sys/class/usb", 'D'));
644
645   if(!sf_class) {
646     ADD2LOG("sysfs: no such class: usb\n");
647     return;
648   }
649
650   for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
651     if(strncmp(sf_class_e->str, "lp", 2)) continue;
652
653     str_printf(&sf_cdev, 0, "/sys/class/usb/%s", sf_class_e->str);
654
655     ADD2LOG(
656       "  usb: name = %s, path = %s\n",
657       sf_class_e->str,
658       hd_sysfs_id(sf_cdev)
659     );
660
661     if((s = get_sysfs_attr_by_path(sf_cdev, "dev"))) {
662       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
663         dev_num.type = 'c';
664         dev_num.major = u1;
665         dev_num.minor = u2;
666         dev_num.range = 1;
667       }
668       ADD2LOG("    dev = %u:%u\n", u1, u2);
669     }
670
671     sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
672
673     if(sf_dev) {
674       bus_id = sf_dev ? strrchr(sf_dev, '/') : NULL;
675       if(bus_id) bus_id++;
676
677       sf_drv_name = NULL;
678       if((sf_drv = hd_read_sysfs_link(sf_dev, "driver"))) {
679         sf_drv_name = strrchr(sf_drv, '/');
680         if(sf_drv_name) sf_drv_name++;
681         sf_drv_name = new_str(sf_drv_name);
682       }
683
684       bus_name = NULL;
685       if((s = hd_read_sysfs_link(sf_dev, "bus"))) {
686         bus_name = strrchr(s, '/');
687         if(bus_name) bus_name++;
688         bus_name = new_str(bus_name);
689       }
690
691       s = hd_sysfs_id(sf_dev);
692
693       ADD2LOG(
694         "    usb device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
695         bus_name,
696         bus_id,
697         sf_drv_name,
698         s
699       );
700
701       for(hd = hd_data->hd; hd; hd = hd->next) {
702         if(
703           hd->module == hd_data->module &&
704           hd->sysfs_id &&
705           s &&
706           !strcmp(s, hd->sysfs_id)
707         ) {
708           t = NULL;
709           str_printf(&t, 0, "/dev/usb/%s", sf_class_e->str);
710
711           hd->unix_dev_name = t;
712           hd->unix_dev_num = dev_num;
713
714           read_usb_lp(hd_data, hd);
715         }
716       }
717
718       bus_name = free_mem(bus_name);
719       sf_drv_name = free_mem(sf_drv_name);
720     }
721
722     sf_dev = free_mem(sf_dev);
723   }
724
725   sf_cdev = free_mem(sf_cdev);
726   sf_class = free_str_list(sf_class);
727 }
728
729
730 #define MATCH_FIELD(field, var) \
731   if(!strncasecmp(sl->str, field, sizeof field - 1)) var = sl->str + sizeof field - 1
732
733 /*
734  * assign /dev/usb/lp* to usb printers.
735  */
736 void read_usb_lp(hd_data_t *hd_data, hd_t *hd)
737 {
738   char *s;
739   char buf[1024];
740   int fd, two_ints[2];
741   unsigned bus;
742   str_list_t *sl0, *sl;
743   char *vend, *prod, *serial, *descr;
744
745   if((fd = open(hd->unix_dev_name, O_RDWR)) < 0) return;
746
747   if(ioctl(fd, LPIOC_GET_BUS_ADDRESS(sizeof two_ints), two_ints) == -1) {
748     close(fd);
749     return;
750   }
751   
752   ADD2LOG("  usb/lp: bus = %d, dev_nr = %d\n", two_ints[0], two_ints[1]);
753   bus = ((two_ints[0] & 0xff) << 8) + (two_ints[1] & 0xff);
754
755   if(ioctl(fd, LPIOC_GET_VID_PID(sizeof two_ints), two_ints) != -1) {
756     /* just for the record */
757     ADD2LOG("  usb/lp: vend = 0x%04x, prod = 0x%04x\n", two_ints[0], two_ints[1]);
758   }
759
760   memset(buf, 0, sizeof buf);
761   if(!ioctl(fd, LPIOC_GET_DEVICE_ID(sizeof buf), buf)) {
762     buf[sizeof buf - 1] = 0;
763     s = canon_str(buf + 2, sizeof buf - 3);
764     ADD2LOG("  usb/lp: \"%s\"\n", s);
765     sl0 = hd_split(';', s);
766     free_mem(s);
767     vend = prod = serial = descr = NULL;
768     for(sl = sl0; sl; sl = sl->next) {
769       MATCH_FIELD("MFG:", vend);
770       MATCH_FIELD("MANUFACTURER:", vend);
771       MATCH_FIELD("MDL:", prod);
772       MATCH_FIELD("MODEL:", prod);
773       MATCH_FIELD("DES:", descr);
774       MATCH_FIELD("DESCRIPTION:", descr);
775       MATCH_FIELD("SERN:", serial);
776       MATCH_FIELD("SERIALNUMBER:", serial);
777     }
778     ADD2LOG(
779       "  usb/lp: vend = %s, prod = %s, descr = %s, serial = %s\n",
780       vend ?: "", prod ?: "", descr ?: "", serial ?: ""
781     );
782     if(descr) {
783       str_printf(&hd->model, 0, "%s", descr);
784     }
785     if(vend && prod) {
786       str_printf(&hd->sub_vendor.name, 0, "%s", vend);
787       str_printf(&hd->sub_device.name, 0, "%s", prod);
788     }
789     if(serial && !hd->serial) {
790       hd->serial = new_str(serial);
791     }
792
793     free_str_list(sl0);
794   }
795
796   close(fd);
797 }
798 #undef MATCH_FIELD
799
800
801 void get_serial_devs(hd_data_t *hd_data)
802 {
803   hd_t *hd;
804   char *s, *t;
805   hd_dev_num_t dev_num = { };
806   unsigned u1, u2;
807   str_list_t *sf_class, *sf_class_e;
808   char *sf_cdev = NULL, *sf_dev;
809   char *sf_drv_name, *sf_drv, *bus_id, *bus_name;
810
811   sf_class = reverse_str_list(read_dir("/sys/class/tty", 'D'));
812
813   if(!sf_class) {
814     ADD2LOG("sysfs: no such class: tty\n");
815     return;
816   }
817
818   for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
819     if(strncmp(sf_class_e->str, "ttyUSB", 6)) continue;
820
821     str_printf(&sf_cdev, 0, "/sys/class/tty/%s", sf_class_e->str);
822
823     ADD2LOG(
824       "  usb: name = %s, path = %s\n",
825       sf_class_e->str,
826       hd_sysfs_id(sf_cdev)
827     );
828
829     if((s = get_sysfs_attr_by_path(sf_cdev, "dev"))) {
830       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
831         dev_num.type = 'c';
832         dev_num.major = u1;
833         dev_num.minor = u2;
834         dev_num.range = 1;
835       }
836       ADD2LOG("    dev = %u:%u\n", u1, u2);
837     }
838
839     sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
840
841     if(sf_dev) {
842       bus_id = sf_dev ? strrchr(sf_dev, '/') : NULL;
843       if(bus_id) bus_id++;
844
845       sf_drv_name = NULL;
846       if((sf_drv = hd_read_sysfs_link(sf_dev, "driver"))) {
847         sf_drv_name = strrchr(sf_drv, '/');
848         if(sf_drv_name) sf_drv_name++;
849         sf_drv_name = new_str(sf_drv_name);
850       }
851
852       bus_name = NULL;
853       if((s = hd_read_sysfs_link(sf_dev, "bus"))) {
854         bus_name = strrchr(s, '/');
855         if(bus_name) bus_name++;
856         bus_name = new_str(bus_name);
857       }
858
859       s = hd_sysfs_id(sf_dev);
860
861       if((t = strrchr(s, '/')) && !strncmp(t + 1, "ttyUSB", sizeof "ttyUSB" - 1)) *t = 0;
862
863       ADD2LOG(
864         "    usb device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
865         bus_name,
866         bus_id,
867         sf_drv_name,
868         s
869       );
870
871       for(hd = hd_data->hd; hd; hd = hd->next) {
872         if(
873           hd->module == hd_data->module &&
874           hd->sysfs_id &&
875           s &&
876           !strcmp(s, hd->sysfs_id)
877         ) {
878           t = NULL;
879           str_printf(&t, 0, "/dev/%s", sf_class_e->str);
880
881           hd->unix_dev_name = t;
882           hd->unix_dev_num = dev_num;
883
884           hd->base_class.id = bc_comm;
885           hd->sub_class.id = sc_com_ser;
886           hd->prog_if.id = 0x80;
887         }
888       }
889     }
890
891     sf_dev = free_mem(sf_dev);
892   }
893
894   sf_cdev = free_mem(sf_cdev);
895   sf_class = free_str_list(sf_class);
896 }
897
898 /** @} */
899