Android驱动——audio输入输出插拔检测

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 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
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值