学习笔记:音频之手机物理按键
2017-10-13 14:15
423 查看
(注:本文仅仅用于个人理顺代码流程思路)
手机物理按键驱动文件分析:
一、GPIO的DTS文件,代码段如下所示:
二、驱动文件gpio_keys.c:
几个重要的结构体:
1、平台驱动注册:
平台驱动结构体gpio_keys_device_driver定义:
可以看到,平台驱动的name 是“gpio-keys”,linux统一设备模型的平台驱动与设备就是通过name进行匹配的,当驱动被注册进内核,该驱动就会去检索相同name的设备,找到后就会开始执行probe回调函数。很显然,该驱动文件对应的就是前面dts文件所定义的GPIO按键。
2、probe回调函数:
以上是probe回调函数的整个执行过程,下面来具体的分析一下上述的部分函数:
gpio_keys_setup_key(pdev, input, bdata, button);
gpio_keys_gpio_work_func函数定义:
gpio_keys_gpio_report_event(bdata)函数定义:
exynos_ss_check_crash_key(button->code, state)定义如下:
三、手机的物理按键从原理图中可知,是直接接在CPU上的,底层驱动通过一个输入设备进行按键的检测,底层有个映射表(【samsung7872】【Z205】文件名:input-event-codes.h),为每个按键都分配了键值,当input设备检测到了按键按下,就会去该按键在映射表中相对应的键值,然后input子系统将获取到的键值上报给上层进行相应的处理。
手机物理按键驱动文件分析:
一、GPIO的DTS文件,代码段如下所示:
/*gpio口的DTS定义*/ gpio_keys { status = "okay"; compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&key_home &key_voldown &key_volup &key_power>; button@1 { label = "gpio-keys: KEY_HOMEPAGE"; interrupts = <4 0 0>; interrupt-parent = <&gpa1>; linux,code = <172>; gpios = <&gpa1 4 0xf>; gpio-key,wakeup = <1>; }; button@2 { label = "gpio-keys: KEY_VOLUMEDOWN"; interrupts = <6 0 0>; interrupt-parent = <&gpa1>; linux,code = <114>; gpios = <&gpa1 6 0xf>; }; button@3 { label = "gpio-keys: KEY_VOLUMEUP"; interrupts = <5 0 0>; interrupt-parent = <&gpa1>; linux,code = <115>; gpios = <&gpa1 5 0xf>; }; button@4 { label = "gpio-keys: KEY_POWER"; interrupts = <0 0 0>; interrupt-parent = <&gpa0>; linux,code = <116>; gpios = <&gpa0 0 0xf>; gpio-key,wakeup = <1>; }; };
二、驱动文件gpio_keys.c:
几个重要的结构体:
/*该结构体用来定义GPIO的按键功能*/ struct gpio_keys_platform_data { struct gpio_keys_button *buttons; //指向gpio_keys_button结构体数组,用于按键的参数设置 int nbuttons; //按键数组内元素个数 unsigned int poll_interval; //轮询时间间隔 unsigned int rep:1; //自动重复使能输入子系统 int (*enable)(struct device *dev); void (*disable)(struct device *dev); const char *name; //输入设备名 }; /*用来关联input系统*/ struct gpio_keys_drvdata { const struct gpio_keys_platform_data *pdata; struct input_dev *input; struct mutex disable_lock; struct gpio_button_data data[0]; }; /** * struct gpio_keys_button - configuration parameters * @code: input event code (KEY_*, SW_*) * @gpio: %-1 if this key does not support gpio * @active_low: %true indicates that button is considered * depressed when gpio is low * @desc: label that will be attached to button's gpio * @type: input event type (%EV_KEY, %EV_SW, %EV_ABS) * @wakeup: configure the button as a wake-up source * @debounce_interval: debounce ticks interval in msecs * @can_disable: %true indicates that userspace is allowed to * disable button via sysfs * @value: axis value for %EV_ABS * @irq: Irq number in case of interrupt keys * @gpiod: GPIO descriptor */ /*按键参数设置*/ struct gpio_keys_button { unsigned int code; int gpio; int active_low; const char *desc; unsigned int type; int wakeup; int debounce_interval; bool can_disable; int value; unsigned int irq; struct gpio_desc *gpiod; };
1、平台驱动注册:
static int __init gpio_keys_init(void) { return platform_driver_register(&gpio_keys_device_driver); }
平台驱动结构体gpio_keys_device_driver定义:
static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = gpio_keys_remove, .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, .of_match_table = of_match_ptr(gpio_keys_of_match), } }; static const struct of_device_id gpio_keys_of_match[] = { { .compatible = "gpio-keys", }, { }, };
可以看到,平台驱动的name 是“gpio-keys”,linux统一设备模型的平台驱动与设备就是通过name进行匹配的,当驱动被注册进内核,该驱动就会去检索相同name的设备,找到后就会开始执行probe回调函数。很显然,该驱动文件对应的就是前面dts文件所定义的GPIO按键。
2、probe回调函数:
/*probe回调函数*/ gpio_keys_probe(struct platform_device *pdev) |-- gpio_keys_get_devtree_pdata(dev); //从设备树中获取GPIO数据 |-- devm_input_allocate_device(dev); //分配input设备 |-- gpio_keys_open |-- gpio_keys_report_state(ddata); |-- gpio_keys_gpio_report_event(bdata); |-- gpio_get_value_cansleep(button->gpio);//读键值 |-- exynos_ss_check_crash_key(button->code, state); |-- input_event(input, type, button->code, button->value);//上报event |-- gpio_keys_close |-- gpio_keys_setup_key(pdev, input, bdata, button)//设置GPIO_key |-- INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func)//工作队列 |-- gpio_keys_gpio_work_func |-- gpio_keys_gpio_report_event(bdata);//event上报函数 |-- gpio_get_value_cansleep(button->gpio);//读键值 |-- exynos_ss_check_crash_key(button->code, state); |-- input_event(input, type, button->code, button->value);//上报event事件 |-- sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);//在sys目录下创建属性文件 |-- input_register_device(input);//input设备注册 |-- device_init_wakeup(&pdev->dev, wakeup);//设备唤醒初始化 |-- device_set_wakeup_capable(dev, val);//设备能不能被唤醒 |-- device_set_wakeup_enable(dev, val);//设备使不使用唤醒
以上是probe回调函数的整个执行过程,下面来具体的分析一下上述的部分函数:
gpio_keys_setup_key(pdev, input, bdata, button);
static int 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";/*@desc:label that will be attached to button's gpio*/ struct device *dev = &pdev->dev; irq_handler_t isr; unsigned long irqflags; int irq; int error; bdata->input = input; bdata->button = button; spin_lock_init(&bdata->lock); if (gpio_is_valid(button->gpio)) { error = devm_gpio_request_one(&pdev->dev, button->gpio, GPIOF_IN, desc); if (error < 0) { dev_err(dev, "Failed to request GPIO %d, error %d\n", button->gpio, error); return error; } 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->software_debounce = button->debounce_interval; } if (button->irq) { bdata->irq = button->irq; } else { irq = gpio_to_irq(button->gpio); if (irq < 0) { error = irq; dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", button->gpio, error); return error; } bdata->irq = irq; } INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);/*工作队列初始化,定义一个gpio_keys_gpio_work_func工作队列 函数去检测按键状态*/ 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->release_delay = button->debounce_interval; setup_timer(&bdata->release_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); /* * Install custom action to cancel release timer and * workqueue item. */ error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata); if (error) { dev_err(&pdev->dev, "failed to register quiesce action, error: %d\n", error); return error; } /* * 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; if (button->wakeup) irqflags |= IRQF_NO_SUSPEND; error = devm_request_any_context_irq(&pdev->dev, bdata->irq, isr, irqflags, desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", bdata->irq, error); return error; } return 0; }
gpio_keys_gpio_work_func函数定义:
static void gpio_keys_gpio_work_func(struct work_struct *work) { struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work.work); gpio_keys_gpio_report_event(bdata); if (bdata->button->wakeup) pm_relax(bdata->input->dev.parent); }
gpio_keys_gpio_report_event(bdata)函数定义:
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state = gpio_get_value_cansleep(button->gpio);/*获取物理按键的状态值*/ struct irq_desc *desc = irq_to_desc(gpio_to_irq(button->gpio)); if (!desc) { dev_err(input->dev.parent, "irq_desc is null! (gpio=%d)\n", button->gpio); return; } if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state\n"); return; } state = (state ? 1 : 0) ^ button->active_low; exynos_ss_check_crash_key(button->code, state);/*该函数是烧录版本的时候按键的操作函数*/ if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value);//上报event事件 } else { input_event(input, type, button->code, irqd_is_wakeup_set(&desc->irq_data) ? 1 : !!state); } input_sync(input); }
exynos_ss_check_crash_key(button->code, state)定义如下:
#ifdef CONFIG_EXYNOS_SNAPSHOT_CRASH_KEY void exynos_ss_check_crash_key(unsigned int code, int value) { static bool volup_p; static bool voldown_p; static int loopcount; static const unsigned int VOLUME_UP = KEY_VOLUMEUP; static const unsigned int VOLUME_DOWN = KEY_VOLUMEDOWN; /* Enter Forced Upload 进入版本下载操作如下: * Hold volume down key first * and then press power key twice * and volume up key should not be pressed */ if (value) { if (code == VOLUME_UP) volup_p = true; if (code == VOLUME_DOWN) voldown_p = true; if (!volup_p && voldown_p) { if (code == KEY_POWER) { pr_info ("exynos-snapshot: count for entering forced upload [%d]\n", ++loopcount); if (loopcount == 2) { panic("Crash Key"); } } } } else { if (code == VOLUME_UP) volup_p = false; if (code == VOLUME_DOWN) { loopcount = 0; voldown_p = false; } } } #endif
三、手机的物理按键从原理图中可知,是直接接在CPU上的,底层驱动通过一个输入设备进行按键的检测,底层有个映射表(【samsung7872】【Z205】文件名:input-event-codes.h),为每个按键都分配了键值,当input设备检测到了按键按下,就会去该按键在映射表中相对应的键值,然后input子系统将获取到的键值上报给上层进行相应的处理。
相关文章推荐
- 学习笔记:音频之耳机按键事件上报流程
- Cocos2d-X 学习笔记 21 CCLayer响应手机按键
- Android 学习笔记多媒体技术之 AsyncTask+实现音频播放...
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(3)
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(2)
- android 多媒体部分学习笔记九----数字音频合成
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(1)
- [cocos2d-x学习笔记][入门基础]Box-2d物理引擎的使用02制作一个简易的愤怒小鸟Demo
- 嵌入式学习ubuntu学习笔记-方便按键
- HBase学习笔记——物理模型
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(2)
- Cocos2d-x学习笔记(十五)-------->物理引擎
- iOS学习笔记——获取手机信息(UIDevice、NSBundle、NSLocale)
- 【HTML5学习笔记】9:音频和视频的嵌入
- Android(OPhone) 学习笔记 - 手机信息管理
- 字符设备驱动学习笔记----查询方式取得按键值
- 物理渲染学习笔记(二)——光的传播
- 编解码学习笔记(五):Mpeg系列——AAC音频
- SDL2.0学习笔记6--用SDL播放音频wave文件
- Andriod学习笔记:欧朋手机浏览器安卓版6.8