当前位置:   article > 正文

Android驱动——audio输入输出插拔检测_android "sys/class/extcon" dock

android "sys/class/extcon" dock

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 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>;
		......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我拿到的驱动代码是通过定时器加工作队列去轮询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));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

这是方法一,实现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事件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这是方法二,实现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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

三、插拔事件处理并广播

/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>
  • 1
  • 2

\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) {
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/319128
推荐阅读
相关标签
  

闽ICP备14008679号