Linux設備驅動之——serio總線

什麼是總線

           總線是處理器與一個或者多個設備之間的通道。在設備模型中所有的設備都是通過總線相連的。甚至那些內部的虛擬"平臺"總線。總線可以互相插入,比如一個 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 端口爲相同端口其它事件

      最新的一個開始如果事件是相同的我們不需要添加新的

     如果不同類型事件,我們需要添加此事件

      因爲我們需要preseve不同的事件隊列
     */

    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;
 

     return serio_match_port(serio_drv->id_table, serio); //只有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);
     retval = drv->connect(serio, drv);   //調用設備驅動的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 *);

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章