linux下矩阵键盘设备名,Linux下矩阵键盘驱动分析与移植

Post Views:

1,598

首先要介绍一下Linux中input子系统的模型,一图胜千言,所以直接上图。

0492d78ddc6d4da118de54190e304618.png

上图一目了然,我们的键盘驱动就是工作在input子系统的最低层。单纯地从驱动角度讲,我们的工作就是最终调用input_event()这个函数,把扫描到的键值传递给input core层。然后的事情就不是我们考虑的了,这样一来感觉很轻松的样子。

好吧!正式开工了!首先说明一下硬件环境,我用的是友善之臂的tiny210开发板。出厂配置的内核是没有加入矩阵键盘的。不过驱动代码是有的,所以在menuconfig里面配置矩阵键盘。说实话,这工作真的很弱智的样子。为什么呢?因为210处理器上是有一个键盘控制器一样的东西。什么扫描,消抖都是硬件在干。唯一需要改动的地方就是键盘的键值表。默认的驱动里面只用44的键值数据,但是我要用的键盘要88才行。

然后我们分析一下驱动的结构,键盘在210上是作为一个平台设备存在的。那么我们就说说这个平台设备的驱动模型。平台设备就相当于一条总线上的设备,不过这条总线是抽象的。平台设备模型开发底层设备驱动的大致流程如下:

platform_device定义:一般都在linux-3.0.8\arch\arm\plat-samsung和linux-3.0.8\arch\arm\plat-s5pv目录下面

samsung下面可能是比较通用的平台设备,而另一个就是比较专用的了。

注册平台设备platform_add_devices():这是由210的初始化函数统一注册起来的。

定义平台设备驱动platform_driver:这里的驱动都分布在driver的目录下,如samsung-keypad.c。

注册平台设备驱动platform_driver_register():这个函数是在驱动的模块加载函数中调用的。同理,platform_driver_unregister()是在驱动卸载函数中调用的。

匹配设备和驱动platform_match():这个函数是被platform_driver_register()调用的这个函数会检查所有的platform_device,如果找到了匹配的设备就调用platform_driver的探测函数probe()。

platform_driver->probe():这个函数中完成了很多任务,资源申请,初始化驱动中的各种数据结构,算是比较重要的一环。完成这个函数的之后,驱动就基本可以正常工作了。

我们就顺着这个流程来读读代码,首先是platform_device的定义。来看看linxu/arch/arm/plat-samsung/里面有什么,发现了dev-keypad.c,一看就知道是我想要的。代码如下:

#include #include #include #include #include #include static struct resource samsung_keypad_resources[] = {

[0] = {

.start = SAMSUNG_PA_KEYPAD,

.end = SAMSUNG_PA_KEYPAD + 0x20 - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_KEYPAD,

.end = IRQ_KEYPAD,

.flags = IORESOURCE_IRQ,

},

};

struct platform_device samsung_device_keypad = {

.name = "samsung-keypad",

.id = -1,

.num_resources = ARRAY_SIZE(samsung_keypad_resources),

.resource = samsung_keypad_resources,

};

void __init samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd)

{

/******/

}

这里面定义了矩阵键盘的结构体,还有就是矩阵键盘占用的io和中断资源。设备定义有了, 谁添加它呢?不急,看看linux/arch/arm/mach-s5pv210下的mach-mini210.c。这个文件可是210的命根子呀!截取一段代码来看看。

static void __init mini210_machine_init(void)

{

platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices));

}

这platform_add-devices()是找到了,可这mini210_devices和samsung_device_keypad又是什么关系呢?再贴代码,还是在mach-mini210.c中。

static struct platform_device *mini210_devices[] __initdata = {   #ifdef CONFIG_KEYBOARD_SAMSUNG       &samsung_device_keypad,   #endif   };

这下就清楚了,mini210_devices是一个指针数组,指向了各种platform_device,然后平 台设备就成功注册了。下一步,继续走到platform_driver。这个驱动很好找,就是linux/driver/input目录下面的samsung-keypad.c。又要贴代码了,我尽量少贴点。。

static bool samsung_keypad_report(struct samsung_keypad *keypad,

unsigned int *row_state)

{

struct input_dev *input_dev = keypad->input_dev;

unsigned int changed;

unsigned int pressed;

unsigned int key_down = 0;

unsigned int val;

unsigned int col, row;

for (col = 0; col < keypad->cols; col++) {

changed = row_state[col] ^ keypad->row_state[col];

key_down |= row_state[col];

if (!changed)

continue;

for (row = 0; row < keypad->rows; row++) {

if (!(changed & (1 << row)))

continue;

pressed = row_state[col] & (1 << row);

dev_dbg(&keypad->input_dev->dev,

"key %s, row: %d, col: %d\n",

pressed ? "pressed" : "released", row, col);

val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);

if(keypad->keycodes[val] != KEY_RESERVED) {

input_event(input_dev, EV_MSC, MSC_SCAN, val);

input_report_key(input_dev,

keypad->keycodes[val], pressed);

//上面的三行代码就是我们驱动的终极目标,键值正式进入

}

}

input_sync(keypad->input_dev);

}

memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));

return key_down;

}

static struct platform_driver samsung_keypad_driver = {

.probe = samsung_keypad_probe,

.remove = __devexit_p(samsung_keypad_remove),

.driver = {

.name = "s3c-keypad",

.owner = THIS_MODULE,

#ifdef CONFIG_PM

.pm = &samsung_keypad_pm_ops,

#endif

},

.id_table = samsung_keypad_driver_ids,

};

static int __init samsung_keypad_init(void)

{

return platform_driver_register(&samsung_keypad_driver);

}

module_init(samsung_keypad_init);

static void __exit samsung_keypad_exit(void)

{

platform_driver_unregister(&samsung_keypad_driver);

}

module_exit(samsung_keypad_exit);

一切都是很有规律,这个驱动在加载的时候就调用了platform_driver_register()。然后驱 动就开始在设备列表中苦苦寻找他的她——platform_device。他们怎么能找到对方呢?因为驱动知道设备的名字,就存放在那个id_table里面。所以他们一定有情人终成眷属了,内核真是个好月老,不像真实世界。。。

驱动和设备结合之后,就会调用驱动里面的probe函数,这个函数真的是非常厉害,把很多事情自己一个人干了。然后呢?走走初始化什么的,驱动基本就可以工作了。遇到中断,扫描,汇报键值,生活就是这样过了下去,有点无聊,好像。。。

工作流程就是这样了,那么说好的移植呢?说好的键值表呢?嘿嘿,马上就来!我还是贴贴probe()的代码吧!他干了不少活呢!

static int __devinit samsung_keypad_probe(struct platform_device *pdev)

{

const struct samsung_keypad_platdata *pdata;

const struct matrix_keymap_data *keymap_data;

struct samsung_keypad *keypad;

struct resource *res;

struct input_dev *input_dev;

unsigned int row_shift;

unsigned int keymap_size;

int error;

/**略 **/

keymap_data = pdata->keymap_data;

if (!keymap_data) {

dev_err(&pdev->dev, "no keymap data defined\n");

return -EINVAL;

}

row_shift = get_count_order(pdata->cols);

keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);

keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL);

input_dev = input_allocate_device();

matrix_keypad_build_keymap(keymap_data, row_shift,

input_dev->keycode, input_dev->keybit);

//略

}

看出来了一切都在这个keymap_data中,那么他是哪儿来的呢?其实我们并不陌生,刚刚擦肩而过。回到mach-mini210.c,它还在那里。

static uint32_t mini210_keymap[] __initdata = {       /* KEY(row, col, keycode) */       KEY(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3),       KEY(0, 6, KEY_4), KEY(0, 7, KEY_5),       KEY(1, 3, KEY_A), KEY(1, 4, KEY_B), KEY(1, 5, KEY_C),       KEY(1, 6, KEY_D), KEY(1, 7, KEY_E), KEY(7, 1, KEY_LEFTBRACE)   };      static struct matrix_keymap_data mini210_keymap_data __initdata = {       .keymap         = mini210_keymap,       .keymap_size    = ARRAY_SIZE(mini210_keymap),   };      static struct samsung_keypad_platdata mini210_keypad_data __initdata = {       .keymap_data    = &mini210_keymap_data,       .rows           = 8,       .cols           = 8,   };

就是它,嘿嘿!看来只要改掉这个mini210_keymap[]里面的东西,就大功告成了。这简直太easy了吧!打开linux-..*/include/linux/input.h。根据需要填表就好了。所以一切就这样结束了。(后边,我想再写写input子系统后面的东西,比如input core和evdev。)

后记:这篇文章其实是本人两年多前写的,这次把它从CSDN转过来的主要原因是,我准备深入学习Android系统层的技术。对于Android系统层,我其实是投入过一些时间的,但学习的目的性不强。到现在为止,我也算是搞了半年Android了,所以认真、系统地学习Android系统层也算是名正言顺的。虽然个人志在全栈,但别人肯定会把我当做搞Android的,重点研究一下肯定没有坏处。

ps:最后一段括号里的话,果然是世界最大谎言!:-p

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值