平臺總線設備驅動設計

平臺總線概述

平臺總線(Platform bus)是linux2.6內核加入的一種虛擬總線,其優勢在於採用了總線的模型對設備與驅動進行了管理,這樣提高了程序的可移植性。

通過平臺總線機制開發設備驅動的 流程 如圖:
這裏寫圖片描述

平臺設備

平臺設備使用 struct platform_device 結構 來描述:

struct platform_device {
    const char *name; /* 設備名 */
    int id; /* 設備編號,配合設備名使用 */
    struct device dev;
    u32 num_resources;
    struct resource *resource; /* 設備資源 */
}

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags; /* 資源的類型 */
    struct resource *parent, *sibling, *child;
};

註冊平臺設備,使用函數:

int platform_device_register(struct platform_device *pdev)

平臺驅動

平臺驅動使用 struct platform_driver 結構體 描述:

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    //……
}

平臺驅動註冊使用函數:

int platform_driver_register(struct platform_driver *)

代碼範例:

以按鍵中斷驅動程序爲例,平臺總線驅動定義了程序的結構,混雜設備驅動定義了程序的功能。

驅動程序:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h> //平臺設備需要該頭文件

MODULE_LICENSE("GPL");

struct work_struct *work;
struct timer_list buttons_timer;

unsigned int key_num = 0;

wait_queue_head_t  key_q;

struct resource *res;
struct resource *res_irq;
unsigned int *key_base;

void work_func(struct work_struct *work)
{
    mod_timer(&buttons_timer, jiffies + (HZ /10));  
}

void buttons_timer_function(unsigned long data)  
{
    unsigned int key_val;

    key_val = readw(key_base+1)&0x1; 
    if (key_val == 0)
       key_num = 4;

    key_val = readw(key_base+1)&0x4;
    if (key_val == 0)
        key_num = 3;

    wake_up(&key_q); 
} 

irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷

    //2. 清除已經發生的按鍵中斷

    //3. 提交下半部
    schedule_work(work);

    //return 0;
    return IRQ_HANDLED;
}

void key_hw_init()
{
    unsigned short data;

    data = readw(key_base);
    data &= ~0b110011;
    data |= 0b100010;

    writew(data,key_base);
}

int key_open(struct inode *node,struct file *filp)
{
    return 0;   
}

ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{ 
    wait_event(key_q,key_num);      
    copy_to_user(buf, &key_num, 4);   
    key_num = 0;
    return 4;
}

struct file_operations key_fops = 
{
    .open = key_open,
    .read = key_read,   
};

struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "key",
    .fops = &key_fops,  
};

int key_probe(struct platform_device *pdev)
{
    int ret,size;

    ret = misc_register(&key_miscdev);

    if (ret !=0)
        printk("register fail!\n");

    //註冊中斷處理程序  
    res_irq =  platform_get_resource(pdev, IORESOURCE_IRQ, 0);

    request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
    request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);

    //按鍵初始化
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    size = (res->end - res->start) + 1;
    key_base = ioremap(res->start, size);

    key_hw_init();

    /* 創建工作 */
    work = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work, work_func);

    /* 初始化定時器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  

    /* 向內核註冊一個定時器 */  
    add_timer(&buttons_timer);  

    /*初始化等待隊列*/
    init_waitqueue_head(&key_q);

    return 0;
}

int key_remove(struct platform_device *dev)
{
    free_irq(res_irq->start, (void *)4);
    free_irq(res_irq->end, (void *)3);

    iounmap(key_base);
    misc_deregister(&key_miscdev);
    return 0;
}

static struct platform_driver key_driver = {  //定義 平臺設備驅動 結構體
    .probe      = key_probe,
    .remove     = key_remove,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = "my-key",
    },
};

static int button_init()//入口函數
{
    return platform_driver_register(&key_driver);//註冊平臺驅動
}

static void button_exit()//出口函數
{      
    platform_driver_unregister(&key_driver);//註銷平臺驅動
}

module_init(button_init);
module_exit(button_exit);

設備程序:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h> //平臺設備需要該頭文件

MODULE_LICENSE("GPL");

#define GPFCON 0x56000050  //寄存器物理地址

static struct resource key_resource[] = { //定義平臺設備的資源,注意數組的寫法
    [0] = {
        .start = GPFCON,
        .end   = GPFCON + 8,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_EINT0,   //中斷
        .end   = IRQ_EINT2,
        .flags = IORESOURCE_IRQ,
    },
};

struct platform_device key_device = { //定義平臺設備
    .name           =  "my-key",
    .id             =  0,
    .num_resources  =  ARRAY_SIZE(key_resource), //平臺設備資源數目
    .resource       =  key_resource,
};

static int button_init()
{
    platform_device_register(&key_device);  //平臺設備註冊   
    return 0;
}

static void button_exit()
{      
    platform_device_unregister(&key_device);//註銷平臺設備
}

module_init(button_init);
module_exit(button_exit);
發佈了5 篇原創文章 · 獲贊 60 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章