/* * USB transport for fastboot using libusb-1.0. * Used on OpenBSD (and other platforms without Linux usbfs). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 */ #include #include #include #include #include #include "usb.h" static libusb_context* g_ctx = nullptr; class LibUsbTransport : public UsbTransport { public: LibUsbTransport(libusb_device_handle* h, uint8_t ep_in, uint8_t ep_out, int ifc_num, uint32_t ms_timeout) : handle_(h), ep_in_(ep_in), ep_out_(ep_out), ifc_num_(ifc_num), ms_timeout_(ms_timeout) {} ~LibUsbTransport() override { Close(); } ssize_t Read(void* data, size_t len) override { int transferred = 0; int r = libusb_bulk_transfer(handle_, ep_in_, reinterpret_cast(data), static_cast(len), &transferred, ms_timeout_); if (r == 0 || r == LIBUSB_ERROR_TIMEOUT) return transferred; return -1; } ssize_t Write(const void* data, size_t len) override { int transferred = 0; int r = libusb_bulk_transfer(handle_, ep_out_, const_cast( reinterpret_cast(data)), static_cast(len), &transferred, ms_timeout_); if (r == 0) return transferred; return -1; } int Close() override { if (handle_) { libusb_release_interface(handle_, ifc_num_); libusb_close(handle_); handle_ = nullptr; } return 0; } int Reset() override { if (handle_) return libusb_reset_device(handle_) ? -1 : 0; return -1; } private: libusb_device_handle* handle_; uint8_t ep_in_, ep_out_; int ifc_num_; uint32_t ms_timeout_; DISALLOW_COPY_AND_ASSIGN(LibUsbTransport); }; std::unique_ptr usb_open(ifc_match_func callback, uint32_t timeout_ms) { if (!g_ctx) { if (libusb_init(&g_ctx) != 0) return nullptr; } libusb_device** devs; ssize_t cnt = libusb_get_device_list(g_ctx, &devs); if (cnt < 0) return nullptr; std::unique_ptr result; for (ssize_t i = 0; i < cnt && !result; i++) { libusb_device* dev = devs[i]; struct libusb_device_descriptor desc; if (libusb_get_device_descriptor(dev, &desc) != 0) continue; struct libusb_config_descriptor* config; if (libusb_get_active_config_descriptor(dev, &config) != 0) continue; for (int j = 0; j < (int)config->bNumInterfaces && !result; j++) { const struct libusb_interface* ifc = &config->interface[j]; for (int k = 0; k < ifc->num_altsetting && !result; k++) { const struct libusb_interface_descriptor* id = &ifc->altsetting[k]; usb_ifc_info info = {}; info.dev_vendor = desc.idVendor; info.dev_product = desc.idProduct; info.dev_class = desc.bDeviceClass; info.dev_subclass = desc.bDeviceSubClass; info.dev_protocol = desc.bDeviceProtocol; info.ifc_class = id->bInterfaceClass; info.ifc_subclass = id->bInterfaceSubClass; info.ifc_protocol = id->bInterfaceProtocol; uint8_t ep_in = 0, ep_out = 0; for (int e = 0; e < (int)id->bNumEndpoints; e++) { const struct libusb_endpoint_descriptor* ep = &id->endpoint[e]; if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != LIBUSB_TRANSFER_TYPE_BULK) continue; if (ep->bEndpointAddress & LIBUSB_ENDPOINT_IN) { ep_in = ep->bEndpointAddress; info.has_bulk_in = 1; } else { ep_out = ep->bEndpointAddress; info.has_bulk_out = 1; } } libusb_device_handle* h; if (libusb_open(dev, &h) != 0) continue; info.writable = 1; if (desc.iSerialNumber) { libusb_get_string_descriptor_ascii( h, desc.iSerialNumber, reinterpret_cast(info.serial_number), sizeof(info.serial_number)); } if (id->iInterface) { libusb_get_string_descriptor_ascii( h, id->iInterface, reinterpret_cast(info.interface), sizeof(info.interface)); } snprintf(info.device_path, sizeof(info.device_path), "usb:%d:%d", libusb_get_bus_number(dev), libusb_get_device_address(dev)); if (callback(&info) == 0 && ep_in && ep_out) { if (libusb_claim_interface(h, id->bInterfaceNumber) == 0) { result = std::make_unique( h, ep_in, ep_out, id->bInterfaceNumber, timeout_ms); } else { libusb_close(h); } } else { libusb_close(h); } } } libusb_free_config_descriptor(config); } libusb_free_device_list(devs, 1); return result; }