Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2759352
  • 博文数量: 79
  • 博客积分: 30130
  • 博客等级: 大将
  • 技术积分: 2608
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-22 14:58
个人简介

博所搬至http://xiaogr.com

文章存档

2015年(2)

2009年(3)

2008年(56)

2007年(18)

分类: LINUX

2008-07-16 14:22:51

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:http://ericxiao.cublog.cn/
------------------------------------------
一:前言
Serio总线同之前分析的platform总线一样,也是一种虚拟总线。它是Serial I/O的输写,表示串行的输入输出设备.很多输入输出设备都是以此为基础的。同前面几篇笔记一样,下面的代码分析是基于linux kernel 2.6.25版本.
二:serio总线的初始化
Serio总线的初始化是在linux2.6.25/drivers/input/serio/serio.c中完成的。代码如下:

static int __init serio_init(void)

{
     int error;
 
     error = bus_register(&serio_bus);
     if (error) {

         printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error);

         return error;
     }
 

     serio_task = kthread_run(serio_thread, NULL, "kseriod");

     if (IS_ERR(serio_task)) {
         bus_unregister(&serio_bus);
         error = PTR_ERR(serio_task);

         printk(KERN_ERR "serio: Failed to start kseriod, error: %d\n", error);

         return error;
     }
 
     return 0;
}
在这里,创建了对应bus_type为serio_bus的总线。还创建了一个名为“kseriod”的内核线程。我们暂且不管它是做什么的。等以后的代码涉及到再来进行分析。
 
三:serio设备注册
Serio设备对应的数据结构为struct serio.对应的注册接口为:serio_register_port().代码如下:

static inline void serio_register_port(struct serio *serio)

{
     __serio_register_port(serio, THIS_MODULE);
}
它是__serio_register_port()的一个封装函数。代码如下:

void __serio_register_port(struct serio *serio, struct module *owner)

{
     serio_init_port(serio);

     serio_queue_event(serio, owner, SERIO_REGISTER_PORT);

}
它先初始化一个serio设备。在serio_init_port()中,它指定了设备的总线类型为serio_bus.之后,调用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) {

         if (event->object == object) {
              if (event->type == event_type)
                   goto out;
              break;
         }
     }
 

     event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);

     if (!event) {
         printk(KERN_ERR

              "serio: Not enough memory to queue event %d\n",

              event_type);
         retval = -ENOMEM;
         goto out;
     }
 
     if (!try_module_get(owner)) {
         printk(KERN_WARNING

              "serio: 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;
 

     list_add_tail(&event->node, &serio_event_list);

     wake_up(&serio_wait);
 
out:

     spin_unlock_irqrestore(&serio_event_lock, flags);

     return retval;
}
这个函数比较简单。就是根据参数信息生成了一个struct serio_event结构。再将此结构链接至serio_event_list末尾。
接着就要来寻找serio_event_list这个链表的处理了。这个就是serio_thread的工作了。还记得初始化的时候所创建的线程么。不错,就是它。Kernel可能认为对serio的操作比较频繁。所以对一些操作事件化,将它以多线程处理。
serio_thread()代码如下:

static int serio_thread(void *nothing)

{
     set_freezable();
     do {
         serio_handle_event();
         wait_event_freezable(serio_wait,

              kthread_should_stop() || !list_empty(&serio_event_list));

     } while (!kthread_should_stop());
 

     printk(KERN_DEBUG "serio: kseriod exiting\n");

     return 0;
}
这个函数的核心处理是serio_handle_event().代码如下:

static void serio_handle_event(void)

{
     struct serio_event *event;
 
     mutex_lock(&serio_mutex);
 
     /*

      * Note that we handle only one event here to give swsusp

      * a chance to freeze kseriod thread. Serio events should

      * be pretty rare so we are not concerned about taking

      * performance hit.

      */
     if ((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_ATTACH_DRIVER:
                   serio_attach_driver(event->object);
                   break;
 
              default:
                   break;
         }
 
         serio_remove_duplicate_events(event);
         serio_free_event(event);
     }
 
     mutex_unlock(&serio_mutex);
}
这个函数的流程大致是这样的:
调用serio_get_event()从链表中取出struct serio_event元素,然后对这个元素的事件类型做不同的时候,处理完了之后,调用serio_remove_duplicate_events()在链表中删除相同请求的event.
对应之前的serio_register_port()函数,它产生的事件类型是SERIO_REGISTER_PORT.也就是说,对于注册serio设备来说,流程会转入serio_add_port().
代码如下:

static void serio_add_port(struct serio *serio)

{
     int error;
 
     if (serio->parent) {
         serio_pause_rx(serio->parent);
         serio->parent->child = serio;
         serio_continue_rx(serio->parent);
     }
 

     list_add_tail(&serio->node, &serio_list);

     if (serio->start)
         serio->start(serio);
     error = device_add(&serio->dev);
     if (error)
         printk(KERN_ERR

              "serio: device_add() failed for %s (%s), error: %d\n",

              serio->phys, serio->name, error);

     else {
         serio->registered = 1;

         error = sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group);

         if (error)
              printk(KERN_ERR

                   "serio: sysfs_create_group() failed for %s (%s), error: %d\n",

                   serio->phys, serio->name, error);

     }
}
我们终于看到serio device注册的庐山真面目了。它会调用设备的start()函数,然后调用device_add()将设备注册到总线上。
同platform总线一样,这里的serio device注册的时候也会产生一个hotplug事件,对应就会调用总线的uenvent函数,在serio_bus的event接口为:

static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)

{
     struct serio *serio;
 
     if (!dev)
         return -ENODEV;
 
     serio = to_serio_port(dev);
 

     SERIO_ADD_UEVENT_VAR("SERIO_TYPE=%02x", serio->id.type);

     SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto);

     SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id);

     SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);

     SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",

                   serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);

 
     return 0;
}
可见。会在hotplug的环境变量中添加几项值。记得我们之前分析设备驱动模型的时候,在总线下面的设备都有一个属性文件,这个文件的内容就是对应bus和kset所添加的环境变量。在/sys文件系统中就可以看到这此环境变量。做个测试:

[root@localhost serio0]# cat /sys/bus/serio/devices/serio0/uevent

DRIVER=atkbd
PHYSDEVBUS=serio
PHYSDEVDRIVER=atkbd
SERIO_TYPE=06
SERIO_PROTO=00
SERIO_ID=00
SERIO_EXTRA=00
MODALIAS=serio:ty06pr00id00ex00
 
四:serio driver的注册
对应serio driver注册的接口噗serio_register_driver()。代码如下:

static inline int serio_register_driver(struct serio_driver *drv)

{

     return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);

}
它是__serio_register_driver()的封装函数,代码如下:

int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)

{
     int manual_bind = drv->manual_bind;
     int error;
 
     drv->driver.bus = &serio_bus;
     drv->driver.owner = owner;
     drv->driver.mod_name = mod_name;
 
     /*

      * Temporarily disable automatic binding because probing

      * takes long time and we are better off doing it in kseriod

      */
     drv->manual_bind = 1;
 

     error = driver_register(&drv->driver);

     if (error) {
         printk(KERN_ERR

              "serio: driver_register() failed for %s, error: %d\n",

              drv->driver.name, error);
         return error;
     }
 
     /*

      * Restore original bind mode and let kseriod bind the

      * driver to free ports

      */
     if (!manual_bind) {
         drv->manual_bind = 0;

         error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);

         if (error) {
              driver_unregister(&drv->driver);
              return error;
         }
     }
 
     return 0;
}
上面这段代码比较简单,在注册驱动的时候,将驱动的总线指定为serio_bus.然后调用driver_register()将驱动注册到总线。如果drv->manual_bind不为1。还会产生一个SERIO_ATTACH_DRIVER事件。drv->manual_bind成员的含义应该是要手动进行驱动与设备的绑定。
在注册驱动的时候,会产生一次驱动与设备的匹配过程。这过程会调用bus->mach.busrobe.看下serio总线是怎么样处理的.
Serio_bus.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_match_port()进行更细致的判断。代码如下:

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 device信息与serio driver的id_table中的信息匹配的时候,才会将设备和驱动绑定起来。
 
Serio_bus.probe的接口函数如下示:

static int serio_driver_probe(struct device *dev)

{
     struct serio *serio = to_serio_port(dev);

     struct serio_driver *drv = to_serio_driver(dev->driver);

 
     return serio_connect_driver(serio, drv);
}

static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)

{
     int retval;
 
     mutex_lock(&serio->drv_mutex);
     retval = drv->connect(serio, drv);
     mutex_unlock(&serio->drv_mutex);
 
     return retval;
}
即会调用设备驱动的connect()函数。
 
在注册serio driver的时候,还会产生SERIO_ATTACH_DRIVER事件。这个事件的处理是在serio_handle_event()中完成的。相应的处理接口为:

static void serio_attach_driver(struct serio_driver *drv)

{
     int error;
 
     error = driver_attach(&drv->driver);
     if (error)
         printk(KERN_WARNING

              "serio: driver_attach() failed for %s with error %d\n",

              drv->driver.name, error);
}
可以看出。这里也是执行一次设备与驱动的匹配过程。
Why! 为什么要这样做呢? 这次事件的动作实现上在driver_register()中已经做了。
 
五:serio 的中断处理函数分析
serio_interrupt()在serio bus构造的驱动也是一个常用的接口,这个接口用来处理serio 设备的中断。我们来看下它的代码:

irqreturn_t serio_interrupt(struct serio *serio,

                   unsigned char data, unsigned int dfl)

{
         unsigned long flags;
         irqreturn_t ret = IRQ_NONE;
 

         spin_lock_irqsave(&serio->lock, flags);

 
        if (likely(serio->drv)) {

                ret = serio->drv->interrupt(serio, data, dfl);

         } else if (!dfl && serio->registered) {

                   serio_rescan(serio);
                   ret = IRQ_HANDLED;
         }
 

         spin_unlock_irqrestore(&serio->lock, flags);

 
         return ret;
}
首先,先判断当前设备是否已经关联到了驱动程序。如果已经被关联了,那么调用驱动的中断处理函数。如果没有。就会转入serio_rescan()中执行。该函数代码如下:

void serio_rescan(struct serio *serio)

{

         serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);

}
由此可见。它是执行了一个SERIO_RESCAN_PORT动作。同样,这个动作是在serio_handle_event()中处理的。相应的处理接口为:

static void serio_add_port(struct serio *serio)

{
         int error;
 
         if (serio->parent) {
                   serio_pause_rx(serio->parent);

                   serio->parent->child = serio;

                   serio_continue_rx(serio->parent);
         }
 

         list_add_tail(&serio->node, &serio_list);

         if (serio->start)
                   serio->start(serio);
         error = device_add(&serio->dev);
         if (error)
                   printk(KERN_ERR

                            "serio: device_add() failed for %s (%s), error: %d\n",

                            serio->phys, serio->name, error);

         else {
                   serio->registered = 1;

                   error = sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group);

                   if (error)
                            printk(KERN_ERR

                                     "serio: sysfs_create_group() failed for %s (%s), error: %d\n",

                                     serio->phys, serio->name, error);

         }
}
这样,相当于注册一个设备,注册时会产生设备与驱动的匹配事件。如果有合适的驱动,就会将之与设备关联起来。
 
六:小结
来小结一下,在serio总线中,总册一个serio设备时,除了将其注册到所属的serio_bus上。还会调用设备的start()函数。在中断处理的时候,如果serio 设备已经关联到驱动,则调用驱动的interrupt函数。如果没有关联,则将其注册到总线。
总的说来,erio 总线的代码很简单。到这里。我们已经分析过platform,serio两种虚拟总线,应该结合这两个虚拟总线的例子好好体会一下结构封装的技巧。

阅读(7512) | 评论(6) | 转发(4) |
给主人留下些什么吧!~~

chinaunix网友2008-09-07 03:20:55

最后对SERIO_RESCAN_PORT的分析错误了哈,在serio_handle_event()对应的处理动作不是serio_add_port

chinaunix网友2008-09-07 03:20:55

最后对SERIO_RESCAN_PORT的分析错误了哈,在serio_handle_event()对应的处理动作不是serio_add_port

chinaunix网友2008-09-07 03:20:55

最后对SERIO_RESCAN_PORT的分析错误了哈,在serio_handle_event()对应的处理动作不是serio_add_port

chinaunix网友2008-09-07 02:54:22

Why! 为什么要这样做呢? 这次事件的动作实现上在driver_register()中已经做了。 ============================================= 代码里是二选一,有判断条件的。

chinaunix网友2008-09-07 02:54:22

Why! 为什么要这样做呢? 这次事件的动作实现上在driver_register()中已经做了。 ============================================= 代码里是二选一,有判断条件的。