USB: Handle the case of a callback removing its event source

This commit is contained in:
Daniel Elstner 2015-09-05 03:56:16 +02:00
parent 358c4ed5b8
commit 5a7d8dac90
2 changed files with 43 additions and 34 deletions

View File

@ -507,7 +507,7 @@ static int sr_session_iteration(struct sr_session *session)
due = source->due;
#if HAVE_LIBUSB_1_0 && !defined(G_OS_WIN32)
if (usb_due < due && source->poll_object
if (usb_due < due && poll_object
== (gintptr)session->ctx->libusb_ctx)
due = usb_due;
#endif
@ -1077,6 +1077,12 @@ SR_PRIV int sr_session_source_remove_internal(struct sr_session *session,
g_array_remove_range(session->pollfds,
fd_index, source->num_fds);
g_array_remove_index(session->sources, i);
/*
* This is a bit of a hack. To be removed when
* porting over to the GLib main loop.
*/
if (poll_object == (gintptr)session->ctx->libusb_ctx)
session->ctx->usb_source_present = FALSE;
return SR_OK;
}
fd_index += source->num_fds;

View File

@ -200,33 +200,48 @@ static int usb_callback(int fd, int revents, void *cb_data)
ctx = cb_data;
start_time = g_get_monotonic_time();
due = ctx->usb_due;
timeout = MAX(due - start_time, 0);
tv.tv_sec = timeout / G_USEC_PER_SEC;
tv.tv_usec = timeout % G_USEC_PER_SEC;
sr_spew("libusb_handle_events enter, timeout %g ms", 1e-3 * timeout);
if (due > start_time) {
timeout = due - start_time;
tv.tv_sec = timeout / G_USEC_PER_SEC;
tv.tv_usec = timeout % G_USEC_PER_SEC;
ret = libusb_handle_events_timeout_completed(ctx->libusb_ctx,
(ctx->usb_timeout < 0) ? NULL : &tv, NULL);
if (ret != 0) {
/* Warn but still invoke the callback, to give the driver
* a chance to deal with the problem.
sr_spew("libusb_handle_events enter: %g ms timeout",
1e-3 * timeout);
ret = libusb_handle_events_timeout_completed(ctx->libusb_ctx,
(ctx->usb_timeout < 0) ? NULL : &tv, NULL);
if (ret != 0) {
/* Warn but still invoke the callback, to give
* the driver a chance to deal with the problem.
*/
sr_warn("Error handling libusb event (%s)",
libusb_error_name(ret));
}
stop_time = g_get_monotonic_time();
sr_spew("libusb_handle_events leave: %g ms elapsed",
1e-3 * (stop_time - start_time));
/*
* The event source may have been removed by the driver's
* libusb transfer callback. Skip the callback in that case.
*/
sr_warn("Error handling libusb event (%s)",
libusb_error_name(ret));
}
stop_time = g_get_monotonic_time();
if (!ctx->usb_source_present)
return TRUE;
} else {
/* Timeout already expired on entry.
*/
stop_time = start_time;
sr_spew("libusb_handle_events leave, %g ms elapsed",
1e-3 * (stop_time - start_time));
sr_spew("libusb_handle_events skipped");
}
if (ctx->usb_timeout >= 0)
ctx->usb_due = stop_time + ctx->usb_timeout;
/*
* Run registered callback to execute any follow-up activity
* to libusb event handling.
* Run the registered callback to execute any follow-up activity
* to libusb's event handling.
*/
return ctx->usb_cb(-1, (stop_time < due) ? G_IO_IN : 0,
ctx->usb_cb_data);
@ -259,7 +274,8 @@ SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
* This will have to do for now, until we implement a proper way to
* deal with libusb events on Windows.
*/
ret = sr_session_source_add(session, -2, 0, 0, &usb_callback, ctx);
ret = sr_session_source_add_internal(session, NULL, 0,
0, &usb_callback, ctx, (gintptr)ctx->libusb_ctx);
#else
const struct libusb_pollfd **lupfd;
GPollFD *pollfds;
@ -293,21 +309,8 @@ SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx)
{
int ret;
if (!ctx->usb_source_present)
return SR_OK;
#ifdef G_OS_WIN32
/* Remove our idle source */
sr_session_source_remove(session, -2);
#else
ret = sr_session_source_remove_internal(session,
return sr_session_source_remove_internal(session,
(gintptr)ctx->libusb_ctx);
#endif
ctx->usb_source_present = FALSE;
return ret;
}
SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len)