usb总线注册、设备模型、通知链

1. usb设备模型图

1.1 kobject kset kobject_type三者关系


1.2 总线子系统内部结构


本文以usb子系统为例分析:

1.2.1 注册usb总线类型为bus_type的子系统;

1.2.2 在usb子系统上创建devices_kset设备集合;

1.2.3 在usb子系统上创建drivers_kset设备驱动集合;

关于devices_kset、drivers_kset集合,他们相当于后面热插拔挂接在usb总线上的设备和驱动的父对象,这样就形成一种树形的层次结构,我们通过如下目录/sys/bus/usb/devices/...,/sys/bus/usb/drivers/...可以看到这种关系。

1.2.4 klist_devices表示设备链表,即后续挂接在usb总线上的设备都是通过该链表连接在一起;

1.2.5 klist_drivers表示驱动链表,即后续挂接在usb总线上的驱动都是通过该链表连接在一起。

2. usb总线注册源码分析

2.1 usb总线类型 usb_bus_type

struct bus_type usb_bus_type = {
	.name =		"usb", //总线类型为"usb"
	.match =	usb_device_match, //设备和驱动在usb总线上匹配的接口函数
	.uevent =	usb_uevent, //usb uevent机制
};

usb_uevent 机制,详见:点击打开链接

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
	/* devices and interfaces are handled separately */
	if (is_usb_device(dev)) { //是否是usb设备

		/* interface drivers never match devices */
		if (!is_usb_device_driver(drv))
			return 0;

		/* TODO: Add real matching code */
		return 1;

	} else if (is_usb_interface(dev)) { //是否是usb接口设备
		struct usb_interface *intf;
		struct usb_driver *usb_drv;
		const struct usb_device_id *id;

		/* device drivers never match interfaces */
		if (is_usb_device_driver(drv)) //判断是否是usb设备驱动
			return 0;

		intf = to_usb_interface(dev); //通过设备获取接口
		usb_drv = to_usb_driver(drv); //通过设备获取驱动

		id = usb_match_id(intf, usb_drv->id_table); //将接口的字段ID与驱动的id_table表进行匹配
		if (id)
			return 1;

		id = usb_match_dynamic_id(intf, usb_drv);
		if (id)
			return 1;
	}

	return 0;
}

usb_device_match()函数功能是将挂在usb总线上的设备(usb设备和usb接口)与驱动进行匹配。

static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	struct usb_device *usb_dev;

	if (is_usb_device(dev)) { //是否是usb设备
		usb_dev = to_usb_device(dev);
	} else if (is_usb_interface(dev)) { //是否是接口设备
		struct usb_interface *intf = to_usb_interface(dev);

		usb_dev = interface_to_usbdev(intf);
	} else {
		return 0;
	}

	if (usb_dev->devnum < 0) {
		/* driver is often null here; dev_dbg() would oops */
		pr_debug("usb %s: already deleted?\n", dev_name(dev));
		return -ENODEV;
	}
	if (!usb_dev->bus) {
		pr_debug("usb %s: bus removed?\n", dev_name(dev));
		return -ENODEV;
	}


	/* per-device configurations are common */
	if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
			   le16_to_cpu(usb_dev->descriptor.idVendor),
			   le16_to_cpu(usb_dev->descriptor.idProduct),
			   le16_to_cpu(usb_dev->descriptor.bcdDevice)))
		return -ENOMEM;

	/* class-based driver binding models */
	if (add_uevent_var(env, "TYPE=%d/%d/%d",
			   usb_dev->descriptor.bDeviceClass,
			   usb_dev->descriptor.bDeviceSubClass,
			   usb_dev->descriptor.bDeviceProtocol))
		return -ENOMEM;

	return 0;
}

usb_uevent()函数主要是增加uevent参数,关于uevent 事件机制,可以参考之前写的一篇博客:点击打开链接

2.2 usb总线注册

 bus_register(&usb_bus_type); //usb_bus_type结构体在1中有分析
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;
        //以下usb总线和子系统互相绑定
	priv->bus = bus; //子系统的总线为bus指针,即usb
	bus->p = priv; //bus->p的子系统为priv

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

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //子系统usb的内核对象名称设置为usb,对应目录/sys/bus/usb,其实客观点的话,只要是涉及到内核对象kobject的创建,它就是对应/sys下的一个目录名称
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset; //绑定子系统usb的父对象为bus_kset,通过/sys/bus/usb可以看到usb在bus目录下
	priv->subsys.kobj.ktype = &bus_ktype; //子系统usb内核对象操作
	priv->drivers_autoprobe = 1; //设置为自动匹配,在设备和驱动的匹配过程中会判断该参数,成功将执行probe探测函数

	retval = kset_register(&priv->subsys); //内核集合注册,即创建usb目录,绝对路径/sys/bus/usb,该函数内部涉及的知识点较多,我在原来的一遍博客里有分析,本文不再赘述!
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent); //内核属性创建,在/sys/bus/usb目录下会生产属性文件 drivers_autoprobe、 uevent
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL, 
						 &priv->subsys.kobj); //在子系统usb目录下创建devices,即/sys/bus/usb/devices 
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj); //在子系统usb目录下创建drivers, 即/sys/bus/usb/drivers
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	INIT_LIST_HEAD(&priv->interfaces);
	__mutex_init(&priv->mutex, "subsys mutex", key); //为子系统usb创建互斥锁
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //初始化子系统usb的设备链表,在usb系统下的设备都会加入到该链表
	klist_init(&priv->klist_drivers, NULL, NULL); //初始化子系统usb的驱动链表,在usb子系统下的驱动都会加入到该链表
	retval = add_probe_files(bus); //在子系统usb上增加探测文件,见下分析!
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus); //为该usb子系统增加默认属性文件
	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;
}

内核集合注册kset_register()涉及到的内容太多,可以参照之前写的一篇博客:点击打开链接 4.1.2内核集合注册。

通过宏定义一个bus属性结构体

#define BUS_ATTR(_name, _mode, _show, _store)	\
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe); //定义bus_attr_drivers_probe探测总线属性,同时绑定对应属性的处理接口函数
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
		show_drivers_autoprobe, store_drivers_autoprobe); //定义bus_attr_drivers_autoprobe自动探测属性,同时绑定对应的接口函数

这里将创建两个结构体属性bus_attr_drivers_probe、bus_attr_drivers_autoprobe给下面的add_probe_file();

在add_probe_file()子系统上增加探测文件

static int add_probe_files(struct bus_type *bus)
{
	int retval;

	retval = bus_create_file(bus, &bus_attr_drivers_probe); //在子系统usb上增加属性文件/sys/bus/usb/drivers_probe
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_drivers_autoprobe); //在子系统usb上增加属性文件/sys/bus/usb/drivers_autoprobe
	if (retval)
		bus_remove_file(bus, &bus_attr_drivers_probe);
out:
	return retval;
}

在bus_add_attrs()上增加子系统usb总线的属性

static int bus_add_attrs(struct bus_type *bus)
{
	int error = 0;
	int i;

	if (bus->bus_attrs) {
		for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
			error = bus_create_file(bus, &bus->bus_attrs[i]);
			if (error)
				goto err;
		}
	}
done:
	return error;
err:
	while (--i >= 0)
		bus_remove_file(bus, &bus->bus_attrs[i]);
	goto done;
}

3. usb总线注册后的设备模型

[root@cl /sys/bus/usb]#ls
devices            drivers_autoprobe  uevent
drivers            drivers_probe
uevent是如何添加的,在子系统usb注册里暂未见到,后续在分析!!!


4. usb总线通知链注册

retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
static struct notifier_block usb_bus_nb = {
	.notifier_call = usb_bus_notify, //通知链收到消息时的回调函数,见下分析
};
struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
};
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
		void *data)
{
	struct device *dev = data;

	switch (action) {
	case BUS_NOTIFY_ADD_DEVICE: //增加设备
		if (dev->type == &usb_device_type) //是usb设备类型
			(void) usb_create_sysfs_dev_files(to_usb_device(dev)); //创建descriptors文件,在sysfs中显示几个属性文件
		else if (dev->type == &usb_if_device_type) //是usb接口类型
			usb_create_sysfs_intf_files(to_usb_interface(dev)); //创建usb接口文件
		break;

	case BUS_NOTIFY_DEL_DEVICE: //删除设备
		if (dev->type == &usb_device_type)
			usb_remove_sysfs_dev_files(to_usb_device(dev));
		else if (dev->type == &usb_if_device_type)
			usb_remove_sysfs_intf_files(to_usb_interface(dev));
		break;
	}
	return 0;
}

上面分析完了通知链结构体,接下来分析注册通知链,在分析注册通知链之前,先看下初始化通知链表头:

初始化通知链表头:

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

链表头初始化在bus_register()函数内部,由于bus->p = priv,所以上面初始化的是usb总线通知链;

注册通知链

int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}

删除通知链:

int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
{
	return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb);
}

向通知链发送消息:

blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev);	

当我们增加设备到总线上时,会调用device_add()设备增加函数,该函数内部会调用blocking_notifier_call_chain()通知增加设备。

关于通知链的具体实现细节,详见:点击打开链接

5. usb主设备初始化

retval = usb_major_init();
int usb_major_init(void)
{
	int error;

	error = register_chrdev(USB_MAJOR, "usb", &usb_fops); //注册一个USB_MAJOR=180 的usb主设备,提供一个公共的fops
	if (error)
		printk(KERN_ERR "Unable to get major %d for usb devices\n",
		       USB_MAJOR);

	return error;
}
static inline int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops)
{
	return __register_chrdev(major, 0, 256, name, fops); //分配256个从设备
}

usb_fops结构体:

static const struct file_operations usb_fops = {
	.owner =	THIS_MODULE,
	.open =		usb_open,
	.llseek =	noop_llseek,
};

usb_open():

#define MAX_USB_MINORS	256
static const struct file_operations *usb_minors[MAX_USB_MINORS]; //分配256个file_operations
static DECLARE_RWSEM(minor_rwsem);

static int usb_open(struct inode * inode, struct file * file)
{
	int minor = iminor(inode);
	const struct file_operations *c;
	int err = -ENODEV;
	const struct file_operations *old_fops, *new_fops = NULL;

	down_read(&minor_rwsem);
	c = usb_minors[minor];

	if (!c || !(new_fops = fops_get(c)))
		goto done;

	old_fops = file->f_op; //备份旧的f_op
	file->f_op = new_fops; //赋值新的f_op
	/* Curiouser and curiouser... NULL ->open() as "no device" ? */
	if (file->f_op->open) //判定新的是否有效
		err = file->f_op->open(inode,file);
	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
 done:
	up_read(&minor_rwsem);
	return err;
}
注册一个以USB_MAJOR=180为主设备号的cdev,不过其还分配了256个从设备号,提供一个公共的open函数,对于每一个usb设备的打开都会先调用主设备的open,而后根据不同的设备调用不同的open函数,公共的open就是提供一个对于所有的usb 设备的公共接口,真正的open其实是usb_minors中存储的fops->open。






  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值