• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libusb event abstraction on POSIX platforms
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 "libusbi.h"
22 
23 #include <errno.h>
24 #include <fcntl.h>
25 #ifdef HAVE_EVENTFD
26 #include <sys/eventfd.h>
27 #endif
28 #ifdef HAVE_TIMERFD
29 #include <sys/timerfd.h>
30 #endif
31 #include <unistd.h>
32 
33 #ifdef HAVE_EVENTFD
34 #define EVENT_READ_FD(e)	((e)->eventfd)
35 #define EVENT_WRITE_FD(e)	((e)->eventfd)
36 #else
37 #define EVENT_READ_FD(e)	((e)->pipefd[0])
38 #define EVENT_WRITE_FD(e)	((e)->pipefd[1])
39 #endif
40 
41 #ifdef HAVE_NFDS_T
42 typedef nfds_t usbi_nfds_t;
43 #else
44 typedef unsigned int usbi_nfds_t;
45 #endif
46 
usbi_create_event(usbi_event_t * event)47 int usbi_create_event(usbi_event_t *event)
48 {
49 #ifdef HAVE_EVENTFD
50 	event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
51 	if (event->eventfd == -1) {
52 		usbi_err(NULL, "failed to create eventfd, errno=%d", errno);
53 		return LIBUSB_ERROR_OTHER;
54 	}
55 
56 	return 0;
57 #else
58 #if defined(HAVE_PIPE2)
59 	int ret = pipe2(event->pipefd, O_CLOEXEC);
60 #else
61 	int ret = pipe(event->pipefd);
62 #endif
63 
64 	if (ret != 0) {
65 		usbi_err(NULL, "failed to create pipe, errno=%d", errno);
66 		return LIBUSB_ERROR_OTHER;
67 	}
68 
69 #if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
70 	ret = fcntl(event->pipefd[0], F_GETFD);
71 	if (ret == -1) {
72 		usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
73 		goto err_close_pipe;
74 	}
75 	ret = fcntl(event->pipefd[0], F_SETFD, ret | FD_CLOEXEC);
76 	if (ret == -1) {
77 		usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
78 		goto err_close_pipe;
79 	}
80 
81 	ret = fcntl(event->pipefd[1], F_GETFD);
82 	if (ret == -1) {
83 		usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
84 		goto err_close_pipe;
85 	}
86 	ret = fcntl(event->pipefd[1], F_SETFD, ret | FD_CLOEXEC);
87 	if (ret == -1) {
88 		usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
89 		goto err_close_pipe;
90 	}
91 #endif
92 
93 	ret = fcntl(event->pipefd[1], F_GETFL);
94 	if (ret == -1) {
95 		usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno);
96 		goto err_close_pipe;
97 	}
98 	ret = fcntl(event->pipefd[1], F_SETFL, ret | O_NONBLOCK);
99 	if (ret == -1) {
100 		usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno);
101 		goto err_close_pipe;
102 	}
103 
104 	return 0;
105 
106 err_close_pipe:
107 	close(event->pipefd[1]);
108 	close(event->pipefd[0]);
109 	return LIBUSB_ERROR_OTHER;
110 #endif
111 }
112 
usbi_destroy_event(usbi_event_t * event)113 void usbi_destroy_event(usbi_event_t *event)
114 {
115 #ifdef HAVE_EVENTFD
116 	if (close(event->eventfd) == -1)
117 		usbi_warn(NULL, "failed to close eventfd, errno=%d", errno);
118 #else
119 	if (close(event->pipefd[1]) == -1)
120 		usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno);
121 	if (close(event->pipefd[0]) == -1)
122 		usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno);
123 #endif
124 }
125 
usbi_signal_event(usbi_event_t * event)126 void usbi_signal_event(usbi_event_t *event)
127 {
128 	uint64_t dummy = 1;
129 	ssize_t r;
130 
131 	r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy));
132 	if (r != sizeof(dummy))
133 		usbi_warn(NULL, "event write failed");
134 }
135 
usbi_clear_event(usbi_event_t * event)136 void usbi_clear_event(usbi_event_t *event)
137 {
138 	uint64_t dummy;
139 	ssize_t r;
140 
141 	r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy));
142 	if (r != sizeof(dummy))
143 		usbi_warn(NULL, "event read failed");
144 }
145 
146 #ifdef HAVE_TIMERFD
usbi_create_timer(usbi_timer_t * timer)147 int usbi_create_timer(usbi_timer_t *timer)
148 {
149 	timer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
150 	if (timer->timerfd == -1) {
151 		usbi_warn(NULL, "failed to create timerfd, errno=%d", errno);
152 		return LIBUSB_ERROR_OTHER;
153 	}
154 
155 	return 0;
156 }
157 
usbi_destroy_timer(usbi_timer_t * timer)158 void usbi_destroy_timer(usbi_timer_t *timer)
159 {
160 	if (close(timer->timerfd) == -1)
161 		usbi_warn(NULL, "failed to close timerfd, errno=%d", errno);
162 }
163 
usbi_arm_timer(usbi_timer_t * timer,const struct timespec * timeout)164 int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout)
165 {
166 	const struct itimerspec it = { { 0, 0 }, { timeout->tv_sec, timeout->tv_nsec } };
167 
168 	if (timerfd_settime(timer->timerfd, TFD_TIMER_ABSTIME, &it, NULL) == -1) {
169 		usbi_warn(NULL, "failed to arm timerfd, errno=%d", errno);
170 		return LIBUSB_ERROR_OTHER;
171 	}
172 
173 	return 0;
174 }
175 
usbi_disarm_timer(usbi_timer_t * timer)176 int usbi_disarm_timer(usbi_timer_t *timer)
177 {
178 	const struct itimerspec it = { { 0, 0 }, { 0, 0 } };
179 
180 	if (timerfd_settime(timer->timerfd, 0, &it, NULL) == -1) {
181 		usbi_warn(NULL, "failed to disarm timerfd, errno=%d", errno);
182 		return LIBUSB_ERROR_OTHER;
183 	}
184 
185 	return 0;
186 }
187 #endif
188 
usbi_alloc_event_data(struct libusb_context * ctx)189 int usbi_alloc_event_data(struct libusb_context *ctx)
190 {
191 	struct usbi_event_source *ievent_source;
192 	struct pollfd *fds;
193 	size_t i = 0;
194 
195 	if (ctx->event_data) {
196 		free(ctx->event_data);
197 		ctx->event_data = NULL;
198 	}
199 
200 	ctx->event_data_cnt = 0;
201 	for_each_event_source(ctx, ievent_source)
202 		ctx->event_data_cnt++;
203 
204 	fds = calloc(ctx->event_data_cnt, sizeof(*fds));
205 	if (!fds)
206 		return LIBUSB_ERROR_NO_MEM;
207 
208 	for_each_event_source(ctx, ievent_source) {
209 		fds[i].fd = ievent_source->data.os_handle;
210 		fds[i].events = ievent_source->data.poll_events;
211 		i++;
212 	}
213 
214 	ctx->event_data = fds;
215 	return 0;
216 }
217 
usbi_wait_for_events(struct libusb_context * ctx,struct usbi_reported_events * reported_events,int timeout_ms)218 int usbi_wait_for_events(struct libusb_context *ctx,
219 	struct usbi_reported_events *reported_events, int timeout_ms)
220 {
221 	struct pollfd *fds = ctx->event_data;
222 	usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
223 	int internal_fds, num_ready;
224 
225 	usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
226 	num_ready = poll(fds, nfds, timeout_ms);
227 	usbi_dbg("poll() returned %d", num_ready);
228 	if (num_ready == 0) {
229 		if (usbi_using_timer(ctx))
230 			goto done;
231 		return LIBUSB_ERROR_TIMEOUT;
232 	} else if (num_ready == -1) {
233 		if (errno == EINTR)
234 			return LIBUSB_ERROR_INTERRUPTED;
235 		usbi_err(ctx, "poll() failed, errno=%d", errno);
236 		return LIBUSB_ERROR_IO;
237 	}
238 
239 	/* fds[0] is always the internal signalling event */
240 	if (fds[0].revents) {
241 		reported_events->event_triggered = 1;
242 		num_ready--;
243 	} else {
244 		reported_events->event_triggered = 0;
245 	}
246 
247 #ifdef HAVE_OS_TIMER
248 	/* on timer configurations, fds[1] is the timer */
249 	if (usbi_using_timer(ctx) && fds[1].revents) {
250 		reported_events->timer_triggered = 1;
251 		num_ready--;
252 	} else {
253 		reported_events->timer_triggered = 0;
254 	}
255 #endif
256 
257 	if (!num_ready)
258 		goto done;
259 
260 	/* the backend will never need to attempt to handle events on the
261 	 * library's internal file descriptors, so we determine how many are
262 	 * in use internally for this context and skip these when passing any
263 	 * remaining pollfds to the backend. */
264 	internal_fds = usbi_using_timer(ctx) ? 2 : 1;
265 	fds += internal_fds;
266 	nfds -= internal_fds;
267 
268 	usbi_mutex_lock(&ctx->event_data_lock);
269 	if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
270 		struct usbi_event_source *ievent_source;
271 
272 		for_each_removed_event_source(ctx, ievent_source) {
273 			usbi_nfds_t n;
274 
275 			for (n = 0; n < nfds; n++) {
276 				if (ievent_source->data.os_handle != fds[n].fd)
277 					continue;
278 				if (!fds[n].revents)
279 					continue;
280 				/* pollfd was removed between the creation of the fds array and
281 				 * here. remove triggered revent as it is no longer relevant. */
282 				usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
283 				fds[n].revents = 0;
284 				num_ready--;
285 				break;
286 			}
287 		}
288 	}
289 	usbi_mutex_unlock(&ctx->event_data_lock);
290 
291 	if (num_ready) {
292 		assert(num_ready > 0);
293 		reported_events->event_data = fds;
294 		reported_events->event_data_count = (unsigned int)nfds;
295 	}
296 
297 done:
298 	reported_events->num_ready = num_ready;
299 	return LIBUSB_SUCCESS;
300 }
301