blob: 30c8bd7fd57600eba968ce76924ea00a449ebfb4 [file] [log] [blame]
Brian Silverman395d6252013-09-13 20:58:14 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2
3#include "glibusb_endpoint.h"
4
5#include <stddef.h>
6#include <glog/logging.h>
7#include <boost/scoped_ptr.hpp>
8#include <boost/date_time/posix_time/posix_time.hpp>
9
10#include "gbuffer.h"
11#include "glibusb_endpoint_internal.h"
12#include "glibusb_internal.h"
13#include "glibusb_transfer.h"
14
15namespace glibusb {
16
17namespace {
18int LibusbGetMaxPacketSize(libusb_device_handle *handle,
19 unsigned char endpoint)
20{
21 libusb_device *device = CHECK_NOTNULL(libusb_get_device(handle));
22 return libusb_get_max_packet_size(device, endpoint);
23}
24
25int LibusbGetMaxIsoPacketSize(libusb_device_handle *handle,
26 unsigned char endpoint)
27{
28 libusb_device *device = CHECK_NOTNULL(libusb_get_device(handle));
29 return libusb_get_max_iso_packet_size(device, endpoint);
30}
31} // namespace
32
33Notification::Notification(bool prenotify)
34 : notified_(prenotify) {}
35
36bool Notification::HasBeenNotified() const {
37 boost::lock_guard<boost::mutex> lock(mutex_);
38 return notified_;
39}
40
41void Notification::WaitForNotification() const {
42 boost::unique_lock<boost::mutex> lock(mutex_);
43 notified_changed_.wait(
44 lock,
45 boost::bind(&Notification::HasBeenNotifiedUnlocked, this));
46}
47
48bool Notification::WaitForNotificationWithTimeout(int64_t milliseconds) const {
49 boost::unique_lock<boost::mutex> lock(mutex_);
50 auto timeout = boost::posix_time::milliseconds(milliseconds);
51 notified_changed_.timed_wait(
52 lock, timeout,
53 boost::bind(&Notification::HasBeenNotifiedUnlocked, this));
54 return notified_;
55}
56
57void Notification::Notify() {
58 boost::lock_guard<boost::mutex> lock(mutex_);
59 CHECK(!notified_) << ": already notified";
60 notified_ = true;
61 notified_changed_.notify_all();
62}
63
64bool Notification::HasBeenNotifiedUnlocked() const {
65 return notified_;
66}
67
68////////////////////////////////////////////////////////////////////////
69
70const int32_t UsbEndpoint::kMaxBulkTransferBytes;
71
72UsbEndpoint::UsbEndpoint(DirectionType direction, TransferType transfer,
73 int endpoint_address)
74 : direction_(direction),
75 transfer_(transfer),
76 endpoint_address_and_direction_(endpoint_address | direction) {
77 CHECK_EQ(endpoint_address_and_direction_ & 0x80, direction)
78 << ": Direction in address doesn't match specified direction.";
79 CHECK_EQ(endpoint_address_and_direction_ & 0x8f,
80 endpoint_address_and_direction_)
81 << ": Invalid endpoint address.";
82}
83
84////////////////////////////////////////////////////////////////////////
85
86UsbInEndpoint::UsbInEndpoint(TransferType transfer, int endpoint_address)
87 : UsbEndpoint(UsbEndpoint::kIn, transfer, endpoint_address) {
88}
89
90bool UsbInEndpoint::Read(Buffer *out) {
91 IoStatus status = ReadAtMostWithTimeout(kMaxBulkTransferBytes, 0, out);
92 return status == kSuccess;
93}
94
95UsbEndpoint::IoStatus
96UsbInEndpoint::ReadWithTimeout(int32_t timeout_milliseconds,
97 Buffer *out) {
98 return ReadAtMostWithTimeout(kMaxBulkTransferBytes,
99 timeout_milliseconds, out);
100}
101
102bool UsbInEndpoint::ReadAtMost(uint32_t length, Buffer *out) {
103 IoStatus status = ReadAtMostWithTimeout(length, 0, out);
104 return status == kSuccess;
105}
106
107UsbEndpoint::IoStatus UsbInEndpoint::ReadAtMostWithTimeout(
108 uint32_t length, int32_t timeout_milliseconds, Buffer *out) {
109 return DoRead(length, timeout_milliseconds, out, NULL);
110}
111
112UsbEndpoint::IoStatus UsbInEndpoint::ReadAtMostWithTimeoutAndNotification(
113 uint32_t length, int32_t timeout_milliseconds, Buffer *out,
114 Notification *quit) {
115 CHECK_NOTNULL(quit);
116 return DoRead(length, timeout_milliseconds, out, quit);
117}
118
119////////////////////////////////////////////////////////////////////////
120
121UsbOutEndpoint::UsbOutEndpoint(TransferType transfer, int endpoint_address)
122 : UsbEndpoint(UsbEndpoint::kOut, transfer, endpoint_address) {
123}
124
125bool UsbOutEndpoint::Write(const Buffer &buffer) {
126 IoStatus status = DoWrite(buffer, 0);
127 return status == kSuccess;
128}
129
130UsbEndpoint::IoStatus UsbOutEndpoint::WriteWithTimeout(const Buffer &buffer,
131 int32_t timeout_milliseconds) {
132 return DoWrite(buffer, timeout_milliseconds);
133}
134
135////////////////////////////////////////////////////////////////////////
136
137PhysicalUsbInEndpoint::PhysicalUsbInEndpoint(
138 struct libusb_context *context,
139 struct libusb_device_handle *handle,
140 const struct libusb_endpoint_descriptor *descriptor)
141 : UsbInEndpoint(DescriptorToTransfer(descriptor),
142 DescriptorToAddress(descriptor)),
143 libusb_context_(CHECK_NOTNULL(context)),
144 handle_(CHECK_NOTNULL(handle)) {
145 VLOG(1) << "0x" << std::hex << static_cast<int>(endpoint_address_and_direction())
146 << ", max_packet_size " << std::dec << descriptor->wMaxPacketSize;
147 CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kIn);
148}
149
150PhysicalUsbInEndpoint::~PhysicalUsbInEndpoint() {
151 CHECK_NOTNULL(handle_);
152 handle_ = nullptr;
153 libusb_context_ = nullptr;
154}
155
156int PhysicalUsbInEndpoint::DoGetMaxPacketSize() {
157 CHECK_NOTNULL(handle_);
158 return LibusbGetMaxPacketSize(handle_, endpoint_address());
159}
160
161int PhysicalUsbInEndpoint::DoGetMaxIsoPacketSize() {
162 CHECK_NOTNULL(handle_);
163 return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
164}
165
166namespace {
167unsigned char LibUsbTransferType(int transfer_type) {
168 switch (transfer_type) {
169 case UsbEndpoint::kControl:
170 return LIBUSB_TRANSFER_TYPE_CONTROL;
171 case UsbEndpoint::kBulk:
172 return LIBUSB_TRANSFER_TYPE_BULK;
173 case UsbEndpoint::kInterrupt:
174 return LIBUSB_TRANSFER_TYPE_INTERRUPT;
175 case UsbEndpoint::kIsochronous:
176 return LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
177 default:
178 LOG(FATAL) << "transfer_type " << transfer_type << " is bogus";
179 }
180}
181
182const char kTransferTypeNameControl[] = "control";
183const char kTransferTypeNameBulk[] = "bulk";
184const char kTransferTypeNameInterrupt[] = "interrupt";
185const char kTransferTypeNameIsochronous[] = "isochronous";
186
187const char *TransferTypeName(int transfer_type) {
188 switch (transfer_type) {
189 case UsbEndpoint::kControl:
190 return kTransferTypeNameControl;
191 case UsbEndpoint::kBulk:
192 return kTransferTypeNameBulk;
193 case UsbEndpoint::kInterrupt:
194 return kTransferTypeNameInterrupt;
195 case UsbEndpoint::kIsochronous:
196 return kTransferTypeNameIsochronous;
197 default:
198 LOG(FATAL) << "transfer_type " << transfer_type << " is bogus";
199 }
200}
201} // namespace
202
203UsbEndpoint::IoStatus PhysicalUsbInEndpoint::DoRead(
204 uint32_t length, int32_t timeout_milliseconds, Buffer *out,
205 Notification *quit) {
206 CHECK_NOTNULL(handle_);
207 CHECK_GE(timeout_milliseconds, 0);
208 CHECK_NOTNULL(out);
209
210 VLOG(2) << "read on 0x" << std::hex << endpoint_address_and_direction()
211 << ", size 0x" << std::hex << length
212 << ", timeout " << std::dec << timeout_milliseconds << " [ms]";
213
214 boost::scoped_ptr<Buffer> whole_buffer(new Buffer());
215 whole_buffer->Resize(length);
216 void *p = whole_buffer->GetBufferPointer(length);
217 int transferred;
218 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
219 int r;
220
221 unsigned char transfer_type = LibUsbTransferType(transfer());
222 r = do_sync_bulk_transfer(libusb_context_,
223 handle_, endpoint_address_and_direction(),
224 static_cast<unsigned char *>(p), length,
225 &transferred, timeout, transfer_type,
226 quit);
227
228 switch (r) {
229 case LIBUSB_SUCCESS:
230 {
231 size_t size_transferred = static_cast<size_t>(transferred);
232 whole_buffer->Resize(size_transferred);
233
234 VLOG(2)
235 << "read on 0x"
236 << std::hex << static_cast<int>(endpoint_address_and_direction())
237 << ", size_transferred=0x" << std::hex << size_transferred;
238 out->Copy(*whole_buffer);
239 return kSuccess;
240 }
241 case LIBUSB_ERROR_TIMEOUT:
242 VLOG(2) << "libusb_" << TransferTypeName(transfer())
243 << "_transfer timeout";
244 return kTimeout;
245 case LIBUSB_ERROR_IO:
246 VLOG(1) << "Device i/o error.";
247 return kFail;
248 case LIBUSB_ERROR_NO_DEVICE:
249 VLOG(1) << "Device disconnected.";
250 return kNoDevice;
251 case LIBUSB_ERROR_OTHER:
252 LOG(INFO) << "libusb_" << TransferTypeName(transfer())
253 << "_transfer failed with r=" << std::dec << r;
254 return kUnknown;
255 default:
256 // Most of these are more esoteric.
257 LOG(INFO) << "libusb_" << TransferTypeName(transfer())
258 << "_transfer failed with r=" << std::dec << r;
259 return kFail;
260 }
261}
262
263////////////////////////////////////////////////////////////////////////
264
265PhysicalUsbOutEndpoint::PhysicalUsbOutEndpoint(
266 struct libusb_context *context,
267 struct libusb_device_handle *handle,
268 const struct libusb_endpoint_descriptor *descriptor)
269 : UsbOutEndpoint(DescriptorToTransfer(descriptor),
270 DescriptorToAddress(descriptor)),
271 libusb_context_(CHECK_NOTNULL(context)),
272 handle_(CHECK_NOTNULL(handle)) {
273 VLOG(1) << "0x" << std::hex << static_cast<int>(endpoint_address_and_direction())
274 << ", max_packet_size " << std::dec << descriptor->wMaxPacketSize;
275 CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kOut);
276}
277
278PhysicalUsbOutEndpoint::~PhysicalUsbOutEndpoint() {
279 CHECK_NOTNULL(handle_);
280 handle_ = nullptr;
281 libusb_context_ = nullptr;
282}
283
284int PhysicalUsbOutEndpoint::DoGetMaxPacketSize() {
285 CHECK_NOTNULL(handle_);
286 return LibusbGetMaxPacketSize(handle_, endpoint_address());
287}
288
289int PhysicalUsbOutEndpoint::DoGetMaxIsoPacketSize() {
290 CHECK_NOTNULL(handle_);
291 return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
292}
293
294UsbEndpoint::IoStatus PhysicalUsbOutEndpoint::DoWrite(
295 const Buffer &buffer, int32_t timeout_milliseconds) {
296 CHECK_NOTNULL(handle_);
297 CHECK_EQ(direction(), kOut);
298
299 VLOG(2) << "writing on 0x" << std::hex << endpoint_address_and_direction()
300 << ", length=" << std::dec << buffer.Length()
301 << ", timeout " << std::dec << timeout_milliseconds << " [ms]";
302
303 size_t length = buffer.Length();
304 const unsigned char *p =
305 static_cast<const unsigned char *>(buffer.GetBufferPointer(length));
306 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
307
308 int transferred;
309 int r;
310
311 switch (transfer()) {
312 case kBulk:
313 VLOG(2) << "libusb_bulk_transfer, length=" << std::dec << length;
314 r = libusb_bulk_transfer(handle_, endpoint_address_and_direction(),
315 const_cast<unsigned char *>(p),
316 length, &transferred,
317 timeout);
318 VLOG(2) << "libusb_bulk_transfer, r=" << std::dec << r
319 << ", transferred=" << std::dec << transferred;
320 break;
321 case kInterrupt:
322 VLOG(2) << "libusb_interrupt_transfer, length="
323 << std::dec << length;
324 r = libusb_interrupt_transfer(handle_, endpoint_address_and_direction(),
325 const_cast<unsigned char *>(p),
326 length, &transferred,
327 timeout);
328 VLOG(2) << "libusb_interrupt_transfer, r=" << std::dec << r
329 << ", transferred=" << std::dec << transferred;
330 break;
331 default:
332 LOG(FATAL) << "bogus transfer() value";
333 }
334
335 size_t size_transferred;
336
337 switch (r) {
338 case LIBUSB_SUCCESS:
339 size_transferred = static_cast<size_t>(transferred);
340 CHECK_EQ(size_transferred, length);
341 return kSuccess;
342 case LIBUSB_ERROR_TIMEOUT:
343 VLOG(2) << "libusb_" << TransferTypeName(transfer()) << "_transfer timeout";
344 return kTimeout;
345 case LIBUSB_ERROR_IO:
346 VLOG(1) << "Device i/o error.";
347 return kFail;
348 case LIBUSB_ERROR_NO_DEVICE:
349 VLOG(1) << "Device disconnected.";
350 return kNoDevice;
351 case LIBUSB_ERROR_OTHER:
352 LOG(INFO) << "libusb_" << TransferTypeName(transfer())
353 << "_transfer failed with r=" << std::dec << r;
354 return kUnknown;
355 default:
356 VLOG(1) << "libusb_" << TransferTypeName(transfer())
357 << "_transfer failed, r=" << std::dec << r;
358 return kFail;
359 }
360}
361
362} // namespace glibusb