]> sigrok.org Git - libsigrok.git/blob - libusbhp.c
Code drop from DreamSourceLabs first source release.
[libsigrok.git] / libusbhp.c
1 /*
2  * This file is part of the DSLogic project.
3  */
4
5 #include "libsigrok.h"
6 #include "hardware/DSLogic/dslogic.h"
7
8 #ifdef __linux__
9 #include <poll.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #endif/*__linux__*/
15
16 #ifdef _WIN32
17 #include <windows.h>
18
19 #include <initguid.h>
20 #include <ddk/usbiodef.h>
21 #include <Setupapi.h>
22
23 #include <tchar.h>
24 #include <conio.h>
25 #include <dbt.h>
26 #include <stdio.h>
27 #include <winuser.h>
28
29 #endif/*_WIN32*/
30
31
32
33 #ifdef __linux__
34 static void dev_list_add(struct libusbhp_t *h, const char *path,
35                          unsigned short vid, unsigned short pid)
36 {
37   struct dev_list_t *dev =
38     (struct dev_list_t*)malloc(sizeof(struct dev_list_t));
39   dev->path = strdup(path);
40   dev->vid = vid;
41   dev->pid = pid;
42   dev->next = NULL;
43
44   struct dev_list_t *p = h->devlist;
45   if(!p) {
46     h->devlist = dev;
47     return;
48   }
49
50   while(p->next) {
51     p = p->next;
52   }
53
54   p->next = dev;
55 }
56
57 static int dev_list_remove(struct libusbhp_t *h, const char *path)
58 {
59   struct dev_list_t *p = h->devlist;
60   if(!p) return 1;
61
62   if(!strcmp(p->path, path)) {
63     h->devlist = p->next;
64     free(p->path);
65     free(p);
66     return 0;
67   }
68
69   while(p->next) {
70     if(!strcmp(p->next->path, path)) {
71       struct dev_list_t *pp = p->next;
72       p->next = pp->next;
73       free(pp->path);
74       free(pp->next);
75       free(pp);
76       return 0;
77     }
78     p = p->next;
79   }
80
81   // Not found
82   return 1;
83 }
84
85 static int dev_list_find(struct libusbhp_t *h, const char *path,
86                          unsigned short *vid, unsigned short *pid)
87 {
88   struct dev_list_t *p = h->devlist;
89   while(p) {
90     if(!strcmp(p->path, path)) {
91       *vid = p->vid;
92       *pid = p->pid;
93       return 0;
94     }
95     p = p->next;
96   }
97
98   // Not found
99   return 1;
100 }
101 #endif/*__linux__*/
102
103 #ifdef _WIN32
104 SR_PRIV LRESULT CALLBACK WinProcCallback(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
105 {
106   struct libusbhp_t *h = (struct libusbhp_t*)GetWindowLong(hwnd, GWL_USERDATA);
107
108   switch(msg) {
109   case WM_DEVICECHANGE:
110     {
111       PDEV_BROADCAST_HDR phdr = (PDEV_BROADCAST_HDR)lp;
112
113       if(!phdr || phdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) break;
114
115       PDEV_BROADCAST_DEVICEINTERFACE devif =
116         (PDEV_BROADCAST_DEVICEINTERFACE)lp;
117
118       HDEVINFO devinfolist = SetupDiCreateDeviceInfoList(NULL, NULL);
119       
120       SP_DEVICE_INTERFACE_DATA devifdata;
121       memset(&devifdata, 0, sizeof(devifdata));
122       devifdata.cbSize = sizeof(devifdata);
123       BOOL b = SetupDiOpenDeviceInterface(devinfolist, devif->dbcc_name, 0,
124                                           &devifdata);
125       
126       DWORD required;
127       SP_DEVICE_INTERFACE_DETAIL_DATA devdetaildata;
128       memset(&devdetaildata, 0, sizeof(devdetaildata));
129       devdetaildata.cbSize = sizeof(devdetaildata);
130
131       SP_DEVINFO_DATA devinfodata;
132       memset(&devinfodata, 0, sizeof(devinfodata));
133       devinfodata.cbSize = sizeof(devinfodata);
134       b = SetupDiGetDeviceInterfaceDetail(devinfolist, &devifdata,
135                                           &devdetaildata,
136                                           sizeof(devdetaildata),
137                                           &required, &devinfodata);
138      
139       TCHAR deviceidw[1024];
140       b = SetupDiGetDeviceInstanceIdW(devinfolist, &devinfodata, deviceidw,
141                                       sizeof(deviceidw), NULL);
142
143       char deviceid[1024];
144       //size_t sz;
145       //wcstombs_s(&sz, deviceid, deviceidw, sizeof(deviceid) - 1);
146       wcstombs(deviceid, deviceidw, sizeof(deviceid) - 1);
147
148       char *vid = strstr(deviceid, "VID_");
149       if(vid != NULL) vid += 4;
150
151       char *pid = strstr(deviceid, "PID_");
152       if(pid != NULL)  pid += 4;
153
154       struct libusbhp_device_t *device = NULL;
155
156       if(pid || vid) {
157         device =
158           (struct libusbhp_device_t*)malloc(sizeof(struct libusbhp_device_t));
159       }
160
161       if(pid) {
162         pid[4] = '\0';
163         device->idProduct = (unsigned short)strtol(pid, NULL, 16); 
164       }
165
166       if(vid) {
167         vid[4] = '\0';
168         device->idVendor = (unsigned short)strtol(vid, NULL, 16);
169       }
170
171       if ((device->idVendor == supported_fx2[0].vid) &&
172           (device->idProduct == supported_fx2[0].pid)) {
173           switch(wp) {
174           case DBT_DEVICEARRIVAL:
175             if(h->attach) h->attach(device, h->user_data);
176             break;
177           case DBT_DEVICEREMOVECOMPLETE:
178             if(h->detach) h->detach(device, h->user_data);
179             break;
180           case DBT_DEVNODES_CHANGED:
181           default:
182             break;
183           }
184       }
185
186       if(device) free(device);
187     }
188     break;
189   default:
190     break;
191   }
192   
193   return DefWindowProc(hwnd, msg, wp, lp);
194 }
195 #endif/*OS_WINDOWS*/
196
197 SR_API int libusbhp_init(struct libusbhp_t **handle)
198 {
199   struct libusbhp_t *h = (struct libusbhp_t *)malloc(sizeof(struct libusbhp_t));
200
201   h->attach = NULL;
202   h->detach = NULL;
203   h->user_data = NULL;
204
205 #ifdef __linux__
206   h->devlist = NULL;
207
208   // create the udev object
209   h->hotplug = udev_new();
210   if(!h->hotplug)
211   {
212     printf("Cannot create udev object\n");
213     free(h);
214     return 1;
215   }
216
217   // create the udev monitor
218   h->hotplug_monitor = udev_monitor_new_from_netlink(h->hotplug, "udev");
219
220   // start receiving hotplug events
221   udev_monitor_filter_add_match_subsystem_devtype(h->hotplug_monitor,
222                                                   "usb", "usb_device");
223   udev_monitor_enable_receiving(h->hotplug_monitor);
224
225   struct udev_enumerate *de = udev_enumerate_new (h->hotplug);
226   udev_enumerate_add_match_subsystem(de, "usb");
227   udev_enumerate_scan_devices(de);
228
229   struct udev_list_entry *lst = udev_enumerate_get_list_entry(de);
230   while(lst) {
231     struct udev_device *dev =
232       udev_device_new_from_syspath(h->hotplug,
233                                    udev_list_entry_get_name(lst));
234
235     if(udev_device_get_devnode(dev)) {
236       unsigned short idVendor =
237         strtol(udev_device_get_sysattr_value(dev, "idVendor"), NULL, 16); 
238       unsigned short idProduct =
239         strtol(udev_device_get_sysattr_value(dev, "idProduct"), NULL, 16); 
240
241       dev_list_add(h, udev_device_get_devnode(dev), idVendor, idProduct);
242     }
243
244     udev_device_unref(dev);
245
246     lst = udev_list_entry_get_next(lst);
247   }
248
249   udev_enumerate_unref(de);
250
251 #endif/*__linux__*/
252
253 #ifdef _WIN32
254   memset(&h->wcex, 0, sizeof(h->wcex));
255   h->wcex.cbSize = sizeof(WNDCLASSEX);
256   h->wcex.lpfnWndProc = WinProcCallback;
257   h->wcex.hInstance = GetModuleHandle(NULL);  
258   h->wcex.lpszClassName = TEXT("UsbHotplugClass");
259   h->wcex.cbWndExtra = sizeof(struct libusbhp_t*); // Size of data.
260
261   RegisterClassEx(&h->wcex);
262
263   h->hwnd =
264     CreateWindowEx(0, h->wcex.lpszClassName, TEXT("UsbHotplug"), 0, 0, 0, 0,
265                    0, 0, NULL, GetModuleHandle(NULL), NULL);
266
267   SetWindowLong(h->hwnd, GWL_USERDATA, (LONG)h);
268
269
270   DEV_BROADCAST_DEVICEINTERFACE *filter =
271   (DEV_BROADCAST_DEVICEINTERFACE*)malloc(sizeof(DEV_BROADCAST_DEVICEINTERFACE));
272
273   memset(filter, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
274   filter->dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
275   filter->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
276   filter->dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
277
278   h->hDeviceNotify =
279     RegisterDeviceNotification(h->hwnd, filter, DEVICE_NOTIFY_WINDOW_HANDLE);
280   
281   if(h->hDeviceNotify == 0) {
282     //printf("RegisterDeviceNotification error\n");
283     free(h);
284     return 1;
285   }
286 #endif/*_WIN32*/
287
288   *handle = h;
289   return 0;
290 }
291
292 SR_API void libusbhp_exit(struct libusbhp_t *h)
293 {
294 #ifdef __linux__
295   // destroy the udev monitor
296   udev_monitor_unref(h->hotplug_monitor);
297
298   // destroy the udev object
299   udev_unref(h->hotplug);
300 #endif/*__linux__*/
301
302 #ifdef _WIN32
303   UnregisterDeviceNotification(h->hDeviceNotify);
304   DestroyWindow(h->hwnd);
305   UnregisterClass(h->wcex.lpszClassName, h->wcex.hInstance);
306 #endif/*_WIN32*/
307
308   free(h);
309 }
310
311 SR_API int libusbhp_handle_events_timeout(struct libusbhp_t *h, struct timeval *tv)
312 {
313   int ms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
314
315 #ifdef __linux__
316   // create the poll item
317   struct pollfd items[1];
318   items[0].fd = udev_monitor_get_fd(h->hotplug_monitor);
319   items[0].events = POLLIN;
320   items[0].revents = 0;
321
322   // while there are hotplug events to process
323   while(poll(items, 1, ms) > 0) {    
324     // receive the relevant device
325     struct udev_device* dev = udev_monitor_receive_device(h->hotplug_monitor);
326     if(!dev) {
327       // error receiving device, skip it
328       continue;
329     }
330
331     if(!strcmp(udev_device_get_action(dev), "add")) {
332       struct libusbhp_device_t device;
333
334       device.idVendor =
335         strtol(udev_device_get_sysattr_value(dev, "idVendor"), NULL, 16); 
336       device.idProduct =
337         strtol(udev_device_get_sysattr_value(dev, "idProduct"), NULL, 16); 
338
339       dev_list_add(h, udev_device_get_devnode(dev),
340                    device.idVendor, device.idProduct);
341
342       if(h->attach) h->attach(&device, h->user_data);
343     }
344
345     if(!strcmp(udev_device_get_action(dev), "remove")) {
346       struct libusbhp_device_t device;
347
348       int res = dev_list_find(h, udev_device_get_devnode(dev),
349                               &device.idVendor, &device.idProduct);
350
351       if(res) {
352         if(h->detach) h->detach(NULL, h->user_data);
353       } else {
354         dev_list_remove(h, udev_device_get_devnode(dev));
355         if(h->detach) h->detach(&device, h->user_data);
356       }
357     }
358
359     // destroy the relevant device
360     udev_device_unref(dev);
361
362     // clear the revents
363     items[0].revents = 0;
364   }
365 #endif/*__linux__*/
366
367 #ifdef _WIN32
368   UINT_PTR timer = SetTimer(h->hwnd, 0, ms, NULL);
369
370   MSG msg; 
371   int ret = GetMessage(&msg, NULL, 0, 0);
372
373   if(ret <= 0) return 0;
374   
375   TranslateMessage(&msg);
376   DispatchMessage(&msg);
377
378   KillTimer(h->hwnd, timer);
379 #endif/*_WIN32*/
380
381   return 0;
382 }
383
384 SR_API void libusbhp_register_hotplug_listeners(struct libusbhp_t *handle,
385                                          libusbhp_hotplug_cb_fn connected_cb,
386                                          libusbhp_hotplug_cb_fn disconnected_cb,
387                                          void *user_data)
388 {
389   handle->attach = connected_cb;
390   handle->detach = disconnected_cb;
391   handle->user_data = user_data;
392 }