背景
目前公司一个产品要实现:一个带按键手咪同时要两种功能,连续三次按键呼sip电话,正常按下使用ptt对讲功能,为此要区分按键的方式从而触发不同功能。
驱动框架和关键结构体的可以参考这个老哥,还算比较详细,推荐:https://blog.csdn.net/wind0419/article/details/120270021
驱动位置:$(TOP_DIR)/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c
按键功能实现脚本
按键功能实现的脚本位置:package/base-files/files/etc/rc.button/ptt。主要有几个参数, ACTION和SEEN,分别代表按键动作(按下/抬起)和上次按键到本次按键按下之间的时间。另外PRESSED_TIMES代表连续三次按键按下,pressed代表按键按下,released代表按键松开
. /lib/functions.sh
if [ "$PRESSED_TIMES" -eq 1 ]
then
echo "AUDIO:3times" > dev/console
cat /www/ptt_backdial| while read line
do
#echo -e $line > dev/console
if [ "$line" = "0" ]
then
# echo "status = 1 call" > dev/console
echo "1" >/www/ptt_backdial
else
# echo "status = 0 hang up" > dev/console
echo "0" >/www/ptt_backdial
fi
done
sync
fi
if [ "$THREE_RELEASED" -eq 1 ]
then
# echo "AUDIO:3times Released" > dev/console
echo "0" > /script/ptt_push
sync
fi
if [ "${ACTION}" = "pressed" ] ; then
# aplay /lib/signal-audio/ptt_waring.wav
# echo "AUDIO-PTT:pressed" > dev/console
# sync
echo "1" > /script/ptt_push
aplay /lib/signal-audio/ptt_waring.wav
fi
if [ "${ACTION}" = "released" ] ; then
echo "0" > /script/ptt_push
# echo "AUDIO-PTT:released" > dev/console
exit 0
fi
return 0
驱动分析
1、button_hotplug_fill_event能够一直读取按键状态,并将状态通知到用户空间,消息封装完毕后使用netlink将消息发送到应用层,例如:bh_event_add_var(event, 0, “ACTION=%s”, event->action),会向应用层传递的变量为ACTION,该变量的值为event->action。
162 static int button_hotplug_fill_event(struct bh_event *event)
163 {
164 int ret;
165
166 ret = bh_event_add_var(event, 0, "HOME=%s", "/");
167 if (ret)
168 return ret;
169
170 ret = bh_event_add_var(event, 0, "PATH=%s",
171 "/sbin:/bin:/usr/sbin:/usr/bin");
172 if (ret)
173 return ret;
174
175 ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button");
176 if (ret)
177 return ret;
178
179 ret = bh_event_add_var(event, 0, "ACTION=%s", event->action);
180 if (ret)
181 return ret;
182
183 ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name);
184 if (ret)
185 return ret;
186
187 if (event->type == EV_SW) {
188 ret = bh_event_add_var(event, 0, "TYPE=%s", "switch");
189 if (ret)
190 return ret;
191 }
192 /******将三次按键是否触发的变量的PRESSED_TIMES和THREE_RELEASED的值传递给应用层******/
197 #if ENABLE_AUDIO
198 ret = bh_event_add_var(event, 0, "PRESSED_TIMES=%d", event->times_flag);
199 if (ret)
200 return ret;
201
202 ret = bh_event_add_var(event, 0, "THREE_RELEASED=%d", event->released_flag);
203 if (ret)
204 return ret;
205 #endif
206
207 ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());
208
209 return ret;
210 }
2、具体事件上报位于button_hotplug_create_event,判断如果是按下pressed事件,会额外检查是否是已经连续按了两次,根据check_ptt_button_pressed函数的返回值确定给times_flag和released_flag赋值,当这两个变量值为真(1)时候,button_hotplug_fill_event向应用层传递的值中就会包含PRESSED_TIMES和THREE_RELEASED的值,这两个值一一对应
240
241 static int button_hotplug_create_event(const char *name, unsigned int type,
242 unsigned long seen, int pressed)
243 {
244 struct bh_event *event;
245
246 //printk("~~~~~~~~~~~seen - dbata_seen = %ld~~~~~~~~~~~\n", seen);
247 pr_debug(PFX "create event, name=%s, seen=%lu, pressed=%d\n",
248 name, seen, pressed);
249
250 event = kzalloc(sizeof(*event), GFP_KERNEL);
251 if (!event)
252 return -ENOMEM;
253
254 event->name = name;
255 event->type = type;
256 event->seen = seen;
257 event->action = pressed ? "pressed" : "released";
258
259 #if ENABLE_AUDIO
260 if (! strcmp(event->action, "pressed")) {
261 if (check_ptt_button_pressed(event)) {
262 event->times_flag = 1;
263 connect_flag = 1;
264 } else {
265 event->times_flag = 0;
266 }
267 }
268
269 if (! strcmp(event->action, "released")) {
270 if (connect_flag) {
271 event->released_flag = 1;
272 connect_flag = 0;
273 } else {
274 event->released_flag = 0;
275 }
276 }
277 #endif
278
279 INIT_WORK(&event->work, (void *)(void *)button_hotplug_work);
280 schedule_work(&event->work);
281
282 return 0;
283 }
3、其中check_ptt_button_pressed函数就是为了检测是否是三次连续按下,实现如下:
144 #if ENABLE_AUDIO
145 static int check_ptt_button_pressed(struct bh_event *event)
146 {
147 //printk("\n--> seen = %ld -->\n", event->seen);
/******由于HZ=100,也就是时钟节拍数每增加100等于1秒,而按键据上次按键时间这个参数seen由事件触发函数 button_hotplug_create_event传递而来,根据实际测试结果当连续按键时间小于30的节拍数就会累加pressed_time******/
148 if (event->seen < 30) {
149 ++pressed_time;
150 } else {
151 //printk("--> gpio_keys_button_dev --> pressed_time = %d , seen = %ld,action = %s\n", pressed_time, event->seen, event->action);
152 pressed_time = 0;
153 }
154 if (pressed_time == 2) {
155 pressed_time = 0;
156 return 1;
157 }
158 return 0;
159 }
160 #endif
4、主要是修改了gpip-button驱动中按键按下时间seen的检测,原先是按秒来计数,内核时钟节拍数unsigned long类型数据整除HZ以后对于0-2之间的秒数都当作1s对待,而根据测试反馈三次按键之间时间太长,所以直接根据系统当前时间节拍数与上次按键时候记录的时间节拍数之差比较,目前定的差值在30,差不多在30/100秒,在现有按键的物理机械特性下还是能和正常按下区别开:
310 static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata)
311 {
312 unsigned int type = bdata->b->type ?: EV_KEY;
313 int state = gpio_button_get_value(bdata);
314 unsigned long seen = jiffies;
315
316 pr_debug(PFX "event type=%u, code=%u, pressed=%d\n",
317 type, bdata->b->code, state);
318
319 /* is this the initialization state? */
320 if (bdata->last_state == -1) {
321 /*
322 * Don't advertise unpressed buttons on initialization.
323 * Just save their state and continue otherwise this
324 * can cause OpenWrt to enter failsafe.
325 */
326 if (type == EV_KEY && state == 0)
327 goto set_state;
328 /*
329 * But we are very interested in pressed buttons and
330 * initial switch state. These will be reported to
331 * userland.
332 */
333 } else if (bdata->last_state == state) {
334 /* reset asserted counter (only relevant for polled keys) */
335 bdata->count = 0;
336 return;
337 }
338
339 if (bdata->count < bdata->threshold) {
340 bdata->count++;
341 return;
342 }
343
344 if (bdata->seen == 0)
345 bdata->seen = seen;
346
347 //button_hotplug_create_event(button_map[bdata->map_entry].name, type,
348 // (seen - bdata->seen) / HZ, state);
/****** 其中可以根据 (seen - bdata->seen) / HZ就可以计算出按键时间,而我需要更加精确的时间,
所以秒数对我来说无法满足应用场景,所以更改为了时钟节拍数差。这个时钟节拍数差值就是为了提高按键
的使用手感 ******/
349 #if ENABLE_AUDIO
350 button_hotplug_create_event(button_map[bdata->map_entry].name, type,
351 (seen - bdata->seen), state);
352 bdata->seen = seen;
353 #endif
354 set_state:
355 bdata->last_state = state;
356 bdata->count = 0;
357 }
358