linux options

1. 前言

    2.1的流程图主要完成两项工作,第一,将当前option驱动注册到usb总线上;第二,将当前option驱动注册usb-serial总线上,我原来不清楚这里为什么要分两次注册到不同的总线上(usb、usb-serial总线),其实这是当usb口接入一个设备时,设备是如何从usb总线跨到usb-serial总线。我们知道usb上的设备注册流程大概是:先是设备匹配-->接口匹配-->这里的工作都是在usb总线上完成的,但是在接口匹配时会涉及到接口驱动与id的匹配(如option驱动内部的id_table表),注意这里还是在usb总线上,为什么还是在usb总线?因为我们的option驱动分别注册到了usb总线和usb-serial总线,当接口驱动和id匹配后,我们通过id可以找到它的驱动,这里即是option驱动,option驱动在注册时就绑定了探测函数usb_serial_probe(),所以会执行它,而在usb_serial_probe()函数内又会调用端口的匹配,即usb_wwan_port_probe(),待这些工作完成之后,最后通过tty_register_device(usb_serial_tty_driver, minor, dev)完成ttyUSBx设备的创建。(在分析该流程时需结合2.1,2.1的流程图)

2. 流程图

2.1 option驱动注册流程图


       

2.2 option驱动被枚举流程图


该流程图只分析虚框部分,即usb口检测到设备时,usb串口驱动是如何工作的,该流程图虚框之外的部分在前面一篇博客分析过,详见:点击打开链接

3. 源码分析

3.1 usb驱动注册

static struct usb_serial_driver option_1port_device = {
	.driver = { //设备驱驱动
		.owner =	THIS_MODULE,
		.name =		"option1", //设备驱动名称
	},
	.description       = "GSM modem (1-port)", //描述符
	.id_table          = option_ids, //厂商id和产品id表
	.num_ports         = 1,
	.probe             = option_probe, //探测之后存储该usb串口设备对应的id(VID/PID)
	.open              = usb_wwan_open,
	.close             = usb_wwan_close,
	.dtr_rts	   = usb_wwan_dtr_rts,
	.write             = usb_wwan_write,
	.write_room        = usb_wwan_write_room,
	.chars_in_buffer   = usb_wwan_chars_in_buffer,
	.set_termios       = usb_wwan_set_termios,
	.tiocmget          = usb_wwan_tiocmget,
	.tiocmset          = usb_wwan_tiocmset,
	.ioctl             = usb_wwan_ioctl,
	.attach            = option_attach, //
	.release           = option_release,
	.port_probe        = usb_wwan_port_probe,
	.port_remove	   = usb_wwan_port_remove,
	.read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
	.suspend           = usb_wwan_suspend,
	.resume            = usb_wwan_resume,
#endif
};
static struct usb_serial_driver * const serial_drivers[] = {
	&option_1port_device, NULL
};
module_usb_serial_driver(serial_drivers, option_ids);
#define module_usb_serial_driver(__serial_drivers, __ids)		\
	usb_serial_module_driver(KBUILD_MODNAME, __serial_drivers, __ids) //KBUILD_MODNAME 表示内核模块名称
KBUILD_MODNAME:表示内核模块命名,这里是“usb-serial”
#define usb_serial_module_driver(__name, __serial_drivers, __ids)	\
static int __init usb_serial_module_init(void)				\
{									\
	return usb_serial_register_drivers(__serial_drivers,		\
					   __name, __ids);		\
}									

usb串口驱动注册:

int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[],
				const char *name,
				const struct usb_device_id *id_table)
{
	int rc;
	struct usb_driver *udriver;
	struct usb_serial_driver * const *sd;

	/*
	 * udriver must be registered before any of the serial drivers,
	 * because the store_new_id() routine for the serial drivers (in
	 * bus.c) probes udriver.
	 *
	 * Performance hack: We don't want udriver to be probed until
	 * the serial drivers are registered, because the probe would
	 * simply fail for lack of a matching serial driver.
	 * So we leave udriver's id_table set to NULL until we are all set.
	 *
	 * Suspend/resume support is implemented in the usb-serial core,
	 * so fill in the PM-related fields in udriver.
	 */
	udriver = kzalloc(sizeof(*udriver), GFP_KERNEL); //分配一个usb驱动空间
	if (!udriver)
		return -ENOMEM;

	udriver->name = name; //驱动名称"usb-serial"
	udriver->no_dynamic_id = 1;
	udriver->supports_autosuspend = 1;
	udriver->suspend = usb_serial_suspend;
	udriver->resume = usb_serial_resume;
	udriver->probe = usb_serial_probe; //绑定探测函数
	udriver->disconnect = usb_serial_disconnect;

	/* we only set the reset_resume field if the serial_driver has one */
	for (sd = serial_drivers; *sd; ++sd) {
		if ((*sd)->reset_resume) {
			udriver->reset_resume = usb_serial_reset_resume;
			break;
		}
	}

	rc = usb_register(udriver); //usb驱动注册,即将当前驱动注册到usb总线上
	if (rc)
		return rc;

	for (sd = serial_drivers; *sd; ++sd) {
		(*sd)->usb_driver = udriver; //绑定串口驱动对应的usb驱动
		rc = usb_serial_register(*sd); //usb serial注册
		if (rc)
			goto failed;
	}

	/* Now set udriver's id_table and look for matches */
	udriver->id_table = id_table;
	rc = driver_attach(&udriver->drvwrap.driver);
	return 0;

 failed:
	while (sd-- > serial_drivers)
		usb_serial_deregister(*sd);
	usb_deregister(udriver);
	return rc;
}
EXPORT_SYMBOL_GPL(usb_serial_register_drivers);

在该函数内部主要完成以下几个重要功能:

1> 分配一个usb驱动内存空间,udriver = kzalloc(sizeof(*udriver), GFP_KERNEL)

2> 在usb总线上驱动注册,usb_register(udriver)

3> 在usb-serial串口总线上注册,usb_serial_register(*sd)

4> 检测当前驱动是否在usb总线上附上, driver_attach(&udriver->drvwrap.driver);

针对以下几点,下面逐个分析...

3.2 在usb总线上注册“usb-serial”驱动

	udriver = kzalloc(sizeof(*udriver), GFP_KERNEL); //分配一个usb驱动空间
	if (!udriver)
		return -ENOMEM;

	udriver->name = name; //驱动名称"usb-serial"
	udriver->no_dynamic_id = 1;
	udriver->supports_autosuspend = 1;
	udriver->suspend = usb_serial_suspend;
	udriver->resume = usb_serial_resume;
	udriver->probe = usb_serial_probe; //绑定probe探测函数
	udriver->disconnect = usb_serial_disconnect;
	rc = usb_register(udriver); //usb驱动注册,即将当前驱动注册到usb总线上
#define usb_register(driver) \
	usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
上面分析过 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;

	//对drvwrap USB驱动包初始化
	new_driver->drvwrap.for_devices = 0;
	new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“usb-serial”
	new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线类型为“usb”
	new_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
	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); //当前的“usb-serial”驱动注册
	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注册驱动最终会调用driver_register(&new_driver->drvwrap.driver)函数,关于该函数内部的细节,详见流程图...,至此,就完成了当前驱动在usb总线上的注册.

3.3 在usb-serial总线上注册“usb-serial”驱动

		(*sd)->usb_driver = udriver; //绑定串口驱动对应的usb驱动
		rc = usb_serial_register(*sd); //usb serial注册
static int usb_serial_register(struct usb_serial_driver *driver)
{
	int retval;

	if (usb_disabled())
		return -ENODEV;

	if (!driver->description)
		driver->description = driver->driver.name;
	if (!driver->usb_driver) {
		WARN(1, "Serial driver %s has no usb_driver\n",
				driver->description);
		return -EINVAL;
	}

	usb_serial_operations_init(driver); //????

	/* Add this device to our list of devices */
	mutex_lock(&table_lock);
	list_add(&driver->driver_list, &usb_serial_driver_list); //将当前驱动增加到全局链表usb_serial_driver_list上

	retval = usb_serial_bus_register(driver); //将当前驱动注册到“usb-serial”总线上,并完成设备模型的注册
	if (retval) {
		pr_err("problem %d when registering driver %s\n", retval, driver->description);
		list_del(&driver->driver_list);
	} else
		pr_info("USB Serial support registered for %s\n", driver->description);

	mutex_unlock(&table_lock);
	return retval;
}

该函数将usb-serial驱动加入到全局链表usb_serial_driver_list链表上,然后将当前驱动注册到usb-serial总线上

retval = usb_serial_bus_register(driver); //将当前驱动注册到“usb-serial”总线上,并完成设备模型的注册
int usb_serial_bus_register(struct usb_serial_driver *driver)
{
	int retval;

	driver->driver.bus = &usb_serial_bus_type; //绑定当前驱动的总线为“usb-serial”
	spin_lock_init(&driver->dynids.lock);
	INIT_LIST_HEAD(&driver->dynids.list);

	retval = driver_register(&driver->driver); //驱动注册

	return retval;
}
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,
};

同样,与3.2相同,usb注册驱动最终会调用driver_register(&driver->driver)函数,关于该函数内部的细节,详见流程图...就完成了当前驱动在usb-serial总线上的注册.

3.4 检测当前驱动是否在usb总线匹配对应的设备

	/* Now set udriver's id_table and look for matches */
	udriver->id_table = id_table;
	rc = driver_attach(&udriver->drvwrap.drive
int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);

4. 未解决的问题

该篇涉及的内容虽然不多,但是要理解从源头(usb口设备接入到枚举,最后调用option驱动)到最终驱动的匹配和使用却相当的麻烦,先到这里,接下来分析如何使用上面的接口,usb、usb-serial、options这三者之间的关系!!!!

5. usb口接入设备时option驱动的工作流程

       option的工作流程,其实就是“4.未解决的问题”,关于内部源码这里不在分析,通过本文流程图2可清晰的知道注册流程。通过前言部分,可以知道usb、usb-serial、option三者之间的关系! 

usb驱动之庞大真的是 “剪不断、理不乱


打开usb_wwan_open函数时的堆栈信息:

Backtrace: 
[<c0012df8>] (dump_backtrace+0x0/0x10c) from [<c0012f1c>] (show_stack+0x18/0x1c)
 r7:c2e74040 r6:c2c97c60 r5:c2e15800 r4:c3ad1e40
[<c0012f04>] (show_stack+0x0/0x1c) from [<c03244bc>] (dump_stack+0x20/0x2c)
[<c032449c>] (dump_stack+0x0/0x2c) from [<c0268a60>] (usb_wwan_open+0xf8/0x100)
[<c0268968>] (usb_wwan_open+0x0/0x100) from [<c026502c>] (serial_port_activate+0x5c/0xa0)
 r7:c2ed6c00 r6:c2c97c98 r5:c2e15804 r4:c2c97c60
[<c0264fd0>] (serial_port_activate+0x0/0xa0) from [<c01d9bac>] (tty_port_open+0xcc/0x12c)
 r7:c2ed6c00 r6:c2e1585c r5:c2ed6c00 r4:c2e15804
[<c01d9ae0>] (tty_port_open+0x0/0x12c) from [<c0265434>] (serial_open+0x24/0x28)
 r9:c2e42000 r8:00000100 r7:c39dc300 r6:c2d218c0 r5:c2ed6c00
r4:c2ed6c00
[<c0265410>] (serial_open+0x0/0x28) from [<c01d2274>] (tty_open+0x1c4/0x59c)
[<c01d20b0>] (tty_open+0x0/0x59c) from [<c00c97a4>] (chrdev_open+0xc8/0x1a8)
[<c00c96dc>] (chrdev_open+0x0/0x1a8) from [<c00c45ac>] (do_dentry_open+0x134/0x26c)
[<c00c4478>] (do_dentry_open+0x0/0x26c) from [<c00c47e8>] (finish_open+0x40/0x5c)
 r9:c2d218c0 r8:00000902 r7:c2e43f68 r6:c2e43ee8 r5:c35974c8
r4:c2e43ea8
[<c00c47a8>] (finish_open+0x0/0x5c) from [<c00d357c>] (do_last+0x678/0xbf4)
 r5:00000000 r4:00000000
[<c00d2f04>] (do_last+0x0/0xbf4) from [<c00d3bbc>] (path_openat+0xc4/0x460)
[<c00d3af8>] (path_openat+0x0/0x460) from [<c00d4070>] (do_filp_open+0x38/0x8c)
[<c00d4038>] (do_filp_open+0x0/0x8c) from [<c00c42e4>] (do_sys_open+0xfc/0x134)
 r8:c000f948 r7:ffffff9c r6:00000012 r5:c2f0c000 r4:00000001
[<c00c41e8>] (do_sys_open+0x0/0x134) from [<c00c4360>] (SyS_open+0x28/0x2c)
 r7:00000005 r6:00000000 r5:ffffffff r4:ffffffff
[<c00c4338>] (SyS_open+0x0/0x2c) from [<c000f7c0>] (ret_fast_syscall+0x0/0x2c)
usb 1-1: unlink qh32-0001/c2f4a140 start 1 [1/0 us]

写ttyUSBx usb_wwan_write的堆栈信息:

Backtrace: 
[<c0012df8>] (dump_backtrace+0x0/0x10c) from [<c0012f1c>] (show_stack+0x18/0x1c)
 r7:c2e49d14 r6:c398c000 r5:c398c000 r4:00000020
[<c0012f04>] (show_stack+0x0/0x1c) from [<c03244d0>] (dump_stack+0x20/0x28)
[<c03244b0>] (dump_stack+0x0/0x28) from [<c0257a0c>] (ehci_urb_enqueue+0x2c/0x1014)
[<c02579e0>] (ehci_urb_enqueue+0x0/0x1014) from [<c0243c90>] (usb_hcd_submit_urb+0xb4/0x960)
[<c0243bdc>] (usb_hcd_submit_urb+0x0/0x960) from [<c0244b60>] (usb_submit_urb+0x1f4/0x45c)
[<c024496c>] (usb_submit_urb+0x0/0x45c) from [<c0268bc4>] (usb_wwan_write+0x148/0x220)
[<c0268a7c>] (usb_wwan_write+0x0/0x220) from [<c0264d80>] (serial_write+0x48/0x80)
[<c0264d38>] (serial_write+0x0/0x80) from [<c01d4b9c>] (n_tty_write+0x334/0x458)
 r7:00000004 r6:c2f2ec00 r5:c2f2ea64 r4:00000000
[<c01d4868>] (n_tty_write+0x0/0x458) from [<c01d07b0>] (tty_write+0x14c/0x234)
[<c01d0664>] (tty_write+0x0/0x234) from [<c00c674c>] (vfs_write+0xc8/0x134)
[<c00c6684>] (vfs_write+0x0/0x134) from [<c00c6898>] (SyS_write+0x4c/0x78)
 r7:00000001 r6:c2ef6000 r5:00000000 r4:00000000
[<c00c684c>] (SyS_write+0x0/0x78) from [<c000f7c0>] (ret_fast_syscall+0x0/0x2c)
 r8:c000f948 r7:00000004 r6:00000000 r5:ffffffff r4:ffffffff
CPU: 0 PID: 496 Comm: klogd Tainted: G           O 3.10.32 #189




  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值