keypad driver

鍵盤是6x6矩陣式,在網上下了對應的PATCH,下載地址是

https://patchwork.kernel.org/patch/71857/

 

這個補丁會創建兩個文件

arch/arm/plat-mxc/include/mach/mxc_keypad.h //mxc_keypad_platform_data鍵盤平臺設備的結構體

/drivers/input/keyboard/mxc_keypad.c //驅動實現文件

打好補丁後,會發現這個驅動是一個通用的驅動,在mx21,25,27,31等板上都可以用,所以要自己實現相應的keymap,自己實現註冊鍵盤設備,還有註冊鍵盤相應的時鐘。

1.添加鍵盤設備:

2.6.32內核源碼裏沒有對mx21鍵盤的支持,所以我們要自己添加鍵盤的結構體

所以在arch/arm/mach-mx2/device.c裏,加上

static struct resource mxc_keypad_resources[] = {
        {
                .start = KPP_BASE_ADDR,
                .end = KPP_BASE_ADDR + SZ_4K - 1,
                .flags = IORESOURCE_MEM,
        },{
                .start = MXC_INT_KPP,
                .end = MXC_INT_KPP,
                .flags = IORESOURCE_IRQ,
        },
};

struct platform_device mxc_keypad_device = {
        .name = "mxc-keypad", //這個名字必須和驅動裏的名字相對應
        .id = 0,
        .num_resources = ARRAY_SIZE(mxc_keypad_resources),
        .resource = mxc_keypad_resources,
};

接下來在arch/arm/mach-mx2/device.h裏最後一行加上結構體的聲明

extern struct platform_device mxc_spi_device1;
extern struct platform_device mxc_spi_device2;
extern struct platform_device mxc_keypad_device;
然後進入到arch/arm/mach-mx2/mx21ads.c裏

在註冊的時候,添加鍵盤對應的註冊函數,找到mxc_register_device函數,添加上

static void __init mx21ads_board_init(void)
{
        mxc_gpio_setup_multiple_pins(mx21ads_pins, ARRAY_SIZE(mx21ads_pins),
                        "mx21ads");

        mxc_register_device(&mxc_uart_device0, &uart_pdata);
        mxc_register_device(&mxc_uart_device2, &uart_norts_pdata);
        mxc_register_device(&mxc_uart_device3, &uart_pdata);
        mxc_register_device(&mxc_fb_device, &mx21ads_fb_data);
        mxc_register_device(&mxc_sdhc_device0, &mx21ads_sdhc_pdata);
        mxc_register_device(&mxc_nand_device, &mx21ads_nand_board_info);
       
mxc_register_device(&mxc_keypad_device, &keypad_data); //keypad_data是struct mxc_keypad_platform_data結構體,需要我們自己完成,在第二步裏我會具體說明

        platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
}

2.添加平臺設備的數據(即定義keypad_data)

首先,根據你的鍵盤的實際情況,設置好keymap數組,這裏我建立一個文件"mxc_keymap.h",它是鍵盤的keymap數組,表明了鍵盤的實際按鍵排列情況。

#ifndef __MXC_KEYMAP_H__

#define __MXC_KEYMAP_H__

static unsigned int keymap[48] = {     //這裏定義大小爲48不是說有48個按鍵,由於在補丁的驅動實現文件mxc_keypad.c裏,在建立掃描碼對應的鍵值時,每行默認爲8個按鍵,所以這裏每行都有兩個是保留鍵。當然具體實現你也可以修改,在mxc_keypad.c裏做相應變動即可
        KEY_F1,                  //EXTRA 5
        KEY_MACRO,               //#
        KEY_0,                  //0
        KEY_KPASTERISK,         //*
        KEY_F8,                 //RECORD
        KEY_POWER,              //POWER
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_F2,                 //EXTRA 4
        KEY_9,                  //9
        KEY_8,
        KEY_7,
        KEY_F9,                 //EXTRA 1
        KEY_VOLUMEDOWN,
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_F3,                 //EXTRA 3
        KEY_6,
        KEY_5,
        KEY_4,
        KEY_F10,                //APP 4
        KEY_VOLUMEUP,
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_F4,                 //EXTRA 2
        KEY_3,
        KEY_2,
        KEY_1,
        KEY_F11,                //APP 3
        KEY_DOWN,               //DOWN
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_BACKSPACE,          //BACK
        KEY_RIGHT,              //RIGHT
        KEY_F6,                 //ACTION
        KEY_LEFT,               //LEFT
        KEY_HOME,               //HOME
        KEY_F16,                //APP 2
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_END,                //END
        KEY_F5,                 //KEY 2
        KEY_UP,                 //UP
        KEY_F7,                 //KEY 1
        KEY_F12,                //SEND
        KEY_F17,                //APP 1
        KEY_RESERVED,
        KEY_RESERVED,
};

#endif /*MXC_KEYMAP_H*/

接下來,進入arch/arm/mach-mx2/mx21ads.c,首先添加包含文件mxc_keypad.h,然後添加靜態結構體變量並初始化。

在文件開頭包含文件的地方添加上

#include <mach/mxc_keypad.h>

然後定義靜態結構體keypad_data

static struct mxc_keypad_platform_data keypad_data = {
        .matrix_key_rows = 6, //實際用到的行,MX21鍵盤模塊最大支持8x8,但我這裏只用到了6x6
        .matrix_key_cols = 6, //實際用到的列
        .matrix_key_map = keymap, //按鍵映射數組
        .matrix_key_map_size = 48, //映射的按鍵數
        .debounce_ms = SAMPLE_PERIOD, //消除抖動的延時,這裏我設置的是20ms,可以根據實際情況修改
};

3.時鐘註冊

查看arch/arm/mach-mx2/clock-mx21.c

可以發現其實鍵盤的時鐘結構體已經註冊過了,

        _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
        _REGISTER_CLOCK("mxc-keypad", NULL, kpp_clk)
       

但是在mxc_keypad.c裏獲得時鐘的時候,調用clk_get的參數與註冊的時鐘名不匹配,

keypad->clk = clk_get(NULL, "kpp");
        if (IS_ERR(keypad->clk)) {
                dev_err(&pdev->dev, "failed to get keypad clock/n");
                error = PTR_ERR(keypad->clk);
                goto failed_free_io;
        }

這裏有兩種方法,第一修改_REGISTER_CLOCK("mxc-keypad",NULL,kpp_clk)

改成 _REGISTER_CLOCK(NULL,"kpp",kpp_clk);

第二種是修改mxc_keypad.c裏的clk_get的參數,改成 clk_get("mxc-keypad",NULL);

個人傾向於第一種,呵呵,改驅動的代碼萬一改錯麻煩就大得多了。

----------------------------------------------

這裏我以爲已經改好了,所以寫了個測試的應用程序keytest來測試

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <linux/input.h>

int fd;
int loop_flag = 1;

void exit_loop()
{
        loop_flag--;
}

void deal_int(int signum)
{
        if(fd > 0)
                close(fd);
        exit_loop();
}

int main()
{
        struct input_event evt;

        fd = open("/dev/input/event0",O_RDWR);

        if(fd < 0)
                printf("Can not open/n");

        while(loop_flag > 0)
        {
                read(fd,&evt,sizeof(struct input_event));
                switch(evt.type)
                {
                        case EV_KEY:
                                printf("Key -->/nCode: %d/nValue: %d/n",evt.code,evt.value);
                        break;
                        case EV_MSC:
                              printf("MSC -->/nCode: %d/nValue: %d/n",evt.code,evt.value);
                        break;
                        case EV_SYN:
                                printf("<-- /n");
                        default:
                                printf(".../n");
                }
        }

        return 0;
}

啓動板子,發現鍵盤設備被識別,而且/dev/input/下有event0,

cat sys/class/input/input0/name得到
mxc-keypad

說明驅動正常,設備也註冊好了

所以使用keytest來測試,結果發現沒有發生EV_KEY,事件,這說明input_report_key函數沒有成功

查看內核源碼,發現input_report_key()實際上調用的是input_event(),這個函數首先會檢測合法位,也就是說會檢測按鍵的code是否已經添加到了struct input_dev的keybit裏(input_dev->keybit)

所以回到驅動實現文件mxc_keypad.c裏

發現static void mxc_keypad_build_keycode(struct mxc_keypad *keypad)
這個函數在添加掃描碼的時候,使用循環來處理

for (i = 0; i < pdata->matrix_key_map_size; i++) {
                unsigned int key = pdata->matrix_key_map[i];
                unsigned int row = KEY_ROW(key);
                unsigned int col = KEY_COL(key);
                unsigned int scancode = MATRIX_SCAN_CODE(row, col,
                                                         MATRIX_ROW_SHIFT);

                keycode = KEY_VAL(key);
                keypad->keycodes[scancode] = keycode;
                __set_bit(keycode, input_dev->keybit);
        }
發現錯在KEY_ROW和KEY_COL這兩個宏,查看include/linux/matrix_keypad.h

KEY_ROW(k) ( ((k) >> 24) & 0xff )

KEY_COL(k)   (((k) >> 16) & 0xff)

也就是說這兩個宏決定行列的規則是行是鍵值的高8位,列是鍵值的次高8位

查看include/linux/input.h,發現相關的KEY_*(KEY_0,KEY_UP等)的值都沒有超過255,這樣確定行列時,就不能使用這兩個宏了,所以我註釋了原來的代碼,自己修改成了

/* MOD---
        for (i = 0; i < pdata->matrix_key_map_size; i++) {
                unsigned int key = pdata->matrix_key_map[i];
                unsigned int row = KEY_ROW(key);
                unsigned int col = KEY_COL(key);
                unsigned int scancode = MATRIX_SCAN_CODE(row, col,
                                                         MATRIX_ROW_SHIFT);

                keycode = KEY_VAL(key);
                keypad->keycodes[scancode] = keycode;
                __set_bit(keycode, input_dev->keybit);
        }
*/

        unsigned int row,col;
        unsigned int key;
        unsigned int scancode;

        row = col = 0;
        for(i = 0;i < pdata->matrix_key_map_size;i++)
        {
                key = pdata->matrix_key_map[i];
                scancode = MATRIX_SCAN_CODE(row,col,MATRIX_ROW_SHIFT);
                col++;
                if(col == MAX_MATRIX_KEY_COLS)
                {
                        col = 0;
                        row++;
                }   

                key = KEY_VAL(key);
                keypad->keycodes[scancode] = key;
                __set_bit(key,input_dev->keybit);
        }

這裏如果哪位有更好的方法歡迎留言,因爲我覺得這個方法不是很好。

重新編譯,啓動板子,運行keytest程序

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