#include "selfdrive/boardd/panda.h" #include #include #include "common/swaglog.h" static int init_usb_ctx(libusb_context **context) { assert(context != nullptr); int err = libusb_init(context); if (err != 0) { LOGE("libusb initialization error"); return err; } #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 err; } PandaUsbHandle::PandaUsbHandle(std::string serial) : PandaCommsHandle(serial) { // init libusb ssize_t num_devices; libusb_device **dev_list = NULL; int err = init_usb_ctx(&ctx); if (err != 0) { goto fail; } // connect by serial num_devices = libusb_get_device_list(ctx, &dev_list); if (num_devices < 0) { goto fail; } for (size_t i = 0; i < num_devices; ++i) { libusb_device_descriptor desc; libusb_get_device_descriptor(dev_list[i], &desc); if (desc.idVendor == 0xbbaa && desc.idProduct == 0xddcc) { int ret = libusb_open(dev_list[i], &dev_handle); if (dev_handle == NULL || ret < 0) { goto fail; } unsigned char desc_serial[26] = { 0 }; ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, desc_serial, std::size(desc_serial)); if (ret < 0) { goto fail; } auto hw_serial = std::string((char *)desc_serial, ret); if (serial.empty() || serial == hw_serial) { break; } libusb_close(dev_handle); dev_handle = NULL; } } if (dev_handle == NULL) goto fail; libusb_free_device_list(dev_list, 1); dev_list = nullptr; if (libusb_kernel_driver_active(dev_handle, 0) == 1) { libusb_detach_kernel_driver(dev_handle, 0); } err = libusb_set_configuration(dev_handle, 1); if (err != 0) { goto fail; } err = libusb_claim_interface(dev_handle, 0); if (err != 0) { goto fail; } return; fail: if (dev_list != NULL) { libusb_free_device_list(dev_list, 1); } cleanup(); throw std::runtime_error("Error connecting to panda"); } PandaUsbHandle::~PandaUsbHandle() { std::lock_guard lk(hw_lock); cleanup(); connected = false; } void PandaUsbHandle::cleanup() { if (dev_handle) { libusb_release_interface(dev_handle, 0); libusb_close(dev_handle); } if (ctx) { libusb_exit(ctx); } } std::vector PandaUsbHandle::list() { // init libusb ssize_t num_devices; libusb_context *context = NULL; libusb_device **dev_list = NULL; std::vector serials; int err = init_usb_ctx(&context); if (err != 0) { return serials; } num_devices = libusb_get_device_list(context, &dev_list); if (num_devices < 0) { LOGE("libusb can't get device list"); goto finish; } for (size_t i = 0; i < num_devices; ++i) { libusb_device *device = dev_list[i]; libusb_device_descriptor desc; libusb_get_device_descriptor(device, &desc); if (desc.idVendor == 0xbbaa && desc.idProduct == 0xddcc) { libusb_device_handle *handle = NULL; int ret = libusb_open(device, &handle); if (ret < 0) { goto finish; } unsigned char desc_serial[26] = { 0 }; ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, desc_serial, std::size(desc_serial)); libusb_close(handle); if (ret < 0) { goto finish; } serials.push_back(std::string((char *)desc_serial, ret).c_str()); } } finish: if (dev_list != NULL) { libusb_free_device_list(dev_list, 1); } if (context) { libusb_exit(context); } return serials; } void PandaUsbHandle::handle_usb_issue(int err, const char func[]) { LOGE_100("usb error %d \"%s\" in %s", err, libusb_strerror((enum libusb_error)err), func); if (err == LIBUSB_ERROR_NO_DEVICE) { LOGE("lost connection"); connected = false; } // TODO: check other errors, is simply retrying okay? } int PandaUsbHandle::control_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout) { int err; const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE; if (!connected) { return LIBUSB_ERROR_NO_DEVICE; } std::lock_guard lk(hw_lock); do { err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, NULL, 0, timeout); if (err < 0) handle_usb_issue(err, __func__); } while (err < 0 && connected); return err; } int PandaUsbHandle::control_read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) { int err; const uint8_t bmRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE; if (!connected) { return LIBUSB_ERROR_NO_DEVICE; } std::lock_guard lk(hw_lock); do { err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout); if (err < 0) handle_usb_issue(err, __func__); } while (err < 0 && connected); return err; } int PandaUsbHandle::bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) { int err; int transferred = 0; if (!connected) { return 0; } std::lock_guard lk(hw_lock); do { // Try sending can messages. If the receive buffer on the panda is full it will NAK // and libusb will try again. After 5ms, it will time out. We will drop the messages. err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout); if (err == LIBUSB_ERROR_TIMEOUT) { LOGW("Transmit buffer full"); break; } else if (err != 0 || length != transferred) { handle_usb_issue(err, __func__); } } while(err != 0 && connected); return transferred; } int PandaUsbHandle::bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) { int err; int transferred = 0; if (!connected) { return 0; } std::lock_guard lk(hw_lock); do { err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout); if (err == LIBUSB_ERROR_TIMEOUT) { break; // timeout is okay to exit, recv still happened } else if (err == LIBUSB_ERROR_OVERFLOW) { comms_healthy = false; LOGE_100("overflow got 0x%x", transferred); } else if (err != 0) { handle_usb_issue(err, __func__); } } while(err != 0 && connected); return transferred; }