草庐IT

Android Switch驱动的耳机检测

你好,工程师 2023-06-20 原文

        在 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);

有关Android Switch驱动的耳机检测的更多相关文章

  1. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  2. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  3. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  4. ruby - 检测由 RSpec、Ruby 运行的代码 - 2

    我想知道我的代码是否在rspec下运行。这可能吗?原因是我正在加载一些错误记录器,这些记录器在测试期间会被故意错误(expect{x}.toraise_error)弄得乱七八糟。我查看了我的ENV变量,没有(明显的)测试环境变量的迹象。 最佳答案 在spec_helper.rb的开头添加:ENV['RACK_ENV']='test'现在您可以在代码中检查RACK_ENV是否经过测试。 关于ruby-检测由RSpec、Ruby运行的代码,我们在StackOverflow上找到一个类似的问题

  5. ruby - 使用 Ruby Daemons gem 检测停止 - 2

    我正在使用rubydaemongem。想知道如何向停止操作添加一些额外的步骤?希望我能检测到停止被调用,并向其添加一些额外的代码。任何人都知道我如何才能做到这一点? 最佳答案 查看守护程序gem代码,它似乎没有用于此目的的明显扩展点。但是,我想知道(在守护进程中)您是否可以捕获守护进程在发生“停止”时发送的KILL/TERM信号...?trap("TERM")do#executeyourextracodehereend或者你可以安装一个at_exit钩子(Hook):-at_exitdo#executeyourextracodehe

  6. ruby - Ruby 脚本如何检测到它正在 irb 中运行? - 2

    我有一个定义类的Ruby脚本。我希望脚本执行语句BoolParser.generate:file_base=>'bool_parser'仅当脚本作为可执行文件被调用时,而不是当它被irbrequire(或通过-r在命令行上传递)时。我可以用什么来包装上面的语句,以防止它在我的Ruby文件加载时执行? 最佳答案 条件$0==__FILE__...!/usr/bin/ruby1.8classBoolParserdefself.generate(args)p['BoolParser.generate',args]endendif$0==_

  7. Ruby 无法检测字符串中的换行符 - 2

    我有以下字符串,我想检测那里的换行符。但是Ruby的字符串方法include?检测不到它。我正在运行Ruby1.9.2p290。我哪里出错了?"/'ædres/\nYour".include?('\n')=>false 最佳答案 \n需要在双引号内,否则无法转义。>>"\n".include?'\n'=>false>>"\n".include?"\n"=>true 关于Ruby无法检测字符串中的换行符,我们在StackOverflow上找到一个类似的问题: h

  8. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  9. ruby - 重新连接 tcpsocket(或如何检测已关闭的套接字) - 2

    我有一个连接到服务器的ruby​​tcpsocket客户端。在发送数据之前如何检查套接字是否已连接?我是否尝试“拯救”断开连接的tcpsocket,重新连接然后重新发送?如果是这样,有没有人有一个简单的代码示例,因为我不知道从哪里开始:(我很自豪我设法在rails中获得了一个持久连接的客户端tcpsocket。然后服务器决定杀死客户端,一切都崩溃了;)编辑我已经使用此代码解决了一些问题-如果未连接,它将尝试重新连接,但如果服务器已关闭则不会处理这种情况(它将继续重试)。这是正确方法的开始吗?谢谢defself.write(data)begin@@my_connection.write(

  10. css - 检测到 Sass 更改但 style.css 仅在我保存时每 5 到 7 次被覆盖 - 2

    我在一台Windows764位机器上使用Sass和Ruby(最新版本),我正在我的家庭服务器上处理一个共享文件夹。(但是,我不得不承认问题本身也出现在服务器上,因为我试图安装Ruby并直接-watch服务器上的文件)。问题如下:如果我第一次保存,检测到变化,我的style.css被直接覆盖。之后,我总是需要保存多达7次才能覆盖style.css。每次都会检测到更改,但不会编译任何内容。这是一个屏幕:>>>Sassiswatchingforchanges.PressCtrl-Ctostop.overwritestyle.css>>>Changedetectedto:E:/Websites

随机推荐