openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
4.0 KiB

#include "selfdrive/boardd/usbdevice.h"
#include <map>
#include <memory>
#include "selfdrive/common/swaglog.h"
namespace {
libusb_context *init_usb_ctx() {
libusb_context *context = nullptr;
int err = libusb_init(&context);
if (err != 0) {
LOGE("libusb initialization error %d", err);
return nullptr;
}
#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
#else
libusb_set_debug(context, 3);
#endif
return context;
}
struct DeviceIterator {
DeviceIterator(libusb_context *ctx) {
ssize_t num_devices = libusb_get_device_list(ctx, &dev_list);
for (ssize_t i = 0; i < num_devices; ++i) {
libusb_device_descriptor desc = {};
int ret = libusb_get_device_descriptor(dev_list[i], &desc);
if (ret < 0 || desc.idVendor != USB_VID || desc.idProduct != USB_PID) continue;
libusb_device_handle *handle = nullptr;
if (libusb_open(dev_list[i], &handle) == 0) {
unsigned char serial[256] = {'\0'};
libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, serial, std::size(serial) - 1);
devices[(const char *)serial] = dev_list[i];
libusb_close(handle);
}
}
}
~DeviceIterator() {
if (dev_list) libusb_free_device_list(dev_list, 1);
}
std::map<std::string, libusb_device *>::iterator begin() { return devices.begin(); }
std::map<std::string, libusb_device *>::iterator end() { return devices.end(); }
std::map<std::string, libusb_device *> devices;
libusb_device **dev_list = nullptr;
};
} // namespace
bool USBDevice::open(const std::string &serial) {
ctx = init_usb_ctx();
if (!ctx) return false;
for (const auto &[s, device] : DeviceIterator(ctx)) {
if (serial.empty() || serial == s) {
usb_serial = s;
libusb_open(device, &dev_handle);
break;
}
}
if (!dev_handle) return false;
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
libusb_detach_kernel_driver(dev_handle, 0);
}
if (libusb_set_configuration(dev_handle, 1) != 0 ||
libusb_claim_interface(dev_handle, 0) != 0) {
return false;
}
return true;
}
USBDevice::~USBDevice() {
if (dev_handle) {
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
}
if (ctx) libusb_exit(ctx);
}
std::vector<std::string> USBDevice::list() {
std::vector<std::string> serials;
if (libusb_context *ctx = init_usb_ctx()) {
for (auto it : DeviceIterator(ctx)) {
serials.push_back(it.first);
}
libusb_exit(ctx);
}
return serials;
}
int USBDevice::control_transfer(libusb_endpoint_direction dir, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout) {
std::lock_guard lk(usb_lock);
int ret = LIBUSB_ERROR_NO_DEVICE;
const uint8_t bmRequestType = dir | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
while (connected) {
ret = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, NULL, 0, timeout);
if (ret >= 0) break;
LOGE_100("usb control_transfer error %d, \"%s\"", ret, libusb_strerror((enum libusb_error)ret));
if (ret == LIBUSB_ERROR_NO_DEVICE) {
LOGE("lost connection");
connected = false;
}
// TODO: check other errors, is simply retrying okay?
}
return ret;
}
int USBDevice::bulk_transfer(uint8_t endpoint, uint8_t *data, int length, unsigned int timeout) {
std::lock_guard lk(usb_lock);
int transferred = 0;
while (connected) {
int ret = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);
if (ret == 0 && length == transferred) break;
if (ret != 0) {
LOGE_100("usb control_transfer error %d, \"%s\"", ret, libusb_strerror((enum libusb_error)ret));
if (ret == LIBUSB_ERROR_NO_DEVICE) {
connected = false;
} else if (ret == LIBUSB_ERROR_OVERFLOW) {
comms_healthy = false;
} else if (ret == LIBUSB_ERROR_TIMEOUT && (endpoint & LIBUSB_ENDPOINT_IN)) {
// timeout is okay to exit, recv still happened
LOGW("bulk_transfer timeout");
break;
}
}
};
return transferred;
}