2008-02-16 12:44:50 +00:00
|
|
|
|
/*
|
|
|
|
|
* Polling/timing management
|
|
|
|
|
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define FP_COMPONENT "poll"
|
|
|
|
|
|
2018-05-23 15:48:55 +00:00
|
|
|
|
#include "fp_internal.h"
|
2018-09-05 15:15:20 +00:00
|
|
|
|
#include "fpi-poll.h"
|
2018-05-23 15:48:55 +00:00
|
|
|
|
|
2008-02-16 12:44:50 +00:00
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
#include <libusb.h>
|
|
|
|
|
|
|
|
|
|
/**
|
2018-05-17 00:58:59 +00:00
|
|
|
|
* SECTION:events
|
|
|
|
|
* @title: Initialisation and events handling
|
2018-11-12 13:20:28 +00:00
|
|
|
|
* @short_description: Initialisation and events handling functions
|
2018-05-17 00:58:59 +00:00
|
|
|
|
*
|
2008-02-16 12:44:50 +00:00
|
|
|
|
* These functions are only applicable to users of libfprint's asynchronous
|
|
|
|
|
* API.
|
|
|
|
|
*
|
|
|
|
|
* libfprint does not create internal library threads and hence can only
|
|
|
|
|
* execute when your application is calling a libfprint function. However,
|
|
|
|
|
* libfprint often has work to be do, such as handling of completed USB
|
|
|
|
|
* transfers, and processing of timeouts required in order for the library
|
|
|
|
|
* to function. Therefore it is essential that your own application must
|
|
|
|
|
* regularly "phone into" libfprint so that libfprint can handle any pending
|
|
|
|
|
* events.
|
|
|
|
|
*
|
|
|
|
|
* The function you must call is fp_handle_events() or a variant of it. This
|
|
|
|
|
* function will handle any pending events, and it is from this context that
|
|
|
|
|
* all asynchronous event callbacks from the library will occur. You can view
|
|
|
|
|
* this function as a kind of iteration function.
|
|
|
|
|
*
|
|
|
|
|
* If there are no events pending, fp_handle_events() will block for a few
|
|
|
|
|
* seconds (and will handle any new events should anything occur in that time).
|
|
|
|
|
* If you wish to customise this timeout, you can use
|
2018-05-17 00:58:59 +00:00
|
|
|
|
* fp_handle_events_timeout() instead. If you wish to do a non-blocking
|
2008-02-16 12:44:50 +00:00
|
|
|
|
* iteration, call fp_handle_events_timeout() with a zero timeout.
|
|
|
|
|
*
|
2018-05-25 13:58:10 +00:00
|
|
|
|
* How to integrate events handling depends on your main loop implementation.
|
|
|
|
|
* The sister fprintd project includes an implementation of main loop handling
|
|
|
|
|
* that integrates into GLib's main loop. The
|
|
|
|
|
* [libusb documentation](http://libusb.sourceforge.net/api-1.0/group__poll.html#details)
|
|
|
|
|
* also includes more details about how to integrate libfprint events into
|
|
|
|
|
* your main loop.
|
2008-02-16 12:44:50 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2018-09-05 15:33:25 +00:00
|
|
|
|
/**
|
|
|
|
|
* SECTION:fpi-poll
|
|
|
|
|
* @title: Timeouts
|
2018-11-12 13:20:28 +00:00
|
|
|
|
* @short_description: Timeout handling helpers
|
2018-09-05 15:33:25 +00:00
|
|
|
|
*
|
|
|
|
|
* Helper functions to schedule a function call to be made after a timeout. This
|
|
|
|
|
* is useful to avoid making blocking calls while waiting for hardware to answer
|
|
|
|
|
* for example.
|
|
|
|
|
*/
|
|
|
|
|
|
2008-02-16 12:44:50 +00:00
|
|
|
|
/* this is a singly-linked list of pending timers, sorted with the timer that
|
|
|
|
|
* is expiring soonest at the head. */
|
|
|
|
|
static GSList *active_timers = NULL;
|
|
|
|
|
|
2008-02-26 22:40:38 +00:00
|
|
|
|
/* notifiers for added or removed poll fds */
|
|
|
|
|
static fp_pollfd_added_cb fd_added_cb = NULL;
|
|
|
|
|
static fp_pollfd_removed_cb fd_removed_cb = NULL;
|
|
|
|
|
|
2008-02-16 12:44:50 +00:00
|
|
|
|
struct fpi_timeout {
|
|
|
|
|
struct timeval expiry;
|
|
|
|
|
fpi_timeout_fn callback;
|
2018-09-18 16:51:14 +00:00
|
|
|
|
struct fp_dev *dev;
|
2008-02-16 12:44:50 +00:00
|
|
|
|
void *data;
|
2018-09-27 12:29:18 +00:00
|
|
|
|
char *name;
|
2008-02-16 12:44:50 +00:00
|
|
|
|
};
|
|
|
|
|
|
2018-11-09 12:33:02 +00:00
|
|
|
|
static gboolean fpi_poll_is_setup(void);
|
|
|
|
|
|
2008-02-16 12:44:50 +00:00
|
|
|
|
static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
|
|
|
|
|
{
|
2018-09-05 15:34:59 +00:00
|
|
|
|
fpi_timeout *a = (fpi_timeout *) _a;
|
|
|
|
|
fpi_timeout *b = (fpi_timeout *) _b;
|
2008-02-16 12:44:50 +00:00
|
|
|
|
struct timeval *tv_a = &a->expiry;
|
|
|
|
|
struct timeval *tv_b = &b->expiry;
|
|
|
|
|
|
|
|
|
|
if (timercmp(tv_a, tv_b, <))
|
|
|
|
|
return -1;
|
|
|
|
|
else if (timercmp(tv_a, tv_b, >))
|
|
|
|
|
return 1;
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 12:29:18 +00:00
|
|
|
|
static void
|
|
|
|
|
fpi_timeout_free(fpi_timeout *timeout)
|
|
|
|
|
{
|
|
|
|
|
if (timeout == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g_free(timeout->name);
|
|
|
|
|
g_free(timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* fpi_timeout_set_name:
|
|
|
|
|
* @timeout: a #fpi_timeout
|
|
|
|
|
* @name: the name to give the timeout
|
|
|
|
|
*
|
|
|
|
|
* Sets a name for a timeout, allowing that name to be printed
|
|
|
|
|
* along with any timeout related debug.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
fpi_timeout_set_name(fpi_timeout *timeout,
|
|
|
|
|
const char *name)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (timeout != NULL);
|
|
|
|
|
g_return_if_fail (name != NULL);
|
|
|
|
|
g_return_if_fail (timeout->name == NULL);
|
|
|
|
|
|
|
|
|
|
timeout->name = g_strdup(name);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-05 15:33:25 +00:00
|
|
|
|
/**
|
|
|
|
|
* fpi_timeout_add:
|
|
|
|
|
* @msec: the time before calling the function, in milliseconds (1/1000ths of a second)
|
|
|
|
|
* @callback: function to callback
|
2018-09-18 16:51:14 +00:00
|
|
|
|
* @dev: a struct #fp_dev
|
|
|
|
|
* @data: data to pass to @callback, or %NULL
|
2018-09-05 15:33:25 +00:00
|
|
|
|
*
|
|
|
|
|
* A timeout is the asynchronous equivalent of sleeping. You create a timeout
|
2008-02-16 12:44:50 +00:00
|
|
|
|
* saying that you'd like to have a function invoked at a certain time in
|
2018-09-05 15:33:25 +00:00
|
|
|
|
* the future.
|
|
|
|
|
*
|
|
|
|
|
* Note that you should hold onto the return value of this function to cancel it
|
|
|
|
|
* use fpi_timeout_cancel(), otherwise the callback could be called while the driver
|
2018-09-27 13:14:05 +00:00
|
|
|
|
* is being torn down.
|
|
|
|
|
*
|
|
|
|
|
* This function can be considered to never fail.
|
2018-09-05 15:33:25 +00:00
|
|
|
|
*
|
|
|
|
|
* Returns: an #fpi_timeout structure
|
|
|
|
|
*/
|
2018-09-18 16:51:14 +00:00
|
|
|
|
fpi_timeout *fpi_timeout_add(unsigned int msec,
|
|
|
|
|
fpi_timeout_fn callback,
|
|
|
|
|
struct fp_dev *dev,
|
|
|
|
|
void *data)
|
2008-02-16 12:44:50 +00:00
|
|
|
|
{
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
struct timeval add_msec;
|
2018-09-05 15:34:59 +00:00
|
|
|
|
fpi_timeout *timeout;
|
2008-02-16 12:44:50 +00:00
|
|
|
|
int r;
|
|
|
|
|
|
2018-09-18 16:51:14 +00:00
|
|
|
|
g_return_val_if_fail (dev != NULL, NULL);
|
2018-10-11 15:21:52 +00:00
|
|
|
|
g_return_val_if_fail (fpi_poll_is_setup(), NULL);
|
2018-09-18 16:51:14 +00:00
|
|
|
|
|
2008-02-16 12:44:50 +00:00
|
|
|
|
fp_dbg("in %dms", msec);
|
|
|
|
|
|
|
|
|
|
r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
fp_err("failed to read monotonic clock, errno=%d", errno);
|
2018-09-27 13:14:05 +00:00
|
|
|
|
BUG();
|
2008-03-03 23:23:50 +00:00
|
|
|
|
return NULL;
|
2008-02-16 12:44:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 12:29:18 +00:00
|
|
|
|
timeout = g_new0(fpi_timeout, 1);
|
2008-02-16 12:44:50 +00:00
|
|
|
|
timeout->callback = callback;
|
2018-09-18 16:51:14 +00:00
|
|
|
|
timeout->dev = dev;
|
2008-02-16 12:44:50 +00:00
|
|
|
|
timeout->data = data;
|
|
|
|
|
TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
|
|
|
|
|
|
|
|
|
|
/* calculate timeout expiry by adding delay to current monotonic clock */
|
|
|
|
|
timerclear(&add_msec);
|
|
|
|
|
add_msec.tv_sec = msec / 1000;
|
|
|
|
|
add_msec.tv_usec = (msec % 1000) * 1000;
|
|
|
|
|
timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
|
|
|
|
|
|
|
|
|
|
active_timers = g_slist_insert_sorted(active_timers, timeout,
|
|
|
|
|
timeout_sort_fn);
|
|
|
|
|
|
2008-03-03 23:23:50 +00:00
|
|
|
|
return timeout;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-05 15:33:25 +00:00
|
|
|
|
/**
|
|
|
|
|
* fpi_timeout_cancel:
|
|
|
|
|
* @timeout: an #fpi_timeout structure
|
|
|
|
|
*
|
|
|
|
|
* Cancels a timeout scheduled with fpi_timeout_add(), and frees the
|
|
|
|
|
* @timeout structure.
|
|
|
|
|
*/
|
2018-09-05 15:34:59 +00:00
|
|
|
|
void fpi_timeout_cancel(fpi_timeout *timeout)
|
2008-03-03 23:23:50 +00:00
|
|
|
|
{
|
2018-05-23 16:26:22 +00:00
|
|
|
|
G_DEBUG_HERE();
|
2008-03-03 23:23:50 +00:00
|
|
|
|
active_timers = g_slist_remove(active_timers, timeout);
|
2018-09-27 12:29:18 +00:00
|
|
|
|
fpi_timeout_free(timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
fpi_timeout_cancel_for_dev(struct fp_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
GSList *l;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (dev != NULL);
|
|
|
|
|
|
|
|
|
|
l = active_timers;
|
|
|
|
|
while (l) {
|
|
|
|
|
struct fpi_timeout *timeout = l->data;
|
|
|
|
|
GSList *current = l;
|
|
|
|
|
|
|
|
|
|
l = l->next;
|
|
|
|
|
if (timeout->dev == dev) {
|
|
|
|
|
fpi_timeout_free (timeout);
|
|
|
|
|
active_timers = g_slist_delete_link (active_timers, current);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-02-16 12:44:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get the expiry time and optionally the timeout structure for the next
|
|
|
|
|
* timeout. returns 0 if there are no expired timers, or 1 if the
|
|
|
|
|
* timeval/timeout output parameters were populated. if the returned timeval
|
|
|
|
|
* is zero then it means the timeout has already expired and should be handled
|
|
|
|
|
* ASAP. */
|
|
|
|
|
static int get_next_timeout_expiry(struct timeval *out,
|
|
|
|
|
struct fpi_timeout **out_timeout)
|
|
|
|
|
{
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
struct fpi_timeout *next_timeout;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
if (active_timers == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
fp_err("failed to read monotonic clock, errno=%d", errno);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
|
|
|
|
|
|
|
|
|
next_timeout = active_timers->data;
|
|
|
|
|
if (out_timeout)
|
|
|
|
|
*out_timeout = next_timeout;
|
|
|
|
|
|
|
|
|
|
if (timercmp(&tv, &next_timeout->expiry, >=)) {
|
2018-09-27 12:42:43 +00:00
|
|
|
|
if (next_timeout->name)
|
|
|
|
|
fp_dbg("first timeout '%s' already expired", next_timeout->name);
|
|
|
|
|
else
|
|
|
|
|
fp_dbg("first timeout already expired");
|
2008-02-16 12:44:50 +00:00
|
|
|
|
timerclear(out);
|
|
|
|
|
} else {
|
|
|
|
|
timersub(&next_timeout->expiry, &tv, out);
|
2018-09-27 12:42:43 +00:00
|
|
|
|
if (next_timeout->name)
|
|
|
|
|
fp_dbg("next timeout '%s' in %ld.%06lds", next_timeout->name,
|
|
|
|
|
out->tv_sec, out->tv_usec);
|
|
|
|
|
else
|
|
|
|
|
fp_dbg("next timeout in %ld.%06lds", out->tv_sec, out->tv_usec);
|
2008-02-16 12:44:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* handle a timeout that has expired */
|
|
|
|
|
static void handle_timeout(struct fpi_timeout *timeout)
|
|
|
|
|
{
|
2018-05-23 16:26:22 +00:00
|
|
|
|
G_DEBUG_HERE();
|
2018-09-18 16:51:14 +00:00
|
|
|
|
timeout->callback(timeout->dev, timeout->data);
|
2008-02-16 12:44:50 +00:00
|
|
|
|
active_timers = g_slist_remove(active_timers, timeout);
|
2018-09-27 12:29:18 +00:00
|
|
|
|
fpi_timeout_free(timeout);
|
2008-02-16 12:44:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int handle_timeouts(void)
|
|
|
|
|
{
|
|
|
|
|
struct timeval next_timeout_expiry;
|
|
|
|
|
struct fpi_timeout *next_timeout;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
|
|
|
|
|
if (r <= 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
if (!timerisset(&next_timeout_expiry))
|
|
|
|
|
handle_timeout(next_timeout);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 00:58:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* fp_handle_events_timeout:
|
|
|
|
|
* @timeout: Maximum timeout for this blocking function
|
|
|
|
|
*
|
2008-02-16 12:44:50 +00:00
|
|
|
|
* Handle any pending events. If a non-zero timeout is specified, the function
|
|
|
|
|
* will potentially block for the specified amount of time, although it may
|
|
|
|
|
* return sooner if events have been handled. The function acts as non-blocking
|
|
|
|
|
* for a zero timeout.
|
|
|
|
|
*
|
2018-05-17 00:58:59 +00:00
|
|
|
|
* Returns: 0 on success, non-zero on error.
|
2008-02-16 12:44:50 +00:00
|
|
|
|
*/
|
|
|
|
|
API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
|
|
|
|
|
{
|
|
|
|
|
struct timeval next_timeout_expiry;
|
|
|
|
|
struct timeval select_timeout;
|
|
|
|
|
struct fpi_timeout *next_timeout;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
if (r) {
|
|
|
|
|
/* timer already expired? */
|
|
|
|
|
if (!timerisset(&next_timeout_expiry)) {
|
|
|
|
|
handle_timeout(next_timeout);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* choose the smallest of next URB timeout or user specified timeout */
|
|
|
|
|
if (timercmp(&next_timeout_expiry, timeout, <))
|
|
|
|
|
select_timeout = next_timeout_expiry;
|
|
|
|
|
else
|
|
|
|
|
select_timeout = *timeout;
|
|
|
|
|
} else {
|
|
|
|
|
select_timeout = *timeout;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-03 04:27:37 +00:00
|
|
|
|
r = libusb_handle_events_timeout(fpi_usb_ctx, &select_timeout);
|
2008-02-16 12:44:50 +00:00
|
|
|
|
*timeout = select_timeout;
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
return handle_timeouts();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 00:58:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* fp_handle_events:
|
|
|
|
|
*
|
2008-02-16 12:44:50 +00:00
|
|
|
|
* Convenience function for calling fp_handle_events_timeout() with a sensible
|
|
|
|
|
* default timeout value of two seconds (subject to change if we decide another
|
|
|
|
|
* value is more sensible).
|
|
|
|
|
*
|
2018-05-17 00:58:59 +00:00
|
|
|
|
* Returns: 0 on success, non-zero on error.
|
2008-02-16 12:44:50 +00:00
|
|
|
|
*/
|
|
|
|
|
API_EXPORTED int fp_handle_events(void)
|
|
|
|
|
{
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
tv.tv_sec = 2;
|
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
return fp_handle_events_timeout(&tv);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 00:58:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* fp_get_next_timeout:
|
2018-09-27 14:08:21 +00:00
|
|
|
|
* @tv: a #timeval structure containing the duration to the next timeout.
|
2018-05-17 00:58:59 +00:00
|
|
|
|
*
|
|
|
|
|
* A zero filled @tv timeout means events are to be handled immediately
|
|
|
|
|
*
|
|
|
|
|
* Returns: returns 0 if no timeouts active, or 1 if timeout returned.
|
|
|
|
|
*/
|
2008-03-01 12:16:31 +00:00
|
|
|
|
API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
|
|
|
|
|
{
|
2018-12-12 14:34:43 +00:00
|
|
|
|
struct timeval fprint_timeout = { 0, 0 };
|
|
|
|
|
struct timeval libusb_timeout = { 0, 0 };
|
2008-03-01 12:16:31 +00:00
|
|
|
|
int r_fprint;
|
|
|
|
|
int r_libusb;
|
|
|
|
|
|
|
|
|
|
r_fprint = get_next_timeout_expiry(&fprint_timeout, NULL);
|
2008-07-03 04:27:37 +00:00
|
|
|
|
r_libusb = libusb_get_next_timeout(fpi_usb_ctx, &libusb_timeout);
|
2008-03-01 12:16:31 +00:00
|
|
|
|
|
|
|
|
|
/* if we have no pending timeouts and the same is true for libusb,
|
|
|
|
|
* indicate that we have no pending timouts */
|
2018-10-23 14:24:14 +00:00
|
|
|
|
if (r_fprint <= 0 && r_libusb <= 0)
|
2008-03-01 12:16:31 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2011-02-16 08:34:55 +00:00
|
|
|
|
/* if fprint have no pending timeouts return libusb timeout */
|
|
|
|
|
else if (r_fprint == 0)
|
|
|
|
|
*tv = libusb_timeout;
|
|
|
|
|
|
|
|
|
|
/* if libusb have no pending timeouts return fprint timeout */
|
|
|
|
|
else if (r_libusb == 0)
|
|
|
|
|
*tv = fprint_timeout;
|
|
|
|
|
|
2008-03-01 12:16:31 +00:00
|
|
|
|
/* otherwise return the smaller of the 2 timeouts */
|
|
|
|
|
else if (timercmp(&fprint_timeout, &libusb_timeout, <))
|
|
|
|
|
*tv = fprint_timeout;
|
|
|
|
|
else
|
|
|
|
|
*tv = libusb_timeout;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 00:58:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* fp_get_pollfds:
|
|
|
|
|
* @pollfds: output location for a list of pollfds. If non-%NULL, must be
|
|
|
|
|
* released with free() when done.
|
|
|
|
|
*
|
2008-02-26 22:40:38 +00:00
|
|
|
|
* Retrieve a list of file descriptors that should be polled for events
|
|
|
|
|
* interesting to libfprint. This function is only for users who wish to
|
2018-05-18 03:51:58 +00:00
|
|
|
|
* combine libfprint's file descriptor set with other event sources – more
|
2008-02-26 22:40:38 +00:00
|
|
|
|
* simplistic users will be able to call fp_handle_events() or a variant
|
|
|
|
|
* directly.
|
|
|
|
|
*
|
2018-05-17 00:58:59 +00:00
|
|
|
|
* Returns: the number of pollfds in the resultant list, or negative on error.
|
2008-02-26 22:40:38 +00:00
|
|
|
|
*/
|
2018-06-26 12:52:26 +00:00
|
|
|
|
API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds)
|
2008-02-26 22:40:38 +00:00
|
|
|
|
{
|
2008-04-06 22:55:41 +00:00
|
|
|
|
const struct libusb_pollfd **usbfds;
|
|
|
|
|
const struct libusb_pollfd *usbfd;
|
2008-02-26 22:40:38 +00:00
|
|
|
|
struct fp_pollfd *ret;
|
2018-06-26 12:52:26 +00:00
|
|
|
|
ssize_t cnt = 0;
|
2008-03-13 12:54:27 +00:00
|
|
|
|
size_t i = 0;
|
2008-02-26 22:40:38 +00:00
|
|
|
|
|
2018-10-11 15:19:21 +00:00
|
|
|
|
g_return_val_if_fail (fpi_usb_ctx != NULL, -EIO);
|
|
|
|
|
|
2008-07-03 04:27:37 +00:00
|
|
|
|
usbfds = libusb_get_pollfds(fpi_usb_ctx);
|
2008-03-13 12:54:27 +00:00
|
|
|
|
if (!usbfds) {
|
2008-02-26 22:40:38 +00:00
|
|
|
|
*pollfds = NULL;
|
2008-03-13 12:54:27 +00:00
|
|
|
|
return -EIO;
|
2008-02-26 22:40:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-03-13 12:54:27 +00:00
|
|
|
|
while ((usbfd = usbfds[i++]) != NULL)
|
|
|
|
|
cnt++;
|
|
|
|
|
|
|
|
|
|
ret = g_malloc(sizeof(struct fp_pollfd) * cnt);
|
|
|
|
|
i = 0;
|
2008-11-25 15:54:56 +00:00
|
|
|
|
while ((usbfd = usbfds[i]) != NULL) {
|
2008-03-13 12:54:27 +00:00
|
|
|
|
ret[i].fd = usbfd->fd;
|
|
|
|
|
ret[i].events = usbfd->events;
|
2008-11-25 15:54:56 +00:00
|
|
|
|
i++;
|
2008-02-26 22:40:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*pollfds = ret;
|
|
|
|
|
return cnt;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 00:58:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* fp_set_pollfd_notifiers:
|
2018-05-22 16:44:10 +00:00
|
|
|
|
* @added_cb: a #fp_pollfd_added_cb callback or %NULL
|
|
|
|
|
* @removed_cb: a #fp_pollfd_removed_cb callback or %NULL
|
2018-05-17 00:58:59 +00:00
|
|
|
|
*
|
2018-05-22 16:44:10 +00:00
|
|
|
|
* This sets the callback functions to call for every new or removed
|
|
|
|
|
* file descriptor used as an event source.
|
2018-05-17 00:58:59 +00:00
|
|
|
|
*/
|
2008-02-26 22:40:38 +00:00
|
|
|
|
API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
|
|
|
|
|
fp_pollfd_removed_cb removed_cb)
|
|
|
|
|
{
|
|
|
|
|
fd_added_cb = added_cb;
|
|
|
|
|
fd_removed_cb = removed_cb;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-03 04:27:37 +00:00
|
|
|
|
static void add_pollfd(int fd, short events, void *user_data)
|
2008-02-26 22:40:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (fd_added_cb)
|
|
|
|
|
fd_added_cb(fd, events);
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-03 04:27:37 +00:00
|
|
|
|
static void remove_pollfd(int fd, void *user_data)
|
2008-02-26 22:40:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (fd_removed_cb)
|
|
|
|
|
fd_removed_cb(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void fpi_poll_init(void)
|
|
|
|
|
{
|
2008-07-03 04:27:37 +00:00
|
|
|
|
libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
|
2008-02-26 22:40:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void fpi_poll_exit(void)
|
|
|
|
|
{
|
2018-09-27 12:29:18 +00:00
|
|
|
|
g_slist_free_full(active_timers, (GDestroyNotify) fpi_timeout_free);
|
2008-02-26 22:40:38 +00:00
|
|
|
|
active_timers = NULL;
|
|
|
|
|
fd_added_cb = NULL;
|
|
|
|
|
fd_removed_cb = NULL;
|
2008-07-03 04:27:37 +00:00
|
|
|
|
libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
|
2008-02-26 22:40:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-09 12:33:02 +00:00
|
|
|
|
static gboolean
|
2018-10-11 15:20:38 +00:00
|
|
|
|
fpi_poll_is_setup(void)
|
|
|
|
|
{
|
|
|
|
|
return (fd_added_cb != NULL && fd_removed_cb != NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 10:21:45 +00:00
|
|
|
|
void
|
|
|
|
|
fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
GSList *l;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (dev != NULL);
|
|
|
|
|
|
|
|
|
|
l = active_timers;
|
|
|
|
|
while (l) {
|
|
|
|
|
struct fpi_timeout *timeout = l->data;
|
|
|
|
|
GSList *current = l;
|
|
|
|
|
|
|
|
|
|
l = l->next;
|
|
|
|
|
if (timeout->dev == dev) {
|
|
|
|
|
g_free (timeout);
|
|
|
|
|
active_timers = g_slist_delete_link (active_timers, current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|