Android Switch驱动的耳机检测

        在 Android 中添加了一个 switch 驱动,用于监测一些开关的变化,例如:HDMI、耳机的插拔检测之类的。

        驱动源码存在内核源码的 drivers/switch 中,目录下有两个主要文件:switch_class.c 和switch_gpio.c,在 switch_class.c 中创建了 switch 设备类

static int create_switch_class(void)

        实现了设备注册

int switch_dev_register(struct switch_dev *sdev)

        在这个函数中,会创建两个文件,一个是保存状态的文件,一个是保存名称的文件,其中状态就开关量的表现,在这个文件中还有一个改变状态的方法

	ret = device_create_file(sdev->dev, &dev_attr_state);
	if (ret < 0)
		goto err_create_file_1;
	ret = device_create_file(sdev->dev, &dev_attr_name);
	if (ret < 0)
		goto err_create_file_2;

        这个 switch_gpio.c 文件中,就是实现了一个由 GPIO 变化来触发的 switch 动作,当设备匹配上时,会注册一个 GPIO 中断,在中断处理函数中,可以看到它会先获取gpio的值,然后设置switch的状态

static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
	struct gpio_switch_data *switch_data =
	    (struct gpio_switch_data *)dev_id;

	schedule_work(&switch_data->work);
	return IRQ_HANDLED;
}
static void gpio_switch_work(struct work_struct *work)
{
	int state;
	struct gpio_switch_data	*data =
		container_of(work, struct gpio_switch_data, work);

	state = gpio_get_value(data->gpio);
	switch_set_state(&data->sdev, state);

	gpio_set_value(AUDIO_SWITCH_GPIO_NUM, (state ? AUDIO_SWITCH_OFF : AUDIO_SWITCH_ON));
}

        这里要注意的是,这个GPIO中断注册的时候默认选择的是低电平触发,如果是检测耳机接入,恰好耳机接入后会将 gpio 电平直接拉低,这时中断就会一直触发,可能会导致系统死机,如果遇到这种场景可将触发条件改成边沿触发就可以了。

@@ -112,7 +112,7 @@ static int gpio_switch_probe(struct platform_device *pdev)
        }

        ret = request_irq(switch_data->irq, gpio_irq_handler,
-                         IRQF_TRIGGER_LOW, pdev->name, switch_data);
+                         (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING), pdev->name, switch_data);
        if (ret < 0)
                goto err_request_irq;

        下面代码实现耳机检测的功能,注册了一个 h2w 的 switch 驱动

#include <linux/module.h>
#include <linux/switch.h>
#include <linux/platform_device.h>

#define H2W_SWITCH_GPIO_NUM   22

static struct gpio_switch_platform_data pdata = {
	.name      = "h2w",
	.gpio      = H2W_SWITCH_GPIO_NUM,
	.name_on   = "1",
	.name_off  = "0",
	.state_on  = "1",
	.state_off = "0",
};

static struct platform_device h2w_switch_dev = {
	.name = "switch-gpio",
	.dev = {
		.platform_data = &pdata,
	},
};

static int __init h2w_switch_init(void) {

	return platform_device_register(&h2w_switch_dev);
}

static void __exit h2w_switch_exit(void) {

	platform_device_unregister(&h2w_switch_dev);
}

module_init(h2w_switch_init);
module_exit(h2w_switch_exit);

        为什么命名为 h2w ?

        原因是 Android 已经给铺好路了,Android 有一个 有线访问管理 机制

        frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java,这里默认就定义了三种类型设备访问并进行管理,其中包括h2w、usb、hdmi,h2w 就是指的有线耳机。

private List<UEventInfo> makeObservedUEventList() {
	List<UEventInfo> retVal = new ArrayList<UEventInfo>();
	UEventInfo uei;

	// Monitor h2w
	if (!mUseDevInputEventForAudioJack) {
		uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);
		if (uei.checkSwitchExists()) {
			retVal.add(uei);
		} else {
			Slog.w(TAG, "This kernel does not have wired headset support");
		}
	}

	// Monitor USB
	uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL, 0);
	if (uei.checkSwitchExists()) {
		retVal.add(uei);
	} else {
		Slog.w(TAG, "This kernel does not have usb audio support");
	}

	// Monitor HDMI
	//
	// If the kernel has support for the "hdmi_audio" switch, use that.  It will be
	// signalled only when the HDMI driver has a video mode configured, and the downstream
	// sink indicates support for audio in its EDID.
	//
	// If the kernel does not have an "hdmi_audio" switch, just fall back on the older
	// "hdmi" switch instead.
	uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0, 0);
	if (uei.checkSwitchExists()) {
		retVal.add(uei);
	} else {
		uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0, 0);
		if (uei.checkSwitchExists()) {
			retVal.add(uei);
		} else {
			Slog.w(TAG, "This kernel does not have HDMI audio support");
		}
	}

	return retVal;
}

        关于这个 有线访问管理 是如何得知这个设备的,在我们设置设置这个 switch 状态的时候可以看到,它同时发送了一个 uevent 消息,然后被上层接收后进行处理。

kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);

        有线访问管理机制 就会告知 AudioManager 设备状态的改变

mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你好,工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值