固定串口编号

Linux Android

Posted by LXG on December 14, 2023

打开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 规则来实现这一点。以下是一个简单的步骤:

  1. 创建一个新的 udev 规则: 在 /etc/udev/rules.d 目录下创建一个新文件,例如 99-ch341.rules。
  2. 编辑规则文件: 使用文本编辑器打开刚才创建的规则文件,并添加以下内容:

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;