linux3.4.0 按键驱动程序分析(pandaboard omap4460)

linux3.4.0 按键驱动程序分析(pandaboard omap4460)

在内核中,按键的驱动程序已经设计好了,要使自己板上的按键工作起来,需要做的是在相应的文件中添加硬件信息,然后对内核进行正确的配置。

以下先使按键工作起来,再对驱动进行分析。
按键属于输入设备,所以在分析按键驱动之前要先学习input子系统的相关知识

1、内核配置:

Device Drivers ->
	input device support ->
		[*] keyboards ->
			[*] GPIO buttons ->
2、添加硬件信息:
修改文件 /arch/arm/mach-omap2/board-omap4panda.c

第一处:
#include 

第二处:
static struct gpio_keys_button pandaboard_exp_gpio_keys[] = {
        {
                .code                           = KEY_HOME, //按键码,需要预先定义
                .gpio                           = 36, //使用的GPIO口
                .active_low             = 1,
                .desc                           = "pandaboard_exp_power",
                .type                           = EV_KEY, //类型
                .wakeup                         = 1,
                .debounce_interval      = 10, //去抖
        },
        {
                .code                           = KEY_PROG1,
                .gpio                           = 32,
                .active_low             = 1,
                .desc                           = "pandaboard_exp_menu",
                .type                           = EV_KEY,
                .wakeup                         = 1,
                .debounce_interval      = 10,
        },
        {
                .code                           = KEY_PROG2,
                .gpio                           = 38,
                .active_low             = 1,
                .desc                           = "pandaboard_exp_back",
                .type                           = EV_KEY,
                .wakeup                         = 1,
                .debounce_interval      = 10,
        },
        {
                .code                           = KEY_PROG3,
                .gpio                           = 33,
                .active_low             = 1,
                .desc                           = "pandaboard_exp_volup",
                .type                           = EV_KEY,
                .wakeup                         = 1,
                .debounce_interval      = 10,
        },
        {
                .code                           = KEY_PROG4,
                .gpio                           = 37,
                .active_low             = 1,
                .desc                           = "pandaboard_exp_voldw",
                .type                           = EV_KEY,
                .wakeup                         = 1,
                .debounce_interval      = 10,
        },
};
static struct gpio_keys_platform_data pandaboard_exp_gpio_keys_data = {
        .buttons        = pandaboard_exp_gpio_keys,
        .nbuttons       = ARRAY_SIZE(pandaboard_exp_gpio_keys),
};

static struct platform_device pandaboard_exp_gpio_keys_device = {
        .name   = "gpio-keys",  //平台设备的名称
        .id     = -1,
        .dev    = {
                .platform_data  = &pandaboard_exp_gpio_keys_data,
        },
};

第三处:
static struct platform_device *panda_devices[] __initdata = {
    	&pandaboard_exp_gpio_keys_device, //加入到系统平台设备数组中
		&wl1271_device,
		&btwilink_device,
};

3、测试
cat /dev/input/event1 ,按下或者抬起按键会在终端输出乱码。

测试应用程序:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(void)
{
	int key_fd;
	int key_value,i=0,count;
	struct input_event ev_key;
	key_fd = open("/dev/event1", O_RDWR);
	if (key_fd < 0) {
		perror("open device keys");
		exit(1);
	}
	while (1) {
		count = read(key_fd,&ev_key,sizeof(struct input_event));
		for(i=0; i<(int)count/sizeof(struct input_event); i++){
			if(EV_KEY==ev_key.type)
			printf("type:%d, code:%d, value:%d", ev_key.type, ev_key.code, ev_key.value);
		}
	}
	close(key_fd);
	return 0;
} 

驱动程序分析:
代码位置:/drivers/input/keyboard/gpio_keys.c

//模块加载函数
static int __init gpio_keys_init(void)
{
	return platform_driver_register(&gpio_keys_device_driver); //注册平台驱动
}

//平台驱动结构体的定义
static struct platform_driver gpio_keys_device_driver = {
	.probe = gpio_keys_probe, //探测函数,在驱动找到对应的设备时执行
	.remove = __devexit_p(gpio_keys_remove),
	.driver = {
		.name = "gpio-keys", //驱动名称 ,可以在 /sys/ 下找到
		.owner = THIS_MODULE,
		.pm = &gpio_keys_pm_ops, //操作函数集
		.of_match_table = gpio_keys_of_match,
	}
};

//探测函数
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
	struct gpio_keys_platform_data *pdata = pdev->dev.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;

	if (!pdata) {
		error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
		if (error)
		return error;
		pdata = &alt_pdata;
	}

	ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data),GFP_KERNEL);
	input = input_allocate_device(); //分配一个输入设备结构体
	if (!ddata || !input) {
		dev_err(dev, "failed to allocate state\n");
		error = -ENOMEM;
		goto fail1;
	}
//对驱动结构体赋值
	ddata->input = input;
	ddata->n_buttons = pdata->nbuttons;
	ddata->enable = pdata->enable;
	ddata->disable = pdata->disable;
	mutex_init(&ddata->disable_lock);
	platform_set_drvdata(pdev, ddata);
	input_set_drvdata(input, ddata);
//对输入设备机构体进行赋值,这些值都可以在系统启动后在 /sys/ 下找到对应项
	input->name = pdata->name ? : pdev->name;
	input->phys = "gpio-keys/input0";
	input->dev.parent = &pdev->dev;
	input->open = gpio_keys_open;
	input->close = gpio_keys_close;
	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
	__set_bit(EV_REP, input->evbit);
// 依次对每个按键进行设置
	for (i = 0; i < pdata->nbuttons; i++) {
		struct gpio_keys_button *button = &pdata->buttons[i];
		struct gpio_button_data *bdata = &ddata->data[i];
		unsigned int type = button->type ?: EV_KEY;
		bdata->input = input;
		bdata->button = button;
//设置按键,相当于初始化,主要工作是申请gpio、设置为输入、设置防抖动时间、中断申请
		error = gpio_keys_setup_key(pdev, bdata, button);
		if (error)
			goto fail2;

		if (button->wakeup)
			wakeup = 1;

		input_set_capability(input, type, button->code); //对输入设备进行类型和码的设置,
	}

	error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); //设置属性并创建接口
	if (error) {
		dev_err(dev, "Unable to export keys/switches, error: %d\n",error);
		goto fail2;
	}

	error = input_register_device(input); //注册输入设备
	if (error) {
		dev_err(dev, "Unable to register input device, error: %d\n",error);
		goto fail3;
	}

/* get current state of buttons */
	for (i = 0; i < pdata->nbuttons; i++) //报告事件
		gpio_keys_report_event(&ddata->data[i]);
	input_sync(input);

	device_init_wakeup(&pdev->dev, wakeup);

	return 0;

fail3:
	sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
fail2:
	while (--i >= 0) {
		free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
		if (ddata->data[i].timer_debounce)
			del_timer_sync(&ddata->data[i].timer);
		cancel_work_sync(&ddata->data[i].work);
		gpio_free(pdata->buttons[i].gpio);
	}

	platform_set_drvdata(pdev, NULL);
fail1:
	input_free_device(input);
	kfree(ddata);
/* If we have no platform_data, we allocated buttons dynamically. */
	if (!pdev->dev.platform_data)
		kfree(pdata->buttons);

	return error;
}

中断处理函数:
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
	struct gpio_button_data *bdata = dev_id;
	struct gpio_keys_button *button = bdata->button;

	BUG_ON(irq != gpio_to_irq(button->gpio));

	if (bdata->timer_debounce)
		mod_timer(&bdata->timer,jiffies + msecs_to_jiffies(bdata->timer_debounce));
	else
		schedule_work(&bdata->work);
	return IRQ_HANDLED;
}

工作流程:
按键触发->gpio_keys_isr->schedule_work(&bdata->work)->gpio_keys_work_func()->gpio_keys_report_event()报告事件
其中work的初始化在gpio_keys_setup_key()函数中:INIT_WORK(&bdata->work, gpio_keys_work_func);



你可能感兴趣的:(驱动)