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