Skip to content

Commit 3f7e959

Browse files
committed
Behave sanely when the Kinect is unplugged.
libusb_handle_events is thread-safe, but not reentrant. Which means that we can't call fnusb_stop_iso which calls libusb_handle_events from inside the isochronous callback function. Instead, we add a member to fnusb_dev that tracks if the device is permanently dead, and set it to true when we get a LIBUSB_TRANSFER_NO_DEVICE (or on resubmission, LIBUSB_ERROR_NO_DEVICE). Then, in freenect_process_events, after calling libusb_handle_events internally, we check to see if any of the devices in the context have this flag set. If they do, then we should return some nonzero error value, which is currently -1. In the future, we intend to provide callbacks so that clients can receive explicit notifications when a device has disappeared, so the code can robustly handle that situation. Future work. Fixes #229 (I think) Signed-off-by: Drew Fisher <drew.m.fisher@gmail.com>
1 parent 75a3eb2 commit 3f7e959

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

src/core.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,34 @@ FREENECTAPI int freenect_shutdown(freenect_context *ctx)
6969

7070
FREENECTAPI int freenect_process_events(freenect_context *ctx)
7171
{
72-
return fnusb_process_events(&ctx->usb);
72+
struct timeval timeout;
73+
timeout.tv_sec = 60;
74+
timeout.tv_usec = 0;
75+
return freenect_process_events_timeout(ctx, &timeout);
7376
}
7477

7578
FREENECTAPI int freenect_process_events_timeout(freenect_context *ctx, struct timeval *timeout)
7679
{
77-
return fnusb_process_events_timeout(&ctx->usb, timeout);
80+
int res = fnusb_process_events_timeout(&ctx->usb, timeout);
81+
// Iterate over the devices in ctx. If any of them are flagged as
82+
freenect_device* dev = ctx->first;
83+
while(dev) {
84+
if (dev->usb_cam.device_dead) {
85+
FN_ERROR("USB camera marked dead, stopping streams\n");
86+
res = -1;
87+
freenect_stop_video(dev);
88+
freenect_stop_depth(dev);
89+
}
90+
#ifdef BUILD_AUDIO
91+
if (dev->usb_audio.device_dead) {
92+
FN_ERROR("USB audio marked dead, stopping streams\n");
93+
res = -1; // Or something else to tell the user that the device just vanished.
94+
freenect_stop_audio(dev);
95+
}
96+
#endif
97+
dev = dev->next;
98+
}
99+
return res;
78100
}
79101

80102
FREENECTAPI int freenect_num_devices(freenect_context *ctx)

src/usb_libusb10.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,8 @@ static void iso_callback(struct libusb_transfer *xfer)
442442
if (res != 0) {
443443
FN_ERROR("iso_callback(): failed to resubmit transfer after successful completion: %d\n", res);
444444
strm->dead_xfers++;
445-
if (res == LIBUSB_TRANSFER_NO_DEVICE) {
446-
fnusb_stop_iso(strm->parent, strm);
445+
if (res == LIBUSB_ERROR_NO_DEVICE) {
446+
strm->parent->device_dead = 1;
447447
}
448448
}
449449
break;
@@ -453,14 +453,28 @@ static void iso_callback(struct libusb_transfer *xfer)
453453
// We lost the device we were talking to. This is a large problem,
454454
// and one that we should eventually come up with a way to
455455
// properly propagate up to the caller.
456-
FN_ERROR("USB device disappeared, cancelling stream :(\n");
456+
if(!strm->parent->device_dead) {
457+
FN_ERROR("USB device disappeared, cancelling stream %02x :(\n", xfer->endpoint);
458+
}
457459
strm->dead_xfers++;
458-
fnusb_stop_iso(strm->parent, strm);
460+
strm->parent->device_dead = 1;
459461
break;
460462
}
461463
case LIBUSB_TRANSFER_CANCELLED:
462464
{
463-
FN_SPEW("EP %02x transfer cancelled\n", xfer->endpoint);
465+
if(strm->dead) {
466+
FN_SPEW("EP %02x transfer cancelled\n", xfer->endpoint);
467+
} else {
468+
// This seems to be a libusb bug on OSX - instead of completing
469+
// the transfer with LIBUSB_TRANSFER_NO_DEVICE, the transfers
470+
// simply come back cancelled by the OS. We can detect this,
471+
// though - the stream should be marked dead if we're
472+
// intentionally cancelling transfers.
473+
if(!strm->parent->device_dead) {
474+
FN_ERROR("Got cancelled transfer, but we didn't request it - device disconnected?\n");
475+
}
476+
strm->parent->device_dead = 1;
477+
}
464478
strm->dead_xfers++;
465479
break;
466480
}
@@ -476,8 +490,8 @@ static void iso_callback(struct libusb_transfer *xfer)
476490
if (res != 0) {
477491
FN_ERROR("Isochronous transfer resubmission failed after unknown error: %d\n", res);
478492
strm->dead_xfers++;
479-
if (res == LIBUSB_TRANSFER_NO_DEVICE) {
480-
fnusb_stop_iso(strm->parent, strm);
493+
if (res == LIBUSB_ERROR_NO_DEVICE) {
494+
strm->parent->device_dead = 1;
481495
}
482496
}
483497
break;
@@ -528,22 +542,29 @@ int fnusb_stop_iso(fnusb_dev *dev, fnusb_isoc_stream *strm)
528542
freenect_context *ctx = dev->parent->parent;
529543
int i;
530544

545+
FN_FLOOD("fnusb_stop_iso() called\n");
546+
531547
strm->dead = 1;
532548

533549
for (i=0; i<strm->num_xfers; i++)
534550
libusb_cancel_transfer(strm->xfers[i]);
551+
FN_FLOOD("fnusb_stop_iso() cancelled all transfers\n");
535552

536553
while (strm->dead_xfers < strm->num_xfers) {
554+
FN_FLOOD("fnusb_stop_iso() dead = %d\tnum = %d\n", strm->dead_xfers, strm->num_xfers);
537555
libusb_handle_events(ctx->usb.ctx);
538556
}
539557

540558
for (i=0; i<strm->num_xfers; i++)
541559
libusb_free_transfer(strm->xfers[i]);
560+
FN_FLOOD("fnusb_stop_iso() freed all transfers\n");
542561

543562
free(strm->buffer);
544563
free(strm->xfers);
545564

565+
FN_FLOOD("fnusb_stop_iso() freed buffers and stream\n");
546566
memset(strm, 0, sizeof(*strm));
567+
FN_FLOOD("fnusb_stop_iso() done\n");
547568
return 0;
548569
}
549570

src/usb_libusb10.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef struct {
6969
typedef struct {
7070
freenect_device *parent; //so we can go up from the libusb userdata
7171
libusb_device_handle *dev;
72+
int device_dead; // set to 1 when the underlying libusb_device_handle vanishes (ie, Kinect was unplugged)
7273
} fnusb_dev;
7374

7475
typedef struct {

0 commit comments

Comments
 (0)