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; due = source->due;
#if HAVE_LIBUSB_1_0 && !defined(G_OS_WIN32) #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) == (gintptr)session->ctx->libusb_ctx)
due = usb_due; due = usb_due;
#endif #endif
@ -1077,6 +1077,12 @@ SR_PRIV int sr_session_source_remove_internal(struct sr_session *session,
g_array_remove_range(session->pollfds, g_array_remove_range(session->pollfds,
fd_index, source->num_fds); fd_index, source->num_fds);
g_array_remove_index(session->sources, i); 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; return SR_OK;
} }
fd_index += source->num_fds; 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; ctx = cb_data;
start_time = g_get_monotonic_time(); start_time = g_get_monotonic_time();
due = ctx->usb_due; 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, sr_spew("libusb_handle_events enter: %g ms timeout",
(ctx->usb_timeout < 0) ? NULL : &tv, NULL); 1e-3 * timeout);
if (ret != 0) {
/* Warn but still invoke the callback, to give the driver ret = libusb_handle_events_timeout_completed(ctx->libusb_ctx,
* a chance to deal with the problem. (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)", if (!ctx->usb_source_present)
libusb_error_name(ret)); return TRUE;
} } else {
stop_time = g_get_monotonic_time(); /* Timeout already expired on entry.
*/
stop_time = start_time;
sr_spew("libusb_handle_events leave, %g ms elapsed", sr_spew("libusb_handle_events skipped");
1e-3 * (stop_time - start_time)); }
if (ctx->usb_timeout >= 0) if (ctx->usb_timeout >= 0)
ctx->usb_due = stop_time + ctx->usb_timeout; ctx->usb_due = stop_time + ctx->usb_timeout;
/* /*
* Run registered callback to execute any follow-up activity * Run the registered callback to execute any follow-up activity
* to libusb event handling. * to libusb's event handling.
*/ */
return ctx->usb_cb(-1, (stop_time < due) ? G_IO_IN : 0, return ctx->usb_cb(-1, (stop_time < due) ? G_IO_IN : 0,
ctx->usb_cb_data); 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 * This will have to do for now, until we implement a proper way to
* deal with libusb events on Windows. * 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 #else
const struct libusb_pollfd **lupfd; const struct libusb_pollfd **lupfd;
GPollFD *pollfds; 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) SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx)
{ {
int ret; return sr_session_source_remove_internal(session,
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,
(gintptr)ctx->libusb_ctx); (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) SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len)