一步一步學習 Linux 驅動之 platform 機制(tiny210 按鍵驅動)

1、platform device

/**
* @Author: ZP1015 
*
* @Copyright:SCUT.
* 
* @Function:Platform device driver for button 
* 
* @Creat:2015-06-10
*
* @Modify:2015-12-29
**/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <linux/gpio_keys.h>
/**
gpio_keys_button
gpio_keys_platform_data 頭文件
**/

static struct gpio_keys_button mini210_button[] = {
    [0] = {
            .gpio   =   S5PV210_GPH2(0),
            .type   =   0,
            .desc   =   "KEY0",
    },
    [1] = {
            .gpio   =   S5PV210_GPH2(1),
            .type   =   1,
            .desc   =   "KEY1",
    },
    [2] = {
            .gpio   =   S5PV210_GPH2(2),
            .type   =   2,
            .desc   =   "KEY2",
    },
    [3] = {
            .gpio   =   S5PV210_GPH2(3),
            .type   =   3,
            .desc   =   "KEY3",
    },
    [4] = {
            .gpio   =   S5PV210_GPH3(0),
            .type   =   4,
            .desc   =   "KEY4",
    },
    [5] = {
            .gpio   =   S5PV210_GPH3(1),
            .type   =   5,
            .desc   =   "KEY5",
    },
    [6] = {
            .gpio   =   S5PV210_GPH3(2),
            .type   =   6,
            .desc   =   "KEY6",
    },
    [7] = {
            .gpio   =   S5PV210_GPH3(3),
            .type   =   7,
            .desc   =   "KEY7",
    }

};

static struct gpio_keys_platform_data mini210_button_data = {
    .buttons = mini210_button,
    .nbuttons = ARRAY_SIZE(mini210_button),
};

static void button_platform_device_release(struct device * dev)
{
    return ;
}

static struct platform_device button_platform_device = {
    .name           = "button_platform_device_driver",
    .id             = -1,
    .dev            = {
          .release  = button_platform_device_release,
          .platform_data = &mini210_button_data,      
    }
};
static int __init button_platform_device_init(void)
{
    printk("button_platform_device add ok!\n");
    return platform_device_register(&button_platform_device);
}
static void __exit button_platform_device_exit(void)
{
    printk("button_platform_device remove ok!\n");
    platform_device_unregister(&button_platform_device);
}
MODULE_AUTHOR("ZP1015");
MODULE_LICENSE("GPL");
module_init(button_platform_device_init);
module_exit(button_platform_device_exit);

2、platform driver

/**
* @Author: ZP1015 
*
* @Copyright:SCUT.
* 
* @Function:platform driver for button 
* 
* @Creat:2015-6-12
*
* @Modify:2015-12-29
**/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <linux/gpio_keys.h>

/*傳給用戶態空間的數據*/
struct buttons_status_copy_to_user {
    int keynum;/*按鍵編號*/
    int buttons_cur_press;/*該按鍵被按了多少次*/
};

static volatile int key_values[] = {
    0, 0, 0, 0, 0, 0, 0, 0
};

#define DEVICE_NAME "mybuttons_platfor_driver"

#define GLOBAL_LED_MAJOR  250

static unsigned int global_led_major = GLOBAL_LED_MAJOR; 
static struct cdev *button_cdev = NULL; 
static struct class *buttons_class = NULL;

struct buttons_status_copy_to_user buttons_status_statistics;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;

static void mini210_buttons_status(struct gpio_keys_button *_data)
{
    struct gpio_keys_button *button_data = (struct gpio_keys_button *)_data;

    /*內核打印當前按鍵狀態*/
    printk("KEY %d: %d\n", button_data->type,key_values[button_data->type]);

    /*TO DO:For IRQ_TYPE_EDGE_FALLING*/
    key_values[button_data->type] = key_values[button_data->type] + 1;
    buttons_status_statistics.buttons_cur_press = key_values[button_data->type];
    buttons_status_statistics.keynum = button_data->type;

    ev_press = 1;
    wake_up_interruptible(&button_waitq);   
}

static irqreturn_t button_interrupt(int irq, void *dev_id)
{
    struct gpio_keys_button *button_data = (struct gpio_keys_button *)dev_id;

    mini210_buttons_status(button_data);

    return IRQ_HANDLED;
}

static int mini210_buttons_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int mini210_buttons_close(struct inode *inode, struct file *file)
{
    return 0;
}
/*mini210_buttons_read()函數的申明*/
/*read調用的具體函數*/
/*由它讀取鍵盤輸入的結果*/
/*實質上就是讀取自定義結構體的值*/
/*它完成了鍵盤作爲輸入設備的核心功能*/
/*數組是否可讀,要根據標誌位ev_press來判斷*/
/*如果數組可讀,則讀取數據到用戶buffer中*/
/*如果數組不可讀,則進程進入等待隊列,等待到數組可讀爲止*/
/*等待隊列機制,是中斷管理中常用到的機制*/
/*因爲有些進程經常需要等待某一事件的發生*/
static int mini210_buttons_read(struct file *filp, char __user *buff,
        size_t count, loff_t *offp)
{
    unsigned long err;

    /*靠ev_press標誌位來判斷的*/
    /*進程等待隊列的機制,是進程調度的一種方法*/
    if (!ev_press) {
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
        /*進程休眠,放進button_waitq等待隊列*/
        /*這裏,把ev_press標誌位設成了休眠進程的標誌位了*/
        /*這是爲了便於利用poll_wait函數*/
        /*也就是利於select函數*/
        else
            wait_event_interruptible(button_waitq, ev_press);
        /*在中斷處理函數中,此進程會被喚醒*/
        /*喚醒前,ev_press 已被置1了*/
        /*喚醒後的執行點從這裏開始*/
    }

    ev_press = 0;

    /*TO DO:For IRQ_TYPE_EDGE_FALLING*/
    #if 1
    err = copy_to_user((struct buttons_status_copy_to_user *)buff, (struct buttons_status_copy_to_user *)(&buttons_status_statistics),
                    min(sizeof(buttons_status_statistics), count));


    /*TO DO:For IRQ_TYPE_EDGE_BOTH*/
    #else
    err = copy_to_user((int*)buff, (const int *)(&key_values),
                min(sizeof(key_values), count));

    memset((int *)key_values,0,sizeof(key_values));
    #endif

    return err ? -EFAULT : min(sizeof(key_values), count);
}

static unsigned int mini210_buttons_poll( struct file *file,
        struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    /*poll_wait()函數*/
    /*會監測進程隊列button_waitq裏的進程*/
    /*例如,如果mini210_button_read所在的進程的標誌位ev_press置爲1了*/
    /*那麼就不會再等待了*/
    /*這實質上就是select函數的運行機制*/

    poll_wait(file, &button_waitq, wait);
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

static struct file_operations button_fops = {
    .owner      = THIS_MODULE,
    .open       = mini210_buttons_open,
    .release    = mini210_buttons_close, 
    .read       = mini210_buttons_read,
    .poll       = mini210_buttons_poll,
};

static int __devinit button_probe(struct platform_device *pdev)
{
    int ret;
    int err,irq;
    dev_t devno;
    int i = 0;
    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;

    printk("button probe start!\n");

    /*1.初始化cdev*/
    button_cdev = cdev_alloc();//分配cdev
    cdev_init(button_cdev,&button_fops);//初始化cdev
    button_cdev->owner = THIS_MODULE;

    /*2.獲取字符設備號*/
    devno = MKDEV(global_led_major,0);//將主設備號和次設備號轉換成dev_t類型
    if (devno) {
        ret = register_chrdev_region(devno,1,DEVICE_NAME);//註冊一系列設備號
    } 
    else {
        ret = alloc_chrdev_region(&devno,0,1,DEVICE_NAME);//未知主設備號註冊設備號
        global_led_major = MAJOR(devno);
    }

    if (ret < 0) {
        return ret;
    }

    /*3.註冊字符設備*/
    err = cdev_add(button_cdev,devno,1);//註冊cdev
    if (err) {
        printk(KERN_NOTICE"Error %d adding button_cdev",err);
        goto cdev_add_fail;
    } 

    printk(KERN_NOTICE"button_platform_driver init ok!\n"); 

    /*4.自動創建設備節點,在/sys/class/下創建類目錄*/
    buttons_class = class_create(THIS_MODULE,"mybuttons_class");//自動創建設備節點
    device_create(buttons_class,NULL,devno,NULL,"mybuttons");

    /*5.註冊按鍵中斷服務函數*/
    for (i = 0; i < pdata->nbuttons; i++) { 
        if (!pdata->buttons[i].gpio)
            continue;

        /*寄存器與中斷引腳的聯結*/
        irq = gpio_to_irq(pdata->buttons[i].gpio);
        /*註冊中斷函數*/
        err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, 
                pdata->buttons[i].desc, (void *)&(pdata->buttons[i]));
        if (err)
            goto request_irq_fail;
    }

    ev_press = 1;

    return 0;

request_irq_fail:
    cdev_del(button_cdev);
    unregister_chrdev_region(devno,1);
    device_destroy(buttons_class,devno);
    class_destroy(buttons_class);   
    i--; /*當前沒註冊成功不需要清除註冊的中斷函數*/
    for (; i >= 0; i--) {
        if (!pdata->buttons[i].gpio)
            continue;

        irq = gpio_to_irq(pdata->buttons[i].gpio);
        disable_irq(irq);
        free_irq(irq, (void *)&(pdata->buttons[i]));
    }

    return -EBUSY;
cdev_add_fail:
    unregister_chrdev_region(devno,1);

    return -1;
}
static int __devexit button_remove(struct platform_device *pdev)
{   

    struct gpio_keys_platform_data *pdata = NULL;
    dev_t devno;    
    int i,irq= 0;
    devno = MKDEV(global_led_major,0);//將主設備號和次設備號轉換成dev_t類型

    printk("button_remove!\n");

    cdev_del(button_cdev);
    unregister_chrdev_region(devno,1);

    device_destroy(buttons_class,devno);
    class_destroy(buttons_class);

    if(!pdev)
        return 0;

    pdata = pdev->dev.platform_data;

    for (i=0; i < pdata->nbuttons; i++) {
        if (!pdata->buttons[i].gpio)
            continue;

        irq = gpio_to_irq(pdata->buttons[i].gpio);
        disable_irq(irq);
        free_irq(irq, (void *)&(pdata->buttons[i]));
    }

    return 0;
}

static struct platform_driver button_platform_driver = {
    .probe  = button_probe,
    .remove = __devexit_p(button_remove),
    .driver = {
        .name = "button_platform_device_driver",
        .owner = THIS_MODULE,
    }
};

static int __init button_platform_driver_init(void)
{
    printk("button_platform_driver_init!\n");

    return platform_driver_register(&button_platform_driver);
}

static void __exit button_platform_driver_exit(void)
{
    printk("button_platform_driver_exit!\n");
    platform_driver_unregister(&button_platform_driver);
}

module_init(button_platform_driver_init);
module_exit(button_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZP1015");

3、Makefile

ifneq ($(KERNELRELEASE),)

obj-m :=button_platform.o

else

KERNELDIR :=/home/ZP1015/Desktop/linux
ARM_LINUX_DIR =-I $(KERNELDIR)/arch/arm/mach-s5pv210/include/mach/  \
               -I $(KERNELDIR)/arch/arm/include/asm

all:
     make -C $(KERNELDIR) $(ARM_LINUX_DIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
    rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~

endif

4、測試程序

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

struct buttons_status_copy_to_user {
    int keynum;
    int buttons_cur_press;
};

int main()
{
    int fd,ret;
    struct buttons_status_copy_to_user buttons_status_statistics;
    fd = open("/dev/mybuttons", 0);
    if(fd < 0) {
        printf("open error!");
        return -1;
    }
    while(1) {
        ret = read(fd, &buttons_status_statistics,\
            sizeof(struct buttons_status_copy_to_user));
        printf("you press the key %d ,%d\n", buttons_status_statistics.keynum+1,\
                               buttons_status_statistics.buttons_cur_press);
    }
    close(fd);

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