Fix fp_get_pollfds()
[libfprint.git] / libfprint / poll.c
blobf78b65869f932fdd60518fd2c62e56b0ad1ff728
1 /*
2 * Polling/timing management
3 * Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #define FP_COMPONENT "poll"
22 #include <config.h>
23 #include <errno.h>
24 #include <time.h>
25 #include <sys/time.h>
27 #include <glib.h>
28 #include <libusb.h>
30 #include "fp_internal.h"
32 /**
33 * @defgroup poll Polling and timing operations
34 * These functions are only applicable to users of libfprint's asynchronous
35 * API.
37 * libfprint does not create internal library threads and hence can only
38 * execute when your application is calling a libfprint function. However,
39 * libfprint often has work to be do, such as handling of completed USB
40 * transfers, and processing of timeouts required in order for the library
41 * to function. Therefore it is essential that your own application must
42 * regularly "phone into" libfprint so that libfprint can handle any pending
43 * events.
45 * The function you must call is fp_handle_events() or a variant of it. This
46 * function will handle any pending events, and it is from this context that
47 * all asynchronous event callbacks from the library will occur. You can view
48 * this function as a kind of iteration function.
50 * If there are no events pending, fp_handle_events() will block for a few
51 * seconds (and will handle any new events should anything occur in that time).
52 * If you wish to customise this timeout, you can use
53 * fp_handle_events_timeout() instead. If you wish to do a nonblocking
54 * iteration, call fp_handle_events_timeout() with a zero timeout.
56 * TODO: document how application is supposed to know when to call these
57 * functions.
60 /* this is a singly-linked list of pending timers, sorted with the timer that
61 * is expiring soonest at the head. */
62 static GSList *active_timers = NULL;
64 /* notifiers for added or removed poll fds */
65 static fp_pollfd_added_cb fd_added_cb = NULL;
66 static fp_pollfd_removed_cb fd_removed_cb = NULL;
68 struct fpi_timeout {
69 struct timeval expiry;
70 fpi_timeout_fn callback;
71 void *data;
74 static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
76 struct fpi_timeout *a = (struct fpi_timeout *) _a;
77 struct fpi_timeout *b = (struct fpi_timeout *) _b;
78 struct timeval *tv_a = &a->expiry;
79 struct timeval *tv_b = &b->expiry;
81 if (timercmp(tv_a, tv_b, <))
82 return -1;
83 else if (timercmp(tv_a, tv_b, >))
84 return 1;
85 else
86 return 0;
89 /* A timeout is the asynchronous equivalent of sleeping. You create a timeout
90 * saying that you'd like to have a function invoked at a certain time in
91 * the future. */
92 struct fpi_timeout *fpi_timeout_add(unsigned int msec, fpi_timeout_fn callback,
93 void *data)
95 struct timespec ts;
96 struct timeval add_msec;
97 struct fpi_timeout *timeout;
98 int r;
100 fp_dbg("in %dms", msec);
102 r = clock_gettime(CLOCK_MONOTONIC, &ts);
103 if (r < 0) {
104 fp_err("failed to read monotonic clock, errno=%d", errno);
105 return NULL;
108 timeout = g_malloc(sizeof(*timeout));
109 timeout->callback = callback;
110 timeout->data = data;
111 TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
113 /* calculate timeout expiry by adding delay to current monotonic clock */
114 timerclear(&add_msec);
115 add_msec.tv_sec = msec / 1000;
116 add_msec.tv_usec = (msec % 1000) * 1000;
117 timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
119 active_timers = g_slist_insert_sorted(active_timers, timeout,
120 timeout_sort_fn);
122 return timeout;
125 void fpi_timeout_cancel(struct fpi_timeout *timeout)
127 fp_dbg("");
128 active_timers = g_slist_remove(active_timers, timeout);
129 g_free(timeout);
132 /* get the expiry time and optionally the timeout structure for the next
133 * timeout. returns 0 if there are no expired timers, or 1 if the
134 * timeval/timeout output parameters were populated. if the returned timeval
135 * is zero then it means the timeout has already expired and should be handled
136 * ASAP. */
137 static int get_next_timeout_expiry(struct timeval *out,
138 struct fpi_timeout **out_timeout)
140 struct timespec ts;
141 struct timeval tv;
142 struct fpi_timeout *next_timeout;
143 int r;
145 if (active_timers == NULL)
146 return 0;
148 r = clock_gettime(CLOCK_MONOTONIC, &ts);
149 if (r < 0) {
150 fp_err("failed to read monotonic clock, errno=%d", errno);
151 return r;
153 TIMESPEC_TO_TIMEVAL(&tv, &ts);
155 next_timeout = active_timers->data;
156 if (out_timeout)
157 *out_timeout = next_timeout;
159 if (timercmp(&tv, &next_timeout->expiry, >=)) {
160 fp_dbg("first timeout already expired");
161 timerclear(out);
162 } else {
163 timersub(&next_timeout->expiry, &tv, out);
164 fp_dbg("next timeout in %d.%06ds", out->tv_sec, out->tv_usec);
167 return 1;
170 /* handle a timeout that has expired */
171 static void handle_timeout(struct fpi_timeout *timeout)
173 fp_dbg("");
174 timeout->callback(timeout->data);
175 active_timers = g_slist_remove(active_timers, timeout);
176 g_free(timeout);
179 static int handle_timeouts(void)
181 struct timeval next_timeout_expiry;
182 struct fpi_timeout *next_timeout;
183 int r;
185 r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
186 if (r <= 0)
187 return r;
189 if (!timerisset(&next_timeout_expiry))
190 handle_timeout(next_timeout);
192 return 0;
195 /** \ingroup poll
196 * Handle any pending events. If a non-zero timeout is specified, the function
197 * will potentially block for the specified amount of time, although it may
198 * return sooner if events have been handled. The function acts as non-blocking
199 * for a zero timeout.
201 * \param timeout Maximum timeout for this blocking function
202 * \returns 0 on success, non-zero on error.
204 API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
206 struct timeval next_timeout_expiry;
207 struct timeval select_timeout;
208 struct fpi_timeout *next_timeout;
209 int r;
211 r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
212 if (r < 0)
213 return r;
215 if (r) {
216 /* timer already expired? */
217 if (!timerisset(&next_timeout_expiry)) {
218 handle_timeout(next_timeout);
219 return 0;
222 /* choose the smallest of next URB timeout or user specified timeout */
223 if (timercmp(&next_timeout_expiry, timeout, <))
224 select_timeout = next_timeout_expiry;
225 else
226 select_timeout = *timeout;
227 } else {
228 select_timeout = *timeout;
231 r = libusb_handle_events_timeout(fpi_usb_ctx, &select_timeout);
232 *timeout = select_timeout;
233 if (r < 0)
234 return r;
236 return handle_timeouts();
239 /** \ingroup poll
240 * Convenience function for calling fp_handle_events_timeout() with a sensible
241 * default timeout value of two seconds (subject to change if we decide another
242 * value is more sensible).
244 * \returns 0 on success, non-zero on error.
246 API_EXPORTED int fp_handle_events(void)
248 struct timeval tv;
249 tv.tv_sec = 2;
250 tv.tv_usec = 0;
251 return fp_handle_events_timeout(&tv);
254 /* FIXME: docs
255 * returns 0 if no timeouts active
256 * returns 1 if timeout returned
257 * zero timeout means events are to be handled immediately */
258 API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
260 struct timeval fprint_timeout;
261 struct timeval libusb_timeout;
262 int r_fprint;
263 int r_libusb;
265 r_fprint = get_next_timeout_expiry(&fprint_timeout, NULL);
266 r_libusb = libusb_get_next_timeout(fpi_usb_ctx, &libusb_timeout);
268 /* if we have no pending timeouts and the same is true for libusb,
269 * indicate that we have no pending timouts */
270 if (r_fprint == 0 && r_libusb == 0)
271 return 0;
273 /* otherwise return the smaller of the 2 timeouts */
274 else if (timercmp(&fprint_timeout, &libusb_timeout, <))
275 *tv = fprint_timeout;
276 else
277 *tv = libusb_timeout;
278 return 1;
281 /** \ingroup poll
282 * Retrieve a list of file descriptors that should be polled for events
283 * interesting to libfprint. This function is only for users who wish to
284 * combine libfprint's file descriptor set with other event sources - more
285 * simplistic users will be able to call fp_handle_events() or a variant
286 * directly.
288 * \param pollfds output location for a list of pollfds. If non-NULL, must be
289 * released with free() when done.
290 * \returns the number of pollfds in the resultant list, or negative on error.
292 API_EXPORTED size_t fp_get_pollfds(struct fp_pollfd **pollfds)
294 const struct libusb_pollfd **usbfds;
295 const struct libusb_pollfd *usbfd;
296 struct fp_pollfd *ret;
297 size_t cnt = 0;
298 size_t i = 0;
300 usbfds = libusb_get_pollfds(fpi_usb_ctx);
301 if (!usbfds) {
302 *pollfds = NULL;
303 return -EIO;
306 while ((usbfd = usbfds[i++]) != NULL)
307 cnt++;
309 ret = g_malloc(sizeof(struct fp_pollfd) * cnt);
310 i = 0;
311 while ((usbfd = usbfds[i]) != NULL) {
312 ret[i].fd = usbfd->fd;
313 ret[i].events = usbfd->events;
314 i++;
317 *pollfds = ret;
318 return cnt;
321 /* FIXME: docs */
322 API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
323 fp_pollfd_removed_cb removed_cb)
325 fd_added_cb = added_cb;
326 fd_removed_cb = removed_cb;
329 static void add_pollfd(int fd, short events, void *user_data)
331 if (fd_added_cb)
332 fd_added_cb(fd, events);
335 static void remove_pollfd(int fd, void *user_data)
337 if (fd_removed_cb)
338 fd_removed_cb(fd);
341 void fpi_poll_init(void)
343 libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
346 void fpi_poll_exit(void)
348 g_slist_free(active_timers);
349 active_timers = NULL;
350 fd_added_cb = NULL;
351 fd_removed_cb = NULL;
352 libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);