天天看點

基于平台總線的按鍵裝置驅動

平台總線

将裝置和驅動分離開來,便于移植,提供裝置與驅動的比對。

裝置子產品的程式

/*Copyright (c) 2018 Caokaipeng,All rights reserved.*/

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

//定義key的控制寄存器
#define GPNCON 0x7f008830

//定義資源
struct resource key_resource[] = {
    [] = { //基位址->控制寄存器和資料寄存器
        .start = GPNCON,
        .end = GPNCON+,
        .flags = IORESOURCE_MEM,
    },
    [] = { //中斷号
        .start = IRQ_EINT(),
        .end = IRQ_EINT(),
        .flags = IORESOURCE_IRQ,
    },
};

//定義一個平台裝置
struct platform_device key_dev ={
    .name = "my_key",
    .id = ,
    .num_resources = ,
    .resource = key_resource,
};

int keydev_init()
{
    //平台裝置的注冊
    platform_device_register(&key_dev);
}

void keydev_exit()
{
    //平台裝置的登出
    platform_device_unregister(&key_dev);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CaoKaipeng");
MODULE_DESCRIPTION("Platform_bus driver");
MODULE_VERSION("V1.0");

module_init(keydev_init);
module_exit(keydev_exit);
           

驅動子產品程式

/*Copyright (c) 2018 Caokaipeng,All rights reserved.*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>     //kmalloc函數需要
#include <asm/uaccess.h>   //copy_to_user函數需要
#include <linux/sched.h>
#include <linux/platform_device.h>

//按鍵編号
unsigned int key_num = ;

//定義下半部工作
struct work_struct *work1;

//定義一個定時器
struct timer_list key_timer;

//定義一個等待隊列
wait_queue_head_t key_wait_queue;

//定義一個resource_irq
struct resource *res_irq;
//定義一個resource_mem
struct resource *res_mem;
//控制寄存器虛拟位址
unsigned int *key_base;

//work1具體執行的函數
void work1_func()
{
    //啟動定時器
    mod_timer(&key_timer,jiffies + HZ/);   //HZ為1秒
}

//定時器的逾時函數
void key_timer_func(unsigned long data)
{
    //資料寄存器第0位的讀取值
    unsigned int key1_val;
    //資料寄存器第1位的讀取值
    unsigned int key2_val;

    //check key1是否仍然按下
    key1_val = readl(key_base+)&;
    if( == key1_val){   //按下為低電平
        key_num = ;
    }
    else{
        //do nothing
    }

    //check key2是否仍然按下
    key2_val = readl(key_base+)&;
    if( == key2_val){   //按下為低電平
        key_num = ;
    }
    else{
        //do nothing
    }

    //喚醒隊列中程序
    wake_up(&key_wait_queue);
}

//中斷處理函數
irqreturn_t key_interrupt(int irq, void *dev_id)
{
    //1.檢測是否發生了按鍵中斷
    //->沒有采用共享中斷,此步驟略

    //2.清除已經發生的按鍵中斷
    //->處理器級别,cpu會進行清除,此步驟略

    //3.送出下半部工作到預設工作隊列處理
    schedule_work(work1);

    return IRQ_HANDLED;
}

//硬體(key)初始化函數
void key_hw_init()
{ 
    //控制寄存器的讀取值
    unsigned int data;
    //讀取控制寄存器的值
    data = readl(key_base);
    //列印GPNCON寄存器設定前的值
    printk(KERN_WARNING"Set_before,GPNCON = 0x%x.\n",data);
    //對控制寄存器進行設定,設定GPF0為中斷模式
    data &= ~b1111;    //寄存器01位設定為00->KEY1對應GPN0,23位設定為00->KEY2對應GPN1
    data |= b1010;     //寄存器01設定為10,23位設定為10
    //列印GPNCON寄存器設定後的值
    printk(KERN_WARNING"Set_after,GPNCON = 0x%x.\n",data);
    writel(data,key_base);
}

//open裝置操作
int key_open(struct inode *node, struct file *filp)
{   
    return ;
}
//read裝置操作
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
    //判斷是否進入等待隊列,condition為key_num
    wait_event(key_wait_queue,key_num);
    //已經喚醒
    printk(KERN_WARNING"in kernel,key_num = %d.\n",key_num);
    //将資料copy to APP
    copy_to_user(buf,&key_num,);
    //清空資料
    key_num = ;

    return ;
}
//定義并初始化file_operations
struct file_operations key_fops =
{
    .open = key_open,
    .read = key_read,
};

//定義并初始化miscdevice結構體
struct miscdevice key_miscdev =
{
    .minor = ,
    .name = "key",
    .fops = &key_fops,
};

//定義probe函數
int __devinit key_probe(struct platform_device *pdev)
{
    int ret;
    int size;
    //注冊miscdevice(混雜裝置)
    ret = misc_register(&key_miscdev);
    if(ret != ){
        printk(KERN_WARNING"register key_misc fail.\n");
    }
    //擷取中斷号
    res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,);
    //注冊中斷->GPN0對應中斷号IRQ_EINT(0)
    request_irq(res_irq->start,key_interrupt,IRQF_TRIGGER_FALLING,"key",);
    //注冊中斷->GPN1對應中斷号IRQ_EINT(1)
    request_irq(res_irq->end,key_interrupt,IRQF_TRIGGER_FALLING,"key",);

    //擷取基位址
    res_mem = platform_get_resource(pdev,IORESOURCE_MEM,);
    size = res_mem->end - res_mem->start +;
    key_base = ioremap(res_mem->start,size);
    //硬體初始化
    key_hw_init();
    //建立工作1
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);   //配置設定空間
    INIT_WORK(work1,work1_func);

    //初始化定時器
    init_timer(&key_timer);
    //設定逾時函數
    key_timer.function = key_timer_func;
    //注冊定時器
    add_timer(&key_timer);

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

    return ret;
}

//定義remove函數
int __devinit key_remove(struct platform_device *pdev)
{
    //登出miscdevice
    misc_deregister(&key_miscdev);
    //登出中斷
    free_irq(IRQ_EINT(),);
}

//定義一個平台驅動
struct platform_driver key_drv = {
    .probe =key_probe ,
    .remove = key_remove,
    .driver = {
        .name   = "my_key",
    },
};

static int button_init()
{
    //注冊平台驅動
    return platform_driver_register(&key_drv);
}

static void button_exit()
{
    //登出平台驅動
    platform_driver_unregister(&key_drv);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CaoKaipeng");
MODULE_DESCRIPTION("Key driver");
MODULE_VERSION("V1.0");

module_init(button_init);
module_exit(button_exit);
           

應用程式

/*Copyright (c) 2018 Caokaipeng. All rights reserved.*/

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int fd;   //檔案指針
    int key_num; //按鍵編号
    //1.打開裝置
    fd = open("/dev/6410key",);
    if(fd<){
        printf("Open device error!\n");
    }
    //2.讀取裝置
    read(fd,&key_num,);
    printf("key_num is %d.\n",key_num);
    //3.關閉裝置
    close(fd);
}
           

運作結果

基于平台總線的按鍵裝置驅動

————————————

2018.02.10

0:20

繼續閱讀