1 #include <nm_core.h>
2 #include <nm_utils.h>
3 #include <nm_usb_devices.h>
4 
5 /* Disable USB on FreeBSD while libudev-devd
6  * will not supports calls udev_hwdb_* */
7 
8 enum {NM_USB_SERIAL_LEN = 127};
9 
10 static inline void nm_usb_dev_free(nm_usb_dev_t *dev);
11 
12 #if defined (NM_OS_LINUX)
13 #include <libudev.h>
14 #include <libusb.h>
15 
16 static const char *nm_usb_hwdb_get(const char *modalias, const char *key);
17 static const char *nm_usb_get_vendor(uint16_t vid);
18 static const char *nm_usb_get_product(uint16_t vid, uint16_t pid);
19 static int nm_usb_get_vendor_str(char *buf, size_t size, uint16_t vid);
20 static int nm_usb_get_product_str(char *buf, size_t size, uint16_t vid, uint16_t pid);
21 
22 static struct udev_hwdb *hwdb = NULL;
23 #endif /* NM_OS_LINUX */
24 
nm_usb_get_devs(nm_vect_t * v)25 void nm_usb_get_devs(nm_vect_t *v)
26 {
27 #if defined (NM_OS_LINUX)
28     libusb_context *ctx = NULL;
29     libusb_device **list = NULL;
30     struct udev *udev = NULL;
31     int rc;
32     ssize_t dev_count, n;
33     char vendor[128], product[128];
34 
35     if ((udev = udev_new()) == NULL)
36         nm_bug(_("%s: udev_new failed"), __func__);
37 
38     if ((hwdb = udev_hwdb_new(udev)) == NULL)
39         nm_bug(_("%s: udev_hwdb_new failed"), __func__);
40 
41     if ((rc = libusb_init(&ctx)) != 0)
42         nm_bug("%s: %s", __func__, libusb_strerror(rc));
43 
44     if ((dev_count = libusb_get_device_list(ctx, &list)) < 1)
45         nm_bug(_("%s: libusb_get_device_list failed"), __func__);
46 
47     //@TODO Some of variables may be moved outside, and freed only once
48     for (n = 0; n < dev_count; n++) {
49         nm_usb_dev_t dev = NM_INIT_USB;
50         libusb_device *device = list[n];
51         struct libusb_device_descriptor desc;
52 
53         memset(&desc, 0, sizeof(desc));
54 
55         if (libusb_get_device_descriptor(device, &desc) != 0)
56             continue;
57 
58         if (nm_usb_get_vendor_str(vendor, sizeof(vendor), desc.idVendor) == 0)
59             nm_str_alloc_text(&dev.name, "vendor-unknown");
60         else
61             nm_str_alloc_text(&dev.name, vendor);
62 
63         if (nm_usb_get_product_str(product, sizeof(product), desc.idVendor, desc.idProduct) == 0)
64             nm_str_add_text(&dev.name, " product-unknown");
65         else
66             nm_str_add_text(&dev.name, product);
67 
68         dev.bus_num = libusb_get_bus_number(device);
69         dev.dev_addr = libusb_get_device_address(device);
70 
71         nm_str_format(&dev.vendor_id, "%04x", desc.idVendor);
72         nm_str_format(&dev.product_id, "%04x", desc.idProduct);
73 
74         nm_vect_insert(v, &dev, sizeof(dev), nm_usb_vect_ins_cb);
75         nm_usb_dev_free(&dev);
76     }
77 
78     /* cleanup */
79     libusb_free_device_list(list, 1);
80     udev_hwdb_unref(hwdb);
81     udev_unref(udev);
82     libusb_exit(ctx);
83 #else
84     (void) v;
85 #endif /* NM_OS_LINUX */
86 }
87 
nm_usb_get_serial(const nm_usb_dev_t * dev,nm_str_t * serial)88 int nm_usb_get_serial(const nm_usb_dev_t *dev, nm_str_t *serial)
89 {
90     int rc = NM_ERR;
91 #if defined (NM_OS_LINUX)
92     libusb_context *ctx = NULL;
93     libusb_device **list = NULL;
94     int usb_rc;
95     ssize_t dev_count;
96 
97     if (dev == NULL)
98         nm_bug(_("%s: null nm_usb_dev_t pointer"), __func__);
99 
100     if ((usb_rc = libusb_init(&ctx)) != 0)
101         nm_bug("%s: %s", __func__, libusb_strerror(usb_rc));
102 
103     if ((dev_count = libusb_get_device_list(ctx, &list)) < 1)
104         nm_bug(_("%s: libusb_get_device_list failed"), __func__);
105 
106     for (ssize_t n = 0; n < dev_count; n++) {
107         uint8_t bus_num, dev_addr;
108         libusb_device *device = list[n];
109 
110         bus_num = libusb_get_bus_number(device);
111         dev_addr = libusb_get_device_address(device);
112 
113         if ((bus_num == dev->bus_num) &&
114             (dev_addr == dev->dev_addr)) {
115             libusb_device_handle *handle;
116             char serial_buf[NM_USB_SERIAL_LEN] = {0};
117             struct libusb_device_descriptor desc;
118 
119             memset(&desc, 0, sizeof(desc));
120 
121             if (libusb_get_device_descriptor(device, &desc) != 0)
122                 break;
123 
124             if ((usb_rc = libusb_open(device, &handle)) != 0)
125                 nm_bug("%s: %s", __func__, libusb_strerror(usb_rc));
126 
127             if (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
128                         (uint8_t *) serial_buf, NM_USB_SERIAL_LEN) > 0) {
129                 nm_str_alloc_text(serial, serial_buf);
130                 rc = NM_OK;
131             }
132 
133             libusb_close(handle);
134 
135             break;
136         }
137     }
138 
139     /* cleanup */
140     libusb_free_device_list(list, 1);
141     libusb_exit(ctx);
142 #else
143     (void) dev;
144     (void) serial;
145 #endif /* NM_OS_LINUX */
146 
147     return rc;
148 }
149 
nm_usb_vect_ins_cb(void * unit_p,const void * ctx)150 void nm_usb_vect_ins_cb(void *unit_p, const void *ctx)
151 {
152     nm_str_copy(nm_usb_name(unit_p), nm_usb_name(ctx));
153     nm_str_copy(nm_usb_vendor_id(unit_p), nm_usb_vendor_id(ctx));
154     nm_str_copy(nm_usb_product_id(unit_p), nm_usb_product_id(ctx));
155     *nm_usb_bus_num(unit_p) = *nm_usb_bus_num(ctx);
156     *nm_usb_dev_addr(unit_p) = *nm_usb_dev_addr(ctx);
157 }
158 
nm_usb_vect_free_cb(void * unit_p)159 void nm_usb_vect_free_cb(void *unit_p)
160 {
161     nm_str_free(nm_usb_name(unit_p));
162     nm_str_free(nm_usb_vendor_id(unit_p));
163     nm_str_free(nm_usb_product_id(unit_p));
164 }
165 
nm_usb_data_vect_ins_cb(void * unit_p,const void * ctx)166 void nm_usb_data_vect_ins_cb(void *unit_p, const void *ctx)
167 {
168     nm_str_copy(nm_usb_data_serial(unit_p), nm_usb_data_serial(ctx));
169     *nm_usb_data_dev(unit_p) = *nm_usb_data_dev(ctx);
170 }
171 
nm_usb_data_vect_free_cb(void * unit_p)172 void nm_usb_data_vect_free_cb(void *unit_p)
173 {
174     nm_str_free(nm_usb_data_serial(unit_p));
175 }
176 
nm_usb_data_free(nm_usb_data_t * usb)177 void nm_usb_data_free(nm_usb_data_t *usb)
178 {
179     nm_str_free(&usb->serial);
180     nm_usb_dev_free(usb->dev);
181 }
182 
nm_usb_dev_free(nm_usb_dev_t * dev)183 static inline void nm_usb_dev_free(nm_usb_dev_t *dev)
184 {
185     nm_str_free(&dev->name);
186     nm_str_free(&dev->vendor_id);
187     nm_str_free(&dev->product_id);
188 }
189 
190 #if defined (NM_OS_LINUX)
nm_usb_hwdb_get(const char * modalias,const char * key)191 static const char *nm_usb_hwdb_get(const char *modalias, const char *key)
192 {
193     struct udev_list_entry *entry = NULL;
194 
195     udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0)) {
196         if (strcmp(udev_list_entry_get_name(entry), key) == 0)
197             return udev_list_entry_get_value(entry);
198     }
199 
200     return NULL;
201 }
202 
nm_usb_get_vendor(uint16_t vid)203 static const char *nm_usb_get_vendor(uint16_t vid)
204 {
205     char modalias[64];
206 
207     sprintf(modalias, "usb:v%04X*", vid);
208     return nm_usb_hwdb_get(modalias, "ID_VENDOR_FROM_DATABASE");
209 }
210 
nm_usb_get_product(uint16_t vid,uint16_t pid)211 static const char *nm_usb_get_product(uint16_t vid, uint16_t pid)
212 {
213     char modalias[64];
214 
215     sprintf(modalias, "usb:v%04Xp%04X*", vid, pid);
216     return nm_usb_hwdb_get(modalias, "ID_MODEL_FROM_DATABASE");
217 }
218 
nm_usb_get_vendor_str(char * buf,size_t size,uint16_t vid)219 static int nm_usb_get_vendor_str(char *buf, size_t size, uint16_t vid)
220 {
221     const char *cp;
222 
223     if (size < 1)
224         return 0;
225 
226     *buf = 0;
227 
228     if (!(cp = nm_usb_get_vendor(vid)))
229         return 0;
230 
231     return snprintf(buf, size, "%s ", cp);
232 }
233 
nm_usb_get_product_str(char * buf,size_t size,uint16_t vid,uint16_t pid)234 static int nm_usb_get_product_str(char *buf, size_t size, uint16_t vid, uint16_t pid)
235 {
236     const char *cp;
237 
238     if (size < 1)
239         return 0;
240 
241     *buf = 0;
242 
243     if (!(cp = nm_usb_get_product(vid, pid)))
244         return 0;
245 
246     return snprintf(buf, size, "%s", cp);
247 }
248 #endif /* NM_OS_LINUX */
249 
250 /* vim:set ts=4 sw=4: */
251