linux usb-serial options

本文详细解析了USB串口驱动的加载过程,包括模块初始化、动态分配tty驱动、总线注册、tty驱动注册及USB驱动注册等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 前言

...

2. 流程图


3. usb serial源码分析

3.1  usb serial驱动加载

module_init(usb_serial_init);
module_exit(usb_serial_exit);

3.2 usb serial初始化

static int __init usb_serial_init(void)
{
	int i;
	int result;

	usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //分配一个usb tty driver结构体
	if (!usb_serial_tty_driver)
		return -ENOMEM;

	/* Initialize our global data */
	for (i = 0; i < SERIAL_TTY_MINORS; ++i) //为tty次设备usb-serial结构体初始化,在usb口热插拔创建设备/dev/ttyUSBx时就会使用一个serial_table[x]
		serial_table[i] = NULL;

	result = bus_register(&usb_serial_bus_type); //"usb-serial"总线注册
	if (result) {
		pr_err("%s - registering bus driver failed\n", __func__);
		goto exit_bus;
	}

	usb_serial_tty_driver->driver_name = "usbserial"; //以下是初始化tty 核心层,包括设备名、驱动名
	usb_serial_tty_driver->name = "ttyUSB";
	usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
	usb_serial_tty_driver->minor_start = 0;
	usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
	usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
	usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
						TTY_DRIVER_DYNAMIC_DEV;
	usb_serial_tty_driver->init_termios = tty_std_termios;
	usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
							| HUPCL | CLOCAL;
	usb_serial_tty_driver->init_termios.c_ispeed = 9600;
	usb_serial_tty_driver->init_termios.c_ospeed = 9600;
	tty_set_operations(usb_serial_tty_driver, &serial_ops); //绑定tty核心层的操作句柄
	result = tty_register_driver(usb_serial_tty_driver); //tty驱动注册
	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); //usb驱动注册
	if (result < 0) {
		pr_err("%s - usb_register failed\n", __func__);
		goto exit_tty;
	}

	/* register the generic driver, if we should */
	result = usb_serial_generic_register();
	if (result < 0) {
		pr_err("%s - registering generic driver failed\n", __func__);
		goto exit_generic;
	}

	return result;

exit_generic:
	usb_deregister(&usb_serial_driver);

exit_tty:
	tty_unregister_driver(usb_serial_tty_driver);

exit_reg_driver:
	bus_unregister(&usb_serial_bus_type);

exit_bus:
	pr_err("%s - returning with error %d\n", __func__, result);
	put_tty_driver(usb_serial_tty_driver);
	return result;
}

usb_serial_init()初始化函数主要包括如下几个重要部分:

1> 动态分配一个tty驱动,usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);

2> "usb-serial"总线注册,result = bus_register(&usb_serial_bus_type);

3> tty驱动注册,result = tty_register_driver(usb_serial_tty_driver);

4> usb串口驱动注册,result = usb_register(&usb_serial_driver);

下面逐个分析...

3.3 动态分配一个tty驱动结构体

usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //SERIAL_TTY_MINORS为254
if (!usb_serial_tty_driver)
	return -ENOMEM;
static inline struct tty_driver *alloc_tty_driver(unsigned int lines)
{
	struct tty_driver *ret = tty_alloc_driver(lines, 0);
	if (IS_ERR(ret))
		return NULL;
	return ret;
}
#define tty_alloc_driver(lines, flags) \
		__tty_alloc_driver(lines, THIS_MODULE, flags)
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
		unsigned long flags)
{
	struct tty_driver *driver;
	unsigned int cdevs = 1;
	int err;

	if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
		return ERR_PTR(-EINVAL);

	driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); //动态分配一个tty_driver内存空间
	if (!driver)
		return ERR_PTR(-ENOMEM);

	kref_init(&driver->kref);
	driver->magic = TTY_DRIVER_MAGIC;
	driver->num = lines; //绑定tty_driver驱动个数
	driver->owner = owner;
	driver->flags = flags; //flags=0

	if (!(flags & TTY_DRIVER_DEVPTS_MEM)) { //条件成立
		driver->ttys = kcalloc(lines, sizeof(*driver->ttys), //分配lines个tty_struct设备
				GFP_KERNEL);
		driver->termios = kcalloc(lines, sizeof(*driver->termios), //分配lines个ktermios内核终端输入、输出配置
				GFP_KERNEL);
		if (!driver->ttys || !driver->termios) {
			err = -ENOMEM;
			goto err_free_all;
		}
	}

	if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) { //条件成立
		driver->ports = kcalloc(lines, sizeof(*driver->ports), //分配lines个tty_port结构体
				GFP_KERNEL);
		if (!driver->ports) {
			err = -ENOMEM;
			goto err_free_all;
		}
		cdevs = lines; //初始化字符设备的个数
	}

	driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL); //分配lines个cedv字符设备
	if (!driver->cdevs) {
		err = -ENOMEM;
		goto err_free_all;
	}

	return driver;
err_free_all:
	kfree(driver->ports);
	kfree(driver->ttys);
	kfree(driver->termios);
	kfree(driver);
	return ERR_PTR(err);
}
EXPORT_SYMBOL(__tty_alloc_driver);

3.4 “usb-serial”总线注册

struct bus_type usb_serial_bus_type = { 
	.name =		"usb-serial", //总线名称
	.match =	usb_serial_device_match, //总线上驱动和设备匹配接口
	.probe =	usb_serial_device_probe, //匹配成功调用的探测接口
	.remove =	usb_serial_device_remove,
	.drv_attrs = 	drv_attrs,
};
result = bus_register(&usb_serial_bus_type);
if (result) {
	pr_err("%s - registering bus driver failed\n", __func__);
	goto exit_bus;
}
int bus_register(struct bus_type *bus)
{
	int retval;
	struct subsys_private *priv;
	struct lock_class_key *key = &bus->lock_key;

	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); //分配一个内核对象子系统
	if (!priv)
		return -ENOMEM;

	priv->bus = bus; //初始化子系统的当前总线
	bus->p = priv; //绑定当前总线的子系统

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //初始化一个阻塞通知链

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //给当前子系统的内核对象命名
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset; //初始化当前子系统的父内核集合为bus_kset
	priv->subsys.kobj.ktype = &bus_ktype; //初始化当前子系统内核对象属性(内核对象属性就是表示对文件的操作)
	priv->drivers_autoprobe = 1; //设置设备和驱动匹配为自动,即从总线上获取对应相同的名称

	retval = kset_register(&priv->subsys); //子系统内核集合初始化
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent); //创建当前总线的事件属性文件
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL, //创建当前总线的“设备”内核集合,即该总线下的设备都把它作为父集合对象
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL, //创建当前总线下的“驱动”内核集合,即该总线下的驱动都把它作为集合对象
						 &priv->subsys.kobj);
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	INIT_LIST_HEAD(&priv->interfaces); //初始化接口链表
	__mutex_init(&priv->mutex, "subsys mutex", key);  //初始化子系统互斥锁
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //创建一个设备链表,即当前总线下的设备都挂接在该链表上
	klist_init(&priv->klist_drivers, NULL, NULL); //创建一个驱动链表,即当前总线下的驱动都挂接在该链表上

	retval = add_probe_files(bus); //增加当前总线的属性文件(如driver_auto_probe、drivers_probe)到sys文件系统
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus); //为当前总线增加属性文件
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
out:
	kfree(bus->p);
	bus->p = NULL;
	return retval;
}
EXPORT_SYMBOL_GPL(bus_register);

3.5 tty驱动注册

	usb_serial_tty_driver->driver_name = "usbserial"; //设置驱动名称,在总线上用来与设备相匹配
	usb_serial_tty_driver->name = "ttyUSB"; //初始化tty设备的名称
	usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //tty主设备号188
	usb_serial_tty_driver->minor_start = 0; //次设备的起始值为0,表示动态分配次设备
	usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //tty驱动类型为串口(tty的类型有很多,如console、pty...)
	usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //tty驱动子类型
	usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
						TTY_DRIVER_DYNAMIC_DEV;
	usb_serial_tty_driver->init_termios = tty_std_termios;
	usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
							| HUPCL | CLOCAL;
	usb_serial_tty_driver->init_termios.c_ispeed = 9600;
	usb_serial_tty_driver->init_termios.c_ospeed = 9600;
	tty_set_operations(usb_serial_tty_driver, &serial_ops); //绑定tty操作集,即tty核心层的接口
	result = tty_register_driver(usb_serial_tty_driver); //tty驱动注册
	if (result) {
		pr_err("%s - tty_register_driver failed\n", __func__);
		goto exit_reg_driver;
	}

tty核心层操作接口:

static const struct tty_operations serial_ops = {
	.open =			serial_open,
	.close =		serial_close,
	.write =		serial_write,
	.hangup =		serial_hangup,
	.write_room =		serial_write_room,
	.ioctl =		serial_ioctl,
	.set_termios =		serial_set_termios,
	.throttle =		serial_throttle,
	.unthrottle =		serial_unthrottle,
	.break_ctl =		serial_break,
	.chars_in_buffer =	serial_chars_in_buffer,
	.wait_until_sent =	serial_wait_until_sent,
	.tiocmget =		serial_tiocmget,
	.tiocmset =		serial_tiocmset,
	.get_icount =		serial_get_icount,
	.cleanup =		serial_cleanup,
	.install =		serial_install,
	.proc_fops =		&serial_proc_fops,
};
result = tty_register_driver(usb_serial_tty_driver);
int tty_register_driver(struct tty_driver *driver)
{
	int error;
	int i;
	dev_t dev;
	struct device *d;

	if (!driver->major) { //major=188
		error = alloc_chrdev_region(&dev, driver->minor_start,
						driver->num, driver->name);
		if (!error) {
			driver->major = MAJOR(dev);
			driver->minor_start = MINOR(dev);
		}
	} else {
		dev = MKDEV(driver->major, driver->minor_start); //创建主、次设备号
		error = register_chrdev_region(dev, driver->num, driver->name); //注册num个字符设备
	}
	if (error < 0)
		goto err;

	if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
		error = tty_cdev_add(driver, dev, 0, driver->num); //tty字符设备增加(会绑定tty_io层的操作集),这个函数内部详见下面
		if (error)
			goto err_unreg_char;
	}

	mutex_lock(&tty_mutex);
	list_add(&driver->tty_drivers, &tty_drivers); //将当前驱动加入到全局链表tty_drivers
	mutex_unlock(&tty_mutex);

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
		for (i = 0; i < driver->num; i++) {
			d = tty_register_device(driver, i, NULL); //注册tty字符设备
			if (IS_ERR(d)) {
				error = PTR_ERR(d);
				goto err_unreg_devs;
			}
		}
	}
	proc_tty_register_driver(driver); //tty proc文件系统注册
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;

err_unreg_devs:
	for (i--; i >= 0; i--)
		tty_unregister_device(driver, i);

	mutex_lock(&tty_mutex);
	list_del(&driver->tty_drivers);
	mutex_unlock(&tty_mutex);

err_unreg_char:
	unregister_chrdev_region(dev, driver->num);
err:
	return error;
}
EXPORT_SYMBOL(tty_register_driver);

字符设备增加:

error = tty_cdev_add(driver, dev, 0, driver->num);
static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
		unsigned int index, unsigned int count)
{
	/* init here, since reused cdevs cause crashes */
	cdev_init(&driver->cdevs[index], &tty_fops); //绑定tty_io层的操作集
	driver->cdevs[index].owner = driver->owner;
	return cdev_add(&driver->cdevs[index], dev, count);
}
static const struct file_operations tty_fops = {
	.llseek		= no_llseek,
	.read		= tty_read,
	.write		= tty_write,
	.poll		= tty_poll,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.release	= tty_release,
	.fasync		= tty_fasync,
};

这个是文件操作集合,对应系统调用的第一层。

3.6 usb驱动注册

static struct usb_driver usb_serial_driver = {
	.name =		"usbserial", //驱动名称
	.probe =	usb_serial_probe, //驱动探测接口
	.disconnect =	usb_serial_disconnect,
	.suspend =	usb_serial_suspend,
	.resume =	usb_serial_resume,
	.no_dynamic_id =	1,
	.supports_autosuspend =	1,
};
result = usb_register(&usb_serial_driver);
#define usb_register(driver) \
	usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
			const char *mod_name)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;

	new_driver->drvwrap.for_devices = 0;
	new_driver->drvwrap.driver.name = (char *) new_driver->name; //“usbserial”
	new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线为“usb”
	new_driver->drvwrap.driver.probe = usb_probe_interface; //绑定接口
	new_driver->drvwrap.driver.remove = usb_unbind_interface;
	new_driver->drvwrap.driver.owner = owner;
	new_driver->drvwrap.driver.mod_name = mod_name;
	spin_lock_init(&new_driver->dynids.lock);
	INIT_LIST_HEAD(&new_driver->dynids.list);

	retval = driver_register(&new_driver->drvwrap.driver); //驱动注册
	if (retval)
		goto out;

	retval = usb_create_newid_files(new_driver);
	if (retval)
		goto out_newid;

	pr_info("%s: registered new interface driver %s\n",
			usbcore_name, new_driver->name);

out:
	return retval;

out_newid:
	driver_unregister(&new_driver->drvwrap.driver);

	printk(KERN_ERR "%s: error %d registering interface "
			"	driver %s\n",
			usbcore_name, retval, new_driver->name);
	goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
至此,就完成了"usb-serial"的总线注册,usb tty(254个tty【ttyUSBx】)驱动"usbserial"的注册,usb串口驱动"usbserial"注册。










03-13
### USB DFU (Device Firmware Upgrade) Protocol and Tools #### Overview of USB DFU The USB Device Firmware Upgrade (DFU) protocol is a standardized method to update firmware on devices connected via the Universal Serial Bus (USB). This protocol allows manufacturers and users to upgrade device software without requiring specific drivers or complex procedures, enhancing both security and convenience. #### Implementation with `dfu-util` `dfu-util`, an implementation of the host-side USB DFU protocol, primarily operates within Unix-like environments but also supports Windows through additional configurations[^1]. Initially developed for the OpenMoko project, this tool has since become widely adopted across various platforms that require robust firmware management solutions. The official repository continues active development at [http://dfu-util.gnumonks.org/](http://dfu-util.gnumonks.org/) where developers can find comprehensive documentation along with source code releases. To use `dfu-util`, one typically follows these steps: ```bash # Install dfu-util package depending on your Linux distribution sudo apt-get install dfu-util # For Debian-based systems like Ubuntu sudo yum install dfu-util # For Red Hat-based systems such as Fedora # List all available DFU-capable devices attached to the system dfu-util --list # Upload new firmware image file to target device using alt setting number dfu-util -a 0 -D path/to/firmware.bin -s address:leave_blank_for_default ``` This command-line utility provides options not only for uploading images (`-D`) but also downloading existing ones from supported hardware interfaces while specifying alternative settings numbers (`-a`). Address parameters allow precise control over memory locations when necessary during deployment processes. #### Application Scenarios In embedded systems engineering projects involving microcontrollers based around architectures similar to STM32 series processors, utilizing DFU mode becomes particularly beneficial due to its simplicity compared to traditional methods relying heavily upon serial connections or proprietary protocols. By leveraging built-in bootloader capabilities present in many modern MCUs alongside standard-compliant APIs provided by operating systems, engineers gain flexibility regarding how they manage product lifecycles post-manufacturing phase efficiently.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值