blob: 6daa637676e4868522fa9e74e581f18f253fd1b5 [file] [log] [blame]
Armando Miragliacd70cfa2018-06-04 15:24:09 +02001// Copyright 2018 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "permission_broker/libusb_wrapper.h"
6
7#include <string>
8#include <utility>
9
10#include <base/logging.h>
11#include <base/time/time.h>
12
13namespace {
14
15constexpr base::TimeDelta kUsbControlTimeout = base::TimeDelta::FromSeconds(5);
16
17const int kLibusbUnrefDevices = 1;
18
19} // namespace
20
21namespace permission_broker {
22
23UsbDevice::UsbDevice(libusb_device* device)
24 : device_(device, libusb_unref_device) {
25 // Increase the ref count to gain ownership of the libusb device object.
26 libusb_ref_device(device_.get());
27
28 // Try to obtain information regarding the device VID/PID from the device
29 // itself.
30 // NB: in the repositories we only depend on versions of libusb which are
31 // newer than 1.0.16. This means that we can ignore the return value
32 // as this API always suceeds in such a case.
33 libusb_device_descriptor descriptor;
34 libusb_get_device_descriptor(device_.get(), &descriptor);
35 info_.vid = descriptor.idVendor;
36 info_.pid = descriptor.idProduct;
37 info_.device_class = descriptor.bDeviceClass;
38}
39
40UsbDevice::~UsbDevice() = default;
41
42UsbDeviceInfo UsbDevice::GetInfo() const {
43 return info_;
44}
45
46std::unique_ptr<UsbDeviceInterface> UsbDevice::GetParent() const {
47 libusb_device* parent_device = libusb_get_parent(device_.get());
48 if (parent_device == nullptr) {
49 LOG(ERROR) << "Unable to find the device parent for '" << info_ << "'";
50 return nullptr;
51 }
Tom Hughes4f300e52020-08-24 18:08:59 -070052 auto parent =
53 std::unique_ptr<UsbDeviceInterface>(new UsbDevice(parent_device));
Armando Miragliacd70cfa2018-06-04 15:24:09 +020054
55 UsbDeviceInfo parent_info = parent->GetInfo();
56 if (parent_info.device_class != LIBUSB_CLASS_HUB) {
57 LOG(ERROR) << "The parent device found for this USB device is not a hub ("
58 << parent_info << ")";
59 return nullptr;
60 }
61
62 return parent;
63}
64
65uint8_t UsbDevice::GetPort() const {
66 return libusb_get_port_number(device_.get());
67}
68
69bool UsbDevice::SetPowerState(bool enabled, uint16_t port) const {
70 if (info_.device_class != LIBUSB_CLASS_HUB) {
71 LOG(ERROR) << "Unable to set power on a port if the device is not a hub "
72 << "(device '" << info_ << "')";
73 return false;
74 }
75
76 libusb_device_handle* handle;
77 int result = libusb_open(device_.get(), &handle);
78 if (result != LIBUSB_SUCCESS) {
79 LOG(ERROR) << "Unable to open the USB device. (error: "
80 << libusb_error_name(result) << ")";
81 return false;
82 }
83
84 uint8_t request =
85 enabled ? LIBUSB_REQUEST_SET_FEATURE : LIBUSB_REQUEST_CLEAR_FEATURE;
86 result = libusb_control_transfer(
87 handle, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER, request,
Ben Chan2956e6c2019-09-20 17:14:01 -070088 USB_PORT_FEAT_POWER, port, nullptr, 0,
89 kUsbControlTimeout.InMilliseconds());
Armando Miragliacd70cfa2018-06-04 15:24:09 +020090 libusb_close(handle);
91
92 if (result < 0) {
93 std::string status = enabled ? "on" : "off";
94 LOG(WARNING) << "Unable to power " << status << " device '" << info_
95 << "' (error: " << libusb_error_name(result) << ")";
96 return false;
97 }
98
99 return true;
100}
101
102UsbDeviceManager::UsbDeviceManager() {
103 libusb_context* ctx;
104 int status = libusb_init(&ctx);
105 if (status != LIBUSB_SUCCESS) {
106 LOG(ERROR) << "Unable to initialize the libusb context. (error: "
107 << libusb_error_name(status) << ")";
108 return;
109 }
110 context_.reset(ctx);
111}
112
113UsbDeviceManager::~UsbDeviceManager() = default;
114
115std::vector<std::unique_ptr<UsbDeviceInterface>>
116UsbDeviceManager::GetDevicesByVidPid(uint16_t vid, uint16_t pid) {
117 libusb_device** device_list;
118 std::vector<std::unique_ptr<UsbDeviceInterface>> devices;
119
120 int num_of_devices = libusb_get_device_list(context_.get(), &device_list);
121 if (num_of_devices < 0) {
122 LOG(ERROR) << "Unable to access the libusb device list. (error: "
123 << libusb_error_name(num_of_devices) << ")";
124 return devices;
125 }
126
127 for (int i = 0; i < num_of_devices; ++i) {
128 auto device = std::make_unique<UsbDevice>(device_list[i]);
129 UsbDeviceInfo info = device->GetInfo();
130
131 if (info.vid == vid && info.pid == pid) {
132 devices.push_back(std::move(device));
133 }
134 }
135
136 // Free the list of devices previously retrieved.
137 libusb_free_device_list(device_list, kLibusbUnrefDevices);
138 return devices;
139}
140
141} // namespace permission_broker