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