blob: 1bfd583b915b3bc5cb811c2fb8ca6e73d6af8d5f [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 Silverman7037e872013-09-14 15:35:45 -0700212 ::std::unique_ptr<Buffer> whole_buffer(new Buffer());
Brian Silverman395d6252013-09-13 20:58:14 -0700213 whole_buffer->Resize(length);
214 void *p = whole_buffer->GetBufferPointer(length);
215 int transferred;
216 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
217 int r;
218
219 unsigned char transfer_type = LibUsbTransferType(transfer());
220 r = do_sync_bulk_transfer(libusb_context_,
221 handle_, endpoint_address_and_direction(),
222 static_cast<unsigned char *>(p), length,
223 &transferred, timeout, transfer_type,
224 quit);
225
226 switch (r) {
227 case LIBUSB_SUCCESS:
228 {
229 size_t size_transferred = static_cast<size_t>(transferred);
230 whole_buffer->Resize(size_transferred);
231
Brian Silverman7037e872013-09-14 15:35:45 -0700232 // TODO(brians): Conditionally enable this.
233 LOG(DEBUG, "read on 0x%x, size_transferred=%zx\n",
234 endpoint_address_and_direction(), size_transferred);
Brian Silverman395d6252013-09-13 20:58:14 -0700235 out->Copy(*whole_buffer);
236 return kSuccess;
237 }
238 case LIBUSB_ERROR_TIMEOUT:
Brian Silverman7037e872013-09-14 15:35:45 -0700239 LOG(DEBUG, "libusb_%s_transfer timeout\n",
240 TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700241 return kTimeout;
242 case LIBUSB_ERROR_IO:
Brian Silverman7037e872013-09-14 15:35:45 -0700243 LOG(DEBUG, "device I/O error\n");
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 Silverman395d6252013-09-13 20:58:14 -0700247 return kNoDevice;
248 case LIBUSB_ERROR_OTHER:
Brian Silverman7037e872013-09-14 15:35:45 -0700249 LOG(INFO, "libusb_%s_transfer other error\n", TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700250 return kUnknown;
251 default:
252 // Most of these are more esoteric.
Brian Silverman7037e872013-09-14 15:35:45 -0700253 LOG(INFO, "libusb_%s_transfer failed with %d: %s\n",
254 TransferTypeName(transfer()), r, libusb_error_name(r));
Brian Silverman395d6252013-09-13 20:58:14 -0700255 return kFail;
256 }
257}
258
259////////////////////////////////////////////////////////////////////////
260
261PhysicalUsbOutEndpoint::PhysicalUsbOutEndpoint(
262 struct libusb_context *context,
263 struct libusb_device_handle *handle,
264 const struct libusb_endpoint_descriptor *descriptor)
265 : UsbOutEndpoint(DescriptorToTransfer(descriptor),
266 DescriptorToAddress(descriptor)),
267 libusb_context_(CHECK_NOTNULL(context)),
268 handle_(CHECK_NOTNULL(handle)) {
Brian Silverman7037e872013-09-14 15:35:45 -0700269 LOG(DEBUG, "0x%x, max_packet_size=%" PRId16 "\n",
270 static_cast<int>(endpoint_address_and_direction()),
271 descriptor->wMaxPacketSize);
Brian Silverman395d6252013-09-13 20:58:14 -0700272 CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kOut);
273}
274
275PhysicalUsbOutEndpoint::~PhysicalUsbOutEndpoint() {
276 CHECK_NOTNULL(handle_);
277 handle_ = nullptr;
278 libusb_context_ = nullptr;
279}
280
281int PhysicalUsbOutEndpoint::DoGetMaxPacketSize() {
282 CHECK_NOTNULL(handle_);
283 return LibusbGetMaxPacketSize(handle_, endpoint_address());
284}
285
286int PhysicalUsbOutEndpoint::DoGetMaxIsoPacketSize() {
287 CHECK_NOTNULL(handle_);
288 return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
289}
290
291UsbEndpoint::IoStatus PhysicalUsbOutEndpoint::DoWrite(
292 const Buffer &buffer, int32_t timeout_milliseconds) {
293 CHECK_NOTNULL(handle_);
294 CHECK_EQ(direction(), kOut);
295
Brian Silverman7037e872013-09-14 15:35:45 -0700296 LOG(DEBUG, "writing on 0x%x, length=%zd, timeout %d [ms]\n",
297 endpoint_address_and_direction(), buffer.Length(), timeout_milliseconds);
Brian Silverman395d6252013-09-13 20:58:14 -0700298
299 size_t length = buffer.Length();
300 const unsigned char *p =
301 static_cast<const unsigned char *>(buffer.GetBufferPointer(length));
302 const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
303
304 int transferred;
305 int r;
306
Brian Silverman7037e872013-09-14 15:35:45 -0700307 // TODO(brians): Conditionally enable this.
308 LOG(DEBUG, "libusb_%s_transfer, length=%d\n",
309 TransferTypeName(transfer()), length);
Brian Silverman395d6252013-09-13 20:58:14 -0700310 switch (transfer()) {
311 case kBulk:
Brian Silverman395d6252013-09-13 20:58:14 -0700312 r = libusb_bulk_transfer(handle_, endpoint_address_and_direction(),
313 const_cast<unsigned char *>(p),
314 length, &transferred,
315 timeout);
Brian Silverman395d6252013-09-13 20:58:14 -0700316 break;
317 case kInterrupt:
Brian Silverman395d6252013-09-13 20:58:14 -0700318 r = libusb_interrupt_transfer(handle_, endpoint_address_and_direction(),
319 const_cast<unsigned char *>(p),
320 length, &transferred,
321 timeout);
Brian Silverman395d6252013-09-13 20:58:14 -0700322 break;
Brian Silverman7037e872013-09-14 15:35:45 -0700323 case kControl:
324 case kIsochronous:
Brian Silverman395d6252013-09-13 20:58:14 -0700325 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700326 LOG(FATAL, "bogus transfer() value\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700327 }
Brian Silverman7037e872013-09-14 15:35:45 -0700328 // TODO(brians): Conditionally enable this.
329 LOG(DEBUG, "libusb_%s_transfer, r=%d (%s), transferred=%d\n",
330 TransferTypeName(transfer()), r, libusb_error_name(r), transferred);
Brian Silverman395d6252013-09-13 20:58:14 -0700331
332 size_t size_transferred;
333
334 switch (r) {
335 case LIBUSB_SUCCESS:
336 size_transferred = static_cast<size_t>(transferred);
337 CHECK_EQ(size_transferred, length);
338 return kSuccess;
339 case LIBUSB_ERROR_TIMEOUT:
Brian Silverman7037e872013-09-14 15:35:45 -0700340 LOG(DEBUG, "libusb_%s_transfer timeout\n",
341 TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700342 return kTimeout;
343 case LIBUSB_ERROR_IO:
Brian Silverman7037e872013-09-14 15:35:45 -0700344 LOG(DEBUG, "device I/O error\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700345 return kFail;
346 case LIBUSB_ERROR_NO_DEVICE:
Brian Silverman7037e872013-09-14 15:35:45 -0700347 LOG(DEBUG, "device disconnected\n");
Brian Silverman395d6252013-09-13 20:58:14 -0700348 return kNoDevice;
349 case LIBUSB_ERROR_OTHER:
Brian Silverman7037e872013-09-14 15:35:45 -0700350 LOG(INFO, "libusb_%s_transfer other error\n", TransferTypeName(transfer()));
Brian Silverman395d6252013-09-13 20:58:14 -0700351 return kUnknown;
352 default:
Brian Silverman7037e872013-09-14 15:35:45 -0700353 LOG(INFO, "libusb_%s_transfer failed with %d: %s\n",
354 TransferTypeName(transfer()), r, libusb_error_name(r));
Brian Silverman395d6252013-09-13 20:58:14 -0700355 return kFail;
356 }
357}
358
359} // namespace glibusb