2022-10-15 22:28:06 +02:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <libusb.h>
|
|
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
2025-05-15 08:09:25 +02:00
|
|
|
#include <iomanip>
|
|
|
|
|
#include <list>
|
2024-05-20 16:18:50 +02:00
|
|
|
#include <vector>
|
|
|
|
|
#include <string>
|
2025-05-15 08:09:25 +02:00
|
|
|
#include <sstream> // For std::stringstream
|
2024-05-20 16:18:50 +02:00
|
|
|
#include <stdexcept>
|
2022-10-15 22:28:06 +02:00
|
|
|
|
|
|
|
|
#include "cable.hpp"
|
|
|
|
|
#include "display.hpp"
|
|
|
|
|
#include "libusb_ll.hpp"
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
libusb_ll::libusb_ll(int vid, int pid, int8_t _verbose):
|
|
|
|
|
_usb_ctx(nullptr), _verbose(_verbose >= 2)
|
2022-10-15 22:28:06 +02:00
|
|
|
{
|
|
|
|
|
(void)vid;
|
|
|
|
|
(void)pid;
|
|
|
|
|
if (libusb_init(&_usb_ctx) < 0)
|
|
|
|
|
throw std::runtime_error("libusb_init_failed");
|
2024-05-20 16:18:50 +02:00
|
|
|
ssize_t list_size = libusb_get_device_list(_usb_ctx, &_dev_list);
|
|
|
|
|
if (list_size < 0)
|
|
|
|
|
throw std::runtime_error("libusb_get_device_list_failed");
|
|
|
|
|
if (list_size == 0)
|
|
|
|
|
printError("No USB devices found");
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printf("found %zd\n", list_size);
|
2022-10-15 22:28:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libusb_ll::~libusb_ll()
|
|
|
|
|
{
|
2024-05-20 16:18:50 +02:00
|
|
|
libusb_free_device_list(_dev_list, 1);
|
2022-10-15 22:28:06 +02:00
|
|
|
libusb_exit(_usb_ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
int libusb_ll::get_devices_list(const cable_t *cable)
|
2022-10-15 22:28:06 +02:00
|
|
|
{
|
2024-05-20 16:18:50 +02:00
|
|
|
int vid = 0, pid = 0;
|
|
|
|
|
uint8_t bus_addr = 0, device_addr = 0;
|
|
|
|
|
bool vid_pid_filter = false; // if vid/pid only keep matching nodes
|
|
|
|
|
bool bus_dev_filter = false; // if bus/dev only keep matching nodes
|
|
|
|
|
|
|
|
|
|
if (cable != nullptr) {
|
|
|
|
|
vid = cable->vid;
|
|
|
|
|
pid = cable->pid;
|
|
|
|
|
bus_addr = cable->bus_addr;
|
|
|
|
|
device_addr = cable->device_addr;
|
|
|
|
|
vid_pid_filter = (vid != 0) && (pid != 0);
|
|
|
|
|
bus_dev_filter = (bus_addr != 0) && (device_addr != 0);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 22:28:06 +02:00
|
|
|
int i = 0;
|
|
|
|
|
libusb_device *usb_dev;
|
|
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
_usb_dev_list.clear();
|
|
|
|
|
|
|
|
|
|
while ((usb_dev = _dev_list[i++]) != nullptr) {
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
printf("%x %x %x %x\n", bus_addr, device_addr,
|
|
|
|
|
libusb_get_device_address(usb_dev),
|
|
|
|
|
libusb_get_bus_number(usb_dev));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* bus addr and device addr provided: check */
|
|
|
|
|
if (bus_dev_filter && (
|
|
|
|
|
bus_addr != libusb_get_device_address(usb_dev) ||
|
|
|
|
|
device_addr != libusb_get_bus_number(usb_dev)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
if (libusb_get_device_descriptor(usb_dev, &desc) != 0) {
|
|
|
|
|
printError("Unable to get device descriptor");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
printf("%x %x %x %x\n", vid, pid,
|
|
|
|
|
desc.idVendor, desc.idProduct);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Linux host controller */
|
|
|
|
|
if (desc.idVendor == 0x1d6b)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* check for VID/PID */
|
|
|
|
|
if (vid_pid_filter && (
|
|
|
|
|
vid != desc.idVendor || pid != desc.idProduct))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
_usb_dev_list.push_back(usb_dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static_cast<int>(_usb_dev_list.size());
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-15 08:09:25 +02:00
|
|
|
struct cable_details_t {
|
|
|
|
|
uint8_t bus;
|
|
|
|
|
uint8_t device;
|
|
|
|
|
uint16_t vid;
|
|
|
|
|
uint16_t pid;
|
|
|
|
|
std::string probe;
|
|
|
|
|
std::string manufacturer;
|
|
|
|
|
std::string serial;
|
|
|
|
|
std::string product;
|
|
|
|
|
cable_details_t(uint8_t& b, uint8_t& d,
|
|
|
|
|
uint16_t& v, uint16_t& p,
|
|
|
|
|
std::string prb, std::string m,
|
|
|
|
|
std::string s, std::string prd):
|
|
|
|
|
bus(b), device(d), vid(v), pid(p),
|
|
|
|
|
probe(prb), manufacturer(m),
|
|
|
|
|
serial(s), product(prd) {}
|
|
|
|
|
};
|
|
|
|
|
std::string formatHex(uint16_t c, int len) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "0x";
|
|
|
|
|
ss << std::hex << std::setfill('0') << std::setw(len)
|
|
|
|
|
<< (static_cast<unsigned int>(static_cast<unsigned short>(c)) & 0xFFFF);
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string formatDec(char c, int len) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << std::setfill('0') << std::setw(len) << std::to_string(c);
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
bool libusb_ll::scan()
|
|
|
|
|
{
|
2025-05-15 08:09:25 +02:00
|
|
|
std::list<cable_details_t> list_cables;
|
|
|
|
|
size_t manufacturer_len = 12;
|
|
|
|
|
size_t probe_len = 10;
|
|
|
|
|
size_t serial_len = 6;
|
2024-05-20 16:18:50 +02:00
|
|
|
|
|
|
|
|
get_devices_list(nullptr);
|
2022-10-15 22:28:06 +02:00
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
for (libusb_device *usb_dev : _usb_dev_list) {
|
2022-10-15 22:28:06 +02:00
|
|
|
bool found = false;
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
if (libusb_get_device_descriptor(usb_dev, &desc) != 0) {
|
|
|
|
|
printError("Unable to get device descriptor");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char probe_type[256];
|
|
|
|
|
|
|
|
|
|
/* ftdi devices */
|
|
|
|
|
// FIXME: missing iProduct in cable_list
|
|
|
|
|
if (desc.idVendor == 0x403) {
|
2024-05-20 16:18:50 +02:00
|
|
|
switch (desc.idProduct) {
|
|
|
|
|
case 0x6010:
|
2022-10-15 22:28:06 +02:00
|
|
|
snprintf(probe_type, 256, "FTDI2232");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
case 0x6011:
|
2022-10-15 22:28:06 +02:00
|
|
|
snprintf(probe_type, 256, "ft4232");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
case 0x6001:
|
2022-11-27 09:51:31 +01:00
|
|
|
snprintf(probe_type, 256, "ft232RL");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
case 0x6014:
|
2022-10-15 22:28:06 +02:00
|
|
|
snprintf(probe_type, 256, "ft232H");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
case 0x6015:
|
2022-10-15 22:28:06 +02:00
|
|
|
snprintf(probe_type, 256, "ft231X");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
case 0x6043:
|
2023-10-11 10:04:29 +02:00
|
|
|
snprintf(probe_type, 256, "FT4232HP");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
2022-10-15 22:28:06 +02:00
|
|
|
snprintf(probe_type, 256, "unknown FTDI");
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2022-10-15 22:28:06 +02:00
|
|
|
found = true;
|
|
|
|
|
} else {
|
|
|
|
|
// FIXME: DFU device can't be detected here
|
2024-05-20 16:18:50 +02:00
|
|
|
for (const auto& b : cable_list) {
|
|
|
|
|
const cable_t *c = &b.second;
|
2023-02-16 08:00:39 +01:00
|
|
|
if (c->vid == desc.idVendor && c->pid == desc.idProduct) {
|
2024-05-20 16:18:50 +02:00
|
|
|
snprintf(probe_type, 256, "%s", b.first.c_str());
|
2022-10-15 22:28:06 +02:00
|
|
|
found = true;
|
2024-05-20 16:18:50 +02:00
|
|
|
break;
|
2022-10-15 22:28:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-05-20 16:18:50 +02:00
|
|
|
libusb_device_handle *handle;
|
2022-10-15 22:28:06 +02:00
|
|
|
int ret = libusb_open(usb_dev, &handle);
|
2022-10-19 07:40:31 +02:00
|
|
|
if (ret != 0) {
|
2025-05-15 08:09:25 +02:00
|
|
|
char mess[1024];
|
2022-10-19 07:40:31 +02:00
|
|
|
snprintf(mess, 1024,
|
|
|
|
|
"Error: can't open device with vid:vid = 0x%04x:0x%04x. "
|
|
|
|
|
"Error code %d %s",
|
|
|
|
|
desc.idVendor, desc.idProduct,
|
|
|
|
|
ret, libusb_strerror(static_cast<libusb_error>(ret)));
|
|
|
|
|
printError(mess);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-10-15 22:28:06 +02:00
|
|
|
|
|
|
|
|
uint8_t iproduct[200];
|
|
|
|
|
uint8_t iserial[200];
|
|
|
|
|
uint8_t imanufacturer[200];
|
|
|
|
|
ret = libusb_get_string_descriptor_ascii(handle,
|
|
|
|
|
desc.iProduct, iproduct, 200);
|
|
|
|
|
if (ret < 0)
|
2024-05-20 16:18:50 +02:00
|
|
|
snprintf(reinterpret_cast<char*>(iproduct), 200, "none");
|
2022-10-15 22:28:06 +02:00
|
|
|
ret = libusb_get_string_descriptor_ascii(handle,
|
|
|
|
|
desc.iManufacturer, imanufacturer, 200);
|
|
|
|
|
if (ret < 0)
|
2024-05-20 16:18:50 +02:00
|
|
|
snprintf(reinterpret_cast<char*>(imanufacturer), 200, "none");
|
2022-10-15 22:28:06 +02:00
|
|
|
ret = libusb_get_string_descriptor_ascii(handle,
|
|
|
|
|
desc.iSerialNumber, iserial, 200);
|
|
|
|
|
if (ret < 0)
|
2024-05-20 16:18:50 +02:00
|
|
|
snprintf(reinterpret_cast<char*>(iserial), 200, "none");
|
2022-10-15 22:28:06 +02:00
|
|
|
uint8_t bus_addr = libusb_get_bus_number(usb_dev);
|
|
|
|
|
uint8_t dev_addr = libusb_get_device_address(usb_dev);
|
|
|
|
|
|
2025-05-15 08:09:25 +02:00
|
|
|
list_cables.emplace_back(cable_details_t(
|
|
|
|
|
bus_addr, dev_addr, desc.idVendor, desc.idProduct,
|
|
|
|
|
std::string(probe_type), std::string((const char *)imanufacturer),
|
|
|
|
|
std::string((const char *)iserial), std::string((const char *)iproduct)));
|
2022-10-15 22:28:06 +02:00
|
|
|
|
2025-05-15 08:09:25 +02:00
|
|
|
if (strlen((const char *)imanufacturer) > manufacturer_len)
|
|
|
|
|
manufacturer_len = strlen((const char *)imanufacturer);
|
|
|
|
|
if (strlen((const char *)probe_type) > probe_len)
|
|
|
|
|
probe_len = strlen((const char *)probe_type);
|
|
|
|
|
if (strlen((const char *)iserial) > serial_len)
|
|
|
|
|
serial_len = strlen((const char *)iserial);
|
2022-10-15 22:28:06 +02:00
|
|
|
|
|
|
|
|
libusb_close(handle);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-15 08:09:25 +02:00
|
|
|
manufacturer_len++;
|
|
|
|
|
serial_len++;
|
|
|
|
|
probe_len++;
|
|
|
|
|
|
|
|
|
|
std::stringstream buffer;
|
|
|
|
|
buffer << std::left
|
|
|
|
|
<< std::setw(4) << "Bus"
|
|
|
|
|
<< std::setw(7) << "device"
|
|
|
|
|
<< std::setw(14) << "vid:pid"
|
|
|
|
|
<< std::setw(probe_len) << "probe type"
|
|
|
|
|
<< std::setw(manufacturer_len) << "manufacturer"
|
|
|
|
|
<< std::setw(serial_len) << "serial"
|
|
|
|
|
<< "product";
|
|
|
|
|
printSuccess(buffer.str());
|
|
|
|
|
|
|
|
|
|
for (const auto& cable : list_cables) {
|
|
|
|
|
std::stringstream buffer;
|
|
|
|
|
buffer << std::left // Left-align all fields
|
|
|
|
|
<< std::setw(4) << formatDec(cable.bus, 3)
|
|
|
|
|
<< std::setw(7) << formatDec(cable.device, 3)
|
|
|
|
|
<< std::setw(14)
|
|
|
|
|
<< (formatHex(cable.vid, 4) + ":" + formatHex(cable.pid, 4))
|
|
|
|
|
<< std::setw(probe_len) << cable.probe
|
|
|
|
|
<< std::setw(manufacturer_len) << cable.manufacturer
|
|
|
|
|
<< std::setw(serial_len) << cable.serial
|
|
|
|
|
<< cable.product;
|
|
|
|
|
|
|
|
|
|
printInfo(buffer.str());
|
|
|
|
|
}
|
2022-10-15 22:28:06 +02:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|