parent
							
								
									4061f50da3
								
							
						
					
					
						commit
						285addeef2
					
				
				 10 changed files with 300 additions and 235 deletions
			
			
		| @ -1 +1 @@ | ||||
| Subproject commit 8d0d148681163929260abf5742701cbf503abb22 | ||||
| Subproject commit 6f95a096e6beb254786759003a38e6e5c4f2c10e | ||||
| @ -1,9 +1,9 @@ | ||||
| Import('env', 'envCython', 'common', 'cereal', 'messaging') | ||||
| 
 | ||||
| libs = ['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj'] | ||||
| env.Program('boardd', ['boardd.cc', 'panda.cc', 'usbdevice.cc', 'pigeon.cc'], LIBS=libs) | ||||
| env.Program('boardd', ['boardd.cc', 'panda.cc', 'pigeon.cc'], LIBS=libs) | ||||
| env.Library('libcan_list_to_can_capnp', ['can_list_to_can_capnp.cc']) | ||||
| 
 | ||||
| envCython.Program('boardd_api_impl.so', 'boardd_api_impl.pyx', LIBS=["can_list_to_can_capnp", 'capnp', 'kj'] + envCython["LIBS"]) | ||||
| if GetOption('test'): | ||||
|   env.Program('tests/test_boardd_usbprotocol', ['tests/test_boardd_usbprotocol.cc', 'panda.cc', 'usbdevice.cc'], LIBS=libs) | ||||
|   env.Program('tests/test_boardd_usbprotocol', ['tests/test_boardd_usbprotocol.cc', 'panda.cc'], LIBS=libs) | ||||
|  | ||||
| @ -1,140 +0,0 @@ | ||||
| #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; | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <mutex> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <libusb-1.0/libusb.h> | ||||
| #include "panda/board/panda.h" | ||||
| 
 | ||||
| #define TIMEOUT 0 | ||||
| 
 | ||||
| class USBDevice { | ||||
| public: | ||||
|   USBDevice() = default; | ||||
|   ~USBDevice(); | ||||
|   bool open(const std::string &serial); | ||||
|   static std::vector<std::string> list(); | ||||
| 
 | ||||
|   inline int write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout = TIMEOUT) { | ||||
|     return control_transfer(LIBUSB_ENDPOINT_OUT, bRequest, wValue, wIndex, timeout); | ||||
|   } | ||||
|   inline int read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned int timeout = TIMEOUT) { | ||||
|     return control_transfer(LIBUSB_ENDPOINT_IN, bRequest, wValue, wIndex, timeout); | ||||
|   } | ||||
|   inline int bulk_write(uint8_t endpoint, uint8_t *data, int length, unsigned int timeout = TIMEOUT) { | ||||
|     return bulk_transfer(LIBUSB_ENDPOINT_OUT | endpoint, data, length, timeout); | ||||
|   } | ||||
|   inline int bulk_read(uint8_t endpoint, uint8_t *data, int length, unsigned int timeout = TIMEOUT) { | ||||
|     return bulk_transfer(LIBUSB_ENDPOINT_IN | endpoint, data, length, timeout); | ||||
|   } | ||||
| 
 | ||||
|   std::atomic<bool> comms_healthy = true; | ||||
|   std::atomic<bool> connected = true; | ||||
|   std::string usb_serial; | ||||
| 
 | ||||
| private: | ||||
|   int control_transfer(libusb_endpoint_direction dir, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout = TIMEOUT); | ||||
|   int bulk_transfer(uint8_t endpoint, uint8_t *data, int length, unsigned int timeout = TIMEOUT); | ||||
| 
 | ||||
|   std::mutex usb_lock; | ||||
|   libusb_context *ctx = nullptr; | ||||
|   libusb_device_handle *dev_handle = nullptr; | ||||
| }; | ||||
					Loading…
					
					
				
		Reference in new issue