1 /*
2  * libusb-winusb-bridge.c
3  *
4  * Simple backend for libusb using MS WinUsb. Only the basic functions
5  * necessary for apcctrl are implemented, although the others could be added
6  * fairly easily.
7  */
8 
9 /*
10  * Copyright (C) 2010 Adam Kropelin
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of version 2 of the GNU General
14  * Public License as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this program; if not, write to the Free
23  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  * MA 02110-1335, USA.
25  */
26 
27 #include "libusb-winusb-bridge.h"
28 #include <windows.h>
29 #include <rpc.h>
30 #include <setupapi.h>
31 #include "apc.h"
32 
33 // winusb.dll entrypoints
34 DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize,
35             (HANDLE, PWINUSB_INTERFACE_HANDLE));
36 DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE));
37 DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface,
38             (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE));
39 DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor,
40             (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR,
41              ULONG, PULONG));
42 DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings,
43             (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR));
44 DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation,
45             (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID));
46 DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting,
47             (WINUSB_INTERFACE_HANDLE, UCHAR));
48 DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting,
49             (WINUSB_INTERFACE_HANDLE, PUCHAR));
50 DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe,
51             (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR,
52              PWINUSB_PIPE_INFORMATION));
53 DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy,
54             (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID));
55 DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy,
56             (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID));
57 DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe,
58             (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG,
59              LPOVERLAPPED));
60 DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe,
61             (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG,
62              LPOVERLAPPED));
63 DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer,
64             (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG,
65              PULONG, LPOVERLAPPED));
66 DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe,
67             (WINUSB_INTERFACE_HANDLE, UCHAR));
68 DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe,
69             (WINUSB_INTERFACE_HANDLE, UCHAR));
70 DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe,
71             (WINUSB_INTERFACE_HANDLE, UCHAR));
72 
73 // GUID for apcctrl-owned UPSes, as specified in our INF file
74 // Binary equivalent of {8c534620-f7e6-11de-8a39-0800200c9a66}
75 GUID APCCTRL_DEVICE_GUID =
76 {
77    0x8c534620,
78    0xf7e6,
79    0x11de,
80    {0x8a, 0x39, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66}
81 };
82 
83 #define MAX_USB_DEVICES 10
84 static struct usb_device devices[MAX_USB_DEVICES];
85 static struct usb_bus bus = { NULL, NULL, "bus0", devices };
86 
87 struct usb_dev_handle
88 {
89    HANDLE hnd;
90    WINUSB_INTERFACE_HANDLE fd;
91 };
92 
usb_init(void)93 void usb_init(void)
94 {
95    // Initialize DLL functions
96    if (!WinUsb_Initialize)
97    {
98       DLL_LOAD(winusb.dll, WinUsb_Initialize);
99       DLL_LOAD(winusb.dll, WinUsb_Free);
100       DLL_LOAD(winusb.dll, WinUsb_GetAssociatedInterface);
101       DLL_LOAD(winusb.dll, WinUsb_GetDescriptor);
102       DLL_LOAD(winusb.dll, WinUsb_QueryInterfaceSettings);
103       DLL_LOAD(winusb.dll, WinUsb_QueryDeviceInformation);
104       DLL_LOAD(winusb.dll, WinUsb_SetCurrentAlternateSetting);
105       DLL_LOAD(winusb.dll, WinUsb_GetCurrentAlternateSetting);
106       DLL_LOAD(winusb.dll, WinUsb_QueryPipe);
107       DLL_LOAD(winusb.dll, WinUsb_SetPipePolicy);
108       DLL_LOAD(winusb.dll, WinUsb_GetPipePolicy);
109       DLL_LOAD(winusb.dll, WinUsb_ReadPipe);
110       DLL_LOAD(winusb.dll, WinUsb_WritePipe);
111       DLL_LOAD(winusb.dll, WinUsb_ControlTransfer);
112       DLL_LOAD(winusb.dll, WinUsb_ResetPipe);
113       DLL_LOAD(winusb.dll, WinUsb_AbortPipe);
114       DLL_LOAD(winusb.dll, WinUsb_FlushPipe);
115    }
116 }
117 
usb_find_busses(void)118 int usb_find_busses(void)
119 {
120    return 1;
121 }
122 
usb_get_device_desc(struct usb_device * dev)123 static int usb_get_device_desc(struct usb_device *dev)
124 {
125    struct usb_dev_handle *hnd = usb_open(dev);
126    if (!hnd)
127       return 1;
128 
129    ULONG actlen = 0;
130    if (!WinUsb_GetDescriptor(hnd->fd, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0,
131                              (unsigned char*)&dev->descriptor,
132                              sizeof(dev->descriptor), &actlen)
133        || actlen != sizeof(dev->descriptor))
134    {
135       return 1;
136    }
137 
138    // Descriptor as read from the device is in little-endian format. No need
139    // to convert since this is guaranteed to be Windows which runs only on
140    // little-endian processors.
141 
142    return usb_close(hnd);
143 }
144 
usb_find_devices(void)145 int usb_find_devices(void)
146 {
147    // Get the set of device interfaces that have been matched by our INF
148    HDEVINFO deviceInfo = SetupDiGetClassDevs(
149       &APCCTRL_DEVICE_GUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
150    if (!deviceInfo)
151    {
152       return 0;
153    }
154 
155    // Iterate over all interfaces
156    int ndevs = 0;
157    int devidx = 0;
158    while (ndevs < MAX_USB_DEVICES)
159    {
160       // Get interface data for next interface and attempt to init it
161       SP_DEVICE_INTERFACE_DATA interfaceData;
162       interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
163       if (!SetupDiEnumDeviceInterfaces(
164             deviceInfo, NULL, &APCCTRL_DEVICE_GUID, devidx++, &interfaceData))
165       {
166          break;
167       }
168 
169       // Determine required size for interface detail data
170       ULONG requiredLength = 0;
171       SetupDiGetDeviceInterfaceDetail(
172          deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL);
173 
174       // Allocate storage for interface detail data
175       PSP_DEVICE_INTERFACE_DETAIL_DATA detailData =
176          (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredLength);
177       detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
178 
179       // Fetch interface detail data
180       if (!SetupDiGetDeviceInterfaceDetail(
181             deviceInfo, &interfaceData, detailData, requiredLength,
182             &requiredLength, NULL))
183       {
184          free(detailData);
185          continue;
186       }
187 
188       // Populate device structure
189       struct usb_device *dev = devices+ndevs;
190       memset(dev, 0, sizeof(*dev));
191       strlcpy(dev->filename, detailData->DevicePath, sizeof(dev->filename));
192       dev->bus = &bus;
193       if (ndevs)
194       {
195          dev->prev = devices + ndevs - 1;
196          dev->prev->next = dev;
197       }
198 
199       // Fetch device descriptor structure
200       if (usb_get_device_desc(dev) == 0)
201          ndevs++;
202 
203       free(detailData);
204    }
205 
206    SetupDiDestroyDeviceInfoList(deviceInfo);
207    return ndevs;
208 }
209 
usb_get_busses(void)210 struct usb_bus *usb_get_busses(void)
211 {
212    return &bus;
213 }
214 
usb_open(struct usb_device * dev)215 usb_dev_handle *usb_open(struct usb_device *dev)
216 {
217    // Open generic handle to device
218    HANDLE hnd = CreateFile(dev->filename,
219                            GENERIC_WRITE | GENERIC_READ,
220                            FILE_SHARE_WRITE | FILE_SHARE_READ,
221                            NULL,
222                            OPEN_EXISTING,
223                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
224                            NULL);
225    if (hnd == NULL)
226       return NULL;
227 
228    // Initialize WinUSB for this device and get a WinUSB handle for it
229    WINUSB_INTERFACE_HANDLE fd;
230    if (!WinUsb_Initialize(hnd, &fd))
231    {
232       CloseHandle(hnd);
233       return NULL;
234    }
235 
236    // Device opened successfully. Allocate storage for handles.
237    struct usb_dev_handle *usb =
238       (struct usb_dev_handle *)malloc(sizeof(struct usb_dev_handle));
239    usb->hnd = hnd;
240    usb->fd = fd;
241    return usb;
242 }
243 
usb_close(usb_dev_handle * dev)244 int usb_close(usb_dev_handle *dev)
245 {
246    WinUsb_Free(dev->fd);
247    CloseHandle(dev->hnd);
248    free(dev);
249    return 0;
250 }
251 
usb_set_configuration(usb_dev_handle * dev,int configuration)252 int usb_set_configuration(usb_dev_handle *dev, int configuration)
253 {
254    return 0;
255 }
256 
usb_claim_interface(usb_dev_handle * dev,int iface)257 int usb_claim_interface(usb_dev_handle *dev, int iface)
258 {
259    return 0;
260 }
261 
usb_release_interface(usb_dev_handle * dev,int iface)262 int usb_release_interface(usb_dev_handle *dev, int iface)
263 {
264    return 0;
265 }
266 
usb_control_msg(usb_dev_handle * dev,int requesttype,int request,int value,int index,char * bytes,int size,int timeout)267 int usb_control_msg(usb_dev_handle *dev, int requesttype,
268                     int request, int value, int index,
269                     char *bytes, int size, int timeout)
270 {
271    // Format parameters into a ctrl setup packet
272    // 'timeout' is ignored; WinUSB has a 5 second default timeout on the ctrl
273    // pipe, which is good enough for us.
274    WINUSB_SETUP_PACKET sp;
275    sp.RequestType = requesttype;
276    sp.Request = request;
277    sp.Value = value;
278    sp.Index = index;
279    sp.Length = size;
280 
281    ULONG actlen = 0;
282    if (!WinUsb_ControlTransfer(dev->fd, sp, (unsigned char*)bytes, size, &actlen, NULL))
283       return -GetLastError();
284 
285    return actlen;
286 }
287 
usb_get_string_simple(usb_dev_handle * dev,int index,char * buf,size_t buflen)288 int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf,
289                           size_t buflen)
290 {
291    unsigned char temp[MAXIMUM_USB_STRING_LENGTH];
292 
293    ULONG actlen = 0;
294    if (!WinUsb_GetDescriptor(dev->fd, USB_STRING_DESCRIPTOR_TYPE, index, 0x0409,
295                              temp, sizeof(temp), &actlen))
296    {
297       return -GetLastError();
298    }
299 
300    // Skip first two bytes of result (descriptor id and length), then take
301    // every other byte as a cheap way to convert Unicode to ASCII
302    unsigned int i, j;
303    for (i = 2, j = 0; i < actlen && j < (buflen-1); i+=2, ++j)
304       buf[j] = temp[i];
305    buf[j] = '\0';
306 
307    return strlen(buf);
308 }
309 
usb_interrupt_read(usb_dev_handle * dev,int ep,char * bytes,int size,int timeout)310 int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
311                        int timeout)
312 {
313    // Set timeout on endpoint
314    // It might be more efficient to use async i/o here, but MS docs imply that
315    // you have to create a new sync object for every call, which doesn't seem
316    // efficient at all. So just set the pipe timeout and use sync i/o.
317    ULONG tmp = timeout;
318    if (!WinUsb_SetPipePolicy(dev->fd, ep, PIPE_TRANSFER_TIMEOUT, sizeof(tmp), &tmp))
319       return -EINVAL;
320 
321    // Perform transfer
322    tmp = 0;
323    if (!WinUsb_ReadPipe(dev->fd, ep, (unsigned char*)bytes, size, &tmp, NULL))
324    {
325       tmp = GetLastError();
326       if (tmp == ERROR_SEM_TIMEOUT)
327          return -ETIMEDOUT;
328       else
329          return -EINVAL;
330    }
331 
332    return tmp;
333 }
334 
usb_interrupt_write(usb_dev_handle * dev,int ep,char * bytes,int size,int timeout)335 int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size,
336                        int timeout)
337 {
338    // Set timeout on endpoint
339    // It might be more efficient to use async i/o here, but MS docs imply that
340    // you have to create a new sync object for every call, which doesn't seem
341    // efficient at all. So just set the pipe timeout and use sync i/o.
342    ULONG tmp = timeout;
343    if (!WinUsb_SetPipePolicy(dev->fd, ep, PIPE_TRANSFER_TIMEOUT, sizeof(tmp), &tmp))
344       return -EINVAL;
345 
346    // Perform transfer
347    tmp = 0;
348    if (!WinUsb_WritePipe(dev->fd, ep, (unsigned char*)bytes, size, &tmp, NULL))
349    {
350       tmp = GetLastError();
351       if (tmp == ERROR_SEM_TIMEOUT)
352          return -ETIMEDOUT;
353       else
354          return -EINVAL;
355    }
356 
357    return tmp;
358 }
359 
usb_strerror(void)360 char *usb_strerror(void)
361 {
362    static char buf[256];
363    snprintf(buf, sizeof(buf), "Windows Error #%lu", GetLastError());
364    return buf;
365 }
366 
usb_reset(usb_dev_handle * dev)367 int usb_reset(usb_dev_handle *dev)
368 {
369    return 0;
370 }
371 
usb_set_debug(int level)372 void usb_set_debug(int level)
373 {
374 }
375