i2c键盘驱动移植

<strong>I2C</strong>

linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。

对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

对于linux输入子系统的框架结构如下图1所示:


图1  linux输入子系统框架结构

 

由上图所展现的内容就是linux输入子系统的分层结构。

/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。

事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。

输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。

下图2简单描述了linux输入子系统的事件处理机制:


图2  linux输入子系统事件处理机制

 

由上图可知输入子系统核心层提供的支持以及如何上报事件到input event drivers。

作为输入设备的驱动开发者,需要做以下几步:

1、          在驱动加载模块中,设置你的input设备支持的事件类型,类型参见表1设置

2、           注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等)

3、           将输入设备注册到输入子系统中

本文是基于TI的TCA8424芯片所修改的驱动



static const struct i2c_device_id tca8424_id[] = {
	{ TCA8424_NAME, 8424, },
	{},
};
MODULE_DEVICE_TABLE(i2c, tca8424_id);
// #define TCA8424_NAME "tca8424_keypad"
static struct i2c_driver tca8424_keypad_driver = {
	.driver = {
		.name	= TCA8424_NAME,
		.owner	= THIS_MODULE,
	},
	.probe		= tca8424_keypad_probe,
	.id_table	= tca8424_id,
};
//将驱动添加到i2c的设备列表中
static int __init tca8424_keypad_init(void)
{
	return i2c_add_driver(&tca8424_keypad_driver);
}
接着主要看看probe函数,上面已经说过input设备需要3个只要步骤,首先是定义input设备事件类型

static int tca8424_keypad_probe(struct i2c_client *client,
					  const struct i2c_device_id *id)
{
 	struct device *dev = &client->dev;
 	const struct tca8424_keypad_platform_data *pdata =
 						dev_get_platdata(dev);
 	struct tca8424_keypad *keypad_data;
 	struct input_dev *input;
 	const struct matrix_keymap_data *keymap_data = NULL;
 	u32 rows = 0, cols = 0;
 	bool rep = false;
 	bool irq_is_gpio = false;
 	int irq;
 	int error, row_shift, max_keys;

	/* Copy the platform data */
	if (pdata) {
		if (!pdata->keymap_data) {
			dev_err(dev, "no keymap data defined\n");
			return -EINVAL;
		}
		keymap_data = pdata->keymap_data;
		rows = pdata->rows;
		cols = pdata->cols;
		rep  = pdata->rep;
		irq_is_gpio = pdata->irq_is_gpio;
	}

	if (!rows || rows > TCA8424_MAX_ROWS) {
		dev_err(dev, "invalid rows\n");
		return -EINVAL;
	}

	if (!cols || cols > TCA8424_MAX_COLS) {
		dev_err(dev, "invalid columns\n");
		return -EINVAL;
	}

	/* Check i2c driver capabilities */
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
		dev_err(dev, "%s adapter not supported\n",
			dev_driver_string(&client->adapter->dev));
		return -ENODEV;
	}

	row_shift = get_count_order(cols);
	max_keys = rows << row_shift;

	/* Allocate memory for keypad_data and input device */
	keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
	if (!keypad_data)
		return -ENOMEM;

	keypad_data->client = client;
	keypad_data->row_shift = row_shift;

	i2c_set_clientdata(client, keypad_data);

	/* Initialize the chip or fail if chip isn't present */
	error = tca8424_configure(keypad_data, rows, cols);
	if (error < 0)
		return error;

	/* Configure input device 
	input = devm_input_allocate_device(dev);
	if (!input)
		return -ENOMEM;*/

	input = input_allocate_device();
	if (!input) {
		error = -ENOMEM;
		return error;
	}
	keypad_data->input = input;
#ifdef WORK_LIST
	INIT_DELAYED_WORK(&keypad_data->dwork, tca8424_keys_work_func);
#endif
	input->phys = "tca8424_keys/input0";<span style="font-family: Arial, Helvetica, sans-serif;">//申请input事件设备号</span>

	input->name = client->name;
	input->dev.parent = &client->dev;
	input->id.bustype = BUS_I2C;
	input->id.vendor  = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

	input->keycode = (void *)keymap_data->keymap;
//导入键盘键值映射表
	matrix_keypad_build_keymap(keymap_data, row_shift, input->keycode, input->keybit);
	if (!input->keybit) {
		dev_err(dev, "Failed to build keymap\n");
		return error;
	}
	
	if (rep)
		__set_bit(EV_REP, input->evbit);
	__set_bit(EV_KEY, input->evbit);

	input_set_drvdata(input, keypad_data);
//申请中断,这里有的input驱动用到的是工作队列的方式,是隔一段时间去自动检测,有兴趣的可以了解一下
//需要注意的是这里中断参数的设置:IRQF_ONESHOT和IRQF_SHARED不能同时使用,这个可以追踪源码查看
	irq = client->irq;
	if (irq_is_gpio)
		irq = gpio_to_irq(irq);

	error = devm_request_threaded_irq(dev, irq, NULL, tca8424_irq_handler,
					  IRQF_TRIGGER_FALLING |
//					  IRQF_TRIGGER_RISING |
//						IRQF_SHARED |
						IRQF_ONESHOT,
					  client->name, keypad_data);
	if (error) {
		dev_err(dev, "Unable to claim irq %d; error %d\n",
			client->irq, error);
		return error;
	}
//第三步:将设备注册到input子设备中
	error = input_register_device(input);
	if (error) {
		dev_err(dev, "Unable to register input device, error: %d\n",
			error);
		return error;
	}
	printk("[TCA8424] tca8424_keypad_probe.\n");

	return 0;
}

其中的EV_REP表示按键重复,即当一个按钮按下之后,系统会每隔一段时间发送一次按下事件,在收到弹起事件后结束重复发送。

EV_KEY表示键盘事件,要与鼠标事件EV_MAC区分。

当probe函数被调用后,就会与中断函数绑定,并且在/dev/input/下生成对应的设备文件。

在中断函数中获取键盘码并上传void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value),驱动源文件可以到http://download.csdn.net/detail/sddsighhz/8352103 下载。

在board文件中添加键盘设备。

static struct tca8424_keypad_platform_data tca8424_date = {
        .keymap_data = &tca8424_mkdata,
        .rows=16,
        .cols=8,
        .rep=1,
        .irq_is_gpio=1,
};

static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
	{
		I2C_BOARD_INFO("rtc-pcf8563", 0x51),
		.type = "pcf8563"
	},
	{
        I2C_BOARD_INFO("tca8424_keypad", 0x3b),
        .type = "tca8424_keypad" ,
        .irq  = MX53_TCA8424_IRQ,
        .platform_data = &tca8424_date
    },
};





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值