友善之臂210矩阵键盘驱动分析与移植

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

       

       上图一目了然,我们的键盘驱动就是工作在input子系统的最低层。单纯地从驱动角度讲,

我们的工作就是最终调用input_event()这个函数,把扫描到的键值传递给input core层。然后

的事情就不是我们考虑的了,这样一来感觉很轻松的样子。

        好吧!正式开工了!首先说明一下硬件环境,我用的是友善之臂的tiny210开发板。出厂

配置的内核是没有加入矩阵键盘的。不过驱动代码是有的,所以在menuconfig里面配置矩阵

键盘。

        说实话,这工作真的很弱智的样子。为什么呢?因为210处理器上是有一个键盘控制器一

样的东西。什么扫描,消抖都是硬件在干。唯一需要改动的地方就是键盘的键值表。默认的驱

动里面只用4*4的键值数据,但是我要用的键盘要8*8才行。

        然后我们分析一下驱动的结构,键盘在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 <linux/platform_device.h>
#include <mach/irqs.h>
#include <mach/map.h>
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/keypad.h>

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)
{
<span style="white-space:pre">	</span>/*省略了*/
}
  这里面定义了矩阵键盘的结构体,还有就是矩阵键盘占用的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);
		<span style="white-space:pre">		</span>//上面的三行代码就是我们驱动的终极目标,键值正式进入
			}
		}
		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;
<span style="white-space:pre">	</span>/**略     **/
	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);
<span style="white-space:pre">	</span>//略//
}

        看出来了一切都在这个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。)



评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值