```
这里我们分析一下rk3288的按键驱动分析
//定时器执行函数
static void keys_timer(unsigned long _data)
{
struct rk_keys_drvdata *pdata = rk_key_get_drvdata();
struct rk_keys_button *button = (struct rk_keys_button *)_data;
struct input_dev *input = pdata->input;
int state;
//首先判断是否按键还是adc的类型,之后通过他们获取state
if (button->type == TYPE_GPIO)
state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^
button->active_low);
else
state = !!button->adc_state;
//将存储的按键值和它对比,如果不一样的话就上报
if (button->state != state) {
button->state = state;
input_event(input, EV_KEY, button->code, button->state);
key_dbg(pdata, "%skey[%s]: report event[%d] state[%d]\n",
button->type == TYPE_ADC ? "adc" : "gpio",
button->desc, button->code, button->state);
input_event(input, EV_KEY, button->code, button->state);
input_sync(input);
}
//如果一直没有抬起的话,那么就一直重新计时
if (state)
mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
}
static irqreturn_t keys_isr(int irq, void *dev_id)
{
struct rk_keys_drvdata *pdata = rk_key_get_drvdata();
struct rk_keys_button *button = (struct rk_keys_button *)dev_id;
struct input_dev *input = pdata->input;
BUG_ON(irq != gpio_to_irq(button->gpio));
if (button->wakeup && pdata->in_suspend) {
button->state = 1;
key_dbg(pdata,
"wakeup: %skey[%s]: report event[%d] state[%d]\n",
(button->type == TYPE_ADC) ? "adc" : "gpio",
button->desc, button->code, button->state);
input_event(input, EV_KEY, button->code, button->state);
input_sync(input);
}
if (button->wakeup)
wake_lock_timeout(&pdata->wake_lock, WAKE_LOCK_JIFFIES);
//重新设置定时器的值,重新计时
mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
return IRQ_HANDLED;
}
static const struct of_device_id rk_key_match[] = {
{ .compatible = "rockchip,key", .data = NULL},
{},
};
MODULE_DEVICE_TABLE(of, rk_key_match);
static int rk_key_adc_iio_read(struct rk_keys_drvdata *data)
{
struct iio_channel *channel = data->chan;
int val, ret;
if (!channel)
return INVALID_ADVALUE;
ret = iio_read_channel_raw(channel, &val);
if (ret < 0) {
pr_err("read channel() error: %d\n", ret);
return ret;
}
return val;
}
//adc的执行函数
static void adc_key_poll(struct work_struct *work)
{
struct rk_keys_drvdata *ddata;
int i, result = -1;
ddata = container_of(work, struct rk_keys_drvdata, adc_poll_work.work);
if (!ddata->in_suspend) {
//读取adc的值
result = rk_key_adc_iio_read(ddata);
//判断
if (result > INVALID_ADVALUE && result < EMPTY_ADVALUE)
ddata->result = result;
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = &ddata->button[i];
//判断是否有adc的值,如果没有的话就继续,
if (!button->adc_value)
continue;
//如果没有adcvalue的值的话就不执行这里了
if (result < button->adc_value + DRIFT_ADVALUE &&
result > button->adc_value - DRIFT_ADVALUE)
button->adc_state = 1;
else
button->adc_state = 0;
如果有adc的值 那么首先判断是否和存储的值状态是否一样 如果不一样的话就通过定时器上报按键值
if (button->state != button->adc_state)
mod_timer(&button->timer,
jiffies + DEBOUNCE_JIFFIES);
}
}
schedule_delayed_work(&ddata->adc_poll_work, ADC_SAMPLE_JIFFIES);
}
//判断是io口还是adc按键
static int rk_key_type_get(struct device_node *node,
struct rk_keys_button *button)
{
u32 adc_value;
if (!of_property_read_u32(node, "rockchip,adc_value", &adc_value))
return TYPE_ADC;
else if (of_get_gpio(node, 0) >= 0)
return TYPE_GPIO;
else
return -1;
}
//解析设备数的函数
static int rk_keys_parse_dt(struct rk_keys_drvdata *pdata,
struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *child_node;
struct iio_channel *chan;
int ret, gpio, i = 0;
u32 code, adc_value, flags;
chan = iio_channel_get(&pdev->dev, NULL);
if (IS_ERR(chan)) {
dev_info(&pdev->dev, "no io-channels defined\n");
chan = NULL;
}
pdata->chan = chan;
for_each_child_of_node(node, child_node) {
if (of_property_read_u32(child_node, "linux,code", &code)) {
dev_err(&pdev->dev,
"Missing linux,code property in the DT.\n");
ret = -EINVAL;
goto error_ret;
}
pdata->button[i].code = code;
pdata->button[i].desc =
of_get_property(child_node, "label", NULL);
pdata->button[i].type =
rk_key_type_get(child_node, &pdata->button[i]);
switch (pdata->button[i].type) {
case TYPE_GPIO:
gpio = of_get_gpio_flags(child_node, 0, &flags);
if (gpio < 0) {
ret = gpio;
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to get gpio flags, error: %d\n",
ret);
goto error_ret;
}
pdata->button[i].gpio = gpio;
pdata->button[i].active_low =
flags & OF_GPIO_ACTIVE_LOW;
pdata->button[i].wakeup =
!!of_get_property(child_node, "gpio-key,wakeup",
NULL);
break;
case TYPE_ADC:
if (of_property_read_u32
(child_node, "rockchip,adc_value", &adc_value)) {
dev_err(&pdev->dev,
"Missing rockchip,adc_value property in the DT.\n");
ret = -EINVAL;
goto error_ret;
}
pdata->button[i].adc_value = adc_value;
break;
default:
dev_err(&pdev->dev,
"Error rockchip,type property in the DT.\n");
ret = -EINVAL;
goto error_ret;
}
i++;
}
return 0;
error_ret:
return ret;
}
static int keys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct rk_keys_drvdata *ddata = NULL;
struct input_dev *input = NULL;
int i, error = 0;
int wakeup, key_num = 0;
//得到设备节点
key_num = of_get_child_count(np);
if (key_num == 0)
dev_info(&pdev->dev, "no key defined\n");
ddata = devm_kzalloc(dev, sizeof(struct rk_keys_drvdata) +
key_num * sizeof(struct rk_keys_button),
GFP_KERNEL);
//这里只是分配一个输入设备,之后设置输入设备 主要还是设置io中断以及版本和名子
input = devm_input_allocate_device(dev);
if (!ddata || !input) {
error = -ENOMEM;
return error;
}
platform_set_drvdata(pdev, ddata);
input->name = "rk29-keypad"; /* pdev->name; */
input->phys = "gpio-keys/input0";
input->dev.parent = dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
ddata->input = input;
/* parse info from dt */
ddata->nbuttons = key_num;
//解析设备数
error = rk_keys_parse_dt(ddata, pdev);
if (error)
goto fail0;
/* Enable auto repeat feature of Linux input subsystem */
if (ddata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = &ddata->button[i];
if (button->code) {
为每个按键分配一个定时器
setup_timer(&button->timer,
keys_timer, (unsigned long)button);
}
if (button->wakeup)
wakeup = 1;
input_set_capability(input, EV_KEY, button->code);
}
wake_lock_init(&ddata->wake_lock, WAKE_LOCK_SUSPEND, input->name);
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = &ddata->button[i];
if (button->type == TYPE_GPIO) {
int irq;
//为io口申请这里的type即为TYPE_GPIO 而不是adc
error =
devm_gpio_request(dev, button->gpio,
button->desc ? : "keys");
if (error < 0) {
pr_err("gpio-keys: failed to request GPIO %d, error %d\n",
button->gpio, error);
goto fail1;
}
//设置为输入
error = gpio_direction_input(button->gpio);
if (error < 0) {
pr_err("gpio-keys: failed to configure input direction for GPIO %d, error %d\n",
button->gpio, error);
gpio_free(button->gpio);
goto fail1;
}
irq = gpio_to_irq(button->gpio);
if (irq < 0) {
error = irq;
pr_err("gpio-keys: Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error);
gpio_free(button->gpio);
goto fail1;
}
//申请中断
error = devm_request_irq(dev, irq, keys_isr,
button->active_low ?
IRQF_TRIGGER_FALLING :
IRQF_TRIGGER_RISING,
button->desc ?
button->desc : "keys",
button);
if (error) {
pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
irq, error);
gpio_free(button->gpio);
goto fail1;
}
}
}
input_set_capability(input, EV_KEY, KEY_WAKEUP);
device_init_wakeup(dev, wakeup);
//注册输入设备
error = input_register_device(input);
if (error) {
pr_err("gpio-keys: Unable to register input device, error: %d\n",
error);
goto fail2;
}
//这里如果adc通道不为空的话那么久启动adc的定时器
/* adc polling work */
if (ddata->chan) {
INIT_DELAYED_WORK(&ddata->adc_poll_work, adc_key_poll);
schedule_delayed_work(&ddata->adc_poll_work,
ADC_SAMPLE_JIFFIES);
}
spdata = ddata;
sinput_dev = input;
return error;
fail2:
device_init_wakeup(dev, 0);
fail1:
wake_lock_destroy(&ddata->wake_lock);
while (--i >= 0)
del_timer_sync(&ddata->button[i].timer);
fail0:
platform_set_drvdata(pdev, NULL);
return error;
}
static int keys_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
int i;
device_init_wakeup(dev, 0);
for (i = 0; i < ddata->nbuttons; i++)
del_timer_sync(&ddata->button[i].timer);
if (ddata->chan)
cancel_delayed_work_sync(&ddata->adc_poll_work);
input_unregister_device(input);
wake_lock_destroy(&ddata->wake_lock);
sinput_dev = NULL;
spdata = NULL;
return 0;
}
#ifdef CONFIG_PM
static int keys_suspend(struct device *dev)
{
struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
int i;
ddata->in_suspend = true;
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = ddata->button + i;
if (button->wakeup)
enable_irq_wake(gpio_to_irq(button->gpio));
}
}
return 0;
}
static int keys_resume(struct device *dev)
{
struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
int i;
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = ddata->button + i;
if (button->wakeup)
disable_irq_wake(gpio_to_irq(button->gpio));
}
preempt_disable();
/* for call resend_irqs, which may call keys_isr */
if (local_softirq_pending())
do_softirq();
preempt_enable_no_resched();
}
ddata->in_suspend = false;
return 0;
}
static const struct dev_pm_ops keys_pm_ops = {
.suspend = keys_suspend,
.resume = keys_resume,
};
#endif
static struct platform_driver keys_device_driver = {
.probe = keys_probe,
.remove = keys_remove,
.driver = {
.name = "rk-keypad",
.owner = THIS_MODULE,
.of_match_table = rk_key_match,
#ifdef CONFIG_PM
.pm = &keys_pm_ops,
#endif
}
};
根据设备数匹配注册按键驱动
module_platform_driver(keys_device_driver);
“`