什么是总线

           总线是处理器与一个或者多个设备之间的通道。在设备模型中所有的设备都是通过总线相连的。甚至那些内部的虚拟"平台"总线。总线可以互相插入,比如一个 USB 控制器通常是一个 PCI 设备。

           linux系统中包含的总线有 i2c、ide、pci、pci express、platform、 pnp、 scsi、 serio、 usb等。

什么是Serio总线

Serio总线是一种虚拟总线。它是Serial I/O的缩写,表示串行的输入输出设备.很多输入输出设备都是以此为基础的。

Serio 源码位于 \drivers\input\serio目录下.

一  serio 相关结构体

struct serio {
     void *port_data;

     char name[32];
     char phys[32];

     bool manual_bind;

     struct serio_device_id id;

     spinlock_t lock;        /* protects critical sections from port's interrupt handler */

     int (*write)(struct serio *, unsigned char);
     int (*open)(struct serio *);
     void (*close)(struct serio *);
     int (*start)(struct serio *);
     void (*stop)(struct serio *);

     struct serio *parent;
     struct list_head child_node;    /* Entry in parent->children list */
     struct list_head children;
     unsigned int depth;        /* level of nesting in serio hierarchy */

     struct serio_driver *drv;    /* accessed from interrupt, must be protected by serio->lock and serio->sem */
     struct mutex drv_mutex;        /* protects serio->drv so attributes can pin driver */

     struct device dev;

     struct list_head node;
 };




struct serio 结构体是对Serio设备的描述,设备注册接口为:serio_register_port().
struct serio_driver {
    const char *description;

    const struct serio_device_id *id_table;
    bool manual_bind;

    void (*write_wakeup)(struct serio *);
    irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int);
    int  (*connect)(struct serio *, struct serio_driver *drv);
    int  (*reconnect)(struct serio *);
    void (*disconnect)(struct serio *);
    void (*cleanup)(struct serio *);

    struct device_driver driver;
};
serio driver 注册的接口:serio_register_driver()



二。serio设备注册


static inline void serio_register_port(struct serio *serio)  // include\linux\Serio.h
 {
     __serio_register_port(serio, THIS_MODULE);
 }
void  __serio_register_port(struct serio *serio, struct module *owner)  // drivers\input\serio
 {
     serio_init_port(serio);
     serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
 }

它先初始化一个serio设备。在serio_init_port()中,它指定了设备的总线类型为serio_bus。再调用serio_queue_event().看这个函数生成了一个struct serio_event结构。再将此结构链接至serio_event_list末尾。


static void serio_init_port(struct serio *serio)
{
       static atomic_t serio_no = ATOMIC_INIT(0);

       __module_get(THIS_MODULE);
 
       INIT_LIST_HEAD(&serio->node);
       spin_lock_init(&serio->lock);
       mutex_init(&serio->drv_mutex);
       device_initialize(&serio->dev);
       dev_set_name(&serio->dev, "serio%ld",
                     (long)atomic_inc_return(&serio_no) - 1);
       serio->dev.bus = &serio_bus;
       serio->dev.release = serio_release_port;
       if (serio->parent) {
              serio->dev.parent = &serio->parent->dev;
              serio->depth = serio->parent->depth + 1;
       } else
              serio->depth = 0;
       lockdep_set_subclass(&serio->lock, serio->depth);
 
}




这是对serio 初始化,初始化端口指明总线类型是serio_bus,对depth的初始化,sysfs将会在sys/devices 下根据depth创建。


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);

     /*


      扫描事件列表Serio 端口为相同端口的其它事件,

      最新的一个开始。如果事件是相同的,我们不需要添加新的。

     如果不同类型的事件,我们需要添加此事件,

不同的事件队列。

*/
     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;
 }


  serio event 事件处理

serio_task = kthread_run(serio_thread, NULL, "kseriod"); // serio_init 函数中创建 serio_thread 内核线程用于处理serio_event。


static int  serio_thread(void *nothing)
{
       do {
              serio_handle_event();
        /*挂起内核线程等条件满足。也就是serio_event_list链表非空*/
              wait_event_interruptible(serio_wait,
                     kthread_should_stop() || !list_empty(&serio_event_list));
              try_to_freeze();
       } while (!kthread_should_stop());
 
       printk(KERN_DEBUG "serio: kseriod exiting\n");
       return 0;
}




serio_handle_event();  // 根据event->type 不同的处理。


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);
}




1.serio_add_port  :修改串口parent设备的参数,把串口设备加入链表,通过 device_add 遍历serio总线的驱动,通过 serio_bus_match 函数找到一个合适的驱动,然后调用probe函数。

2.serio_reconnect_port重新连接Serio端口(重新初始化连接的设备)。如果重新连接失败(旧设备不再连接或没有设备开始),我们希望找到一个端口的驱动程序重新扫描。

3.serio_disconnect_por:serio_disconnect_port()解除端口的驱动程序。作为一个副作用所有子端口绑定将被破坏。

    serio_find_driver :

4.serio_reconnect_subtree( 重新初始化连接的附属设备)。----这是linux 3.0.9的代码,2.6的是 serio_reconnect_chain函数。

5.serio_attach_driver:绑定设备到驱动程序。


三。serio驱动注册

serio driver注册的接口serio_register_driver()。


static inline  int __must_check serio_register_driver(struct serio_driver *drv)
 {
     return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
 }
int__serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
 {
     bool manual_bind = drv->manual_bind;
     int error;

     drv->driver.bus = &serio_bus;  // 指定为serio_bus
     drv->driver.owner = owner;
     drv->driver.mod_name = mod_name;

     /*
       暂时禁用自动绑定,因为探测需要很长时间,我们最好在kseriod线程
      */
     drv->manual_bind = true;

     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;
     }

     /*
     恢复原来的绑定模式,让kseriod线程绑定的驱动程序释放端口
      */
     if (!manual_bind) {
         drv->manual_bind = false;
         error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
         if (error) {
             driver_unregister(&drv->driver);
             return error;
         }
     }

     return 0;
 }

   在注册驱动的时候,会产生一次驱动与设备的匹配过程。这过程会调用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;


 


//只有serio device信息与serio driver的id_table中的信息匹配的时候,才会将设备和驱动绑定起来。


}


serio 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);
 
 //调用设备驱动的connect()函数。
 
     mutex_unlock(&serio->drv_mutex);
 
 
 
     return retval;
 
}



驱动的connect 函数自己具体实现的。



serio_interrupt()函数分析

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 && device_is_registered(&serio->dev)) {
        serio_rescan(serio);
        ret = IRQ_HANDLED;
    }

    spin_unlock_irqrestore(&serio->lock, flags);

    return ret;
}



驱动实现函数就可以了

struct serio_driver {
   
     void (*write_wakeup)(struct serio *);
     irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int);
     int  (*connect)(struct serio *, struct serio_driver *drv);
     int  (*reconnect)(struct serio *);
     void (*disconnect)(struct serio *);
     void (*cleanup)(struct serio *);
 }