程式語言 - GNU - C/C++ - Virtual /dev/ttyUSB(FT232)



假如Linux系統上沒有USB Serial Kernel Module,可以透過libusb + PTY做USB Bridge轉換

main.c

#define _GNU_SOURCE
#include <libusb-1.0/libusb.h>
#include <pty.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <termios.h>

#define FT232_VID   0x0403
#define FT232_PID   0x6001
#define IFACE       0x00
#define EP_IN       0x81
#define EP_OUT      0x02
#define BUF_SIZE    512

static void set_raw(int fd)
{
    struct termios tio;

    tcgetattr(fd, &tio);
    cfmakeraw(&tio);
    tcsetattr(fd, TCSANOW, &tio);
}

static void ft232_init(libusb_device_handle *dev)
{
    uint16_t divisor = 0x001a;

    if (libusb_control_transfer(dev, 0x40, 0x03, divisor, 0, NULL, 0, 1000) < 0) {
        perror("FT232 SET_BAUD");
        exit(1);
    }

    if (libusb_control_transfer(dev, 0x40, 0x04, 8, 0, NULL, 0, 1000) < 0) {
        perror("FT232 SET_DATA");
        exit(1);
    }
}

int main(void)
{
    libusb_context *ctx = NULL;
    libusb_device_handle *dev;
    int pty_master;
    int pty_slave;
    char pty_name[64];
    unsigned char buf[BUF_SIZE];

    libusb_init(&ctx);

    dev = libusb_open_device_with_vid_pid(ctx, FT232_VID, FT232_PID);
    if (!dev) {
        fprintf(stderr, "FT232 device not found\n");
        return 1;
    }

    libusb_detach_kernel_driver(dev, IFACE);
    libusb_claim_interface(dev, IFACE);

    ft232_init(dev);

    if (openpty(&pty_master, &pty_slave, pty_name, NULL, NULL) < 0) {
        perror("openpty");
        return 1;
    }

    set_raw(pty_master);
    printf("FT232 PTY ready: %s\n", pty_name);

    struct pollfd pfd = {
        .fd = pty_master,
        .events = POLLIN
    };

    while (1) {
        if (poll(&pfd, 1, 5) > 0) {
            int n = read(pty_master, buf, sizeof(buf));
            if (n > 0) {
                int sent;
                libusb_bulk_transfer(dev, EP_OUT, buf, n, &sent, 0);
            }
        }

        int received;
        int rc = libusb_bulk_transfer(dev, EP_IN, buf, sizeof(buf), &received, 10);
        if (rc == 0 && received > 2) {
            write(pty_master, buf + 2, received - 2);
        }
    }
}

編譯、測試

$ gcc main.c -o usb_ft232_bridge -lusb-1.0
$ sudo ./usb_ft232_bridge &
    FT232 PTY ready: /dev/pts/2

$ lsusb -v -d 0x0403:0x6001 | egrep -e "EP|Transfer"
        bEndpointAddress     0x81  EP 1 IN
          Transfer Type            Bulk
        bEndpointAddress     0x02  EP 2 OUT
          Transfer Type            Bulk

$ sudo ln -s /dev/pts/2 /dev/ttyUSB9999
$ sudo minicom -D /dev/ttyUSB9999 -b 115200