serio总线------向serio总线注册设备

  从第5章的input 驱动的分析中,我们可以了解到驱动可以分成几个层次,驱动之间可以嵌套。和这种类型相似,总线也可以分为几个层次,一种类型的总线可以架构在另一种总线之上。

  第6章platform总线驱动提供probe函数中,调用serio_register_port函数,引出总线嵌套的概念 以及在内核中占据极为重要的总线适配的概念。


7.1 总线适配器

  PCI总线 是连接CPU和外部设备的标准总线。声卡网卡显卡、SCSI卡等设备很多都是用PCI卡的形式出现,擦入计算机的PCI插槽。这些设备中,声卡显卡加载后可以直接读写操作。SCSI卡本身连接SCSI硬盘设备,因此加载SCSI的PCI驱动后,必须进行SCSI总线扫描,发现SCSI硬盘设备,才能正确的读写硬盘。 SCSI卡就担当了总线桥的任务,提供了总线之间的协议转换 和 互操作。 像SCSI卡这样的设备,称为 主机总线适配器HBA,一方面是PCI设备,另一方面管理SCSI总线的设备。


7.2 向serio总线注册设备

  6章中,注册到platform总线的设备和驱动匹配后,驱动本身会探测端口并注册到serio总线。serio_register_port函数就执行这个注册操作,serio总线建筑在platform总线之上,分工合作,共同完成完整的驱动功能。

  从总线架构来看,serio总线这种总线嵌套使用模式类似于总线适配器的模式,虽然从物理上来说,物理上存在总线适配器和serio存在不同之处。

7.2.1 注册端口登记事件

  接续第6章分析,serio_register_port函数的作用是注册serio 总线,代码:

static inline void serio_register_port(struct serio *serio)
{
	__serio_register_port(serio, THIS_MODULE);
}

/*
 * Submits register request to kseriod for subsequent execution.
 * Note that port registration is always asynchronous.
 */
void __serio_register_port(struct serio *serio, struct module *owner)
{
	serio_init_port(serio);  //---初始化一个serio结构
	//注册一个 SERIO_REGISTER_PORT事件
	serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
}

  serio_register_port函数的输入参数 serio 设置了端口类型是SERIO_8042,说明是8042兼容型的(I8042是Intel开发的键盘控制芯片)。

  serio_register_port 函数封装了 __serio_register_port函数, 后者初始化一个serio 结构,设置总线类型为serio 总线,然后调用 serio_queue_event 函数向系统注册一个端口登记事件。

  serio_queue_event 函数作用是登记端口,代码:

static int serio_queue_event(void *object, struct module *owner,
			     enum serio_event_type event_type)
{
	unsigned long flags;
	struct serio_event *event;
	int retval = 0;

	spin_lock_irqsave(&serio_event_lock, flags);

	/*
	 * Scan event list for the other events for the same serio port,
	 * starting with the most recent one. If event is the same we
	 * do not need add new one. If event is of different type we
	 * need to add this event and should not look further because
	 * we need to preseve sequence of distinct events.
	 */
	list_for_each_entry_reverse(event, &serio_event_list, node) {
		//如果发现相同的event,退出
		if (event->object == object) {
			if (event->type == event_type)
				goto out;
			break;
		}
	}

	event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
	if (!event) {
		pr_err("Not enough memory to queue event %d\n", event_type);
		retval = -ENOMEM;
		goto out;
	}

	if (!try_module_get(owner)) {
		pr_warning("Can't get module reference, dropping event %d\n",
			   event_type);
		kfree(event);
		retval = -EINVAL;
		goto out;
	}

	event->type = event_type;
	event->object = object;
	event->owner = owner;

	//event事件 添加到链表尾, 并 唤醒线程
	list_add_tail(&event->node, &serio_event_list);
	queue_work(system_long_wq, &serio_event_work);

out:
	spin_unlock_irqrestore(&serio_event_lock, flags);
	return retval;
}

  serio_queue_event 函数:

  首先 遍历内核的serio_event_list链表,检查所有注册的事件,如果发现相同类型的事件,直接退出。说明同一个端口只能只能注册一次,如果重复登记,把它们合并成一次。

  然后 创建一个serio_event结构,设置这个serio_event结构的类型为端口注册,唤醒处理这个事件的任务。


	list_add_tail(&event->node, &serio_event_list);
	queue_work(system_long_wq, &serio_event_work);

static DECLARE_WORK(serio_event_work, serio_handle_event);

static void serio_handle_event(struct work_struct *work)
{
	struct serio_event *event;

	mutex_lock(&serio_mutex);

	while ((event = serio_get_event())) {

		switch (event->type) {

		case SERIO_REGISTER_PORT:
			serio_add_port(event->object);
			break;

		case SERIO_RECONNECT_PORT:
			serio_reconnect_port(event->object);
			break;

		case SERIO_RESCAN_PORT:
			serio_disconnect_port(event->object);
			serio_find_driver(event->object);
			break;

		case SERIO_RECONNECT_SUBTREE:
			serio_reconnect_subtree(event->object);
			break;

		case SERIO_ATTACH_DRIVER:
			serio_attach_driver(event->object);
			break;
		}

		serio_remove_duplicate_events(event->object, event->type);
		serio_free_event(event);
	}

	mutex_unlock(&serio_mutex);
}



  serio_handle_event函数处理各种事件,端口的注册和撤销、重新扫描端口等。对于SERIO_REGISTER_PORT事件,通过serio_add_port处理,代码如下:

/*
 * Complete serio port registration.
 * Driver core will attempt to find appropriate driver for the port.
 */
static void serio_add_port(struct serio *serio)
{
	struct serio *parent = serio->parent;
	int error;

	if (parent) {
		//修改端口父设备的参数
		serio_pause_rx(parent);
		list_add_tail(&serio->child_node, &parent->children);
		serio_continue_rx(parent);
	}
	//把串口设备参数加入全局链表
	list_add_tail(&serio->node, &serio_list);

	if (serio->start)
		serio->start(serio);

	error = device_add(&serio->dev);
	if (error)
		dev_err(&serio->dev,
			"device_add() failed for %s (%s), error: %d\n",
			serio->phys, serio->name, error);
}
  serio_add_port函数调用serio 结构的 start 函数,因为在端口q40kbd注册端口的时候并没有设置start函数,所以此次并不执行


7.2.2 遍历总线的驱动

  serio_add_port 函数的关键部分是device_add函数,在第6章platform总线的分析中,已经分析过,作用是遍历总线的驱动,通过总线提供match函数找到一个合适的驱动,调用总线的probe函数。

/**
 * device_add - add device to device hierarchy.
 * @dev: device.
 *
 * This is part 2 of device_register(), though may be called
 * separately _iff_ device_initialize() has been called separately.
 *
 * This adds @dev to the kobject hierarchy via kobject_add(), adds it
 * to the global and sibling lists for the device, then
 * adds it to the other relevant subsystems of the driver model.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up your
 * reference instead.
 */
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	setup_parent(dev, parent);

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &devt_attr);
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);
	}

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysf_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->class_mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->class_interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->class_mutex);
	}
done:
	put_device(dev);
	return error;
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		devtmpfs_delete_node(dev);
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &devt_attr);
 ueventattrError:
	device_remove_file(dev, &uevent_attr);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	if (parent)
		put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

  我们首先分析serio总线的match函数,在分析probe函数;

1、match函数

static int serio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct serio *serio = to_serio_port(dev);
	struct serio_driver *serio_drv = to_serio_driver(drv);

	if (serio->manual_bind || serio_drv->manual_bind)
		return 0;

	return serio_match_port(serio_drv->id_table, serio);
}

  函数在设备注册时多次调用,输入参数是serio总线上注册的每一个驱动,需要逐个检查端口设备的serio和驱动的匹配情况。如果设备或者驱动设置了手动绑定直接返回,否则调用serio_match_port函数检查设备和驱动的ID,代码如下:

static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
{
	while (ids->type || ids->proto) {
		if ((ids->type == SERIO_ANY || ids->type == serio->id.type) &&
		    (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) &&
		    (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) &&
		    (ids->id == SERIO_ANY || ids->id == serio->id.id))
			return 1;
		ids++;
	}
	return 0;
}

  serio_match_port函数,检查设备和驱动的ID表的type、proto等参数是否相同。登记设备的时候,赋予的type是SERIO_8042,搜索内核代码,和其匹配的驱动就是目录drivers/input/keyboard 下的 atkbd.c 文件


2、probe函数
  现在返回device_add函数,设备和驱动匹配之后,首先调用serio总线提供probe函数,也就是serio_driver_probe函数,代码如下:


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值