2014-01-15 19:48:01 +08:00

393 lines
9.9 KiB
C

/*
* This file is part of the DSLogic project.
*/
#include "libsigrok.h"
#include "hardware/DSLogic/dslogic.h"
#ifdef __linux__
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#endif/*__linux__*/
#ifdef _WIN32
#include <windows.h>
#include <initguid.h>
#include <ddk/usbiodef.h>
#include <Setupapi.h>
#include <tchar.h>
#include <conio.h>
#include <dbt.h>
#include <stdio.h>
#include <winuser.h>
#endif/*_WIN32*/
#ifdef __linux__
static void dev_list_add(struct libusbhp_t *h, const char *path,
unsigned short vid, unsigned short pid)
{
struct dev_list_t *dev =
(struct dev_list_t*)malloc(sizeof(struct dev_list_t));
dev->path = strdup(path);
dev->vid = vid;
dev->pid = pid;
dev->next = NULL;
struct dev_list_t *p = h->devlist;
if(!p) {
h->devlist = dev;
return;
}
while(p->next) {
p = p->next;
}
p->next = dev;
}
static int dev_list_remove(struct libusbhp_t *h, const char *path)
{
struct dev_list_t *p = h->devlist;
if(!p) return 1;
if(!strcmp(p->path, path)) {
h->devlist = p->next;
free(p->path);
free(p);
return 0;
}
while(p->next) {
if(!strcmp(p->next->path, path)) {
struct dev_list_t *pp = p->next;
p->next = pp->next;
free(pp->path);
free(pp->next);
free(pp);
return 0;
}
p = p->next;
}
// Not found
return 1;
}
static int dev_list_find(struct libusbhp_t *h, const char *path,
unsigned short *vid, unsigned short *pid)
{
struct dev_list_t *p = h->devlist;
while(p) {
if(!strcmp(p->path, path)) {
*vid = p->vid;
*pid = p->pid;
return 0;
}
p = p->next;
}
// Not found
return 1;
}
#endif/*__linux__*/
#ifdef _WIN32
SR_PRIV LRESULT CALLBACK WinProcCallback(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
struct libusbhp_t *h = (struct libusbhp_t*)GetWindowLong(hwnd, GWL_USERDATA);
switch(msg) {
case WM_DEVICECHANGE:
{
PDEV_BROADCAST_HDR phdr = (PDEV_BROADCAST_HDR)lp;
if(!phdr || phdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) break;
PDEV_BROADCAST_DEVICEINTERFACE devif =
(PDEV_BROADCAST_DEVICEINTERFACE)lp;
HDEVINFO devinfolist = SetupDiCreateDeviceInfoList(NULL, NULL);
SP_DEVICE_INTERFACE_DATA devifdata;
memset(&devifdata, 0, sizeof(devifdata));
devifdata.cbSize = sizeof(devifdata);
BOOL b = SetupDiOpenDeviceInterface(devinfolist, devif->dbcc_name, 0,
&devifdata);
DWORD required;
SP_DEVICE_INTERFACE_DETAIL_DATA devdetaildata;
memset(&devdetaildata, 0, sizeof(devdetaildata));
devdetaildata.cbSize = sizeof(devdetaildata);
SP_DEVINFO_DATA devinfodata;
memset(&devinfodata, 0, sizeof(devinfodata));
devinfodata.cbSize = sizeof(devinfodata);
b = SetupDiGetDeviceInterfaceDetail(devinfolist, &devifdata,
&devdetaildata,
sizeof(devdetaildata),
&required, &devinfodata);
TCHAR deviceidw[1024];
b = SetupDiGetDeviceInstanceIdW(devinfolist, &devinfodata, deviceidw,
sizeof(deviceidw), NULL);
char deviceid[1024];
//size_t sz;
//wcstombs_s(&sz, deviceid, deviceidw, sizeof(deviceid) - 1);
wcstombs(deviceid, deviceidw, sizeof(deviceid) - 1);
char *vid = strstr(deviceid, "VID_");
if(vid != NULL) vid += 4;
char *pid = strstr(deviceid, "PID_");
if(pid != NULL) pid += 4;
struct libusbhp_device_t *device = NULL;
if(pid || vid) {
device =
(struct libusbhp_device_t*)malloc(sizeof(struct libusbhp_device_t));
}
if(pid) {
pid[4] = '\0';
device->idProduct = (unsigned short)strtol(pid, NULL, 16);
}
if(vid) {
vid[4] = '\0';
device->idVendor = (unsigned short)strtol(vid, NULL, 16);
}
if ((device->idVendor == supported_fx2[0].vid) &&
(device->idProduct == supported_fx2[0].pid)) {
switch(wp) {
case DBT_DEVICEARRIVAL:
if(h->attach) h->attach(device, h->user_data);
break;
case DBT_DEVICEREMOVECOMPLETE:
if(h->detach) h->detach(device, h->user_data);
break;
case DBT_DEVNODES_CHANGED:
default:
break;
}
}
if(device) free(device);
}
break;
default:
break;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
#endif/*OS_WINDOWS*/
SR_API int libusbhp_init(struct libusbhp_t **handle)
{
struct libusbhp_t *h = (struct libusbhp_t *)malloc(sizeof(struct libusbhp_t));
h->attach = NULL;
h->detach = NULL;
h->user_data = NULL;
#ifdef __linux__
h->devlist = NULL;
// create the udev object
h->hotplug = udev_new();
if(!h->hotplug)
{
printf("Cannot create udev object\n");
free(h);
return 1;
}
// create the udev monitor
h->hotplug_monitor = udev_monitor_new_from_netlink(h->hotplug, "udev");
// start receiving hotplug events
udev_monitor_filter_add_match_subsystem_devtype(h->hotplug_monitor,
"usb", "usb_device");
udev_monitor_enable_receiving(h->hotplug_monitor);
struct udev_enumerate *de = udev_enumerate_new (h->hotplug);
udev_enumerate_add_match_subsystem(de, "usb");
udev_enumerate_scan_devices(de);
struct udev_list_entry *lst = udev_enumerate_get_list_entry(de);
while(lst) {
struct udev_device *dev =
udev_device_new_from_syspath(h->hotplug,
udev_list_entry_get_name(lst));
if(udev_device_get_devnode(dev)) {
unsigned short idVendor =
strtol(udev_device_get_sysattr_value(dev, "idVendor"), NULL, 16);
unsigned short idProduct =
strtol(udev_device_get_sysattr_value(dev, "idProduct"), NULL, 16);
dev_list_add(h, udev_device_get_devnode(dev), idVendor, idProduct);
}
udev_device_unref(dev);
lst = udev_list_entry_get_next(lst);
}
udev_enumerate_unref(de);
#endif/*__linux__*/
#ifdef _WIN32
memset(&h->wcex, 0, sizeof(h->wcex));
h->wcex.cbSize = sizeof(WNDCLASSEX);
h->wcex.lpfnWndProc = WinProcCallback;
h->wcex.hInstance = GetModuleHandle(NULL);
h->wcex.lpszClassName = TEXT("UsbHotplugClass");
h->wcex.cbWndExtra = sizeof(struct libusbhp_t*); // Size of data.
RegisterClassEx(&h->wcex);
h->hwnd =
CreateWindowEx(0, h->wcex.lpszClassName, TEXT("UsbHotplug"), 0, 0, 0, 0,
0, 0, NULL, GetModuleHandle(NULL), NULL);
SetWindowLong(h->hwnd, GWL_USERDATA, (LONG)h);
DEV_BROADCAST_DEVICEINTERFACE *filter =
(DEV_BROADCAST_DEVICEINTERFACE*)malloc(sizeof(DEV_BROADCAST_DEVICEINTERFACE));
memset(filter, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
filter->dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
filter->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
filter->dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
h->hDeviceNotify =
RegisterDeviceNotification(h->hwnd, filter, DEVICE_NOTIFY_WINDOW_HANDLE);
if(h->hDeviceNotify == 0) {
//printf("RegisterDeviceNotification error\n");
free(h);
return 1;
}
#endif/*_WIN32*/
*handle = h;
return 0;
}
SR_API void libusbhp_exit(struct libusbhp_t *h)
{
#ifdef __linux__
// destroy the udev monitor
udev_monitor_unref(h->hotplug_monitor);
// destroy the udev object
udev_unref(h->hotplug);
#endif/*__linux__*/
#ifdef _WIN32
UnregisterDeviceNotification(h->hDeviceNotify);
DestroyWindow(h->hwnd);
UnregisterClass(h->wcex.lpszClassName, h->wcex.hInstance);
#endif/*_WIN32*/
free(h);
}
SR_API int libusbhp_handle_events_timeout(struct libusbhp_t *h, struct timeval *tv)
{
int ms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
#ifdef __linux__
// create the poll item
struct pollfd items[1];
items[0].fd = udev_monitor_get_fd(h->hotplug_monitor);
items[0].events = POLLIN;
items[0].revents = 0;
// while there are hotplug events to process
while(poll(items, 1, ms) > 0) {
// receive the relevant device
struct udev_device* dev = udev_monitor_receive_device(h->hotplug_monitor);
if(!dev) {
// error receiving device, skip it
continue;
}
if(!strcmp(udev_device_get_action(dev), "add")) {
struct libusbhp_device_t device;
device.idVendor =
strtol(udev_device_get_sysattr_value(dev, "idVendor"), NULL, 16);
device.idProduct =
strtol(udev_device_get_sysattr_value(dev, "idProduct"), NULL, 16);
dev_list_add(h, udev_device_get_devnode(dev),
device.idVendor, device.idProduct);
if(h->attach) h->attach(&device, h->user_data);
}
if(!strcmp(udev_device_get_action(dev), "remove")) {
struct libusbhp_device_t device;
int res = dev_list_find(h, udev_device_get_devnode(dev),
&device.idVendor, &device.idProduct);
if(res) {
if(h->detach) h->detach(NULL, h->user_data);
} else {
dev_list_remove(h, udev_device_get_devnode(dev));
if(h->detach) h->detach(&device, h->user_data);
}
}
// destroy the relevant device
udev_device_unref(dev);
// clear the revents
items[0].revents = 0;
}
#endif/*__linux__*/
#ifdef _WIN32
UINT_PTR timer = SetTimer(h->hwnd, 0, ms, NULL);
MSG msg;
int ret = GetMessage(&msg, NULL, 0, 0);
if(ret <= 0) return 0;
TranslateMessage(&msg);
DispatchMessage(&msg);
KillTimer(h->hwnd, timer);
#endif/*_WIN32*/
return 0;
}
SR_API void libusbhp_register_hotplug_listeners(struct libusbhp_t *handle,
libusbhp_hotplug_cb_fn connected_cb,
libusbhp_hotplug_cb_fn disconnected_cb,
void *user_data)
{
handle->attach = connected_cb;
handle->detach = disconnected_cb;
handle->user_data = user_data;
}