假如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 <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <termios.h>
#define VID 0x067b
#define PID 0x2303
#define IFACE 0x00
#define EP_OUT 0x02
#define EP_IN 0x83
#define BUF_SIZE 512
void pl2303_set_line_coding(libusb_device_handle *dev)
{
unsigned char buf[7];
unsigned int baud = 115200;
buf[0] = baud & 0xff;
buf[1] = (baud >> 8) & 0xff;
buf[2] = (baud >> 16) & 0xff;
buf[3] = (baud >> 24) & 0xff;
buf[4] = 0; // stop bits: 0 = 1
buf[5] = 0; // parity: 0 = none
buf[6] = 8; // data bits: 8
int r = libusb_control_transfer(dev,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
0x20,
0,
IFACE,
buf,
sizeof(buf),
1000
);
if (r < 0) {
fprintf(stderr, "Failed SET_LINE_CODING: %d\n", r);
exit(1);
}
}
void pl2303_init(libusb_device_handle *dev)
{
int r = libusb_control_transfer(dev,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
0x22,
0x0003, // DTR + RTS
IFACE,
NULL, 0,
1000);
if (r < 0) {
fprintf(stderr, "SET_CONTROL_LINE_STATE failed: %d\n", r);
exit(1);
}
pl2303_set_line_coding(dev);
}
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, VID, PID);
if (!dev) {
fprintf(stderr, "USB device not found\n");
return 1;
}
libusb_detach_kernel_driver(dev, IFACE);
libusb_claim_interface(dev, IFACE);
pl2303_init(dev);
if (openpty(&pty_master, &pty_slave, pty_name, NULL, NULL) < 0) {
perror("openpty");
return 1;
}
printf("TTY available at: %s\n", pty_name);
struct pollfd fds[1];
fds[0].fd = pty_master;
fds[0].events = POLLIN;
while (1) {
int r = poll(fds, 1, 10);
if (r > 0 && (fds[0].revents & POLLIN)) {
int n = read(pty_master, buf, BUF_SIZE);
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, BUF_SIZE, &received, 10);
if (rc == 0 && received > 0) {
write(pty_master, buf, received);
}
}
libusb_release_interface(dev, IFACE);
libusb_close(dev);
libusb_exit(ctx);
return 0;
}
編譯、測試
$ gcc main.c -o usb_pl2303_bridge -lusb-1.0
$ sudo ./usb_pl2303_bridge &
TTY available at: /dev/pts/2
$ lsusb -v -d 067b:2303 | egrep -e "EP|Transfer"
bEndpointAddress 0x81 EP 1 IN
Transfer Type Interrupt
bEndpointAddress 0x02 EP 2 OUT
Transfer Type Bulk
bEndpointAddress 0x83 EP 3 IN
Transfer Type Bulk
$ sudo ln -s /dev/pts/2 /dev/ttyUSB9999
$ sudo minicom -D /dev/ttyUSB9999 -b 115200