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

来源:互联网 发布:哈基姆.奥拉朱旺数据 编辑:程序博客网 时间:2024/06/03 18:59

  从第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函数,代码如下:


















原创粉丝点击