core: update usbi_dbg to take the context as an argument
[platform/upstream/libusb.git] / libusb / os / events_windows.c
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
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
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
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
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
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
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
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
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
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
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(ctx, "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(ctx, "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 }