1 /*
2 * libusb event abstraction on Microsoft Windows
3 *
4 * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <config.h>
22
23 #include "libusbi.h"
24 #include "windows_common.h"
25
usbi_create_event(usbi_event_t * event)26 int usbi_create_event(usbi_event_t *event)
27 {
28 event->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
29 if (event->hEvent == NULL) {
30 usbi_err(NULL, "CreateEvent failed: %s", windows_error_str(0));
31 return LIBUSB_ERROR_OTHER;
32 }
33
34 return 0;
35 }
36
usbi_destroy_event(usbi_event_t * event)37 void usbi_destroy_event(usbi_event_t *event)
38 {
39 if (!CloseHandle(event->hEvent))
40 usbi_warn(NULL, "CloseHandle failed: %s", windows_error_str(0));
41 }
42
usbi_signal_event(usbi_event_t * event)43 void usbi_signal_event(usbi_event_t *event)
44 {
45 if (!SetEvent(event->hEvent))
46 usbi_warn(NULL, "SetEvent failed: %s", windows_error_str(0));
47 }
48
usbi_clear_event(usbi_event_t * event)49 void usbi_clear_event(usbi_event_t *event)
50 {
51 if (!ResetEvent(event->hEvent))
52 usbi_warn(NULL, "ResetEvent failed: %s", windows_error_str(0));
53 }
54
55 #ifdef HAVE_OS_TIMER
usbi_create_timer(usbi_timer_t * timer)56 int usbi_create_timer(usbi_timer_t *timer)
57 {
58 timer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
59 if (timer->hTimer == NULL) {
60 usbi_warn(NULL, "CreateWaitableTimer failed: %s", windows_error_str(0));
61 return LIBUSB_ERROR_OTHER;
62 }
63
64 return 0;
65 }
66
usbi_destroy_timer(usbi_timer_t * timer)67 void usbi_destroy_timer(usbi_timer_t *timer)
68 {
69 if (!CloseHandle(timer->hTimer))
70 usbi_warn(NULL, "CloseHandle failed: %s", windows_error_str(0));
71 }
72
usbi_arm_timer(usbi_timer_t * timer,const struct timespec * timeout)73 int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout)
74 {
75 struct timespec systime, remaining;
76 FILETIME filetime;
77 LARGE_INTEGER dueTime;
78
79 /* Transfer timeouts are based on the monotonic clock and the waitable
80 * timers on the system clock. This requires a conversion between the
81 * two, so we calculate the remaining time relative to the monotonic
82 * clock and calculate an absolute system time for the timer expiration.
83 * Note that if the timeout has already passed, the remaining time will
84 * be negative and thus an absolute system time in the past will be set.
85 * This works just as intended because the timer becomes signalled
86 * immediately. */
87 usbi_get_monotonic_time(&systime);
88
89 TIMESPEC_SUB(timeout, &systime, &remaining);
90
91 GetSystemTimeAsFileTime(&filetime);
92 dueTime.LowPart = filetime.dwLowDateTime;
93 dueTime.HighPart = filetime.dwHighDateTime;
94 dueTime.QuadPart += (remaining.tv_sec * 10000000LL) + (remaining.tv_nsec / 100LL);
95
96 if (!SetWaitableTimer(timer->hTimer, &dueTime, 0, NULL, NULL, FALSE)) {
97 usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0));
98 return LIBUSB_ERROR_OTHER;
99 }
100
101 return 0;
102 }
103
usbi_disarm_timer(usbi_timer_t * timer)104 int usbi_disarm_timer(usbi_timer_t *timer)
105 {
106 LARGE_INTEGER dueTime;
107
108 /* A manual-reset waitable timer will stay in the signalled state until
109 * another call to SetWaitableTimer() is made. It is possible that the
110 * timer has already expired by the time we come in to disarm it, so to
111 * be entirely sure the timer is disarmed and not in the signalled state,
112 * we will set it with an impossibly large expiration and immediately
113 * cancel. */
114 dueTime.QuadPart = LLONG_MAX;
115 if (!SetWaitableTimer(timer->hTimer, &dueTime, 0, NULL, NULL, FALSE)) {
116 usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0));
117 return LIBUSB_ERROR_OTHER;
118 }
119
120 if (!CancelWaitableTimer(timer->hTimer)) {
121 usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0));
122 return LIBUSB_ERROR_OTHER;
123 }
124
125 return 0;
126 }
127 #endif
128
usbi_alloc_event_data(struct libusb_context * ctx)129 int usbi_alloc_event_data(struct libusb_context *ctx)
130 {
131 struct usbi_event_source *ievent_source;
132 HANDLE *handles;
133 size_t i = 0;
134
135 /* Event sources are only added during usbi_io_init(). We should not
136 * be running this function again if the event data has already been
137 * allocated. */
138 if (ctx->event_data) {
139 usbi_warn(ctx, "program assertion failed - event data already allocated");
140 return LIBUSB_ERROR_OTHER;
141 }
142
143 ctx->event_data_cnt = 0;
144 for_each_event_source(ctx, ievent_source)
145 ctx->event_data_cnt++;
146
147 /* We only expect up to two HANDLEs to wait on, one for the internal
148 * signalling event and the other for the timer. */
149 if (ctx->event_data_cnt != 1 && ctx->event_data_cnt != 2) {
150 usbi_err(ctx, "program assertion failed - expected exactly 1 or 2 HANDLEs");
151 return LIBUSB_ERROR_OTHER;
152 }
153
154 handles = calloc(ctx->event_data_cnt, sizeof(HANDLE));
155 if (!handles)
156 return LIBUSB_ERROR_NO_MEM;
157
158 for_each_event_source(ctx, ievent_source) {
159 handles[i] = ievent_source->data.os_handle;
160 i++;
161 }
162
163 ctx->event_data = handles;
164 return 0;
165 }
166
usbi_wait_for_events(struct libusb_context * ctx,struct usbi_reported_events * reported_events,int timeout_ms)167 int usbi_wait_for_events(struct libusb_context *ctx,
168 struct usbi_reported_events *reported_events, int timeout_ms)
169 {
170 HANDLE *handles = ctx->event_data;
171 DWORD num_handles = (DWORD)ctx->event_data_cnt;
172 DWORD result;
173
174 usbi_dbg("WaitForMultipleObjects() for %lu HANDLEs with timeout in %dms", ULONG_CAST(num_handles), timeout_ms);
175 result = WaitForMultipleObjects(num_handles, handles, FALSE, (DWORD)timeout_ms);
176 usbi_dbg("WaitForMultipleObjects() returned %lu", ULONG_CAST(result));
177 if (result == WAIT_TIMEOUT) {
178 if (usbi_using_timer(ctx))
179 goto done;
180 return LIBUSB_ERROR_TIMEOUT;
181 } else if (result == WAIT_FAILED) {
182 usbi_err(ctx, "WaitForMultipleObjects() failed: %s", windows_error_str(0));
183 return LIBUSB_ERROR_IO;
184 }
185
186 result -= WAIT_OBJECT_0;
187
188 /* handles[0] is always the internal signalling event */
189 if (result == 0)
190 reported_events->event_triggered = 1;
191 else
192 reported_events->event_triggered = 0;
193
194 #ifdef HAVE_OS_TIMER
195 /* on timer configurations, handles[1] is the timer */
196 if (usbi_using_timer(ctx)) {
197 /* The WaitForMultipleObjects() function reports the index of
198 * the first object that became signalled. If the internal
199 * signalling event was reported, we need to also check and
200 * report whether the timer is in the signalled state. */
201 if (result == 1 || WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0)
202 reported_events->timer_triggered = 1;
203 else
204 reported_events->timer_triggered = 0;
205 } else {
206 reported_events->timer_triggered = 0;
207 }
208 #endif
209
210 done:
211 /* no events are ever reported to the backend */
212 reported_events->num_ready = 0;
213 return LIBUSB_SUCCESS;
214 }
215