blob: ee69ec9e107cb979299ffaa0114d6c5946e4c2ba [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());
219 r = do_sync_bulk_transfer(libusb_context_,
220 handle_, endpoint_address_and_direction(),
221 static_cast<unsigned char *>(p), length,
222 &transferred, timeout, transfer_type,
223 quit);
224
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 Silverman395d6252013-09-13 20:58:14 -0700239 return kTimeout;
240 case LIBUSB_ERROR_IO:
Brian Silverman7037e872013-09-14 15:35:45 -0700241 LOG(DEBUG, "device I/O error\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700242 return kFail;
243 case LIBUSB_ERROR_NO_DEVICE:
Brian Silverman7037e872013-09-14 15:35:45 -0700244 LOG(DEBUG, "device disconnected\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700245 return kNoDevice;
246 case LIBUSB_ERROR_OTHER:
Brian Silverman7037e872013-09-14 15:35:45 -0700247 LOG(INFO, "libusb_%s_transfer other error\n", TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700248 return kUnknown;
249 default:
250 // Most of these are more esoteric.
Brian Silverman7037e872013-09-14 15:35:45 -0700251 LOG(INFO, "libusb_%s_transfer failed with %d: %s\n",
252 TransferTypeName(transfer()), r, libusb_error_name(r));
Brian Silverman395d6252013-09-13 20:58:14 -0700253 return kFail;
254 }
255}
256
257////////////////////////////////////////////////////////////////////////
258
259PhysicalUsbOutEndpoint::PhysicalUsbOutEndpoint(
260 struct libusb_context *context,
261 struct libusb_device_handle *handle,
262 const struct libusb_endpoint_descriptor *descriptor)
263 : UsbOutEndpoint(DescriptorToTransfer(descriptor),
264 DescriptorToAddress(descriptor)),
265 libusb_context_(CHECK_NOTNULL(context)),
266 handle_(CHECK_NOTNULL(handle)) {
Brian Silverman7037e872013-09-14 15:35:45 -0700267 LOG(DEBUG, "0x%x, max_packet_size=%" PRId16 "\n",
268 static_cast<int>(endpoint_address_and_direction()),
269 descriptor->wMaxPacketSize);
Brian Silverman395d6252013-09-13 20:58:14 -0700270 CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kOut);
271}
272
273PhysicalUsbOutEndpoint::~PhysicalUsbOutEndpoint() {
274 CHECK_NOTNULL(handle_);
275 handle_ = nullptr;
276 libusb_context_ = nullptr;
277}
278
279int PhysicalUsbOutEndpoint::DoGetMaxPacketSize() {
280 CHECK_NOTNULL(handle_);
281 return LibusbGetMaxPacketSize(handle_, endpoint_address());
282}
283
284int PhysicalUsbOutEndpoint::DoGetMaxIsoPacketSize() {
285 CHECK_NOTNULL(handle_);
286 return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
287}
288
289UsbEndpoint::IoStatus PhysicalUsbOutEndpoint::DoWrite(
290 const Buffer &buffer, int32_t timeout_milliseconds) {
291 CHECK_NOTNULL(handle_);
292 CHECK_EQ(direction(), kOut);
293
Brian Silverman7037e872013-09-14 15:35:45 -0700294 LOG(DEBUG, "writing on 0x%x, length=%zd, timeout %d [ms]\n",
295 endpoint_address_and_direction(), buffer.Length(), timeout_milliseconds);
Brian Silverman395d6252013-09-13 20:58:14 -0700296
297 size_t length = buffer.Length();
298 const unsigned char *p =
299 static_cast<const unsigned char *>(buffer.GetBufferPointer(length));
300 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
301
302 int transferred;
303 int r;
304
Brian Silverman7037e872013-09-14 15:35:45 -0700305 // TODO(brians): Conditionally enable this.
306 LOG(DEBUG, "libusb_%s_transfer, length=%d\n",
307 TransferTypeName(transfer()), length);
Brian Silverman395d6252013-09-13 20:58:14 -0700308 switch (transfer()) {
309 case kBulk:
Brian Silverman395d6252013-09-13 20:58:14 -0700310 r = libusb_bulk_transfer(handle_, endpoint_address_and_direction(),
311 const_cast<unsigned char *>(p),
312 length, &transferred,
313 timeout);
Brian Silverman395d6252013-09-13 20:58:14 -0700314 break;
315 case kInterrupt:
Brian Silverman395d6252013-09-13 20:58:14 -0700316 r = libusb_interrupt_transfer(handle_, endpoint_address_and_direction(),
317 const_cast<unsigned char *>(p),
318 length, &transferred,
319 timeout);
Brian Silverman395d6252013-09-13 20:58:14 -0700320 break;
Brian Silverman7037e872013-09-14 15:35:45 -0700321 case kControl:
322 case kIsochronous:
Brian Silverman395d6252013-09-13 20:58:14 -0700323 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700324 LOG(FATAL, "bogus transfer() value\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700325 }
Brian Silverman7037e872013-09-14 15:35:45 -0700326 // TODO(brians): Conditionally enable this.
327 LOG(DEBUG, "libusb_%s_transfer, r=%d (%s), transferred=%d\n",
328 TransferTypeName(transfer()), r, libusb_error_name(r), transferred);
Brian Silverman395d6252013-09-13 20:58:14 -0700329
330 size_t size_transferred;
331
332 switch (r) {
333 case LIBUSB_SUCCESS:
334 size_transferred = static_cast<size_t>(transferred);
335 CHECK_EQ(size_transferred, length);
336 return kSuccess;
337 case LIBUSB_ERROR_TIMEOUT:
Brian Silverman7037e872013-09-14 15:35:45 -0700338 LOG(DEBUG, "libusb_%s_transfer timeout\n",
339 TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700340 return kTimeout;
341 case LIBUSB_ERROR_IO:
Brian Silverman7037e872013-09-14 15:35:45 -0700342 LOG(DEBUG, "device I/O error\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700343 return kFail;
344 case LIBUSB_ERROR_NO_DEVICE:
Brian Silverman7037e872013-09-14 15:35:45 -0700345 LOG(DEBUG, "device disconnected\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700346 return kNoDevice;
347 case LIBUSB_ERROR_OTHER:
Brian Silverman7037e872013-09-14 15:35:45 -0700348 LOG(INFO, "libusb_%s_transfer other error\n", TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700349 return kUnknown;
350 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700351 LOG(INFO, "libusb_%s_transfer failed with %d: %s\n",
352 TransferTypeName(transfer()), r, libusb_error_name(r));
Brian Silverman395d6252013-09-13 20:58:14 -0700353 return kFail;
354 }
355}
356
357} // namespace glibusb