最近,产品基本定型了,所以开始着手升级版。说是升级版,只是在操作上面有所修改。产品需要使用旋钮进行人机交互。对linux驱动十窍通了九窍,一窍不通,所以从最低级的开始看起,然后把gpio_key.c这个东西,看了一遍又一遍。都快看吐了 。
首先修改设备树文件,这个东西是linux3.0的新东西,弄的我无限糊涂,到现在依然糊涂。我只能稍微修改一点需要的东西,让我写肯定写不出来。以后再慢慢研究。在这个文件的KEY_X0里面,把对应的,改成。因为是使用设备树这种方式,所以需要修改对应的gpio_keys_get_devtree_pdata这个函数,
修改以上两处,这个函数的改动就结束了。这样改完编译会发现有错误,因为button里面没有ckgpio这些东西。所以修改对应linux目录中include/linux/gpio_key.h
struct gpio_keys_button {
/* Configuration parameters */
unsigned int code; /* input event code (KEY_*, SW_*) */
int gpio; /* -1 if this key does not support gpio */
int active_low;
const char *desc;
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */
int wakeup; /* configure the button as a wake-up source */
int debounce_interval; /* debounce ticks interval in msecs */
bool can_disable;
int value; /* axis value for EV_ABS */
unsigned int irq; /* Irq number in case of interrupt keys */
};
改成:
struct gpio_keys_button {
/* Configuration parameters */
unsigned int code; /* input event code (KEY_*, SW_*) */
int gpio; /* -1 if this key does not support gpio */
int active_low;
const char *desc;
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */
int wakeup; /* configure the button as a wake-up source */
int debounce_interval; /* debounce ticks interval in msecs */
bool can_disable;
int value; /* axis value for EV_ABS */
unsigned int irq; /* Irq number in case of interrupt keys */
int ckgpio; /*zxf*/ 保存旋转编码器的另外一个引脚
unsigned int oldcode; 记录原始键值
unsigned int ckcode; /*zxf*/ 记录新键值
};
这样设备树就修改完了。
接下来修改中断触发方式,我只需下降沿,就可以判断是正转还是反转了,所以:
if (button->ckgpio != button->gpio)
irqflags = IRQF_TRIGGER_FALLING;对应旋钮部分
else
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;对应按键部分
因为ec11这种东西有旋钮部分,也有按钮部分,所以把对应的中断响应函数进行了区分,按钮部分不动,旋钮部分改成:
static irqreturn_t gpio_knob_gpio_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
BUG_ON(irq != bdata->irq);
if(gpio_get_value(bdata->button->ckgpio) )判断对应的另外一个引脚电平状态。
bdata->button->code = bdata->button->oldcode;
else
bdata->button->code = bdata->button->ckcode;
//printk(KERN_ALERT"gpio:%d, ckgpio:%d, check=%d", bdata->button->gpio, bdata->button->ckgpio,bdata->button->check);
if (bdata->timer_debounce)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(bdata->timer_debounce));
else
schedule_work(&bdata->work);
return IRQ_HANDLED;
}
中断分为上半部分,和下半部分,上述代码是中断的上半部分。功能就是检测另外一个引脚的点评状态,并给code赋不同的键值。
中断的下半部分,事件上报函数改成:
static void gpio_knob_gpio_report_event(struct gpio_button_data *bdata)
{
struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
input_event(input, type, button->code, 1);
//printk(KERN_ALERT"type:%d,code:%d\n", type,button->code);
input_sync(input);
input_event(input, type, button->code, 0);
//printk(KERN_ALERT"type:%d,code:%d\n", type,button->code);
input_sync(input);
}
可以看到这里面有两条重复的语句,原因是按键有按下和放开两个状态,而我的旋钮,我值捕捉到了下降沿,所以上升沿部分我自己编造一个,反正内核不知道我是认为编造的。红色部分很重要,如果只有蓝色部分,那就相当于按键一直按下,而没有弹起。cat event*会发现一直不停的输出。我在这就卡了两天,因为没有好好理解什么是key。
中断部分修改完了以后,修改gpio_keys_setup_key()函数。
if (gpio_is_valid(button->gpio)) {
error = gpio_request(button->gpio, desc);
if (error < 0) {
dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->gpio, error);
return error;
}
if (button->ckgpio != button->gpio){
error = gpio_request(button->ckgpio, desc);
if (error < 0) {
dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->ckgpio, 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->ckgpio != button->gpio){
error = gpio_direction_input(button->ckgpio);
if (error < 0) {
dev_err(dev,
"Failed to configure direction for GPIO %d, error %d\n",
button->ckgpio, error);
goto fail;
}
}
就是增加了对旋钮部分按键的配置操作。
现在编译应该可以通过,使用hexdump测试event会发现,只有一个方向的旋转才有输出。另一个方向是没有反映的,通过跟踪代码发现,是在input_handle_event()函数出现的问题:
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
红色部分就是跟踪后发现问题的地方,另一个方向旋转并不能进入到这个if语句中,再继续跟踪发现:
static inline int is_event_supported(unsigned int code, unsigned long *bm, unsigned int max)
{
return code <= max && test_bit(code, bm);
}
这个函数的test_bit函数出的问题,它会测试input_dev是否支持这个键值。这个地方卡了我两周的时间,因为不了解linux的输入子系统。我现在还不知道,可以上报的那个键值是在哪设置的,有知道的前辈请不吝告诉我。为了让input_dev支持另外一个键值,在gpio_keys_setup_key()函数增加如下代码:
set_bit(button->ckcode, input->keybit);
这样就可以了。