blob: 23468931c07abc6bee240f93998ec0f5215b7426 [file] [log] [blame]
Brian Silverman395d6252013-09-13 20:58:14 -07001// Copyright 2012 Google Inc. All Rights Reserved.
Brian Silverman7037e872013-09-14 15:35:45 -07002//
3// Modified by FRC Team 971.
Brian Silverman395d6252013-09-13 20:58:14 -07004
5#include "glibusb_endpoint.h"
6
7#include <stddef.h>
Brian Silverman7037e872013-09-14 15:35:45 -07008#include <inttypes.h>
9#include <memory>
Brian Silverman395d6252013-09-13 20:58:14 -070010
Brian Silverman7037e872013-09-14 15:35:45 -070011#include "aos/common/logging/logging.h"
Brian Silverman395d6252013-09-13 20:58:14 -070012#include "gbuffer.h"
13#include "glibusb_endpoint_internal.h"
14#include "glibusb_internal.h"
15#include "glibusb_transfer.h"
16
17namespace glibusb {
18
19namespace {
20int LibusbGetMaxPacketSize(libusb_device_handle *handle,
21 unsigned char endpoint)
22{
23 libusb_device *device = CHECK_NOTNULL(libusb_get_device(handle));
24 return libusb_get_max_packet_size(device, endpoint);
25}
26
27int LibusbGetMaxIsoPacketSize(libusb_device_handle *handle,
28 unsigned char endpoint)
29{
30 libusb_device *device = CHECK_NOTNULL(libusb_get_device(handle));
31 return libusb_get_max_iso_packet_size(device, endpoint);
32}
33} // namespace
34
35Notification::Notification(bool prenotify)
Brian Silverman7037e872013-09-14 15:35:45 -070036 : notified_changed_(&mutex_), notified_(prenotify) {}
Brian Silverman395d6252013-09-13 20:58:14 -070037
38bool Notification::HasBeenNotified() const {
Brian Silverman7037e872013-09-14 15:35:45 -070039 ::aos::MutexLocker lock(&mutex_);
Brian Silverman395d6252013-09-13 20:58:14 -070040 return notified_;
41}
42
43void Notification::WaitForNotification() const {
Brian Silverman7037e872013-09-14 15:35:45 -070044 ::aos::MutexLocker lock(&mutex_);
45 while (HasBeenNotifiedUnlocked()) notified_changed_.Wait();
Brian Silverman395d6252013-09-13 20:58:14 -070046}
47
48void Notification::Notify() {
Brian Silverman7037e872013-09-14 15:35:45 -070049 ::aos::MutexLocker lock(&mutex_);
50 if (notified_) {
51 LOG(FATAL, "already notified\n");
52 }
Brian Silverman395d6252013-09-13 20:58:14 -070053 notified_ = true;
Brian Silverman7037e872013-09-14 15:35:45 -070054 notified_changed_.Broadcast();
Brian Silverman395d6252013-09-13 20:58:14 -070055}
56
57bool Notification::HasBeenNotifiedUnlocked() const {
58 return notified_;
59}
60
61////////////////////////////////////////////////////////////////////////
62
63const int32_t UsbEndpoint::kMaxBulkTransferBytes;
64
65UsbEndpoint::UsbEndpoint(DirectionType direction, TransferType transfer,
66 int endpoint_address)
67 : direction_(direction),
68 transfer_(transfer),
69 endpoint_address_and_direction_(endpoint_address | direction) {
Brian Silverman7037e872013-09-14 15:35:45 -070070 if ((endpoint_address_and_direction_ & 0x80) != direction) {
71 LOG(FATAL, "Direction in address %x doesn't match direction %x.\n",
72 endpoint_address_and_direction_, direction);
73 }
74 if ((endpoint_address_and_direction_ & 0x8f) !=
75 endpoint_address_and_direction_) {
76 LOG(FATAL, "Invalid endpoint address %x.\n",
77 endpoint_address_and_direction_);
78 }
Brian Silverman395d6252013-09-13 20:58:14 -070079}
80
81////////////////////////////////////////////////////////////////////////
82
83UsbInEndpoint::UsbInEndpoint(TransferType transfer, int endpoint_address)
84 : UsbEndpoint(UsbEndpoint::kIn, transfer, endpoint_address) {
85}
86
87bool UsbInEndpoint::Read(Buffer *out) {
88 IoStatus status = ReadAtMostWithTimeout(kMaxBulkTransferBytes, 0, out);
89 return status == kSuccess;
90}
91
92UsbEndpoint::IoStatus
93UsbInEndpoint::ReadWithTimeout(int32_t timeout_milliseconds,
94 Buffer *out) {
95 return ReadAtMostWithTimeout(kMaxBulkTransferBytes,
96 timeout_milliseconds, out);
97}
98
99bool UsbInEndpoint::ReadAtMost(uint32_t length, Buffer *out) {
100 IoStatus status = ReadAtMostWithTimeout(length, 0, out);
101 return status == kSuccess;
102}
103
104UsbEndpoint::IoStatus UsbInEndpoint::ReadAtMostWithTimeout(
105 uint32_t length, int32_t timeout_milliseconds, Buffer *out) {
106 return DoRead(length, timeout_milliseconds, out, NULL);
107}
108
109UsbEndpoint::IoStatus UsbInEndpoint::ReadAtMostWithTimeoutAndNotification(
110 uint32_t length, int32_t timeout_milliseconds, Buffer *out,
111 Notification *quit) {
112 CHECK_NOTNULL(quit);
113 return DoRead(length, timeout_milliseconds, out, quit);
114}
115
116////////////////////////////////////////////////////////////////////////
117
118UsbOutEndpoint::UsbOutEndpoint(TransferType transfer, int endpoint_address)
119 : UsbEndpoint(UsbEndpoint::kOut, transfer, endpoint_address) {
120}
121
122bool UsbOutEndpoint::Write(const Buffer &buffer) {
123 IoStatus status = DoWrite(buffer, 0);
124 return status == kSuccess;
125}
126
127UsbEndpoint::IoStatus UsbOutEndpoint::WriteWithTimeout(const Buffer &buffer,
128 int32_t timeout_milliseconds) {
129 return DoWrite(buffer, timeout_milliseconds);
130}
131
132////////////////////////////////////////////////////////////////////////
133
134PhysicalUsbInEndpoint::PhysicalUsbInEndpoint(
135 struct libusb_context *context,
136 struct libusb_device_handle *handle,
137 const struct libusb_endpoint_descriptor *descriptor)
138 : UsbInEndpoint(DescriptorToTransfer(descriptor),
139 DescriptorToAddress(descriptor)),
140 libusb_context_(CHECK_NOTNULL(context)),
141 handle_(CHECK_NOTNULL(handle)) {
Brian Silverman7037e872013-09-14 15:35:45 -0700142 LOG(DEBUG, "0x%x, max_packet_size=%" PRId16 "\n",
143 static_cast<int>(endpoint_address_and_direction()),
144 descriptor->wMaxPacketSize);
Brian Silverman395d6252013-09-13 20:58:14 -0700145 CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kIn);
146}
147
148PhysicalUsbInEndpoint::~PhysicalUsbInEndpoint() {
149 CHECK_NOTNULL(handle_);
150 handle_ = nullptr;
151 libusb_context_ = nullptr;
152}
153
154int PhysicalUsbInEndpoint::DoGetMaxPacketSize() {
155 CHECK_NOTNULL(handle_);
156 return LibusbGetMaxPacketSize(handle_, endpoint_address());
157}
158
159int PhysicalUsbInEndpoint::DoGetMaxIsoPacketSize() {
160 CHECK_NOTNULL(handle_);
161 return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
162}
163
164namespace {
165unsigned char LibUsbTransferType(int transfer_type) {
166 switch (transfer_type) {
167 case UsbEndpoint::kControl:
168 return LIBUSB_TRANSFER_TYPE_CONTROL;
169 case UsbEndpoint::kBulk:
170 return LIBUSB_TRANSFER_TYPE_BULK;
171 case UsbEndpoint::kInterrupt:
172 return LIBUSB_TRANSFER_TYPE_INTERRUPT;
173 case UsbEndpoint::kIsochronous:
174 return LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
175 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700176 LOG(FATAL, "transfer_type %d is bogus", transfer_type);
Brian Silverman395d6252013-09-13 20:58:14 -0700177 }
178}
179
180const char kTransferTypeNameControl[] = "control";
181const char kTransferTypeNameBulk[] = "bulk";
182const char kTransferTypeNameInterrupt[] = "interrupt";
183const char kTransferTypeNameIsochronous[] = "isochronous";
184
185const char *TransferTypeName(int transfer_type) {
186 switch (transfer_type) {
187 case UsbEndpoint::kControl:
188 return kTransferTypeNameControl;
189 case UsbEndpoint::kBulk:
190 return kTransferTypeNameBulk;
191 case UsbEndpoint::kInterrupt:
192 return kTransferTypeNameInterrupt;
193 case UsbEndpoint::kIsochronous:
194 return kTransferTypeNameIsochronous;
195 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700196 LOG(FATAL, "transfer_type %d is bogus", transfer_type);
Brian Silverman395d6252013-09-13 20:58:14 -0700197 }
198}
199} // namespace
200
201UsbEndpoint::IoStatus PhysicalUsbInEndpoint::DoRead(
202 uint32_t length, int32_t timeout_milliseconds, Buffer *out,
203 Notification *quit) {
204 CHECK_NOTNULL(handle_);
205 CHECK_GE(timeout_milliseconds, 0);
206 CHECK_NOTNULL(out);
207
Brian Silverman7037e872013-09-14 15:35:45 -0700208 // TODO(brians): Conditionally enable this.
209 LOG(DEBUG, "read on 0x%x, size 0x%x, timeout %" PRId32 " [ms]\n",
210 endpoint_address_and_direction(), length, timeout_milliseconds);
Brian Silverman395d6252013-09-13 20:58:14 -0700211
Brian Silverman93871ee2013-09-14 18:15:28 -0700212 out->Resize(length);
213 void *p = out->GetBufferPointer(length);
Brian Silverman395d6252013-09-13 20:58:14 -0700214 int transferred;
215 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
216 int r;
217
218 unsigned char transfer_type = LibUsbTransferType(transfer());
Brian Silverman14a7b1a2013-10-11 21:34:03 -0700219 r = do_sync_transfer(libusb_context_,
220 handle_, endpoint_address_and_direction(),
221 static_cast<unsigned char *>(p), length,
222 &transferred, timeout, transfer_type,
223 quit);
Brian Silverman395d6252013-09-13 20:58:14 -0700224
225 switch (r) {
226 case LIBUSB_SUCCESS:
227 {
228 size_t size_transferred = static_cast<size_t>(transferred);
Brian Silverman93871ee2013-09-14 18:15:28 -0700229 out->Resize(size_transferred);
Brian Silverman395d6252013-09-13 20:58:14 -0700230
Brian Silverman7037e872013-09-14 15:35:45 -0700231 // TODO(brians): Conditionally enable this.
232 LOG(DEBUG, "read on 0x%x, size_transferred=%zx\n",
233 endpoint_address_and_direction(), size_transferred);
Brian Silverman395d6252013-09-13 20:58:14 -0700234 return kSuccess;
235 }
236 case LIBUSB_ERROR_TIMEOUT:
Brian Silverman7037e872013-09-14 15:35:45 -0700237 LOG(DEBUG, "libusb_%s_transfer timeout\n",
238 TransferTypeName(transfer()));
Brian Silverman14a7b1a2013-10-11 21:34:03 -0700239 out->Resize(0);
Brian Silverman395d6252013-09-13 20:58:14 -0700240 return kTimeout;
241 case LIBUSB_ERROR_IO:
Brian Silverman7037e872013-09-14 15:35:45 -0700242 LOG(DEBUG, "device I/O error\n");
Brian Silverman14a7b1a2013-10-11 21:34:03 -0700243 out->Resize(0);
Brian Silverman395d6252013-09-13 20:58:14 -0700244 return kFail;
245 case LIBUSB_ERROR_NO_DEVICE:
Brian Silverman7037e872013-09-14 15:35:45 -0700246 LOG(DEBUG, "device disconnected\n");
Brian Silverman14a7b1a2013-10-11 21:34:03 -0700247 out->Resize(0);
Brian Silverman395d6252013-09-13 20:58:14 -0700248 return kNoDevice;
249 case LIBUSB_ERROR_OTHER:
Brian Silverman7037e872013-09-14 15:35:45 -0700250 LOG(INFO, "libusb_%s_transfer other error\n", TransferTypeName(transfer()));
Brian Silverman14a7b1a2013-10-11 21:34:03 -0700251 out->Resize(0);
Brian Silverman395d6252013-09-13 20:58:14 -0700252 return kUnknown;
253 default:
254 // Most of these are more esoteric.
Brian Silverman7037e872013-09-14 15:35:45 -0700255 LOG(INFO, "libusb_%s_transfer failed with %d: %s\n",
256 TransferTypeName(transfer()), r, libusb_error_name(r));
Brian Silverman14a7b1a2013-10-11 21:34:03 -0700257 out->Resize(0);
Brian Silverman395d6252013-09-13 20:58:14 -0700258 return kFail;
259 }
260}
261
262////////////////////////////////////////////////////////////////////////
263
264PhysicalUsbOutEndpoint::PhysicalUsbOutEndpoint(
265 struct libusb_context *context,
266 struct libusb_device_handle *handle,
267 const struct libusb_endpoint_descriptor *descriptor)
268 : UsbOutEndpoint(DescriptorToTransfer(descriptor),
269 DescriptorToAddress(descriptor)),
270 libusb_context_(CHECK_NOTNULL(context)),
271 handle_(CHECK_NOTNULL(handle)) {
Brian Silverman7037e872013-09-14 15:35:45 -0700272 LOG(DEBUG, "0x%x, max_packet_size=%" PRId16 "\n",
273 static_cast<int>(endpoint_address_and_direction()),
274 descriptor->wMaxPacketSize);
Brian Silverman395d6252013-09-13 20:58:14 -0700275 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
Brian Silverman7037e872013-09-14 15:35:45 -0700299 LOG(DEBUG, "writing on 0x%x, length=%zd, timeout %d [ms]\n",
300 endpoint_address_and_direction(), buffer.Length(), timeout_milliseconds);
Brian Silverman395d6252013-09-13 20:58:14 -0700301
302 size_t length = buffer.Length();
303 const unsigned char *p =
304 static_cast<const unsigned char *>(buffer.GetBufferPointer(length));
305 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
306
307 int transferred;
308 int r;
309
Brian Silverman7037e872013-09-14 15:35:45 -0700310 // TODO(brians): Conditionally enable this.
311 LOG(DEBUG, "libusb_%s_transfer, length=%d\n",
312 TransferTypeName(transfer()), length);
Brian Silverman395d6252013-09-13 20:58:14 -0700313 switch (transfer()) {
314 case kBulk:
Brian Silverman395d6252013-09-13 20:58:14 -0700315 r = libusb_bulk_transfer(handle_, endpoint_address_and_direction(),
316 const_cast<unsigned char *>(p),
317 length, &transferred,
318 timeout);
Brian Silverman395d6252013-09-13 20:58:14 -0700319 break;
320 case kInterrupt:
Brian Silverman395d6252013-09-13 20:58:14 -0700321 r = libusb_interrupt_transfer(handle_, endpoint_address_and_direction(),
322 const_cast<unsigned char *>(p),
323 length, &transferred,
324 timeout);
Brian Silverman395d6252013-09-13 20:58:14 -0700325 break;
Brian Silverman7037e872013-09-14 15:35:45 -0700326 case kControl:
327 case kIsochronous:
Brian Silverman395d6252013-09-13 20:58:14 -0700328 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700329 LOG(FATAL, "bogus transfer() value\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700330 }
Brian Silverman7037e872013-09-14 15:35:45 -0700331 // TODO(brians): Conditionally enable this.
332 LOG(DEBUG, "libusb_%s_transfer, r=%d (%s), transferred=%d\n",
333 TransferTypeName(transfer()), r, libusb_error_name(r), transferred);
Brian Silverman395d6252013-09-13 20:58:14 -0700334
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:
Brian Silverman7037e872013-09-14 15:35:45 -0700343 LOG(DEBUG, "libusb_%s_transfer timeout\n",
344 TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700345 return kTimeout;
346 case LIBUSB_ERROR_IO:
Brian Silverman7037e872013-09-14 15:35:45 -0700347 LOG(DEBUG, "device I/O error\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700348 return kFail;
349 case LIBUSB_ERROR_NO_DEVICE:
Brian Silverman7037e872013-09-14 15:35:45 -0700350 LOG(DEBUG, "device disconnected\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700351 return kNoDevice;
352 case LIBUSB_ERROR_OTHER:
Brian Silverman7037e872013-09-14 15:35:45 -0700353 LOG(INFO, "libusb_%s_transfer other error\n", TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700354 return kUnknown;
355 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700356 LOG(INFO, "libusb_%s_transfer failed with %d: %s\n",
357 TransferTypeName(transfer()), r, libusb_error_name(r));
Brian Silverman395d6252013-09-13 20:58:14 -0700358 return kFail;
359 }
360}
361
362} // namespace glibusb