以Linux/Android主板而言,I2S是ASoC中的CPU Platform驱动。一般情况下需要同时搭上Codec和Machine驱动才能够启用。启用后会在/proc/asound/cards中查看到多出一个声卡。(如果硬件上I2S确实没有连接Codec,那么一般CPU厂家内核中会有 Plublic Machine 的Machine驱动来保证单独的I2S也可以被使用起来)
以Allwinner H3的I2S0为例,由于板子I2S0没有连接Codec,需要按照以下配置启用该I2S:
其中 SoC daudio0 tdm interface for SUNXI chips 为ASoC中的CPU Platform驱动, Daudio0 Public Machine for SUNXI chips 为ASoC中的 Machine + 虚拟Codec驱动。
对于Allwinner的平台而言还要确定sys_config.fex中的相应配置有没有选中,相应的GPIO有无占用。
这样编译烧写系统后, cat /proc/asound/cards 可以查看到多了一个声卡。
使用I2S就是按照普通声卡的方式进行使用,比如Android下的tinyalsa都可以做简单的播放录制等等。
更多使用方法见: Android音频底层调试-基于tinyalsa 。
I2S中的一般常用的bit有16 24 32,这些I2S输出的CLK都是32个。播放16bit时,放到了前32bit的前16bit;播放32bit一般是刚好是32bit;而播放24bit时,需要把数据按照32bit传递给设备节点,也是前24bit。如果使用tinyalsa播放一个真24bit数据时,没有转换成32bit下传时播放会出现噪声。
I2S硬件输出的波形如下:
当然还有一点,一些宣称支持到32bit的,实际输出的是时候可能会按照24bit输出(当低8bit丢弃了),比如Allwinner H3。或许只是kernel驱动的问题。
<完>
GXL:BL1:9ac50e:bb16dcFEAT:BDFC31BC:0POC:3RCY:0EMMC:0READ:00.00.0CHK:0TE: 257051
BL2 Built : 11:58:42, May 27 2017.
gxl gc3c9a84 - xiaobo.gu@droid05
set vdd cpu_a to 1120 mv
set vdd cpu_b to 1050 mv
set vddee to 1000 mv
Board ID = 4
CPU clk: 1200MHz
DQS-corr enabled
DDR scramble enabled
DDR3 chl: Rank0+1 [url=home.php?mod=space&uid=162986]@[/url] 912MHz - PASS
Rank0: 1024MB(auto)-2T-13
Rank1: 1024MB(auto)-2T-13
DataBus test pass!
AddrBus test pass!
-s
Load fip header from eMMC, src: 0x0000c200, des: 0x01400000, size: 0x00004000
aml log : R2048 check pass!
New fip structure!
Load bl30 from eMMC, src: 0x00010200, des: 0x01700000, size: 0x0000d600
aml log : R2048 check pass!
Load bl31 from eMMC, src: 0x00020200, des: 0x01700000, size: 0x0002c600
aml log : R2048 check pass!
Load bl33 from eMMC, src: 0x00050200, des: 0x01700000, size: 0x00053400
aml log : R2048 check pass!
NOTICE: BL3-1: v1.0(release):a625749
NOTICE: BL3-1: Built : 11:25:15, Aug 25 2017
[BL31]: GXL CPU setup!
NOTICE: BL31: BL33 decompress pass
mpu_config_enable:ok
[Image: gxl_v1.1.3243-377db0f 2017-09-07 11:28:58 qiufang.dai@droid07]
OPS=0x04
8d 85 8 c5 d3 cb 99 9e 8c aa b0 61 [0.845055 Inits done]
secure task start!
high task start!
low task start!
ERROR: Error initializing runtime service opteed_fast
U-Boot 2015.01-00010-gfe36fb9 (Mar 02 2018 - 19:00:28)
DRAM: 2 GiB
Relocation Offset is: 76ed9000
register usb cfg[0][1] = 0000000077f5d330
[CANVAS]canvas init
vpu: error: vpu: check dts: FDT_ERR_BADMAGIC, load default parameters
vpu: clk_level = 7
vpu: set clk: 666667000Hz, readback: 666660000Hz(0x300)
vpu: vpu_clk_gate_init_off
vpp: vpp_init
MMC: aml_priv->desc_buf = 0x0000000073ed9640
aml_priv->desc_buf = 0x0000000073edb960
SDIO Port B: 0, SDIO Port C: 1
emmc/sd response timeout, cmd8, status=0x1ff2800
emmc/sd response timeout, cmd55, status=0x1ff2800
init_part() 293: PART_TYPE_AML
[mmc_init] mmc init success
dtb magic 71b104da
aml log : R2048 check pass!
Amlogic multi-dtb tool
Multi dtb detected
Multi dtb tool version: v2 .
Support 2 dtbs.
aml_dt soc: gxl platform: p230 variant: 2g
dtb 0 soc: gxl plat: p230 vari: 1g
dtb 1 soc: gxl plat: p230 vari: 2g
Find match dtb: 1
start dts,buffer=0000000073ede1b0,dt_addr=0000000073ee91b0
Amlogic multi-dtb tool
Multi dtb detected
Multi dtb tool version: v2 .
Support 2 dtbs.
aml_dt soc: gxl platform: p230 variant: 2g
dtb 0 soc: gxl plat: p230 vari: 1g
dtb 1 soc: gxl plat: p230 vari: 2g
Find match dtb: 1
parts: 10
00: logo 0000000002000000 1
01: recovery 0000000002000000 1
02: rsv 0000000000800000 1
03: tee 0000000000800000 1
04: crypt 0000000002000000 1
05: misc 0000000002000000 1
06: boot 0000000002000000 1
07:system 0000000050000000 1
08: cache 0000000020000000 2
09: data ffffffffffffffff 4
init_part() 293: PART_TYPE_AML
eMMC/TSD partition table have been checked OK!
check pattern success
mmc env offset: 0x27400000
In:serial
Out: serial
Err: serial
reboot_mode=cold_boot
hpd_state=1
[1080p60hz] is invalid for cvbs.
set hdmitx VIC = 16
config HPLL = 2970000
HPLL: 0xc000027b
config HPLL done
j = 4 vid_clk_div = 1
hdmitx: set enc for VIC: 16
HDMITX-DWC addr=0x10004006 rd_data=0x40
Error: HDMITX-DWC exp_data=0xff mask=0x9f
rx version is 2.0 div=10
hdmtix: set audio
hdmitx phy setting done
[store]To run cmd[emmc dtb_read 0x1000000 0x40000]
_verify_dtb_checksum()-917: calc 96094b61, store 96094b61
_verify_dtb_checksum()-917: calc 96094b61, store 96094b61
dtb_read()-1039: total valid 2
dtb_read()-1106: do nothing
aml log : R2048 check pass!
Amlogic multi-dtb tool
Multi dtb detected
Multi dtb tool version: v2 .
Support 2 dtbs.
aml_dt soc: gxl platform: p230 variant: 2g
dtb 0 soc: gxl plat: p230 vari: 1g
dtb 1 soc: gxl plat: p230 vari: 2g
Find match dtb: 1
vpp: vpp_pq_load pq val error !!!
Net: Phy 0 not found
dwmac.c9410000amlkey_init() enter!
[EFUSE_MSG]keynum is 4
[BL31]: tee size: 0
Start read misc partition datas!
info->attemp_times = 0
info->active_slot = 0
info->slot_info[0].bootable = 1
info->slot_info[0].online = 1
info->slot_info[1].bootable = 0
info->slot_info[1].online = 0
info->attemp_times = 0
attemp_times = 0
active slot = 0
wipe_data=successful
wipe_cache=successful
upgrade_step=2
[OSD]load fb addr from dts
[OSD]fb_addr for logo: 0x7f851000
[OSD]load fb addr from dts
[OSD]fb_addr for logo: 0x7f851000
[CANVAS]addr=0x7f851000 width=3840, height=2160
amlkey_init() enter!
amlkey_init() 71: already init!
[EFUSE_MSG]keynum is 4
[BL31]: tee size: 0
[BL31]: tee size: 0
[BL31]: tee size: 0
[BL31]: tee size: 0
[BL31]: tee size: 0
[KM]Error:f[key_manage_query_size]L507:key[deviceid] not programed yet
gpio: pin GPIOAO_2 (gpio 102) value is 1
InUsbBurn
noSof
Hit Enter or space or Ctrl+C key to stop autoboot -- : 0
[imgread]szTimeStamp[2018030720035817]
[imgread]secureKernelImgSz=0xd88800
aml log : R-2048 check pass!
aml log : R2048 check pass!
aml log : R2048 check pass!
ee_gate_off ...
mmc env offset: 0x27400000
Writing to MMC(1)... done
## Booting Android Image at 0x01080000 ...
reloc_addr =73f5e400
copy done
Kernel command line: buildvariant=user
load dtb from 0x1000000 ......
Amlogic multi-dtb tool
Single dtb detected
Uncompressing Kernel Image ... OK
kernel loaded at 0x01080000, end = 0x021d4598
Loading Ramdisk to 73894000, end 73ec7000 ... OK
Loading Device Tree to 000000001fff2000, end 000000001ffff6b8 ... OK
fdt_instaboot: no instaboot image
Starting kernel ...
uboot time: 3883528 us
[0.000000@0] Initializing cgroup subsys cpu
[0.000000@0] Initializing cgroup subsys cpuacct
[0.000000@0] Linux version 3.14.29-00007-gff93835 (jenkins@slave2) (gcc version 4.9.3 (Ubuntu/Linaro 4.9.3-13ubuntu2) ) #1 SMP PREEMPT Wed Mar 7 19:32:34 CST 2018
[0.000000@0] CPU: AArch64 Processor [410fd034] revision 4
[0.000000@0] no prop version_code
[0.000000@0] bootconsole [earlycon0] enabled
[0.000000@0] Reserved memory: incorrect alignment of CMA region
[0.000000@0] [amvecm] pq string error !!!
[0.187655@0] genirq: Setting trigger mode 8 for irq 241 failed (gic_set_type+0x0/0xbc)
[0.190157@0] genirq: Setting trigger mode 8 for irq 242 failed (gic_set_type+0x0/0xbc)
[0.198344@0] genirq: Setting trigger mode 8 for irq 241 failed (gic_set_type+0x0/0xbc)
[0.206249@0] genirq: Setting trigger mode 8 for irq 242 failed (gic_set_type+0x0/0xbc)
domain-0 init dvfs: 4
[0.228385@0] tv_vout: chrdev devno 266338304 for disp
[0.242559@0] 1507 node need 400ms
[0.927350@0] Initramfs unpacking failed: junk in compressed archive
[1.020166@1] mtdoops: mtd device (mtddev=name/number) must be supplied
[1.150815@1] amlogic-new-usb3 d0078080.usb3phy: This phy has no usb port
[1.157031@1] ATVR driver - init called!
[1.166026@0] get_storage_dev return 1
[1.305368@1] DI-di_probe: get flag_cma error.
[1.307366@1] PPMGRDRV: err: ppmgr_driver_probe called
efuse_pw_en: 0x7
WARNING! efuse bits is disabled
Enable A53 JTAG to AO
[1.338469@1] cectx aocec: no hdmirx reg resource
[1.338498@1] cectx aocec: no hhi reg resource
[1.557834@1] aml_snd_m8_card aml_m8_snd.47: ASoC: no source widget found for LOUTL
[1.559838@1] aml_snd_m8_card aml_m8_snd.47: ASoC: Failed to add route LOUTL ->direct ->Ext Spk
[1.568717@1] aml_snd_m8_card aml_m8_snd.47: ASoC: no source widget found for LOUTR
[1.576247@1] aml_snd_m8_card aml_m8_snd.47: ASoC: Failed to add route LOUTR ->direct ->Ext Spk
相关资源:adb工具+USB调试驱动.zip_usb调试驱动-其它工具类资源-CSDN文库
点击阅读全文
打开CSDN,阅读体验更佳
参与评论 请先 登录 后发表或查看评论
智能家居-斐讯N1安装篇_ITPaint的博客
2、 开启ADB调试 N1 用 HDMI 连显示器,正常开机后用鼠标点击斐讯 N1 官方固件的那个版本号 4 次,出现 【ADB调试开启】的字样即可,如下图 3、选择2,N1降级 4、输入 .\fastboot.exe reboot 重启设备 二、刷入官改 1、安装USB烧...
斐讯N1救砖指南!值得收藏_ITPaint的博客_n1救砖
一、准备工作 1、拆除底部四个螺,在胶垫下面 2、下载刷机工具USB_Burning_Tool 3、 二、刷机流程 1、打开刷机工具导入N1官改固件包,等待加载完成 2、去除 擦除flash 擦除bootloader 两个勾选。 3、连接USB双公线,短接如下图两个...
斐讯n1卡在android,斐讯N1,看本地高清电影请务必刷coreelec
斐讯N1,看本地高清电影请务必刷coreelec2019-10-11 18:38:5149点赞461收藏94评论kodi很多人都知道也在使用,但是很多朋友并不清楚coreelec下的kodi,和安卓盒子上的kodi到底有什么区别。以至于很多买了n1的朋友,觉得安卓上面的kodi也一般般。简单地说:coreelec是一个linux系统,约等于你买了一台电脑,安装了 *** 作系统,上面只运行kodi,kod...
继续访问
斐讯N1变砖终于有救了,使用ddbr恢复官改系统救砖方法
斐讯 N1 使用 ddbr 恢复官改系统 ddbr 官改镜像下载: 链接: https://pan.baidu.com/s/1IRPeEeyrdcGyAaaMDfo5kw 提取码: c388 首先按照步骤制作一个能够从 U 盘正常启动的 armbian 系统,进入后系统后执行指令 ddbr 首先输入 b(backup),在进度条开始后即可 Ctrl+C 取消。这样可以获取到 ddbr 备份的文件的名称(不同版本的 Armbian 备份出来的包名有不同) 打开 winscp 用 sftp 加你的 ssh
继续访问
斐讯N1 – 完美刷机Armbian教程_Jeans Liu的博客
为了让斐讯N1的各个硬件可以被armbian正常调度,所以需要给斐讯N1适配一套dtb文件。 dtb文件需要随着内核编译,所以不同armbian内核版本必须使用配套的dtb文件。 目前斐讯N1的dtb文件已经被armbian收录到官方源码库里,但是使用的时候linux负载会显...
N1刷Android TV,贫民种草指北 篇二:N1盒子:不谈刷机,只谈使用!_眠子子 ...
最后,不推荐连鼠标,蓝牙鼠标成本高。有线麻烦手机app控制,电视盒子系统听说可以下载斐讯遥控控制(未测试成功),coreelec可以通过skbu for kodi控制(实测ok,使用也还ok),手机和N1处于同一局域网,打开app自动连接。 *** 作比较全如下图。
HP服务器放电后进不了系统,遇到奇怪的问题,armbian系统 reboot之后 卡在开机界面 进不去系统 断电重启正常...
GXL:BL1:9ac50e:bb16dcFEAT:BDFC31BC:0POC:3RCY:0EMMC:0READ:00.00.0CHK:0TE: 483967BL2 Built : 11:58:42, May 27 2017.gxl gc3c9a84 - xiaobo.gu@droid05set vdd cpu_a to 1120 mvset vdd cpu_b to 1050 ...
继续访问
arm linux开机第一屏,小白求助大神T1刷机提示成功,但开机卡在斐讯白屏界面。...
本帖最后由 taczgk 于 2020-5-28 10:42 编辑请大神帮帮忙,我的斐讯T1盒子原来用的YYF系统,刷了W大3.0和3.5系统都是开机卡在斐讯界面,刷YYF系统就卡在安卓机器人界面,打勾救砖模式都提示错误:[0x00101004]擦除bootloader/USB限流/设备识别/命令结果返回错误,不打勾的话,刷机提示刷机成功,电脑也可以识别,刷完就卡在开机界面,无法联网无法ADB,求...
继续访问
斐讯 N1 降级、刷机及 Armbian 安装 [2019.7.23]
最前 最近捡垃圾不断,之前购买了斐讯遗产 K2P(真香,最近刷了 OpenWrt)、T1(刷了电视盒子)和蜗牛星际(刷了黑群晖),最近又捡了台斐讯 N1,主要用于做一些简单的爬虫和 Adguard Home 服务。其实原先有过一台树莓派 3B,但是 emmmm 吃灰了一年,然后二手卖了。虽然近期树莓派 4 发布了,性能有较大提升,但由于价格的原因,还是 N1 香。刷机过程由于已有的几篇文章有些内容...
继续访问
手机开机卡在android画面,手机一直停在开机画面怎么解决【图文】
手机我相信人们不会不知道,这几年手机的发展速度非常地快,不知道大家平时使用手机的过程之中出现一些故障没有,相信大多数的用户是碰到过的,但是我相信大多数的手机用户是不会解决碰到的故障的。大家平时遇到最对的故障是什么呢?手机开机后一直停在开机界面这种情况相信大家都经历过吧!今天小编就教教大家如何解决这种故障。手机开机后一直停在开机界面怎么办top1:长按电源键12S左右重启手机手机在执行系统升级,刷机...
继续访问
最新发布 PHICOMM(斐讯)N1盒子 - recovery模式救砖卡登录页LOGO卡1%卡4%卡26%
PHICOMM(斐讯)N1盒子recovery模式救砖卡登录页LOGO卡1%卡4%卡26% 卡1%:需要先刷入T1_1.3T47_mod_by_webpad_v3_20180419_2.img救砖包,再重新刷 卡4%:USB供电不足。使用台式机或USB加5V(如带供电的USB分线器) 卡26%:换不卡的包,如RUSH的包...
继续访问
Initramfs unpacking failed:junk in compressed archive
使用kvm创建自定义镜像后重装云主机启动报错如下: 解决办法: 重做镜像,内存原来的2047MB改成了1024MB,其实也不知道对不对,反正做了好几遍最后一次好了。
继续访问
机顶盒ttl无法输入_请教大神,机顶盒接TTL进不了uboot模式
本帖最后由 cgl4134 于 2019-12-24 00:13 编辑更新一下... 自己刚开始折腾机顶盒,好多地方都搞不太懂.. 正在学习中..这个uboot 跟刷机包有关系,换了个潜龙的线刷包,就可以进uboot了,,不过现在uboot好像让我搞坏了, uboot模式下, usb_update system 会失败..欢迎其他小白交流学习----------------------------...
继续访问
关于解决 inittramfs unpacking failed:Decoding failed 报错
解决办法 vi /etc/initramfs-tools/initramfs.conf 更改COMPRESS=lz4以COMPRESS=gzip 保存更改 sudo update-initramfs -u
继续访问
armbian 斐讯n1_记录一下斐讯N1盒子刷Armbian的各种坑
最近搞了一个斐讯N1盒子,准备拿来刷Armbian,也就是linux。armbian使用了AArch64架构,也就是说,是armv8,安装nodejs时注意下载armv8的版本。斐讯N1盒子自带的系统是安卓系统,版本是V2.2,刷机主要参考的教程在这里:https://www.uselys.com/archives/85.html第一步,需要将盒子的系统版本降级到V2.19,方便后面刷入系统。首先...
继续访问
HDMI相关
HDMI相关基础4.4 与 9.0 差异AVMUTE 的使用hdcp异常检查 HDCP 交互:检查 ksv(视频加密密钥):HDCP 的内部检测:显示模式分析查看是否已连接电视机查看当前显示模式更改当前显示分辨率查看 EDID 信息查看显示设备的 EDID 信息查看电视机支持的最佳分辨率Android 系统对 HDMI 输出制式的设置1. 查看当前输出制式的节点命令:2. 输出制式在 Framework 层的代码位置:主要接口说明最优分辨率、颜色空间、色深的配置使用 HDMI Bist 模式验证 HDMI
继续访问
斐讯n1刷鸿蒙系统,【总结】在N1上面成功刷入armbian并启动的步骤
本帖最后由 ruojiner 于 2018-6-25 00:08 编辑用的是Armbian_5.44_S9xxx_Debian_stretch_3.14.29_server_20180601.img.xz这个镜像。TIM图片20180624230645.png (48.41 KB, 下载次数: 398)2018-6-24 23:22 上传TIM图片20180624230657.png (18.02...
继续访问
Linux学习笔记—驱动篇(一)
注:以下驱动只是均以GEC6818开发板为例进行撰写 要想写驱动我们得先了解什么是u-boot 一、什么是u-boot ? Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目。U-Boot的作用是系统引导。U-Boot从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是...
继续访问
linux initramfs启动原理
当linux选择支持initramfs方式启动,并且在initramfs source file中选择了要打包的rootfs路径以后,则会尝试以initramfs方式启动。initramfs方式会对rootfs进行压缩,和linux kernel打包在同一个镜像文件中。然后系统加载的时候uboot会把整个镜像文件都加载到内存中。以该种方式加载的rootfs,是没办法修改flash中rootfs的数...
继续访问
n1盒子救砖_斐讯N1盒子刷机救砖教程
https://codess.cc/archives/108.html自从斐讯0元购的车翻了之后,咸鱼上的斐讯产品也非常多,从路由器到空气净化器再到矿机。前几天我在撕逼鱼收了两个斐讯N1盒子,这东西原来是挖矿的,矿场坍塌倒闭之后,矿老板们大量甩卖矿机,有的甚至是全新未拆...大概70块左右包邮的价格,配置一点也不低,比市面上绝大多数的电视盒子的配置都高。最重要的是,它能解码4K! 解码真4K! 单...
继续访问
Kernel启动时间优化
1、问题描述 Linux的内核用的是3.14版本。UI层跑的是类似Qt的系统。目前Kernel从启动到init进程大概需要3.5秒时间,另外到加载U盘需要7.5秒左右时间。 由于Bootloader执行过程很快,所以不是本次需要优化的目标。 系统原始的Kernel启动部分Log如下: 15:29:01.552: [ 0.000000]Linux version 3.14.19 (g
继续访问
热门推荐 N1救砖总结帖
一、正常降级刷固件的教程在恩山论坛蛮多的就不多赘述了 正常刷机教程-来自恩山 二、变砖原因: 1.刷错T1等非N1的固件 2.刷机过程中由于各种原因断开了USB连接线(我就是手欠用的USB-Hub连接没有固定好) 三、变砖现象 四、救砖方法 ①有用焊接使用TTL直刷的,这种成本高,要求也高,还麻烦,暂不考虑,有兴趣去恩山看下帖子就好 ②我抄作业学到的: 需要的软件:USB_Burning_To...
继续访问
斐讯n1开机卡在android
斐讯n1卡在android
1. 耳机检测的硬件原理一般的耳机检测包含普通的耳机检测和带mic的耳机检测两种,这两种耳机统称为Headset,而对于不带mic的耳机,一般称之为Headphone。
对于Headset装置的插入检测,一般通过Jack即耳机插座来完成,大致的原理是使用带检测机械结构的耳机插座,将检测脚连到可GPIO中断上,当耳机插入时,耳机插头的金属会碰到检测脚,使得检测脚的电平产生变化,从而引起中断。这样就可以在中断处理函数中读取GPIO的的值,进一步判断出耳机是插入还是拔出。
而对于Headset是否带mic的检测,需要通过codec附加的micbias电流的功能。
Android耳机插拔可以有两个机制实现:
1. InputEvent
2. UEvent
其中UEvent是Android系统默认的耳机插拔机制,所以这里代码是基于UEvent实现的,对于InputEvent机制只是大概看了看,并没有具体实现。
1.1 两种机制的切换
Android默认提供了两种解决方法,那么一定也提供了两种方式的切换,这个提供切换的设置名为config_useDevInputEventForAudioJack( 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),对Android源代码进行全局搜索,可以看到它在frameworks/base/core/res/res/values/config.xml中,默认为false,即不使用InputEvent方式,另外在源码包的厂商相关的文件夹中也找到了相关的设置,如下:
/android/4.2/device/asus/flo/overlay/frameworks/base/core/res/res/values/config.xml
false
/android/4.2/device/samsung/manta/overlay/frameworks/base/core/res/res/values/config.xml
True
可以看到有些厂商的确是使用了InputEvent的方式来进行耳机检测。具体对这个变量的修改是在device下还是frameworks下我想应该都可以,device下可能更好。violet源码device/mstar/mstarnike/overlay/frameworks/base/core/res/res/values/config.xml 中,没有对config_useDevInputEventForAudioJack 设置。
1.2 Android耳机插拨检测流程
2 InputEvent
2.1 Framework层对InputEvent的处理机制
InputEvent的处理主要在frameworks/base/services/java/com/android/server/input/
InputManagerService.java中。在InputManagerService构造函数中,通过如下函数,
mUseDevInputEventForAudioJack = context.getResources().
getBoolean(R.bool.config_useDevInputEventForAudioJack)
判断当前是否通过InputEvent实现耳机插拔检测。
当Android得到InputEvent后,会调用InputManagerService.java中notifySwitch的函数,进而转至WiredAccessoryManager.java文件中的notifyWiredAccessoryChanged函数,之后的流程就和UEvent相同了,在后续会讲到。
2.2 Kernel层的处理机制
Kernel层对耳机插拔InputEvent处理主要是通过input_report_key/input_report_switch(include/linux/input.h)来实现,而在实际使用中,ASOC已经为我们封装好了相应Jack接口函数,只要符合规范就可以拿来使用。下面列出几个常用的接口函数(/sound/soc/soc-jack.c)。
int snd_soc_jack_new(structsnd_soc_codec *codec,
const char *id, int type, struct snd_soc_jack *jack)
生成一个新的jack对象,定义其被检测的类型,即可能插入的设备类型。一般定义为SND_JACK_HEADSET,其余也可以根据接口支持种类添加SND_JACK_LINEOUT,SND_JACK_AVOUT等。
这个函数中调用了snd_jack_new,而在snd_jack_new中可以看到调用 input_allocate_device()分配了input device,就可以在后续产生input event了。
int snd_soc_jack_add_pins(structsnd_soc_jack *jack, int count, struct snd_soc_jack_pin *pins)
将之前定义好的pins加入dapm widgets中,方便dapm统一管理。这一步和InputEvent没有一定联系,可以不调用,主要是可以将耳机插座定义为widgets加入dapm进行省电管理。
viod snd_soc_jack_report(structsnd_soc_jack *jack, int status, int mask)
汇报jack插拔状态,主要完成以下两个工作:
a) 根据插入拔出状态更新前面通过snd_soc_jack_add_pins加入的dapm pin的状态,对其进行上电下电管理。
b) 调用snd_jack_report,在其中通过input_report_key/input_report_switch来向上层汇报input event。
基于上面的函数,可以用以下做法来实现基于InputEvent机制的耳机插拔检测:
a) snd_soc_jack_new 创建jack对象
b) snd_soc_jack_add_pins将其加入到dapm wigets中
c) 通过request irq申请耳机插拔中断,在中断处理函数中通过检测线高低电平判断耳机是插入还是拔出,通过读取codec寄存器来判断是headset还是headphone
d) 根据判断结果调用snd_soc_jack_report发送InputEvent
此外,ASOC还提供了一个封装好的函数来实现上述c)和d)步骤的功能:
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios)
该函数通过标准GPIO驱动申请GPIO及GPIO对应中断,并提供了统一的中断处理函数来汇报事件。此函数只适用于耳机中断接至GPIO且GPIO驱动为Linux标准驱动的情况下,并且不支持mic检测。
3. UEvent
3.1 Switch 基本原理
Switch是Android引进的新的驱动,目的是用于检测一些开关量,比如检测耳机插入、检测 USB 设备插入等。Switch在sysfs文件系统中创建相应entry,用户可以通过sysfs与之交互; 此外还可以通过uevent机制与之交互, 从而检测switch状态。
3.1.1 Switch的实现
Switch class在Android中实现为一个module,可动态加载;而具体的switch gpio则是基于 platform device框架。代码在drivers\switch\switch_class.c和drivers\switch\switch_gpio.c中。其中switch_class.c实现了一个switch class,而switch_gpio.c则是这个class中的一个device,即针对gpio的一个switch设备。
switch_class.c文件创建了一个switch_class,实现了内核的switch机制,提供支持函数供其他switch device驱动调用。
static int __init switch_class_init(void){
return create_switch_class()}
static void __exit switch_class_exit(void){
class_destroy(switch_class)}
module_init(switch_class_init)
module_exit(switch_class_exit)
init函数调用create_switch_class->class_create创建switch_class设备类。相对应 exit则是销毁这个设备类。
该文件导出两个函数供其他switch设备驱动调用,分别是注册switch设备switch_dev_register和注销switch_dev_unregister(drivers/switch/switch_class.c)。
int switch_dev_register(struct switch_dev *sdev)
{
int ret
if (!switch_class) {
ret = create_switch_class()
if (ret <0)
return ret
}
sdev->index = atomic_inc_return(&device_count)
sdev->dev = device_create(switch_class, NULL,
MKDEV(0, sdev->index), NULL, sdev->name)
if (IS_ERR(sdev->dev))
return PTR_ERR(sdev->dev)
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
dev_set_drvdata(sdev->dev, sdev)
sdev->state = 0
return 0
err_create_file_2:
device_remove_file(sdev->dev, &dev_attr_state)
err_create_file_1:
device_destroy(switch_class, MKDEV(0, sdev->index))
printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name)
return ret
}
EXPORT_SYMBOL_GPL(switch_dev_register)
void switch_dev_unregister(struct switch_dev *sdev)
{
device_remove_file(sdev->dev, &dev_attr_name)
device_remove_file(sdev->dev, &dev_attr_state)
dev_set_drvdata(sdev->dev, NULL)
device_destroy(switch_class, MKDEV(0, sdev->index))
}
EXPORT_SYMBOL_GPL(switch_dev_unregister)
然后是两个sysfs *** 作函数(state_show和name_show),分别用于输出switch device的name和state。当用户读取sysfs中对应的switch entry(/sys/class/switch/<dev_name>/name和/sys/class/switch/<dev_name>/state)时候,系统会自动调用这两个函数向用户返回switch设备的名称和状态。
static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct switch_dev *sdev = (struct switch_dev *)dev_get_drvdata(dev)
if (sdev->print_state) {
int ret = sdev->print_state(sdev, buf)
if (ret >= 0)
return ret
}
return sprintf(buf, "%d\n", sdev->state)
}
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct switch_dev *sdev = (struct switch_dev *)dev_get_drvdata(dev)
if (sdev->print_name) {
int ret = sdev->print_name(sdev, buf)
if (ret >= 0)
return ret
}
return sprintf(buf, "%s\n", sdev->name)
}
可见,这两个函数就是直接调用对应的switch_dev中的print_state和print_name函数;如果没有定义这两个函数,则调用sprintf把信息打印到buf缓冲区里。
最后是 switch_set_state 函数,该函数是内核内部使用,并不为用户调用,它完成的功能主要是两件事: 调用name_show和state_show输出switch设备名称和状态至sysfs文件系统;发送uevent通知用户switch device的信息(名称和状态) 。
switch_gpio.c文件基于switch class实现了一个gpio的switch设备驱动,其实现的原理如下: 基于platform device/driver框架,在probe函数中完成初始化,包括获取gpio的使用权限,设置gpio方向为输入,注册switch_dev设备,为gpio分配中断,指定中断服务程序,初始化一个gpio_switch_work工作,最后读取gpio初始状态。
当 GPIO 引脚状态发生变化时,则会触发中断,在中断服务程序中调用schedule_work,这个被schedule的work即前面初始化的gpio_switch_work,最后这个work被执行,在gpio_switch_work函数中读取当前gpio电平,调用 switch_set_state更新sysfs并通过 uevent通知上层应用。
这个设备驱动只实现了print_state函数:switch_gpio_print_state,没有实现 print_name函数。当gpio_switch_work执行的时候,里面调用switch_set_state->switch_gpio_print_state输出GPIO状态到 sysfs。
3.1.2 Switch模块的用户接口
sysfs文件为sys/class/switch/<dev_name>/name, sys/class/switch/<dev_name>/state,uevent环境变量为SWITCH_NAME=<name>, SWITCH_STATE=<state>。sysfs文件系统和uevent机制。
UEvent机制比较简单,它基于switch driver,switch driver会在Android建立耳机插拔的目录/sys/devices/virtual/switch/h2w,在此目录下有个设备结点名为state,driver通过更新state的值,从而通知Android上层耳机状态的改变。
3.2 Framework层对UEvent的处理机制
Android在frameworks/base/services/java/com/android/server/WiredAccessoryManager. java中实现针对UEvent的机制。
在这个文件中,从UEventObserver中继承了类WiredAccessoryObserver,在makeObservedUEventList中将要观察的事件加入到UEvent系统中:
if(!mUseDevInputEventForAudioJack)
{
uei = new UEventInfo(NAME_H2W,BIT_HEADSET, BIT_HEADSET_NO_MIC)
……
}
可以看到,只有当不使用InputEvent时才添加UEvent事件,NAME_H2W就是headphone对应的switch driver的名字。BIT_HEADSET和BIT_HEADSET_NO_MIC是state结点的两个值,分别表示有mic和无mic的耳机。
当UEvent事件到来时,类WiredAccessoryObserver中重载的onUEvent函数会被回调,从而调用updateStateLocked(devPath,name, state) ,其中state的值就是通过/sys/devices/virtual/switch/h2w/state结点来获得。
最后,程序会进入setDeviceStateLocked函数中处理,在setDeviceStateLocked中根据state的值设置device,然后调用mAudioManager.setWiredDeviceConnectionState,最后进入AudioPolicyManagerBase::setDeviceConnectionState。
3.3 Kernel层的机制
前面说过,基于UEvent的耳机检测机制需要实现一只switch driver,它会建立一个用于耳机插拔检测的目录/sys/devices/virtual/switch/h2w,在此目录下有个设备结点名为state,switch driver通过更新state的值,从而通知Android上层耳机状态的改变。
switch driver的目录在Linux kernel的drivers/staging/android/switch目录下,可以从目录名称中看到这只driver是为了Android专门产生的。在switch目录下有两个已有文件,switch_class.c是switch driver的内部实现,它提供了switch driver所需的一些API;switch_gpio.c是一个例子,它实现了一个基于GPIO中断的switch driver。
另外,在drivers/switch目录下也有同样的文件,不同之处是两者在Android下生成的结点的位置不同,如果要按照drivers/switch目录下的switch driver来实现,需要更改WiredAccessoryManager.java文件。
下面讲讲如何添加switch driver。添加switch driver很简单,可以仿照switch_gpio.c,大致步骤如下:
a) 在drivers/staging/android/switch目录下新建一个platform driver,其中包含一个全局变量struct switch_dev sdev,即要注册的switch device。
b) 在platformdriver的probe函数中调用switch_dev_register将前面的sdev注册到系统中。
c) 申请用于耳机检测的中断处理函数。对于耳机插拔来说,由于用户的插拔快慢等可能产生多次中断,所以一般是在中断处理函数中实现一个延时工作队列,即INIT_DELAYED_WORK,在队列的回调函数中来进行实际判断。
d) 当中断发生后,通过switch_set_state设置state节点的值,这个值要和WiredAccessoryManager.java文件中定义的一致,可参看BIT_HEADSET和BIT_HEADSET_NO_MIC的定义。目前是0表示无耳机插入,1表示带Mic的耳机,2表示不带Mic的耳机。switch_set_state这个函数调用了kobject_uevent_env/kobject_uevent,这两个函数就是kernel通过uevent来通知user space的核心函数了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)