]> sigrok.org Git - libsigrok.git/blame - libusbhp.c
Code drop from DreamSourceLabs first source release.
[libsigrok.git] / libusbhp.c
CommitLineData
f1b296fc
BV
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__
34static 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
57static 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
85static 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
104SR_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
197SR_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
292SR_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
311SR_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
384SR_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}