Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
255291af4This file is pinned to the selected release. Download the original to open it in your design tool.
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (c) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#include <libusb.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <list>
#include <vector>
#include <string>
#include <sstream> // For std::stringstream
#include <stdexcept>
#include "cable.hpp"
#include "display.hpp"
#include "libusb_ll.hpp"
using namespace std;
libusb_ll::libusb_ll(int vid, int pid, int8_t _verbose):
_usb_ctx(nullptr), _verbose(_verbose >= 2)
{
(void)vid;
(void)pid;
if (libusb_init(&_usb_ctx) < 0)
throw std::runtime_error("libusb_init_failed");
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);
}
libusb_ll::~libusb_ll()
{
libusb_free_device_list(_dev_list, 1);
libusb_exit(_usb_ctx);
}
int libusb_ll::get_devices_list(const cable_t *cable)
{
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);
}
int i = 0;
libusb_device *usb_dev;
_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());
}
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();
}
bool libusb_ll::scan()
{
std::list<cable_details_t> list_cables;
size_t manufacturer_len = 12;
size_t probe_len = 10;
size_t serial_len = 6;
get_devices_list(nullptr);
for (libusb_device *usb_dev : _usb_dev_list) {
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) {
switch (desc.idProduct) {
case 0x6010:
snprintf(probe_type, 256, "FTDI2232");
break;
case 0x6011:
snprintf(probe_type, 256, "ft4232");
break;
case 0x6001:
snprintf(probe_type, 256, "ft232RL");
break;
case 0x6014:
snprintf(probe_type, 256, "ft232H");
break;
case 0x6015:
snprintf(probe_type, 256, "ft231X");
break;
case 0x6043:
snprintf(probe_type, 256, "FT4232HP");
break;
default:
snprintf(probe_type, 256, "unknown FTDI");
break;
}
found = true;
} else {
// FIXME: DFU device can't be detected here
for (const auto& b : cable_list) {
const cable_t *c = &b.second;
if (c->vid == desc.idVendor && c->pid == desc.idProduct) {
snprintf(probe_type, 256, "%s", b.first.c_str());
found = true;
break;
}
}
}
if (!found)
continue;
libusb_device_handle *handle;
int ret = libusb_open(usb_dev, &handle);
if (ret != 0) {
char mess[1024];
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;
}
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)
snprintf(reinterpret_cast<char*>(iproduct), 200, "none");
ret = libusb_get_string_descriptor_ascii(handle,
desc.iManufacturer, imanufacturer, 200);
if (ret < 0)
snprintf(reinterpret_cast<char*>(imanufacturer), 200, "none");
ret = libusb_get_string_descriptor_ascii(handle,
desc.iSerialNumber, iserial, 200);
if (ret < 0)
snprintf(reinterpret_cast<char*>(iserial), 200, "none");
uint8_t bus_addr = libusb_get_bus_number(usb_dev);
uint8_t dev_addr = libusb_get_device_address(usb_dev);
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)));
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);
libusb_close(handle);
}
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());
}
return true;
}