提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
前言
实现耳机输入输出的插拔检测功能:修改插拔驱动代码,驱动中上报inputevent事件,通过framework处理事件并发出广播;
平台:amlogic
soc:a311d
一、audio插拔检测原理图
输入检测gpio口(microphone):
输出检测gpio口(headphone):
插拔检测原理就是通过gpio口的电平变化来判断插拔状态,可以通过gpio中断、轮询gpio电平两种方式获取插拔状态的变化。
二、驱动相关代码
DTS配置gpio资源:
auge_sound {
compatible = "amlogic, g12a-sound-card";
aml-audio-card,name = "AML-AUGESOUND";
/*avout mute gpio*/
avout_mute-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>;
es7241_rst-gpios = <&gpio_ao GPIOAO_10 GPIO_ACTIVE_HIGH>;
+ /*headphone 插拔检测gpio*/
+ aml-audio-card,hp-det-gpio = <&gpio GPIOA_8 GPIO_ACTIVE_HIGH>;
+ /*micphone 插拔检测gpio*/
+ aml-audio-card,mic-det-gpio = <&gpio GPIOA_7 GPIO_ACTIVE_HIGH>;
......
}
我拿到的驱动代码是通过定时器加工作队列去轮询gpio电平来判断插拔状态的:
static void jack_timer_func(unsigned long data)
{
struct aml_card_data *card_data = (struct aml_card_data *)data;
unsigned long delay = msecs_to_jiffies(150);
schedule_work(&card_data->work);/*每隔150ms去调度一个工作队列,轮询插拔状态*/
mod_timer(&card_data->timer, jiffies + delay);
}
static void jack_work_func(struct work_struct *work)
{
flag = jack_audio_hp_detect(card_data);/*检测gpio电平*/
if (flag == -1)
goto micphone_detect;
if (card_data->hp_detect_flag != flag) {
/*如果电平有变化*/
card_data->hp_detect_flag = flag;
if (flag) {
/*方法一:使用uevent,上报同步extcon事件,会更新/sys/class/extcon/xxx/state节点的值;
但是目前上次java还是使用的switch的方式来处理uevent事件,所以上下层不兼容导致该方法无法使用;
不过文件系统下的extcon节点是可以根据插拔改变state值的*/
extcon_set_state_sync(audio_extcon_headphone, EXTCON_JACK_HEADPHONE, 1);
/*方法二:使用inputevent,上报input事件;
我的java层是兼容inputevent的,所以使用此方法上报插拔事件*/
snd_soc_jack_report(&card_data->hp_jack.jack, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE);
}
}
}
static void audio_jack_detect(struct aml_card_data *card_data)
{
init_timer(&card_data->timer);
card_data->timer.function = jack_timer_func;
card_data->timer.data = (unsigned long)card_data;
INIT_WORK(&card_data->work, jack_work_func);
jack_audio_start_timer(card_data, msecs_to_jiffies(5000));
}
这是方法一,实现extcon的API接口及步骤:
static const unsigned int headphone_cable[] = {
EXTCON_JACK_HEADPHONE,
EXTCON_NONE,
};
edev = extcon_dev_allocate(headphone_cable);//创建一个extcon数据结构
if (IS_ERR(edev)) {
pr_info("failed to allocate audio extcon headphone\n");
return;
}
edev->dev.parent = dev;
edev->name = "audio_extcon_headphone";//这将是extcon节点下name的值
dev_set_name(&edev->dev, "headphone");//设置名称,这将会是文件系统下extcon节点的名称
ret = extcon_dev_register(edev);//将extcon注册,最后会生成/sys/class/extcon/xxx/目录,下面包括name、state等文件
if (ret < 0) {
pr_info("failed to register audio extcon headphone\n");
return;
}
audio_extcon_headphone = edev;
extcon_set_state_sync(audio_extcon_headphone, EXTCON_JACK_HEADPHONE, 1);//上报一个extcon事件
这是方法二,实现inputevent的API接口及步骤:
pin_name = "Headphones";
mask = SND_JACK_HEADPHONE;
//生成一个新的jack对象,定义其被检测的类型,即拔插的设备类型
snd_soc_card_jack_new(card, pin_name, mask, &sjack->jack, &sjack->pin, 1);
snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);
/*汇报jack插拔状态,主要完成以下两个工作:
a) 根据插入拔出状态更新前面通过snd_soc_jack_add_pins加入的dapm pin的状态,对其进行上电下电管理。
b) 调用snd_jack_report,在其中通过input_report_key/input_report_switch来向上层汇报input event。*/
snd_soc_jack_report(&card_data->mic_jack.jack, SND_JACK_MICROPHONE, SND_JACK_MICROPHONE);
//snd_soc_jack_report(&card_data->mic_jack.jack, 0, SND_JACK_MICROPHONE);
三、插拔事件处理并广播
/frameworks/base/core/res/res/values/config.xml
将config_useDevInputEventForAudioJack修改为true才能使用inputevent方式上报插拔事件:
<!-- When true use the linux /dev/input/event subsystem to detect the switch changes on the headphone/microphone jack. When false use the older uevent framework. -->
<bool name="config_useDevInputEventForAudioJack">true</bool>
\frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
当底层有input事件上报后,会调用notifySwitch方法:
// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
if (DEBUG) {
Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
+ ", mask=" + Integer.toHexString(switchMask));
}
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
mWindowManagerCallbacks