blob: 5dd007564760312c0d0b561ba04bc21ecda067f9 [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.h"
6
Brian Silverman7037e872013-09-14 15:35:45 -07007#include <inttypes.h>
Brian Silverman395d6252013-09-13 20:58:14 -07008#include <cstdio>
9#include <sstream>
10#include <iomanip>
11#include <string>
Brian Silverman7037e872013-09-14 15:35:45 -070012#include <libusb-1.0/libusb.h>
Brian Silverman395d6252013-09-13 20:58:14 -070013
Brian Silverman7037e872013-09-14 15:35:45 -070014#include "aos/common/logging/logging.h"
Brian Silverman395d6252013-09-13 20:58:14 -070015#include "glibusb_device_internal.h"
16
17namespace glibusb {
18
19namespace {
20bool safe_strtou32(const std::string &s, uint32_t *value) {
21 CHECK_NOTNULL(value);
22 std::stringstream stream(s);
23 stream >> std::dec;
24 return stream >> *value;
25}
26
27bool safe_strtou32_hex(const std::string &s, uint32_t *value) {
28 CHECK_NOTNULL(value);
29 std::stringstream stream(s);
30 stream >> std::hex;
31 return stream >> *value;
32}
33}
34
35std::string DeviceLocation::ToString() const
36{
37 return DeviceLocationToString(*this);
38}
39
40std::string DeviceLocationToString(const DeviceLocation &location) {
41 std::stringstream stream;
42 stream
43 << std::dec << std::setw(3) << std::setfill('0')
44 << static_cast<int>(location.bus_number)
45 << ":"
46 << std::dec << std::setw(3) << std::setfill('0')
47 << static_cast<int>(location.device_address);
48 return stream.str();
49}
50
51std::ostream &operator <<(std::ostream &out,
52 const DeviceLocation &location) {
53 out << DeviceLocationToString(location);
54 return out;
55}
56
57std::string VendorProductIdToString(const VendorProductId &vendor_product_id) {
58 std::stringstream stream;
59 stream
60 << std::hex << std::setw(4) << std::setfill('0')
61 << static_cast<int>(vendor_product_id.vendor_id)
62 << ":"
63 << std::hex << std::setw(4) << std::setfill('0')
64 << static_cast<int>(vendor_product_id.product_id);
65 return stream.str();
66}
67
68std::string DeviceLocationAndId::ToString() const
69{
70 return DeviceLocationAndIdToString(*this);
71}
72
73std::string DeviceLocationAndIdToString(const DeviceLocationAndId &location_and_id) {
74 return DeviceLocationToString(location_and_id.location) + " " +
75 VendorProductIdToString(location_and_id.id);
76}
77
78std::ostream &operator <<(std::ostream &out,
79 const DeviceLocationAndId &location_and_id) {
80 out << DeviceLocationAndIdToString(location_and_id);
81 return out;
82}
83
84//////////////////////////////////////////////////////////////////////
85
86Libusb::Libusb() {
87 CHECK_EQ(libusb_init(&libusb_context_), 0);
88}
89
90Libusb::~Libusb() {
91 libusb_exit(libusb_context_);
92}
93
94void Libusb::SetDebug(int level) {
95 libusb_set_debug(libusb_context_, level);
96}
97
98void Libusb::FindDeviceLocationAndId(std::vector<DeviceLocationAndId> *result) {
99 CHECK_NOTNULL(result);
100 struct libusb_device **devices_head;
101 CHECK_GE(libusb_get_device_list(libusb_context_, &devices_head), 0);
102 CHECK_NOTNULL(devices_head);
103
104 for (struct libusb_device **devices = devices_head;
105 *devices != NULL; ++devices) {
106 struct libusb_device_descriptor descriptor;
107 CHECK_GE(libusb_get_device_descriptor(*devices, &descriptor), 0);
Brian Silverman7037e872013-09-14 15:35:45 -0700108 LOG(DEBUG, "idVendor = 0x%" PRIx16 " idProduct = 0x%" PRIx16 "\n",
109 descriptor.idVendor, descriptor.idProduct);
Brian Silverman395d6252013-09-13 20:58:14 -0700110 DeviceLocationAndId dev_location_id;
111 dev_location_id.location.bus_number = libusb_get_bus_number(*devices);
112 dev_location_id.location.device_address =
113 libusb_get_device_address(*devices);
114 dev_location_id.id.vendor_id = descriptor.idVendor;
115 dev_location_id.id.product_id = descriptor.idProduct;
116 result->push_back(dev_location_id);
117 }
118 libusb_free_device_list(devices_head, /*unref_devices=*/ 1);
119}
120
121// Find a single device that matches the vendor_id and product_id, and
122// optionally bus_number and device_address. CHECK if more than one device is
123// found or no devices are found.
124//
125// This is the implementation behind FindSingleMatchingDeviceAtLocationOrLose()
126// and FindSingleMatchingDeviceOrLose().
127static libusb_device_handle *FindSingleDevice(
128 struct libusb_context *context,
129 bool match_location,
130 const DeviceLocationAndId &dev_location_id) {
131 struct libusb_device **devices_head;
132 CHECK_GE(libusb_get_device_list(context, &devices_head), 0);
133 CHECK_NOTNULL(devices_head);
134
135 struct libusb_device *matching_device = NULL;
136 for (struct libusb_device **devices = devices_head;
137 *devices != NULL; ++devices) {
138 if (match_location) {
139 uint8_t device_bus_number = libusb_get_bus_number(*devices);
140 uint8_t device_device_address = libusb_get_device_address(*devices);
141 if (device_bus_number != dev_location_id.location.bus_number ||
142 device_device_address != dev_location_id.location.device_address)
143 continue;
144 }
145
146 struct libusb_device_descriptor descriptor;
147 CHECK_GE(libusb_get_device_descriptor(*devices, &descriptor), 0);
Brian Silverman7037e872013-09-14 15:35:45 -0700148 LOG(DEBUG, "idVendor = 0x%" PRIx16 " idProduct = 0x%" PRIx16 "\n",
149 descriptor.idVendor, descriptor.idProduct);
Brian Silverman395d6252013-09-13 20:58:14 -0700150 if (descriptor.idVendor == dev_location_id.id.vendor_id &&
151 descriptor.idProduct == dev_location_id.id.product_id) {
Brian Silverman7037e872013-09-14 15:35:45 -0700152 if (matching_device != NULL) {
153 LOG(FATAL, "found multiple matching devices\n");
154 }
Brian Silverman395d6252013-09-13 20:58:14 -0700155 matching_device = *devices;
156 }
157 }
158 if (match_location) {
159 int bus_number = static_cast<int>(dev_location_id.location.bus_number);
160 int device_address =
161 static_cast<int>(dev_location_id.location.device_address);
Brian Silverman7037e872013-09-14 15:35:45 -0700162 if (matching_device == NULL) {
163 LOG(FATAL, "no matching device found for vid=%" PRIx16 ", pid=%" PRIx16
164 ", bus_number=%d, device_address=%d\n",
165 dev_location_id.id.vendor_id, dev_location_id.id.product_id,
166 bus_number, device_address);
167 }
Brian Silverman395d6252013-09-13 20:58:14 -0700168 } else {
169 const int vendor_id = dev_location_id.id.vendor_id;
170 const int product_id = dev_location_id.id.product_id;
Brian Silverman7037e872013-09-14 15:35:45 -0700171 if (matching_device == NULL) {
172 LOG(FATAL, "no matching device found for vid=%d, pid=%d\n",
173 vendor_id, product_id);
174 }
Brian Silverman395d6252013-09-13 20:58:14 -0700175 }
176
177 struct libusb_device_handle *handle = NULL;
178 if (matching_device != NULL) {
179 int return_value = libusb_open(matching_device, &handle);
180 if (return_value < 0) {
Brian Silverman7037e872013-09-14 15:35:45 -0700181 LOG(FATAL, "Failed to open device: %s\n",
182 libusb_error_name(return_value));
Brian Silverman395d6252013-09-13 20:58:14 -0700183 }
184 CHECK_NOTNULL(handle); // should never happen
185 }
186 libusb_free_device_list(devices_head, /*unref_devices=*/ 1);
187 return handle;
188}
189
190UsbDevice *Libusb::FindSingleMatchingDeviceAtLocationOrLose(
191 const DeviceLocationAndId &dev_location_id) {
192 return CHECK_NOTNULL(FindSingleMatchingDeviceAtLocation(dev_location_id));
193}
194
195UsbDevice *Libusb::FindSingleMatchingDeviceAtLocation(
196 const DeviceLocationAndId &dev_location_id) {
197 auto handle = FindSingleDevice(libusb_context_,
198 /* match_location= */ true,
199 dev_location_id);
200 if (handle == NULL) {
201 return NULL;
202 } else {
203 return new PhysicalUsbDevice(libusb_context_, handle);
204 }
205}
206
207UsbDevice *Libusb::FindSingleMatchingDeviceOrLose(
208 const VendorProductId &id) {
209 return CHECK_NOTNULL(FindSingleMatchingDeviceOrLose(id));
210}
211
212UsbDevice *Libusb::FindSingleMatchingDevice(
213 const VendorProductId &id) {
214 DeviceLocationAndId dev_location_id;
215 dev_location_id.id = id;
216 auto handle = FindSingleDevice(libusb_context_,
217 /* match_location= */ false,
218 dev_location_id);
219 if (handle == NULL) {
220 return NULL;
221 } else {
222 return new PhysicalUsbDevice(libusb_context_, handle);
223 }
224}
225
226void Libusb::FindDeviceBySpecification(
227 const std::string &target_vendor_product_id,
228 const std::string &target_device_location,
229 DeviceLocationAndId *dev_location_id) {
230 std::vector<VendorProductId> target_ids;
231 ParseProductVendorString(target_vendor_product_id, &target_ids);
232 FindSingleDeviceMatchingTargetIds(target_ids, target_device_location,
233 dev_location_id);
234}
235
236/*static*/ void Libusb::ParseProductVendorString(
237 const std::string &target_vendor_product_id,
238 std::vector<VendorProductId> *target_ids) {
239 CHECK_NE(target_vendor_product_id, "");
Brian Silverman7037e872013-09-14 15:35:45 -0700240 CHECK_EQ(target_vendor_product_id.size(), 9u);
Brian Silverman395d6252013-09-13 20:58:14 -0700241 CHECK_EQ(target_vendor_product_id[4], ':');
242 uint32_t vendor_id;
243 CHECK(safe_strtou32_hex(
244 target_vendor_product_id.substr(0, 4), &vendor_id));
245 uint32_t product_id;
246 CHECK(safe_strtou32_hex(
247 target_vendor_product_id.substr(5, 4), &product_id));
248 VendorProductId temp;
249 temp.vendor_id = vendor_id;
250 temp.product_id = product_id;
251 target_ids->push_back(temp);
252}
253
254/*static*/ void Libusb::ParseDeviceLocationString(
255 const std::string &target_device_location, DeviceLocation *location) {
Brian Silverman7037e872013-09-14 15:35:45 -0700256 CHECK_EQ(target_device_location.size(), 7u);
Brian Silverman395d6252013-09-13 20:58:14 -0700257 CHECK_EQ(target_device_location[3], ':');
258 uint32_t parsed_bus_number;
259 CHECK(safe_strtou32(
260 target_device_location.substr(0, 3), &parsed_bus_number));
261 uint32_t parsed_device_address;
262 CHECK(safe_strtou32(
263 target_device_location.substr(4, 3), &parsed_device_address));
264 location->bus_number = parsed_bus_number;
265 location->device_address = parsed_device_address;
266}
267
268/*static*/ void Libusb::FindSingleDeviceMatchingTargetId(
269 const VendorProductId &target_id,
270 const std::string &target_device_location,
271 DeviceLocationAndId *dev_location_id) {
272 std::vector<VendorProductId> target_ids;
273 target_ids.push_back(target_id);
274 FindSingleDeviceMatchingTargetIds(
275 target_ids, target_device_location, dev_location_id);
276}
277
278void Libusb::FindSingleDeviceMatchingTargetIds(
279 const std::vector<VendorProductId> &target_ids,
280 const std::string &target_device_location,
281 DeviceLocationAndId *result_dev_location_id) {
282
283 bool have_target_device_location = (target_device_location != "");
284 DeviceLocation location;
285 if (have_target_device_location) {
286 ParseDeviceLocationString(target_device_location, &location);
287 }
288
289 // Get the location and vendor/product IDs for all attached devices.
290 std::vector<DeviceLocationAndId> dev_location_ids;
291 FindDeviceLocationAndId(&dev_location_ids);
292
293 // Filter the list by target parameters. Make sure that exactly one device
294 // is found.
295 bool found_exactly_one_device = false;
296 for (const auto &dev_location_id : dev_location_ids) {
297 if (have_target_device_location) {
298 if (dev_location_id.location.bus_number != location.bus_number ||
299 dev_location_id.location.device_address != location.device_address)
300 continue;
301 }
302
303 bool found_matching_product_vendor_id = false;
304 for (const auto &target : target_ids) {
305 if (target.vendor_id == dev_location_id.id.vendor_id &&
306 target.product_id == dev_location_id.id.product_id) {
307 found_matching_product_vendor_id = true;
308 break;
309 }
310 }
311 if (!found_matching_product_vendor_id)
312 continue;
313
314 CHECK(!found_exactly_one_device);
315 found_exactly_one_device = true;
316 *result_dev_location_id = dev_location_id;
317 }
318 CHECK(found_exactly_one_device);
319}
320
321} // namespace glibusb