linux驅動開發:平臺設備驅動框架

有一定的mcu51基礎或者其他類似芯片基礎的人大概都聽說過IIC,SPI,IIS,USB,PCIe,CAN等總線。這些都是實實在在存在的總線。上面可以掛接遵循其協議的一個或多個設備。
linux內核自己虛擬地構建了一個稱作platform的平臺總線,對應的可以在其下面掛載平臺設備和平臺驅動。這是一條虛擬的總線。
緊接上上一篇的input子系統,我們將上一個例子做成掛載在平臺總線下的設備和驅動,以此來簡單的瞭解平臺設備驅動框架的實現原理和步驟。

頭文件:

#define _HIGH               1
#define _LOW                0

#define _ZERO                       0


enum button_event 
{
    _EV_UP,
    _EV_DOWN,
};


struct gpio_buttons
{
    int gpio;  // gpio num
    int code;  // key code
    char *desc;//gpio description
};

平臺設備文件:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/gpio.h>



#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/bitops.h>

#include "gpio_buttons.h"



void gpio_buttons_release(struct device * dev);

/*******    void        *platform_data; Platform specific data, device    ****/
static struct gpio_buttons gpio_buttons_data[] =
{
        {
            .gpio =S5PV210_GPH2(0),
            .code =KEY_LEFT,
            .desc ="LEFT",
        },
        {
            .gpio =S5PV210_GPH2(1),
            .code =KEY_RIGHT,
            .desc ="RIGHT",
        },
        {
            .gpio =S5PV210_GPH2(2),
            .code =KEY_UP,
            .desc ="UP",
        },
        {
            .gpio =S5PV210_GPH2(3),
            .code =KEY_DOWN,
            .desc ="DOWN",
        },

        {
            .gpio =S5PV210_GPH3(0),
            .code =KEY_MENU,
            .desc ="MENU",
        },
        {
            .gpio =S5PV210_GPH3(1),
            .code =KEY_ENTER,
            .desc ="ENTER",
        },
        {
            .gpio =S5PV210_GPH3(2),
            .code =KEY_BACKSPACE,
            .desc ="BACKSPACE",
        },
        {
            .gpio =S5PV210_GPH3(3),
            .code =KEY_DELETE,
            .desc ="DELETE",
        },
};



/*******    struct resource *resource;  *****/

static struct resource butttons_resource[] =
{
    {
        .start  =IRQ_EINT(16),
        .end    =IRQ_EINT(16),
        .flags  =IORESOURCE_IRQ,//resource type
    },
    {
        .start  =IRQ_EINT(17),
        .end    =IRQ_EINT(17),
        .flags  =IORESOURCE_IRQ,//resource type
    },
    {
        .start  =IRQ_EINT(18),
        .end    =IRQ_EINT(18),
        .flags  =IORESOURCE_IRQ,//resource type
    },
    {
        .start  =IRQ_EINT(19),
        .end    =IRQ_EINT(19),
        .flags  =IORESOURCE_IRQ,//resource type
    },

    {
        .start  =IRQ_EINT(24),
        .end    =IRQ_EINT(24),
        .flags  =IORESOURCE_IRQ,//resource type
    },
    {
        .start  =IRQ_EINT(25),
        .end    =IRQ_EINT(25),
        .flags  =IORESOURCE_IRQ,//resource type
    },
    {
        .start  =IRQ_EINT(26),
        .end    =IRQ_EINT(26),
        .flags  =IORESOURCE_IRQ,//resource type
    },
    {
        .start  =IRQ_EINT(27),
        .end    =IRQ_EINT(27),
        .flags  =IORESOURCE_IRQ,//resource type
    },
};
static struct platform_device buttons_dev =
{
    .name ="p_buttons",
    .resource =butttons_resource,
    .num_resources =ARRAY_SIZE(butttons_resource),
    .dev =
    {
        .platform_data =gpio_buttons_data,
        .release =gpio_buttons_release,
    },

};

/*******func release: void  (*release)(struct device *dev)*****/

void gpio_buttons_release(struct device * dev)
{

    printk("enter gpio_buttons_release.\n");
}

int __init buttons_dev_init(void)
{

    platform_device_register(&buttons_dev);

    return 0;
}

void __exit buttons_dev_exit(void)
{

    platform_device_unregister(&buttons_dev);
}







module_init(buttons_dev_init);
module_exit(buttons_dev_exit);

MODULE_LICENSE("GPL");



平臺驅動:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/gpio.h>



#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/bitops.h>

#include "gpio_buttons.h"


/*****irq use****/
volatile static struct gpio_buttons *g_buttons_desc_info = NULL;
/*****input device use****/
static struct input_dev *buttons_input_dev =NULL;

/*****timer use****/
static struct timer_list buttons_timer;

/******button irq******/

static irqreturn_t smart210_buttons_irq(int irq, void* dev_id)
{
    //get whitch key trigger the irq
    g_buttons_desc_info =(volatile struct gpio_buttons *)dev_id;

    //Eliminate key jitter,delay 10 ms to get key value
    mod_timer(&buttons_timer,jiffies + HZ/100);

    return IRQ_HANDLED;
}
/******timer del func******/
static void buttons_timer_func(unsigned long data)
{
    int uc_keysta =0;

    struct gpio_buttons *uc_buttons_desc =(struct gpio_buttons *)g_buttons_desc_info;

    if(!uc_buttons_desc)
    {
        return;
    }

    //get the pin level :high /low
    //pree up 0,pree down 1
    if(gpio_get_value(uc_buttons_desc->gpio) == _HIGH)
    {
        uc_keysta =_EV_UP;
    }
    else
    {
        uc_keysta =_EV_DOWN;
    }

    input_event(buttons_input_dev,EV_KEY, uc_buttons_desc->code,uc_keysta);
    input_event(buttons_input_dev,EV_SYN, SYN_REPORT,_ZERO);


}



/*******int (*probe)(struct platform_device *) *******/
static int buttons_probe(struct platform_device *pdev)
{
    int i=0;
    int ret =0;
    struct resource *res =NULL;
    struct gpio_buttons *button_data = pdev->dev.platform_data;

    //1.alloc input dev device
    buttons_input_dev = input_allocate_device();
    if (!buttons_input_dev) 
    {
        ret = -ENOMEM;
        printk("1.error allocate input device!\n");
        goto err_allocate_input_dev;
    }
    //2.set up the input dev param,Report event
    //EV_KEY :key event
    //EV_SYN: sync event
    set_bit(EV_KEY,buttons_input_dev->evbit);
    set_bit(EV_SYN,buttons_input_dev->evbit);
    set_bit(EV_REP,buttons_input_dev->evbit);
    //set EV_KEY key code
    for(i=0; i < pdev->num_resources;i++)
    {
        set_bit(button_data[i].code,buttons_input_dev->keybit);
    }
    //set input dev name
    buttons_input_dev->name = "buttons";

    //3.register device
    ret = input_register_device(buttons_input_dev);
    if(ret) 
    {
        printk("2.error to register input device!\n");
        goto err_register_input_dev;
    }


/******special func user add for different input device*********/

    //4.irq request
    for(i=0; i < pdev->num_resources;i++)
    {
        res =platform_get_resource(pdev,IORESOURCE_IRQ,i);
        ret =request_irq(res->start,smart210_buttons_irq,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                        button_data[i].desc,&(button_data[i]));
        if(ret)
        {
            break;
        }
    }
    if(ret) 
    {
        i--;
        for(; i >0;i--)
        {
            if(!button_data[i].gpio)
            {
                continue;
            }
            res =platform_get_resource(pdev,IORESOURCE_IRQ,i);
            disable_irq(res->start);
            free_irq(res->start,&(button_data[i]));

        }

        printk("3.error to request irq !\n");
        goto err_request_irq;
    }
    //5.timer initial
    init_timer(&buttons_timer);
    buttons_timer.function =buttons_timer_func;
    add_timer(&buttons_timer);


    printk("4.success driver probe func is ok !\n");
    return 0;
err_request_irq:
    input_unregister_device(buttons_input_dev);
err_register_input_dev:
err_allocate_input_dev:
    input_free_device(buttons_input_dev);
    return ret;

    return 0;
}

/*******int (*remove)(struct platform_device *)******/

static int buttons_remove(struct platform_device *pdev)
{
    int i;
    struct resource *res =NULL;
    struct gpio_buttons *button_data = pdev->dev.platform_data;

    del_timer(&buttons_timer);

    for(i=0; i < pdev->num_resources;i++)
    {
        res =platform_get_resource(pdev,IORESOURCE_IRQ,i);
        disable_irq(res->start);
        free_irq(res->start,&(button_data[i]));
    }

    input_unregister_device(buttons_input_dev);

    input_free_device(buttons_input_dev);

    printk("5.success driver remove func is ok !\n");
    return 0;
}

static struct platform_driver buttons_drv =
{
    .probe  = buttons_probe,
    .remove = buttons_remove,
    .driver =
    {
        .name = "p_buttons",
    },

};
int __init buttons_drv_init(void)
{
    platform_driver_register(&buttons_drv);
    return 0;
}

void __exit buttons_drv_exit(void)
{
    platform_driver_unregister(&buttons_drv);
}



module_init(buttons_drv_init);
module_exit(buttons_drv_exit);
MODULE_LICENSE("GPL");

這裏寫圖片描述
1.當驅動,設備沒有問題時,會在sys/bus/platform下面的devices和drivers目錄下找到我們創建的對應的設備和驅動文件。我們的設備和驅動都叫做p_buttons。
這裏寫圖片描述
2.安裝模塊成功後,如果驅動寫的沒有問題,最後會調用驅動的probe函數.
這裏寫圖片描述
3.調用測試程序,測試驅動是否動作正常.
這裏寫圖片描述
4.測試模塊的安裝,卸載動作是否正常。

整個例子就是這樣,下一篇將會大概介紹下整個流程和原理

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