当前位置:首页 > 文章列表 > 文章 > linux > Linux 自带的耳机拔插检测驱动

Linux 自带的耳机拔插检测驱动

来源:良许Linux教程网 2025-01-17 12:15:53 0浏览 收藏

今天golang学习网给大家带来了《Linux 自带的耳机拔插检测驱动》,其中涉及到的知识点包括等等,无论你是小白还是老手,都适合看一看哦~有好的建议也欢迎大家在评论留言,若是看完有所收获,也希望大家能多多点赞支持呀!一起加油学习~

Linux系统默认的耳机插拔检测驱动程序被整合在声卡驱动中,这使得耳机插拔状态能够通过输入子系统进行报告。

这一功能的具体实现位于kernel-5.15版本的sound/soc/generic/simple-card-utils.c文件中。

571  int asoc_simple_init_jack(struct snd_soc_card *card,
572       struct asoc_simple_jack *sjack,
573       int is_hp, char *prefix,
574       char *pin)
575  {
576   struct device *dev = card->dev;
577   enum of_gpio_flags flags;
578   char prop[128];
579   char *pin_name;
580   char *gpio_name;
581   int mask;
582   int det;
583  
584   if (!prefix)
585    prefix = "";
586  
587   sjack->gpio.gpio = -ENOENT;
588  
589   if (is_hp) {
590    snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
591    pin_name = pin ? pin : "Headphones";
592    gpio_name = "Headphone detection";
593    mask  = SND_JACK_HEADPHONE;
594   } else {
595    snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
596    pin_name = pin ? pin : "Mic Jack";
597    gpio_name = "Mic detection";
598    mask  = SND_JACK_MICROPHONE;
599   }
600  
601   det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
602   if (det == -EPROBE_DEFER)
603    return -EPROBE_DEFER;
604  
605   if (gpio_is_valid(det)) {
606    sjack->pin.pin  = pin_name;
607    sjack->pin.mask  = mask;
608  
609    sjack->gpio.name = gpio_name;
610    sjack->gpio.report = mask;
611    sjack->gpio.gpio = det;
612    sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
613    sjack->gpio.debounce_time = 150;
614  
615    snd_soc_card_jack_new(card, pin_name, mask,
616            &sjack->jack,
617            &sjack->pin, 1);
618  
619    snd_soc_jack_add_gpios(&sjack->jack, 1,
620             &sjack->gpio);
621   }
622  
623   return 0;
624  }
625  EXPORT_SYMBOL_GPL(asoc_simple_init_jack);

第 589~593 行,【构建】用于查找设备树中 GPIO 属性的属性名称 prop。设置 pin_name 为”Headphones”,表示插孔的名称。设置 gpio_name 为 “Headphone detection”,表示 GPIO 的名称。设置 mask 为 SND_JACK_HEADPHONE,表示这是一个耳机插孔。

第 601 行,使用设备树函数 of_get_named_gpio_flags 获取与属性名称 prop 关联的 GPIO 描述符,并存储在 det 中。如果 GPIO 未定义,det 将为负数。

第 606~613 行,如果设置了检测 GPIO,那么设置结构体指针 sjack 的一些属性。设置插孔的引脚信息,比如 sjack->pin.pin 引脚名字。设置耳机插孔的一些 GPIO 关联信息,如 sjack->gpio.gpio 是表示 GPIO 描述符,sjack->gpio.invert 表示根据设备树中的属性决定是否反转 GPIO 状态,GPIO_ACTIVE_LOW 是低电平表示活动,当耳机插入时,检测脚将被拉低,说明是低有效。debounce_time 这个是设置消抖时间,防止误检测。

第 615 行,这里将耳机插孔与声卡绑定。

第 619 行,绑定 GPIO,就会触发耳机插拨事件。

函数名重定义:/include/sound/simple_card_utils.h

14  #define asoc_simple_init_hp(card, sjack, prefix) \
15   asoc_simple_init_jack(card, sjack, 1, prefix, NULL)
16  #define asoc_simple_init_mic(card, sjack, prefix) \
17   asoc_simple_init_jack(card, sjack, 0, prefix, NULL)

在声卡驱动 probe 时调用

dts 中配置声卡节点 compatible = “simple-audio-card”

734  static const struct of_device_id simple_of_match[] = {
735   { .compatible = "simple-audio-card", },
736   { .compatible = "simple-scu-audio-card",
737     .data = (void *)DPCM_SELECTABLE },
738   {},
739  };
740  MODULE_DEVICE_TABLE(of, simple_of_match);

614  static int simple_soc_probe(struct snd_soc_card *card)
615  {
616   struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
617   int ret;
618  
619   ret = asoc_simple_init_hp(card, &priv->hp_jack, PREFIX);
620   if (ret return ret;
622  
623   ret = asoc_simple_init_mic(card, &priv->mic_jack, PREFIX);
624   if (ret return ret;
626  
627   return 0;
628  }

这个驱动文件负责声卡的初始化,音频流管理,控制接口等。在第 619 行,调用了耳机检测 IO 初始化的代码。

耳机拔插上报 flow

asoc_simple_init_jack 会调用 snd_soc_card_jack_new,添加检测管脚 pins,进而一路调用下来

60  int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
61       struct snd_soc_jack *jack,
62       struct snd_soc_jack_pin *pins, unsigned int num_pins)
63  {
64   int ret;
65  
66   mutex_init(&jack->mutex);
67   jack->card = card;
68   INIT_LIST_HEAD(&jack->pins);
69   INIT_LIST_HEAD(&jack->jack_zones);
70   BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
71  
72   ret = snd_jack_new(card->snd_card, id, type, &jack->jack, false, false);
73   if (ret)
74    goto end;
75  
76   if (num_pins)
77    ret = snd_soc_jack_add_pins(jack, num_pins, pins);
78  end:
79   return soc_card_ret(card, ret);
80  }
81  EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
137  int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
138       struct snd_soc_jack_pin *pins)
139  {
140   int i;
141  
142   for (i = 0; i if (!pins[i].pin) {
144     dev_err(jack->card->dev, "ASoC: No name for pin %d\n",
145      i);
146     return -EINVAL;
147    }
148    if (!pins[i].mask) {
149     dev_err(jack->card->dev, "ASoC: No mask for pin %d"
150      " (%s)\n", i, pins[i].pin);
151     return -EINVAL;
152    }
153  
154    INIT_LIST_HEAD(&pins[i].list);
155    list_add(&(pins[i].list), &jack->pins);
156    snd_jack_add_new_kctl(jack->jack, pins[i].pin, pins[i].mask);
157   }
158  
159   /* Update to reflect the last reported status; canned jack
160    * implementations are likely to set their state before the
161    * card has an opportunity to associate pins.
162    */
163   snd_soc_jack_report(jack, 0, 0);
164  
165   return 0;
166  }
167  EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);
34  void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
35  {
36   struct snd_soc_dapm_context *dapm;
37   struct snd_soc_jack_pin *pin;
38   unsigned int sync = 0;
39  
40   if (!jack)
41    return;
42   trace_snd_soc_jack_report(jack, mask, status);
43  
44   dapm = &jack->card->dapm;
45  
46   mutex_lock(&jack->mutex);
47  
48   jack->status &= ~mask;
49   jack->status |= status & mask;
50  
51   trace_snd_soc_jack_notify(jack, status);
52  
53   list_for_each_entry(pin, &jack->pins, list) {
54    int enable = pin->mask & jack->status;
55  
56    if (pin->invert)
57     enable = !enable;
58  
59    if (enable)
60     snd_soc_dapm_enable_pin(dapm, pin->pin);
61    else
62     snd_soc_dapm_disable_pin(dapm, pin->pin);
63  
64    /* we need to sync for this case only */
65    sync = 1;
66   }
67  
68   /* Report before the DAPM sync to help users updating micbias status */
69   blocking_notifier_call_chain(&jack->notifier, jack->status, jack);
70  
71   if (sync)
72    snd_soc_dapm_sync(dapm);
73  
74   snd_jack_report(jack->jack, jack->status);
75  
76   mutex_unlock(&jack->mutex);
77  }
78  EXPORT_SYMBOL_GPL(snd_soc_jack_report);
652  void snd_jack_report(struct snd_jack *jack, int status)
653  {
654   struct snd_jack_kctl *jack_kctl;
655   unsigned int mask_bits = 0;
656  #ifdef CONFIG_SND_JACK_INPUT_DEV
657   int i;
658  #endif
659  
660   if (!jack)
661    return;
662  
663   jack->hw_status_cache = status;
664  
665   list_for_each_entry(jack_kctl, &jack->kctl_list, list)
666    if (jack_kctl->sw_inject_enable)
667     mask_bits |= jack_kctl->mask_bits;
668    else
669     snd_kctl_jack_report(jack->card, jack_kctl->kctl,
670            status & jack_kctl->mask_bits);
671  
672  #ifdef CONFIG_SND_JACK_INPUT_DEV
673   mutex_lock(&jack->input_dev_lock);
674   if (!jack->input_dev) {
675    mutex_unlock(&jack->input_dev_lock);
676    return;
677   }
678  
679   for (i = 0; i key); i++) {
680    int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
681  
682    if (jack->type & testbit)
683     input_report_key(jack->input_dev, jack->key[i],
684        status & testbit);
685   }
686  
687   for (i = 0; i if (jack->type & testbit)
691     input_report_switch(jack->input_dev,
692           jack_switch_types[i],
693           status & testbit);
694   }
695  
696   input_sync(jack->input_dev);
697   mutex_unlock(&jack->input_dev_lock);
698  #endif /* CONFIG_SND_JACK_INPUT_DEV */
699  }
700  EXPORT_SYMBOL(snd_jack_report);

第 683 行,input 上报事件,参数 1 为耳机事件类型,参数 2 为耳机事件键值,参数 3 表示耳机插拨的状态。第 696 行,同步事件。

若你要使用 Linux 自带的耳机拔插检测驱动,则需要在对应的声卡驱动的 dts 节点中声明你所使用的 GPIO 口,加载时就会自动帮你配置好检测逻辑。

Linux 自带的耳机拔插检测驱动

Linux 自带的耳机拔插检测功能有限,大部分平台都有自己的耳机检测逻辑,例如 RK 平台的耳机检测在这:

kernel/drivers/headset_observe/rockchip_headset_core.c

MTK 平台的耳机拔插检测驱动在:

kernel/drivers/misc/mediatek/accdet/

kernel/sound/soc/codecs/mt6xxx-accdet.c

到这里,我们也就讲完了《Linux 自带的耳机拔插检测驱动》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于Linux,Linux系统,Shell脚本,Linux命令,linux入门,linux教程,linux学习,嵌入式Linux的知识点!

版本声明
本文转载于:良许Linux教程网 如有侵犯,请联系study_golang@163.com删除
设计数据库架构日
下一篇
设计数据库架构日
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • AI Make Song:零门槛AI音乐创作平台,助你轻松制作个性化音乐
    AI Make Song
    AI Make Song是一款革命性的AI音乐生成平台,提供文本和歌词转音乐的双模式输入,支持多语言及商业友好版权体系。无论你是音乐爱好者、内容创作者还是广告从业者,都能在这里实现“用文字创造音乐”的梦想。平台已生成超百万首原创音乐,覆盖全球20个国家,用户满意度高达95%。
    7次使用
  • SongGenerator.io:零门槛AI音乐生成器,快速创作高质量音乐
    SongGenerator
    探索SongGenerator.io,零门槛、全免费的AI音乐生成器。无需注册,通过简单文本输入即可生成多风格音乐,适用于内容创作者、音乐爱好者和教育工作者。日均生成量超10万次,全球50国家用户信赖。
    7次使用
  •  BeArt AI换脸:免费在线工具,轻松实现照片、视频、GIF换脸
    BeArt AI换脸
    探索BeArt AI换脸工具,免费在线使用,无需下载软件,即可对照片、视频和GIF进行高质量换脸。体验快速、流畅、无水印的换脸效果,适用于娱乐创作、影视制作、广告营销等多种场景。
    6次使用
  • SEO标题协启动:AI驱动的智能对话与内容生成平台 - 提升创作效率
    协启动
    SEO摘要协启动(XieQiDong Chatbot)是由深圳协启动传媒有限公司运营的AI智能服务平台,提供多模型支持的对话服务、文档处理和图像生成工具,旨在提升用户内容创作与信息处理效率。平台支持订阅制付费,适合个人及企业用户,满足日常聊天、文案生成、学习辅助等需求。
    13次使用
  • Brev AI:零注册门槛的全功能免费AI音乐创作平台
    Brev AI
    探索Brev AI,一个无需注册即可免费使用的AI音乐创作平台,提供多功能工具如音乐生成、去人声、歌词创作等,适用于内容创作、商业配乐和个人创作,满足您的音乐需求。
    14次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码