Linux驅動 - GPIO Buttons移植

Linux驅動 - GPIO Buttons移植

這個設備驅動適用於,每一個按鍵是鏈接到一個io口, 並且這個io口還有中斷功能的linux

驅動移植

須要在linux內核配置裏選上相關的配置。在內核源碼目錄下:數組

# make menuconfig
Device Drivers  --->
         Input device support  --->
             [*]   Keyboards  --->
                 <*>   GPIO Buttons

選擇上後,再編內核,再使用新的內核鏡像啓動系統函數

使用新內核啓動後,能夠查看出設備驅動是否已選擇上: 源碼分析

/sys/bus/platform/drivers/目錄下應有」gpio-keys」目錄this

驅動源碼分析

驅動源碼在」drivers/input/keyboard/gpio_keys.c」, 裏面是一個平臺驅動,咱們只要寫平臺設備描述硬件的資源與此驅動匹配便可.編碼

static struct platform_driver gpio_keys_device_driver = {
    .probe      = gpio_keys_probe,
    .remove     = __devexit_p(gpio_keys_remove),
    .driver     = {
        .name   = "gpio-keys", // 可匹配名爲"gpio-keys"的平臺設備
        .owner  = THIS_MODULE,
        .pm = &gpio_keys_pm_ops,
        .of_match_table = gpio_keys_of_match, //按這個成員來匹配平臺設備也是能夠的,要求設備的名字爲"gpio-keys"
    }
};
//經過閱讀平臺驅動的probe函數,可得知咱們寫的平臺設備應提供具本哪些硬件信息.
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
    const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; //這裏可得知咱們寫的平臺設備的platform_data成員應當提供gpio_keys_platform_data類型數據
    struct gpio_keys_drvdata *ddata;  //在設備驅動裏對每一個匹配上的設備都準備一個獨立的數據
    struct device *dev = &pdev->dev;
    struct gpio_keys_platform_data alt_pdata;
    struct input_dev *input;
    int i, error;
    int wakeup = 0;
...
//對gpio_keys_drvdata對象的初始化
//輸入設備對象的初始化
...
}
/////////////////////
//經過probe函數,能夠肯定咱們寫平臺設備時只需經過platform_data成員提供平臺驅動所需的信息,無需再提供resource. 
//再肯定結構體gpio_keys_platform_data的每一個成員的做用便可,如不清楚具體用途,能夠在驅動代碼裏經過查看對成員值的訪問推出用途.
//"include/linux/gpio_keys.h"
//每一個struct gpio_key_button的對象表示一個按鍵的具體信息
struct gpio_keys_button {
    //此按鍵對應的鍵碼
    unsigned int code;  /* input event code (KEY_*, SW_*) */
//此按鍵對應的一個io口
    int gpio;       /* -1 if this key does not support gpio */
//經過查看驅動代碼,可得知表示是否按鍵按下是低電平,如是則設1.
    int active_low;
//就是申請io口,申請中斷時使用的名字
    const char *desc;
//輸入設備的事件類型,按鍵用EV_KEY
    unsigned int type;  /* input event type (EV_KEY, EV_SW, EV_ABS) */
//表示按鍵按下時是否喚醒系統, 這個須要io口硬件上有這功能
    int wakeup;     /* configure the button as a wake-up source */
//防抖動用,間隔多久時間
    int debounce_interval;  /* debounce ticks interval in msecs */
    ...
}; 
//gpio_keys_paltform_data對象表示一個輸入設備, 一個輸入設備可有多個按鍵
struct gpio_keys_platform_data {
    //多個按鍵須要用gpio_keys_button的變量數組才能夠, buttons成員用於裝數組首地址
    struct gpio_keys_button *buttons;
//在按鍵數組裏的元素個數
    int nbuttons;
//輪詢的按鍵的平臺驅動所用  
    unsigned int poll_interval; /* polling interval in msecs -
                       for polling driver only */
//鍵按住時,是否重複提交按鍵
    unsigned int rep:1;     /* enable input subsystem auto repeat */
//設備這邊需在使用前所作的初始化工做,由設備驅動調用. 在輸入設備產生的設備文件打開時觸發調用
    int (*enable)(struct device *dev);
//設備這邊需在結束工做前所作的工做, 由設備驅動調用.在輸入設備產生的設備文件關閉時觸發調用
    void (*disable)(struct device *dev);
const char *name;       /* input device name */
};

添加硬件信息

現用一個按鍵鏈接再板上,SIG腳接到PA20. 當鍵按下時,SIG腳爲高電平。鍵鬆開時,SIG腳爲低電平.spa

如下信息能夠添加到arch/arm/mach-board/mach-soc.c。線程

//mypdev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <mach/gpio.h>
struct gpio_keys_button btns[] = {
    {KEY_L, GPIOA(20), 0, "mygpio-keys", EV_KEY, 0, 100},
};
struct gpio_keys_platform_data pdata = {
    .buttons = btns,
    .nbuttons = ARRAY_SIZE(btns),
    .rep = 1,
    .name = "mygpio-keys",
};
struct platform_device mypdev = {
    .name = "gpio-keys", //與平臺驅動的名字一致纔會匹配上
    .id = -1,
    .dev = {
        .platform_data = &pdata,
    },
};
module_driver(mypdev, platform_device_register, platform_device_unregister);
MODULE_LICENSE("GPL");

GPIO_KEY使用

使用方式比較簡單,和普通的文件操做同樣, 先打開設備文件, 再讀文件獲取鍵值便可:code

打開設備文件

Linux中的按鍵檢測經過循環讀取設備文件/dev/input/event[x](其中x能夠爲0,1,2…)設備文件獲取按鍵事件,不一樣的平臺設備文件可能會有差別, 若是不清楚對應的設備文件, 能夠用下面的命令來查看:orm

1546502976401

打開設備代碼以下:

/*1.key device*/

fd_key= open(KEY_DEVICE_FILE, O_RDONLY);

if(fd_key< 0) {

           LOGE("can'topen key device file");

           returnfd_key;

}

按鍵事件通常由主線程循環獲取按鍵事件,而後經過消息隊列通知其餘子線程,從而作出響應。

在Linux內核中,按鍵事件用input_event結構體描述,該結構體在頭文件<linux/input.h>中定義,同時該文件還定義了有關按鍵事件的API函數接口、標準按鍵編碼等。

input_event結構體定義以下:

struct input_event {  
    struct timeval time;  
    __u16 type;  
    __u16 code;  
    __s32 value;  
};

<linux/input.h>還定義了經常使用標準按鍵編碼:

#define KEY_RESERVED        0  
#define KEY_ESC         1  
#define KEY_1           2  
#define KEY_2           3  
#define KEY_3           4  
#define KEY_4           5  
#define KEY_5           6  
#define KEY_6           7

在用戶態,咱們只須要循環讀取設備文件/dev/input/eventx,就能夠獲得相應的鍵盤事件,代碼以下:

#include <linux/input.h>  
#include <fcntl.h>  
#include <stdio.h>  
#include <stdint.h>  
  
#define  KEY_EVENT_DEV1_NAME    "/dev/input/event1"  
  
int sysKeyScan(void)  
{  
    int l_ret = -1;  
    int i = 0;  
      
    int key_fd  = 0;  
    struct input_event key_event  = {0};  
      
    key_fd = open(KEY_EVENT_DEV1_NAME, O_RDONLY);  
    if(key_fd <= 0)  
    {  
        printf("---open /dev/input/event1 device error!---\n");  
        return l_ret;  
    }  
      
    while(1)  
    {  
        l_ret = lseek(key_fd, 0, SEEK_SET);  
        l_ret = read(key_fd, &key_event, sizeof(key_event));  
          
        if(l_ret)  
        {  
            if(key_event.type == EV_KEY  
                && (key_event.value == 0 || key_event.value == 1))  
            {  
                printf("key %d %s\n", key_event.code, (key_event.value) ? "pressed" : "released");  
                  
                if(key_event.code == KEY_ESC)  
                {  
                    break;  
                }  
                  
            }  
        }  
  
    }  
      
    close(key_fd);  
      
    return l_ret;  
      
}  
  
int main(int arg, char *arc[])  
{  
    printf("---This is a key event test!---\n");  
      
    sysKeyScan();  
      
    return 0;  
}

有時候,咱們的的Linux內核也能夠把不一樣的按鍵封裝到不一樣的event中,例如數字鍵鍵盤事件經過event1通知用戶態,而功能鍵經過event0通知用戶態。此時咱們可使用poll函數來同時監測多個等待事件,若事件未發生,進程睡眠,放棄CPU控制權,直到有鍵盤事件發生,poll將喚醒睡眠的進程,並執行相應的操做。代碼以下:

#include <linux/input.h>  
#include <fcntl.h>  
#include <poll.h>  
#include <stdio.h>  
#include <stdint.h>  
  
#define  KEY_EVENT_DEV0_NAME    "/dev/input/event0"  
#define  KEY_EVENT_DEV1_NAME    "/dev/input/event1"  
  
int sysKeyScan(void)  
{  
    int l_ret = -1;  
    int i = 0;  
  
    int key_fd[2]  = {0};  
    struct pollfd key_fds[2] = {0};  
    struct input_event key_event  = {0};  
  
    key_fd[0] = open(KEY_EVENT_DEV0_NAME, O_RDONLY);  
    if(key_fd[0] <= 0)  
    {  
        printf("---open /dev/input/event0 device error!---\n");  
        return l_ret;  
    }  
  
    key_fd[1] = open(KEY_EVENT_DEV1_NAME, O_RDONLY);  
    if(key_fd[1] <= 0)  
    {  
        printf("---open /dev/input/event1 device error!---\n");  
        return l_ret;  
    }  
  
    for(i = 0; i < 2; i++)  
    {  
        key_fds[i].fd = key_fd[i];  
        key_fds[i].events = POLLIN;  
    }  
  
    while(1)  
    {  
        l_ret = poll(key_fds, 2, -1);  
  
        for(i = 0; i < 2; i++)  
        {  
            l_ret = lseek(key_fd[i], 0, SEEK_SET);  
            l_ret = read(key_fd[i], &key_event, sizeof(key_event));  
  
            if(l_ret)  
            {  
                if(key_event.type == EV_KEY  
                    && (key_event.value == 0 || key_event.value == 1))  
                {  
  
                    printf("key value(%d) %s", key_event.code, key_event.value ? "press" : "release");  
  
                }  
            }  
        }  
    }  
  
    close(key_fd[0]);  
    close(key_fd[1]);  
  
    return l_ret;  
  
}  
  
int main(int argc, char *argv[])  
{  
    printf("---This is a key event test!---\n");  
  
    sysKeyScan();  
  
    return 0;  
}
相關文章
相關標籤/搜索