背景:
公司的板子,对于HDMI的显示器热拔插不支持,只能在插入HDMI时启动才能输出,而当开机之后,再插入HDMI显示器则无输出,不知道原因。
推测如下:
1、设备树的引脚配置有误,导致插入HDMI显示器时,系统无法检测到新设备;
2、系统驱动有问题。
因此借着这个机会,学习下韦东山视频,看是否能够有所帮助。
————————————————————————————————————————————————————————
当注册了设备之后,为什么还要注册类和类设备?
是为了让mdev根据这些信息来创建设备节点。
下面分析class_device_create函数,
调用到了kobject_uevent
具体的调用暂时先不去细看,先看下思路
其中,在打印环境变量时,由于不能确定有多少个环境变量,所以使用了一个技巧
for(i=0; envp[i];i++)
do something;
即只要envp[i]存在就能判断成功,从而执行打印。
envp是char**类型的变量,打印时只写写字符串地址即可,即%s, envp[i]
总结:
当创建一个设备的时候,会导致mdev用户程序的运行,根据环境变量创建设备节点。
而当插入U盘,是如何自动挂载的。
分析:
插入U盘后的现象,自动在/dev/路径下创建sda等设备节点。
分析/etc/mdev.conf配置文件,发现配置文件正则表达式如下:
mdev.conf的格式:
: [<@||∗>]@Runaftercreatingthedevice.|∗>]@Runaftercreatingthedevice. Run before removing the device.
* Run both after creating and before removing the device.device regex: 正则表达试,表示哪一个设备
uid: onwer
gid: 组ID
octal permissions: 以八进制表示的属性,权限@: 创建设备节点之后执行命令
$: 删除设备节点之前执行命令
*: 创建设备节点之后 和 删除设备节点之前 执行命令常用正则:
. 表示任意字符,(换行符除外)
* 重复0次或者更多次
+ 重复1次或者更多次
? 重复0次或1次
[abc] 表示这些字符里的某一个
————————————————
版权声明:本文为CSDN博主「dotafast」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012617366/article/details/73557068
综上,也就是说,在韦东山的视频教程中,相当于讲解了系统在识别出设备之后做的操作,而没有去详细讲解系统如何检测到有新设备热插拔了。
而目前的问题,可能很大程度上是由于系统没有检测到新设备,或者说系统没有去检测是否有新设备。
不过,视频教程还是有帮助的,可以快速的有个入门的概念。
下面就要从HDMI入手,去尝试分析和解决问题。
首先,要先去了解HDMI接口的概念,参考博客:
https://blog.csdn.net/wurifeng0531_201702/article/details/59212811
得知,板子的HDMI接口是标准型,共19pin。但是每个pin的作用是什么,并未有详细说明,只是从图中可以看出,有3组数据传输接口,每组分为data +,data shield ,data -三个引脚。另有一组时钟引脚clock +, clock shield, clock -
以上四组引脚,均有前缀TMDS,百度百科如下:
https://baike.baidu.com/item/%E6%9C%80%E5%B0%8F%E5%8C%96%E4%BC%A0%E8%BE%93%E5%B7%AE%E5%88%86%E8%AE%AF%E5%8F%B7/23227083?fr=aladdin
个人推测如下:
每组引脚中,data + 和data -传输的信号是相反的,这样可以避免外部干扰引起的数据错误,因为差值是一定的,shield引脚起保护作用,接地。
以上共有12个引脚(pin1~pin12),其他的7个引脚作用:
https://www.cnblogs.com/cute/archive/2011/05/25/2056417.html
pin19,hot plug detect 很明显,热拔插的检测引脚,从原理图上看出接地,推测是插入时,会将某个芯片引脚拉低或者拉高,类似usb。
pin18,+5V,电源引脚
pin17, DDC/CEC Ground,
pin16,SDA
pin15,SCL
SCL的全文是Serial Clock,SDA的全文是Serial Data。
这两支脚位是用来让source(DVD) 和 display (TV)作沟通,在电视内部都有一个记忆体,存放有这台电视所支持的分辨率,例如:720P 或1080P,如果source(DVD)不知道目前所连接的电视所支持的电视分辨率是多少时,source(DVD) 就不知道要放送出什么分辨率的讯号。因此在一开始source(DVD)会透过这两支脚位去读取电视所支持的分辨率,当source(DVD) 知道后source(DVD) 才放送出符合电视分辨率的影像画面。其中SCL是时钟(CLOCK)脚位,SDA是资料脚位。
————————————————
版权声明:本文为CSDN博主「zhangzhibin160410132」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhangzhibin160410132/article/details/54408393
pin14,Reserved,保留引脚,悬空
通过链接中的说明,可以知道,HDMI显示器的识别信息存储在DDC存储器,里面保存的是EDID信息(类似于id),通过pin18给这个存储器供电,确保即使不开显示器,仍然能读到设备信息。如上图,当计算机通过HDMI接口与显示器相连接时,主机通过HDMI的第18脚(PWR_CON_PIN18)将+5V电压加到显示器的DDC存储器(EDID数据存储器)向DDC存储器供电,确保即使显示器不开机,计算机主机也能通过HDMI接口读取EDID数据。
主机开机后产生 5V_SYS并通过第18脚向显示器供电,此时显示器接收到5V电压后通过内部电路使HDMI接口第19脚HPD转变为高电平,并驱动Q1使CE导通,致使HPD_GPU也转变为高电平,主机(显卡控制器)检测到HPD为高电平时,判断显示器通过HDMI与主机连接,并通过HDMI接口的第15、16脚 DDC通道(I2C)读取显示器中的EDID数据,并使主机显卡中的TMDS信号发送电路开始工作。
当显示器与主机之间的HDMI连接断开时,主机一侧的HDP信号为低电平,主机显卡中的TMDS信号发送电路停止工作。
附加:https://www.cnblogs.com/cute/archive/2011/05/25/2056417.html
1、DDC是显示器与电脑主机进行通信的一个总线标准,其全称是:DISPLAY DATA CHNNEL。它的基本功能就是将显示器的电子档案资料信息,诸如可接收行场频范围、生产厂商、生产日期、产品序列号、产品型号、标准显示模式及其参数、所支持的DDC标准类别、EDID的版本信息等等。高版本的DDC标准总线还可以允许电脑主机直接调节显示器的基本参数,诸如亮度、对比度、行场幅度的大小、行场中心位置、色温参数等等。2、EDID数据标准:EDID(Extended Display Identification Data Standard) 就是显示器通过DDC传输给电脑主机的标准数据信息,至今已发布到第三版本,即EDID Version 3,前面分别有EDID Version 1.0,Revision 0,EDID Version 1,Revision 1,EDID Version 2,Revision 0,EDID Version 2,Revision 1等版本。就数据信息量而分,EDID分为128 BYTE和256 BYTE,将来也许会有更多数据信息量的新版EDID公布。
3、TMDS是最小化传输差分信号的英文缩写。
Silicon Image公司开始采用面板连接、数字可视接口(DVI)和高清多媒体接口(HDMI)的形式向显示行业推广其所有权标准——最小化传输差分信号 (TMDS)。在该情况下,发射端混合了具有在铜导线上降低EMI特性的更高级编码算法,从而使得接收端具有健壮的时钟恢复性能。
pin13,CEC,消费电子控制通道,通过这条通道可以控制视听设备的工作。
CEC全文为Consumer Electronics Control,用来传送工业规格的AV Link协议信号,以便支持单一遥控器操作多台AV机器。为单芯线双向串行总线。
以上是原理部分,基本上已经有个大概的认识了,下面先看下启动过程中的相关信息:
其中,有个报错由来已久,且当时并未去深究,安卓并无此报错,因此当时采用简单替换的方法,替换kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c文件后,即无此报错,下面计划尝试一下。
dmesg | grep hdmi
[ 1.374892] i2c i2c-9: of_i2c: modalias failure on /hdmi@ff940000/ports
[ 1.375564] dwhdmi-rockchip ff940000.hdmi: registered DesignWare HDMI I2C bus driver
[ 1.376487] dwhdmi-rockchip ff940000.hdmi: Detected HDMI TX controller v2.11a with HDCP (DWC HDMI 2.0 TX PHY)
[ 1.380544] rockchip-drm display-subsystem: bound ff940000.hdmi (ops 0xffffff8008ac9008)
[ 1.759339] of_get_named_gpiod_flags: can't parse 'simple-audio-card,hp-det-gpio' property of node '/hdmi-sound[0]'
[ 1.759371] of_get_named_gpiod_flags: can't parse 'simple-audio-card,mic-det-gpio' property of node '/hdmi-sound[0]'
[ 1.760638] asoc-simple-card hdmi-sound: i2s-hifi <-> ff8a0000.i2s mapping ok
[ 1.831670] #1: rockchip,hdmi
[ 5.530750] [drm:dw_hdmi_rockchip_set_property] *ERROR* failed to set rockchip hdmi connector property
[ 5.531967] [drm:dw_hdmi_rockchip_set_property] *ERROR* failed to set rockchip hdmi connector property
[ 5.533100] [drm:dw_hdmi_rockchip_set_property] *ERROR* failed to set rockchip hdmi connector property
[ 5.534108] [drm:dw_hdmi_rockchip_set_property] *ERROR* failed to set rockchip hdmi connector property
[ 5.535041] [drm:dw_hdmi_rockchip_set_property] *ERROR* failed to set rockchip hdmi connector property
[ 5.535985] [drm:dw_hdmi_rockchip_set_property] *ERROR* failed to set rockchip hdmi connector property
下面是安卓中的搜索结果(开机时未插入HDMI线):
rk3399_all:/ $ dmesg | grep hdmi
[ 1.374326] i2c i2c-9: of_i2c: modalias failure on /hdmi@ff940000/ports
[ 1.374388] dwhdmi-rockchip ff940000.hdmi: registered DesignWare HDMI I2C bus driver
[ 1.374536] dwhdmi-rockchip ff940000.hdmi: Detected HDMI TX controller v2.11a with HDCP (DWC HDMI 2.0 TX PHY)
[ 1.376963] rockchip-drm display-subsystem: bound ff940000.hdmi (ops dw_hdmi_rockchip_ops)
[ 1.628032] rk-hdmi-dp-sound hdmi-dp-sound: control 3:0:0:ELD:0 is already present
[ 1.628106] hdmi-audio-codec hdmi-audio-codec.10.auto: ASoC: Failed to add ELD: -16
[ 1.629274] rk-hdmi-dp-sound hdmi-dp-sound: multicodec <-> ff8a0000.i2s mapping ok
[ 1.629396] hdmi-audio-codec hdmi-audio-codec.10.auto: ASoC: Failed to create Playback debugfs file
[ 1.667674] #2: rk-hdmi-dp-sound
开机后插入HDMI线,此时屏幕的电源线未连接,得出如下信息,在连接好HDMI的电源线后,屏幕有图像输出:
rk3399_all:/ $ [ 154.502074] dwhdmi-rockchip ff940000.hdmi: failed to get edid
[ 154.512115] dwhdmi-rockchip ff940000.hdmi: failed to get edid
[ 154.524524] rockchip-vop ff8f0000.vop: [drm:vop_crtc_enable] Update mode to 1280x720p60, type: 11
当重新拔插HDMI线时,会在插入时打印最后一句:
[ 154.524524] rockchip-vop ff8f0000.vop: [drm:vop_crtc_enable] Update mode to 1280x720p60, type: 11
安卓的结果,很可能意味着,这个pin18和pin19是正常的,以下为实际测量结果:
未插入HDMI时,pin18电压是5v,pin19是0V;
当连接上HDMI的线后,pin19变为2.6V,linux和android系统表现一致。
因此判定,板子硬件本身没有问题,问题出在linux的驱动层或者应用设置层。
由于上述有报错的问题,所以替换了该文件,但是很显然,并不起作用,因此,想通读HDMI驱动程序,希望能够理解。
麻痹的,读了半天,没看懂
再联系韦东山的教程,还是觉得udev目录下应该有东西,于是烧写了firefly的文件系统,发现支持热插拔,证明驱动确实没有问题,查看udev目录下的文件,果然发现了一些文件,于是计划全部复制过来。
虽然画面不显示,但是插入时可以看到屏幕的背光会亮一下,查看设备节点发现,系统确实已经检测到了HDMI屏幕,可以看到/sys/class/drm/card0-HDMI-A-1/status已经变为了connected,
然后运行了一下sudo startx
屏幕就亮了,所以,基本上可以认为,其实驱动是没有问题的,问题可能出现在文件系统的设置上,由于是自己制作的文件系统,所以可能某些设置没有弄好。但是需要注意的时,此时的界面时xface4
为了弄清楚startx的工作内容,因此列出了哪些程序在执行
perfxlab@perfxlab:~$ ps
PID TTY TIME CMD
1142 ttyFIQ0 00:00:00 bash
1157 ttyFIQ0 00:00:00 psroot@perfxlab:/home/perfxlab# ps
PID TTY TIME CMD
807 ttyFIQ0 00:00:00 login
1193 ttyFIQ0 00:00:00 su
1198 ttyFIQ0 00:00:00 bash
1209 ttyFIQ0 00:00:00 startx
1228 ttyFIQ0 00:00:00 xinit
1244 ttyFIQ0 00:00:00 sh
1282 ttyFIQ0 00:00:00 dbus-launch
1302 ttyFIQ0 00:00:00 xfce4-session
1312 ttyFIQ0 00:00:00 xfwm4
1316 ttyFIQ0 00:00:01 xfce4-panel
1318 ttyFIQ0 00:00:00 Thunar
1320 ttyFIQ0 00:00:02 xfdesktop
1322 ttyFIQ0 00:00:01 applet.py
1330 ttyFIQ0 00:00:03 blueman-applet
1331 ttyFIQ0 00:00:00 light-locker
1334 ttyFIQ0 00:00:00 polkit-gnome-au
1339 ttyFIQ0 00:00:01 nm-applet
1449 ttyFIQ0 00:00:00 panel-6-systray
1450 ttyFIQ0 00:00:00 panel-2-actions
1518 ttyFIQ0 00:00:00 ps
也就是说startx启动了以上的工作,对自己的文件系统,就是以上的情况。
对于firefly的文件系统,就是另一种情况,且更加符合热插拔设备的机制。
在/etc/udev目录下,有三个文件/文件夹,其中目录hwdb.d和文件udev.conf均为空,目录rules.d中有若干文件:
firefly@firefly:/etc/udev$ ls
hwdb.d rules.d udev.conf
和hdmi有关的是:
firefly@firefly:/etc/udev$ cd rules.d/
firefly@firefly:/etc/udev/rules.d$ ls
hdmi.rules
查看该脚本:
firefly@firefly:/etc/udev/rules.d$ cat hdmi.rules
SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/hdmi-toggle"
很明显,结合教程中的mdev机制,应该是检测到drm子系统的变化之后,就去调用执行另一个脚本/usr/local/bin/hdmi-toggle:
firefly@firefly:/etc/udev/rules.d$ cat /usr/local/bin/hdmi-toggle
#!/usr/bin/env bashUSER="$(who | grep :0\) | cut -f 1 -d ' ')"
export XAUTHORITY=/home/$USER/.Xauthority
export DISPLAY=:0########### Settings ############ Use 'xrandr' to find these
DP="DP-1"
VGA="VGA-1"
HDMI="HDMI-1"
INTERNAL_DISPLAY="LVDS-1"# Check /sys/class/drm for the exact location
DP_STATUS="$(cat /sys/class/drm/card0-DP-1/status)"
VGA_STATUS="$(cat /sys/class/drm/card0-VGA-1/status)"
HDMI_STATUS="$(cat /sys/class/drm/card0-HDMI-A-1/status)"# Do no change!
EXTERNAL_DISPLAY=""# Check to see if the external display is connected
if [ "${DP_STATUS}" = connected ]; thenEXTERNAL_DISPLAY=$DP
fi
if [ "${VGA_STATUS}" = connected ]; thenEXTERNAL_DISPLAY=$VGA
fi
if [ "${HDMI_STATUS}" = connected ]; thenEXTERNAL_DISPLAY=$HDMI
fi# The external display is connected
if [ "$EXTERNAL_DISPLAY" != "" ]; then# Set the display settingsxrandrxrandr --output $EXTERNAL_DISPLAY --auto
else# Restore to single displayxrandr --output $EXTERNAL_DISPLAY --off
fiexit 0
第一行是获得当前用户名:firefly
第二、三行获取当前的环境变量
然后,定义几个变量,代表不同的输入
依次去读取设备文件,看是否status变为了connected,如果是,则设置EXTERNAL_DISPLAY为当前的输出方式,然后就去执行显示的命令:
xrandr
xrandr --output $EXTERNAL_DISPLAY --auto
其中,变量取值如下
DP="DP-1"
VGA="VGA-1"
HDMI="HDMI-1"
INTERNAL_DISPLAY="LVDS-1"
感觉也没什么,下面尝试一下
将用户名直接写死为firefly,对firefly的文件系统无影响。
将用户名直接写死为perfxlab,则依然无法显示。
但是可以将该hdmi-toggle文件内容替换为startx,这样就可以支持热插拔,但问题是插着hdmi启动和不插着启动,进入的图形界面不同。
加之,firefly的文件系统中并没有startx命令,所以推测为两个图形界面的冲突所导致的。
计划明天重新制作一个文件系统试试。制作一个纯净版的。
也顺便验证一下是否可以解决pkgwarning的问题。
************************************************************************************
在安装图形界面时,如果不使用xubuntu-desktop,而是使用ubuntu-desktop则不会出现这个bug,但是图形界面启动速度明显比较慢。
所以基本可以确定,问题出在文件系统,可能是xubuntu-desktop的bug——也可能是需要自己去设置某些东西,但是自己不知道。
然后,就找原因,firefly的文件系统没有安装startx,所以也不是这个命令的问题。
然后,找到了一篇文章,是介绍怎么切换图形界面管理程序lightdm和gdm的,觉得可能有用,于是执行了ligdm的restart命令,发现确实可以。最终解决方法如下:
在/etc/udev/rules.d路径下,添加一个文件hdmi.rules,内容为:
SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/hdmi-toggle"
在/usr/local/bin/hdmi-toggle文件中,重启该服务:
#!/usr/bin/env bash
/etc/init.d/ lightdm restart
exit 0
至此,问题解决,但是和之前说的一样,还是没深入到hdmi里面。
韦东山的教程还是可以的,比较实用。
************************************************************************************************
你以为自己懂了,其实没有,以上的方法存在问题:
每次插拔hdmi都会导致桌面重启,那么用户的窗口、文档等就会丢失,这显然不是自己想要的结果。
于是就想着,是否可以让hdmi只在第一次插拔的时候重启lightdm服务?
那么就需要对脚本进行判断,本想着使用全局变量,但是不太懂shell语法,于是想了简单的方法:使用一个文件来记录。
具体方法为:
1、在某个路径下添加flag文件,记录是否是第一次热插拔hdmi。
touch /etc/udev/rules.d/hdmistatus
2、0代表需要重启lightdm服务,1代表不需要,那么如果不插着hdmi启动,则此时应该为0。于是修改如下:
echo 0 > /etc/udev/rules.d/hdmistatuscat /usr/local/bin/hdmi-toggle
#!/usr/bin/env bash
HDMI_STATUS="$(cat /etc/udev/rules.d/hdmistatus)"
if [ "${HDMI_STATUS}" = 0 ]; then/etc/init.d/lightdm restartecho 1 > /etc/udev/rules.d/hdmistatus
fi
exit 0cat /etc/rc.local
echo 0 > /etc/udev/rules.d/hdmistatus
只要经过第一次热插拔,那么该脚本就会将hdmistatus中写为1。第二次热插拔hdmi时就不会再次重启lightdm服务。开机时,自动将该文件写0.
经过测试,这样可以解决不插着hdmi启动时的显示问题。
3、但是,如果插着hdmi启动,那么hdmistatus就会是0,如果此时热插拔仍然会造成重启lightdm服务。所以仍有bug,仍需修改。那么就需要在插着hdmi启动时,干脆也重启一下lightdm服务就行了。
cat /etc/rc.localHDMI_STATUS="$(cat /sys/class/drm/card0-HDMI-A-1/status)"
echo 0 > /etc/udev/rules.d/hdmistatus
if [ "${HDMI_STATUS}" = connected ]; thenecho 1 > /etc/udev/rules.d/hdmistatus/etc/init.d/lightdm restartecho "/etc/init.d/lightdm restart done!"
elseecho 0 > /etc/udev/rules.d/hdmistatus
fi
至此,问题彻底解决。