forked from charlieh0tel/google-libusb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathglibusb_transfer.cc
136 lines (116 loc) · 3.51 KB
/
glibusb_transfer.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Alternative libusb call to do transfers that quits when
// a notification is notified.
//
// This code was originally from third_party/libusb/libusb1/sync.c
// and has been slightly modified to poll the notification.
#include "glibusb_transfer.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <libusb.h>
#include "glibusb_endpoint.h"
DEFINE_int32(notification_poll,
60,
"Time in seconds to wait between checking the quit notification "
"when waiting for USB messages.");
namespace {
static bool GEQZero(const char* flagname, int32_t value) {
if (value < 0) {
printf("Invalid value for --%s: %d\n", flagname, value);
return false;
}
return true;
}
static const bool notification_poll_dummy = google::RegisterFlagValidator(
&FLAGS_notification_poll, &GEQZero);
} // namespace
namespace glibusb {
namespace {
// Static code from libusb1/sync.c
void bulk_transfer_cb(struct libusb_transfer *transfer) {
int *completed = static_cast<int*>(transfer->user_data);
*completed = 1;
VLOG(3) << "actual_length=" << transfer->actual_length;
/* caller interprets results and frees transfer */
}
} // namespace
int do_sync_bulk_transfer(
struct libusb_context *context,
struct libusb_device_handle *dev_handle,
unsigned char endpoint, unsigned char *buffer, int length,
int *transferred, unsigned int timeout, unsigned char type,
Notification *quit) {
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int completed = 0;
int r;
if (!transfer)
return LIBUSB_ERROR_NO_MEM;
libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length,
bulk_transfer_cb, &completed, timeout);
transfer->type = type;
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
return r;
}
while (!completed) {
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
r = libusb_handle_events_timeout_completed(context, &tv, &completed);
if (quit != NULL && quit->HasBeenNotified()) {
LOG(WARNING) << "Caught quit notification. Canceling transfer.";
r = LIBUSB_ERROR_TIMEOUT;
}
if (r < 0) {
if (r == LIBUSB_ERROR_INTERRUPTED) {
continue;
}
libusb_cancel_transfer(transfer);
while (!completed) {
struct timeval cancel_tv;
cancel_tv.tv_sec = (quit == NULL ? FLAGS_notification_poll : 60);
cancel_tv.tv_usec = 0;
if (libusb_handle_events_timeout_completed(context, &cancel_tv,
&completed) < 0) {
break;
}
}
libusb_free_transfer(transfer);
return r;
}
}
*transferred = transfer->actual_length;
switch (transfer->status) {
case LIBUSB_TRANSFER_COMPLETED:
r = 0;
break;
case LIBUSB_TRANSFER_TIMED_OUT:
r = LIBUSB_ERROR_TIMEOUT;
break;
case LIBUSB_TRANSFER_STALL:
r = LIBUSB_ERROR_PIPE;
break;
case LIBUSB_TRANSFER_OVERFLOW:
r = LIBUSB_ERROR_OVERFLOW;
break;
case LIBUSB_TRANSFER_NO_DEVICE:
r = LIBUSB_ERROR_NO_DEVICE;
break;
case LIBUSB_TRANSFER_ERROR:
case LIBUSB_TRANSFER_CANCELLED:
r = LIBUSB_ERROR_IO;
break;
default:
LOG(WARNING) << "unrecognised status code " << transfer->status;
r = LIBUSB_ERROR_OTHER;
}
libusb_free_transfer(transfer);
return r;
}
} // namespace glibusb