Skip to content

Commit 160cec9

Browse files
committed
wip: Automatically detach ftdi_sio driver
Signed-off-by: Douglas Reis <doreis@lowrisc.org>
1 parent 84307b6 commit 160cec9

7 files changed

Lines changed: 97 additions & 6 deletions

File tree

app/main.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121

2222
using Action = std::function<int()>;
2323

24-
static std::vector<ftdi::DeviceInfo> scan() {
25-
auto result = ftdi::Discovery::scan();
24+
static std::vector<ftdi::DeviceInfo> scan(uint16_t pid = 0x6010) {
25+
auto result = ftdi::Discovery::scan(pid);
2626
if (!result) {
2727
std::cerr << "Error: Failed to communicate with FTDI driver." << std::endl;
2828
exit(0);

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
ft4222
4343
libmpsse
4444
];
45-
buildInputs = [ft4222 libmpsse ftd2xx];
45+
buildInputs = with pkgs; [ft4222 libmpsse ftd2xx libusb1 systemd];
4646
shellHook = ''
4747
# Setting LD_LIBRARY_PATH for libftd2xx
4848
export LD_LIBRARY_PATH="${ftd2xx}/lib:$LD_LIBRARY_PATH"

lib/ftdi/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ target_sources(ftdipp
88
PRIVATE
99
spi_host.cc
1010
gpio.cc
11+
kernel_driver.cc
1112
PUBLIC
1213
FILE_SET HEADERS
1314
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
@@ -16,6 +17,7 @@ target_sources(ftdipp
1617
spi_host.hh
1718
gpio.hh
1819
log.hh
20+
kernel_driver.hh
1921
)
2022

2123
target_include_directories(ftdipp
@@ -25,7 +27,8 @@ target_include_directories(ftdipp
2527
)
2628

2729
target_link_libraries(ftdipp
28-
PUBLIC magic_enum::magic_enum embeddedpp mpsse ft4222
30+
PUBLIC magic_enum::magic_enum embeddedpp mpsse ft4222
31+
PRIVATE usb-1.0 udev
2932
)
3033
target_compile_options(ftdipp PRIVATE "-fno-exceptions")
3134

lib/ftdi/ftdi.hh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
#pragma once
66

77
#include "ftd2xx.h"
8-
#include <cstdint>
98
#include <optional>
109
#include <vector>
1110
#include <iostream>
1211
#include "device_info.hh"
12+
#include "kernel_driver.hh"
1313

1414
namespace ftdi {
1515

@@ -19,7 +19,11 @@ class Discovery {
1919
* @brief Scans for all connected FTDI devices.
2020
* @return A vector of devices if successful, or std::nullopt if the driver call failed.
2121
*/
22-
static std::optional<std::vector<DeviceInfo>> scan() {
22+
23+
static std::optional<std::vector<DeviceInfo>> scan(uint16_t pid) {
24+
// Detach ftdi_sio (if present) so D2XX can enumerate the device.
25+
detach_ftdi_kernel_drivers(pid);
26+
2327
DWORD num_devs = 0;
2428

2529
// 1. Get the number of devices

lib/ftdi/kernel_driver.cc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "kernel_driver.hh"
6+
7+
#include <libudev.h>
8+
#include <libusb-1.0/libusb.h>
9+
10+
#include <iostream>
11+
12+
namespace ftdi {
13+
14+
enum { kFtdiVid = 0x0403 };
15+
16+
// Use libusb to temporarily detach ftdi_sio from every interface of
17+
// every FTDI USB device. The kernel driver is re-attached automatically when
18+
// the D2XX / libft4222 handle is closed.
19+
void detach_ftdi_kernel_drivers(uint16_t pid) {
20+
libusb_context* ctx = nullptr;
21+
if (libusb_init(&ctx) != LIBUSB_SUCCESS) {
22+
std::cerr << "libusb_init failed\n";
23+
return;
24+
}
25+
26+
libusb_device** devs = nullptr;
27+
ssize_t cnt = libusb_get_device_list(ctx, &devs);
28+
if (cnt < 0) {
29+
libusb_exit(ctx);
30+
return;
31+
}
32+
33+
for (ssize_t i = 0; i < cnt; i++) {
34+
libusb_device_descriptor desc{};
35+
if (libusb_get_device_descriptor(devs[i], &desc) != LIBUSB_SUCCESS) continue;
36+
if (desc.idVendor != kFtdiVid || desc.idProduct != pid) continue;
37+
38+
libusb_device_handle* handle = nullptr;
39+
if (libusb_open(devs[i], &handle) != LIBUSB_SUCCESS) continue;
40+
41+
libusb_config_descriptor* config = nullptr;
42+
if (libusb_get_active_config_descriptor(devs[i], &config) == LIBUSB_SUCCESS) {
43+
for (uint8_t j = 0; j < config->bNumInterfaces; j++) {
44+
if (libusb_kernel_driver_active(handle, j) != 1) continue;
45+
int ret = libusb_detach_kernel_driver(handle, j);
46+
if (ret != LIBUSB_SUCCESS) {
47+
std::cerr << "Failed to detach kernel driver on interface " << (int)j << ": "
48+
<< libusb_error_name(ret) << "\n";
49+
}
50+
}
51+
libusb_free_config_descriptor(config);
52+
}
53+
54+
libusb_close(handle);
55+
}
56+
57+
libusb_free_device_list(devs, 1);
58+
libusb_exit(ctx);
59+
}
60+
61+
} // namespace ftdi

lib/ftdi/kernel_driver.hh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include <cstdint>
8+
#include <format>
9+
#include <string>
10+
#include <vector>
11+
12+
namespace ftdi {
13+
14+
// Option A: Detach ftdi_sio (or any kernel driver) from all FTDI USB interfaces
15+
// using libusb. This allows the D2XX library to claim the device afterwards.
16+
// Requires write access to the USB device files — install 99-ftditool.rules.
17+
void detach_ftdi_kernel_drivers(uint16_t pid);
18+
19+
} // namespace ftdi

nix/ftditool.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
argparse,
1313
magic-enum,
1414
picosha2,
15+
libusb1,
16+
systemd,
1517
makeWrapper,
1618
}:
1719
stdenv.mkDerivation {
@@ -33,6 +35,8 @@ stdenv.mkDerivation {
3335
magic-enum
3436
picosha2
3537
ftd2xx
38+
libusb1
39+
systemd
3640
];
3741

3842
cmakeFlags = [

0 commit comments

Comments
 (0)