输入子系统之典型源码分析

输入子系统之典型源码分析

初始化函数中注册platform_driver结构体。这是个虚拟总线驱动。

static int __init gpio_keys_init(void)

{

return platform_driver_register(&gpio_keys_device_driver); //注册 platform_driver

}

该platform_driver的定义如下。

static struct platform_driver gpio_keys_device_driver = {

.probe = gpio_keys_probe, //probe函数,它是重点。

.remove = __devexit_p(gpio_keys_remove),

.driver = {

.name = "gpio-keys",

.owner = THIS_MODULE,

.pm = &gpio_keys_pm_ops,

.of_match_table = gpio_keys_of_match,

}

};

platform总线框架可知,在调用platform_driver_register(&gpio_keys_device_driver);platform_bus注册platform_driver时,会调用platform_match()函数来和总线中的设备链表一一匹配,如果有同名的platform_dev时,驱动程序会调用gpio_keys_probe()函数。

(platform_dev结构以\arch\arm\mach-s3c24xx\Mach-mini2440.c文件中的定义为例

下面先来看下,Mach-mini2440.c文件中的platform_dev结构体。

static struct platform_device mini2440_button_device = {

.name = "gpio-keys", //名字和platform_driver中匹配

.id = -1,

.dev = {

.platform_data = &mini2440_button_data, //附加信息。开发者可自定义

}

};

static struct gpio_keys_platform_data mini2440_button_data = {

.buttons = mini2440_buttons, //表示按键的结构体

.nbuttons = ARRAY_SIZE(mini2440_buttons),  //按键数=5

};

 

static struct gpio_keys_button mini2440_buttons[] = { //按键的结构体

{

.gpio = S3C2410_GPG(0), /* K1 */

.code = KEY_F1, //键码值

.desc = "Button 1",

.active_low = 1,

},

{

.gpio = S3C2410_GPG(3), /* K2 */

.code = KEY_F2,

.desc = "Button 2",

.active_low = 1,

},

{

.gpio = S3C2410_GPG(5), /* K3 */

.code = KEY_F3,

.desc = "Button 3",

.active_low = 1,

},

{

.gpio = S3C2410_GPG(6), /* K4 */

.code = KEY_POWER,

.desc = "Power",

.active_low = 1,

},

{

.gpio = S3C2410_GPG(7), /* K5 */

.code = KEY_F5,

.desc = "Button 5",

.active_low = 1,

},

#if 0

/* this pin is also known as TCLK1 and seems to already

 * marked as "in use" somehow in the kernel -- possibly wrongly */

{

.gpio = S3C2410_GPG(11), /* K6 */

.code = KEY_F6,

.desc = "Button 6",

.active_low = 1,

},

#endif

};

下面进入到probe函数中,它会用到platform_device结构体里面的私有数据

static int __devinit gpio_keys_probe(struct platform_device *pdev)

{

const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; //获得platform_device结构体里面的附加数据

struct gpio_keys_drvdata *ddata;

struct device *dev = &pdev->dev; //获得设备结构体

struct gpio_keys_platform_data alt_pdata;

struct input_dev *input; //input_dev结构体,输入子系统的重点

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); //分配一块内存空间,用来存放结构struct gpio_keys_drvdata数据和多个按键相关gpio_button_data结构体。

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); //该函数的功能是

/*ddataprobe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将ddata保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。*/

input_set_drvdata(input, ddata);  //该函数功能同上,将ddata保存到input总线设备的私有数据。

 

input->name = pdata->name ? : pdev->name;  //设置input_dev,重点

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++) {    //重点在gpio_keys_setup_key(pdev, input, bdata, button);设置定时器,申请中断等操作。

const struct gpio_keys_button *button = &pdata->buttons[i];

struct gpio_button_data *bdata = &ddata->data[i];

 

error = gpio_keys_setup_key(pdev, input, bdata, button);

if (error)

goto fail2;

 

if (button->wakeup)

wakeup = 1;

}

 

error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);//linux sysfs下创建文件给用户层使用。

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 that are connected to GPIOs */ //上报当前状态

for (i = 0; i < pdata->nbuttons; i++) {

struct gpio_button_data *bdata = &ddata->data[i];

if (gpio_is_valid(bdata->button->gpio))

gpio_keys_gpio_report_event(bdata);

}

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)

gpio_remove_key(&ddata->data[i]);

 

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 int __devinit gpio_keys_setup_key(struct platform_device *pdev,

 struct input_dev *input,

 struct gpio_button_data *bdata,

 const struct gpio_keys_button *button)

{

const char *desc = button->desc ? button->desc : "gpio_keys";

struct device *dev = &pdev->dev;

irq_handler_t isr;

unsigned long irqflags;

int irq, error;

 

bdata->input = input;

bdata->button = button;

spin_lock_init(&bdata->lock);   

 

if (gpio_is_valid(button->gpio)) {

 

error = gpio_request(button->gpio, desc);  //申请GPIO管脚,暂时未明白

if (error < 0) {

dev_err(dev, "Failed to request GPIO %d, error %d\n",

button->gpio, error);

return error;

}

 

error = gpio_direction_input(button->gpio); //管脚方向设置为输入

if (error < 0) {

dev_err(dev,

"Failed to configure direction for GPIO %d, error %d\n",

button->gpio, error);

goto fail;

}

 

if (button->debounce_interval) {

error = gpio_set_debounce(button->gpio,  //设置去抖动时间,并没有提供此函数,应该会导致程序错误

button->debounce_interval * 1000);

/* use timer if gpiolib doesn't provide debounce */

if (error < 0)

bdata->timer_debounce =

button->debounce_interval;

}

 

irq = gpio_to_irq(button->gpio);         //GPIO映射为IRQ中断:并没有提供此函数,应该会导致程序错误

if (irq < 0) {

error = irq;

dev_err(dev,

"Unable to get irq number for GPIO %d, error %d\n",

button->gpio, error);

goto fail;

}

bdata->irq = irq;

 

INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);

setup_timer(&bdata->timer,              //设置定时器

    gpio_keys_gpio_timer, (unsigned long)bdata);

 

isr = gpio_keys_gpio_isr;

irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

 

} else {

if (!button->irq) {

dev_err(dev, "No IRQ specified\n");

return -EINVAL;

}

bdata->irq = button->irq;

 

if (button->type && button->type != EV_KEY) {

dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");

return -EINVAL;

}

 

bdata->timer_debounce = button->debounce_interval;

setup_timer(&bdata->timer,

    gpio_keys_irq_timer, (unsigned long)bdata);

 

isr = gpio_keys_irq_isr;

irqflags = 0;

}

 

input_set_capability(input, button->type ?: EV_KEY, button->code);  //设置产生哪类事件

 

/*

 * If platform has specified that the button can be disabled,

 * we don't want it to share the interrupt line.

 */

if (!button->can_disable)

irqflags |= IRQF_SHARED;

 

error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); //申请中断

if (error < 0) {

dev_err(dev, "Unable to claim irq %d; error %d\n",

bdata->irq, error);

goto fail;

}

 

return 0;

 

fail:

if (gpio_is_valid(button->gpio))

gpio_free(button->gpio);

 

return error;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值