本系列调试说明记录了Linux在全志T113-S3/S4平台上的移植过程,说明过程中可能会技术瑕疵,希望大家提供宝贵意见。
本文移植的硬件平台来自于盈鹏飞嵌入式的SBC-T113S(处理器是T113-S3)主板。
SBC-T113S产品特性:
- 采用Allwinner公司Cortex-A7双核T113-S3/S4处理器,运行最高速度为1.2GHZ;
- 内置64-bit XuanTie C906 RISC-V协处理器(仅T113-S4支持);
- 支持JPEG/MJPEG视频编码,最大分辨率1080p@60fps;支持多格式1080P@60fps视频解码 (H.265,H.264, MPEG-1/2/4);
- 支持RGB666/LVDS/MIPI-DSI,分辨率最高1920x1080;
- 支持128-256M Bytes DDR3 SDRAM,其中T113-S3内置128MB;T113-S4内置256MB;
- 支持SPI NAND存储和启动(默认:256MB;最大1GB)或者EMMC启动(默认:4GB,最大32GB);
- 支持一路USB2.0 OTG(设计为TYPE-A接口);支持二路USB2.0 HOST;
- 支持七路RS232通信或者6路RS232和1路RS485(隔离);
- 支持一路CAN BUS通信(隔离);
- 支持一路10/100M以太网;
- 稳定的操作系统的支持,可预装LINUX 5.4或者OpenWRT;
- 经典尺寸主板,尺寸为120*100MM;
产品功能评估图:
产品尺寸:
打开CH341 CP210X驱动
CONFIG_USB_SERIAL_CONSOLE=y
CONFIG_USB_SERIAL_GENERIC=y
CONFIG_USB_SERIAL_SIMPLE=y
CONFIG_USB_SERIAL_CH341=y
CONFIG_USB_SERIAL_CP210X=y
ttyS3 修改为 ttyS4
longan/kernel/linux-4.9/drivers/tty/tty_io.c
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
old mode 100644 new mode 100755 index 33f80b0..3945013 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1346,11 +1346,17 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p)
*/
static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
{
+ //int len = -1; if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE)
return sprintf(p, "%s", driver->name);
- else - return sprintf(p, "%s%d", driver->name, - index + driver->name_base); + else{ + if ((index + driver->name_base) == 3) + return sprintf(p, "%s%d", driver->name, + index + driver->name_base + 6); // ttyS3->ttyS9 + else + return sprintf(p, "%s%d", driver->name, + index + driver->name_base); + } }
串口软链接
symlink /dev/ttyS3 /dev/s3c2410_serial3
USB 转串口设备的识别过程
当我们的USB Modem设备插入USB端口时,要调用bus_add_device()在USB总线上添加一个USB设备。
./drivers/usb/core/usb.c
static int __init usb_init(void)
{
int retval;
//---------------------------------------------
//可以看到在 usb_init 初始化函数中调用了bus_register 注册了一条USB总线
retval = bus_register(&usb_bus_type);
//---------------------------------------------
// 将usb_generic_driver 这个驱动程序注册给系统
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
}
./drivers/usb/core/generic.c
该USB设备由于有USB设备号,会找到刚才注册的usb_generic_driver中的generic_probe()函数,在这个函数中经过一系列的函数调用最后会 进入usb_set_configuration()
static int generic_probe(struct usb_device *udev)
{
int err, c;
/* Choose and set the configuration. This registers the interfaces * with the driver core and lets interface drivers bind to them. */
if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err && err != -ENODEV) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
/* This need not be fatal. The user can try to * set other configurations. */
}
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);
return 0;
}
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM .suspend = generic_suspend,
.resume = generic_resume,
#endif .supports_autosuspend = 1,
drivers/usb/serial/usb-serial.c
usb_set_configuration()函数会根据HOST和Device沟通的情况,进行总线枚举, 该函数会依次将这个interface添加到USB总线上, 根据VID和PID找到合适自己的probe函数,这里设备接口会进入usb_serial_probe()。
usb_serial_init()函数会调用tty_register_driver(usb_serial_tty_driver)向内核注册tty类的设备驱动,并在USB转串口总线上添加这个驱动。
static int __init usb_serial_init(void)
{
//-----------------------------------------------------------------------------
result = tty_register_driver(usb_serial_tty_driver);
if (result) {
pr_err("%s - tty_register_driver failed\n", __func__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver);
if (result < 0) {
pr_err("%s - usb_register failed\n", __func__);
goto exit_tty;
}
//---------------------------------------------------------------------------------------
}
static int allocate_minors(struct usb_serial *serial, int num_ports)
{
struct usb_serial_port *port;
unsigned int i, j;
int minor;
dev_dbg(&serial->interface->dev, "%s %d\n", __func__, num_ports);
mutex_lock(&table_lock);
for (i = 0; i < num_ports; ++i) {
port = serial->port[i];
//modify by lixiaogang start
if (!strcmp(serial->type->description, "ch341-uart")) {
minor = idr_alloc(&serial_minors, port, 8,
USB_SERIAL_TTY_MINORS, GFP_KERNEL);
} else {
minor = idr_alloc(&serial_minors, port, 0,
USB_SERIAL_TTY_MINORS, GFP_KERNEL);
}
//modify by lixiaogang end
if (minor < 0)
goto error;
port->minor = minor;
port->port_number = i;
}
//-----------------------------------------------------------------------------------------
return minor;
}
static int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct device *ddev = &interface->dev;
struct usb_device *dev = interface_to_usbdev(interface);
// 保存该设备的详细信息
struct usb_serial *serial = NULL;
struct usb_serial_port *port;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
struct usb_serial_driver *type = NULL;
//----------------------------------------------------------------------------------------
serial = create_serial(dev, interface, type);
if (!serial) {
module_put(type->driver.owner);
return -ENOMEM;
}
//---------------------------------------------------------------------------------------
if (allocate_minors(serial, num_ports)) {
dev_err(ddev, "No more free serial minor numbers\n");
goto probe_error;
}
/* register all of the individual ports with the driver core */
for (i = 0; i < num_ports; ++i) {
port = serial->port[i];
dev_set_name(&port->dev, "ttyUSB%d", port->minor);
dev_dbg(ddev, "registering %s\n", dev_name(&port->dev));
device_enable_async_suspend(&port->dev);
retval = device_add(&port->dev);
if (retval)
dev_err(ddev, "Error registering port device, continuing\n");
}
//---------------------------------------------------------------------------------------
}
./drivers/usb/serial/ch341.c
static int ch341_port_probe(struct usb_serial_port *port)
{
struct ch341_private *priv;
int r;
priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->baud_rate = DEFAULT_BAUD_RATE;
r = ch341_configure(port->serial->dev, priv);
if (r < 0)
goto error;
usb_set_serial_port_data(port, priv);
return 0;
error: kfree(priv);
return r;
}
static struct usb_serial_driver ch341_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ch341-uart",
},
.id_table = id_table,
.num_ports = 1,
.open = ch341_open,
.dtr_rts = ch341_dtr_rts,
.carrier_raised = ch341_carrier_raised,
.close = ch341_close,
.set_termios = ch341_set_termios,
.break_ctl = ch341_break_ctl,
.tiocmget = ch341_tiocmget,
.tiocmset = ch341_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.read_int_callback = ch341_read_int_callback,
.port_probe = ch341_port_probe,
.port_remove = ch341_port_remove,
.reset_resume = ch341_reset_resume,
};
可插拔 USB 转串口固定编号
Linux
在 Linux 系统中,CH341 USB 转串口转换器的设备文件通常被分配为 /dev/ttyUSBx 的形式,其中 x 是一个数字。这个数字表示设备在系统中的顺序,当插入或拔出其他 USB 设备时,它可能会发生变化。
如果你希望固定 CH341 的 ttyUSB 串口号,你可以通过修改 udev 规则来实现这一点。以下是一个简单的步骤:
- 创建一个新的 udev 规则: 在 /etc/udev/rules.d 目录下创建一个新文件,例如 99-ch341.rules。
- 编辑规则文件: 使用文本编辑器打开刚才创建的规则文件,并添加以下内容:
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ch341"
这条规则表示所有 vendor ID 为 1a86 和 product ID 为 7523 的设备(这是 CH341 转换器的默认值)都应该被创建一个符号链接,名称为 ch341。
android
/kernel/linux-4.9/drivers/usb/serial/usb-serial.c
CH341串口编号从ttyUSB8开始分配
diff --git a/longan/kernel/linux-4.9/drivers/usb/serial/usb-serial.c b/longan/kernel/linux-4.9/drivers/usb/serial/usb-serial.c
index 4a037b4a79..ed9a2fc5f9 100644 --- a/longan/kernel/linux-4.9/drivers/usb/serial/usb-serial.c +++ b/longan/kernel/linux-4.9/drivers/usb/serial/usb-serial.c @@ -96,8 +96,15 @@ static int allocate_minors(struct usb_serial *serial, int num_ports)
mutex_lock(&table_lock);
for (i = 0; i < num_ports; ++i) {
port = serial->port[i];
- minor = idr_alloc(&serial_minors, port, 0, - USB_SERIAL_TTY_MINORS, GFP_KERNEL); + //modify by lixiaogang start + if (!strcmp(serial->type->description, "ch341-uart")) { + minor = idr_alloc(&serial_minors, port, 8, + USB_SERIAL_TTY_MINORS, GFP_KERNEL); + } else { + minor = idr_alloc(&serial_minors, port, 0, + USB_SERIAL_TTY_MINORS, GFP_KERNEL); + } + //modify by lixiaogang end if (minor < 0)
goto error;
port->minor = minor,