3588平台内核在某个阶段之后,如果启动硬光标之后,容易在一些场景下概率出现光标消失的问题。且无法恢复,只有重新启动。根据此问题现象,可以确定根本原因在内核,下面介绍一下此问题,以及如何解决和规避光标消失问题。
在3588平台,客户经常反馈光标时不时就消失了,可能和插拔显示器,休眠唤醒,开关显示器有关系。当鼠标消失之后,没有任何方法来正常打开鼠标,只有重启系统。
根据问题描述
“只有重启能够恢复”,能够说明问题在内核 “插拔显示器,休眠唤醒,开关显示”。能够说明和RK平台的VOP有关
而RK平台的VOP驱动主要在 文件 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
于是调试此文件即可
留意这笔提交
Author: Andy Yan <andy.yan@rock-chips.com> Date: Fri Feb 25 20:31:08 2022 +0800 drm/rockchip: vop2: A workaround for PD_CLUSTER0 off The internal PD of VOP2 on rk3588 take effect immediately for power up and take effect by vsync for power down. And the PD_CLUSTER0 is a parent PD of PD_CLUSTER1/2/3, we may have this use case: Cluster0 is attached to VP0 for HDMI output, Cluster1 is attached to VP1 for MIPI DSI, When we enable Cluster1 on VP1, we should enable PD_CLUSTER0 as it is the parent PD, event though HDMI is plugout, VP1 is disabled, the PD of Cluster0 should keep power on. When system go to suspend: (1) Power down PD of Cluster1 before VP1 standby(the power down is take effect by vsync) (2) Power down PD of Cluster0 But we have problem at step (2), Cluster0 is attached to VP0. bus VP0 is in standby mode, as it is never used or hdmi plugout. So there is no vsync, the power down will never take effect. According to IC designer: We must power down all internal PD of VOP before we power down the global PD_VOP. So we get this workaround: We we found a VP is in standby mode when we want power down a PD is attached to it, we release the VP from standby mode, than it will run a default timing and generate vsync. Than we can power down the PD by this vsync. After all this is done, we standby the VP at last. Signed-off-by: Andy Yan <andy.yan@rock-chips.com> Change-Id: Ib9be8628f07d783c6bc3b7678c5eebfc63aabe1c
可以发现RK曾经为了解决某一问题做了一次workaround修复。
再看如下函数
static void vop2_power_domain_get(struct vop2_power_domain *pd) { if (pd->parent) vop2_power_domain_get(pd->parent); spin_lock(&pd->lock); if (pd->ref_count == 0) { if (pd->vop2->data->delayed_pd) cancel_delayed_work(&pd->power_off_work); vop2_power_domain_on(pd); } pd->ref_count++; spin_unlock(&pd->lock); } static void vop2_power_domain_put(struct vop2_power_domain *pd) { spin_lock(&pd->lock); /* * For a nested power domain(PD_Cluster0 is the parent of PD_CLuster1/2/3) * the parent power domain must be enabled before child power domain * is on. * * So we may met this condition: Cluster0 is not on a activated VP, * but PD_Cluster0 must enabled as one of the child PD_CLUSTER1/2/3 is enabled. * when all child PD is disabled, we want disable the parent * PD(PD_CLUSTER0), but as module CLUSTER0 is not attcthed on a activated VP, * the turn off operation(which is take effect by vsync) will never take effect. * so we will see a "wait pd0 off timeout" log when we turn on PD_CLUSTER0 next time. * * So we have a check here */ if (--pd->ref_count == 0 && vop2_power_domain_can_off_by_vsync(pd)) { if (pd->vop2->data->delayed_pd) schedule_delayed_work(&pd->power_off_work, msecs_to_jiffies(2500)); else vop2_power_domain_off(pd); } spin_unlock(&pd->lock); if (pd->parent) vop2_power_domain_put(pd->parent); }
留意这个注释
+ /* + * @lock: protect power up/down procedure. + * power on take effect immediately, + * power down take effect by vsync. + * we must check power_domain_status register + * to make sure the power domain is down before + * send a power on request. + * + */
可以看到RK需要确保 窗口的poweron是必须要在窗口已经poweroff下才能进行的。
所以可以留意这个使用引用计数变量:
pd->ref_count
也就是说,RK用引用计数的方式来统计vop的窗口是否已经正常poweroff了,如果引用计数不为0,说明窗口是正常打开的,故不会主动再次打开窗口
所以RK drm/rockchip: vop2: A workaround for PD_CLUSTER0 off 这笔提交出了问题。
留意如下注释和提交
According to IC designer: We must power down all internal PD of VOP before we power down the global PD_VOP.
代码如下:
if (vp) { ret = clk_prepare_enable(vp->dclk); if (ret < 0) DRM_DEV_ERROR(vop2->dev, "failed to enable dclk for video port%d - %d\n", vp->id, ret); crtc = &vp->rockchip_crtc.crtc; VOP_MODULE_SET(vop2, vp, standby, 0); vop2_power_domain_off(pd); vop2_cfg_done(crtc); vop2_wait_power_domain_off(pd); reinit_completion(&vp->dsp_hold_completion); vop2_dsp_hold_valid_irq_enable(crtc); VOP_MODULE_SET(vop2, vp, standby, 1); ret = wait_for_completion_timeout(&vp->dsp_hold_completion, msecs_to_jiffies(50)); if (!ret) DRM_DEV_INFO(vop2->dev, "wait for vp%d dsp_hold timeout\n", vp->id); vop2_dsp_hold_valid_irq_disable(crtc); clk_disable_unprepare(vp->dclk); }
这里是有一个漏洞,也就是这笔workaround的补丁,会主动关闭vop,但是关闭vop的时候,并没有给引用计数清零。所以窗口因为这笔workaround提交被异常关闭了,就很难再被打开了。
解决问题的方法如下:
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 996afa881289..e648a23bcc9f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -3454,6 +3454,7 @@ static void vop2_power_domain_off_by_disabled_vp(struct vop2_power_domain *pd) crtc = &vp->rockchip_crtc.crtc; VOP_MODULE_SET(vop2, vp, standby, 0); vop2_power_domain_off(pd); + pd->ref_count = 0; vop2_cfg_done(crtc); vop2_wait_power_domain_off(pd);
现在我们知道,光标消失和RK的一笔workaround有关,细心看一下这个补丁可以发现如下:
if (pd->data->id == VOP2_PD_CLUSTER0 || pd->data->id == VOP2_PD_CLUSTER1 || pd->data->id == VOP2_PD_CLUSTER2 || pd->data->id == VOP2_PD_CLUSTER3) { phys_id = ffs(pd->data->module_id_mask) - 1; win = vop2_find_win_by_phys_id(vop2, phys_id); vp_id = ffs(win->vp_mask) - 1; vp = &vop2->vps[vp_id]; } else { DRM_DEV_ERROR(vop2->dev, "unexpected power on pd%d\n", ffs(pd->data->id) - 1); }
也就是只有plane是cluster0/1/2/3的情况下,workaround才生效,那么针对内核,可以设置光标层不使用cluster层即可,方法如下:
&vp0 { rockchip,plane-mask = <(1 << ROCKCHIP_VOP2_CLUSTER0 | 1 << ROCKCHIP_VOP2_ESMART0)>; rockchip,primary-plane = <ROCKCHIP_VOP2_ESMART0>; cursor-win-id = <ROCKCHIP_VOP2_ESMART1>;; };
这里拿esmart1来举例,可以使用任意的非cluster层即可。
如果使用修改引用计数的方法来解决问题,那么实际上还是有概率会把鼠标层关闭,但下一次drm_atomic_check_only的时候,还是会正常打开。那么现象为:
概率发现光标消失(在插拔显示器,休眠唤醒的情况下),但是动一下鼠标光标会正常显示。
考虑到此情况下,影响很小,不算bug,无需再次修改。
如果使用规避方法,此影响也不复存在
如果当前现象仍需要提高体验,可以进一步定制,也就是识别如果识别是光标层,且光标绑定在crtc上时,就将其设置为可见。而非不可见。但个人觉得没多大必要。
@@ -4131,6 +4132,8 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, struct drm_plane_sta plane->name, state->src_x >> 16, state->src_y >> 16, state->src_w >> 16, state->src_h >> 16, state->crtc_x, state->crtc_y, state->crtc_w, state->crtc_h); + if(plane == crtc->cursor) + state->visible = true; return 0; }
这样就保证了,鼠标窗口不会因为其他特殊情况出现异常关闭的情况。
此问题为RK为了解决其他问题引入的新问题。此问题和引用计数有关,内核有很多的地方都使用引用计数,但需要留意的是,引用计数必须要考虑周全,一旦某种情况下某人修改问题不注意使用引用计数,那么整个引用计数逻辑判断就会失效,从而诞生问题。
CAN(Controller Area Network)是一种有效支持分布式控制或实时控制的串行通信网络。CAN总线是一种在汽车上广泛采用的总线协议,被设计作为汽车环境中的微控制器通讯,公司项目逐渐朝车载领域发展,而车载领域使用can的场景越来越多,这里分析一下can的调试技巧,有助于排查can总线的问题
drivers/net/can/rockchip/rockchip_can.c drivers/net/can/rockchip/rockchip_canfd.c
rk3588有两个can驱动,均可通过设备树自由选择,默认可选canfd通讯,则设备树如下配置
can0: can@fea50000 { compatible = "rockchip,can-2.0"; reg = <0x0 0xfea50000 0x0 0x1000>; interrupts = <0 341 4>; clocks = <&cru 112>, <&cru 111>; clock-names = "baudclk", "apb_pclk"; resets = <&cru 185>, <&cru 184>; reset-names = "can", "can-apb"; pinctrl-names = "default"; pinctrl-0 = <&can0m0_pins>; tx-fifo-depth = <1>; rx-fifo-depth = <6>; status = "disabled"; }; &can0 { assigned-clocks = <&cru 112>; assigned-clock-rates = <200000000>; status = "okay"; };
真的can总线,默认可以调整其时钟的频率,默认是200M。如遇到分频不精确,可以调整时钟源为其他值,从而提高can总线的采样点
ip -details -statistics link show can0 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10 link/can promiscuity 0 minmtu 0 maxmtu 0 can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 1 bitrate 250000 sample-point 0.868 tq 40 prop-seg 42 phase-seg1 43 phase-seg2 13 sjw 1 rockchip_canfd: tseg1 1..128 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 2 clock 99000000 re-started bus-errors arbit-lost error-warn error-pass bus-off 12020 0 0 12132 3438 12450 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 RX: bytes packets errors dropped overrun mcast 320320 40040 0 0 0 0 TX: bytes packets errors dropped carrier collsns 0 0 0 12020 0 0
这里有如下信息可以着重看到
a.state can总线的状态 b.bitrate 比特率 c.sample-point 采样点 d.TX,RX 数据包状态
ip link set can0 type can tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
通过设置如上参数,可以修改can的采样,也可以直接设置比特率来自动计算采样点
ip link set can0 type can bitrate 250000
ip link set can0 down ip link set can0 up type can bitrate 250000 ip link set can0 up while true do cansend can0 00000000#0000000000000000 done
上述命令可以直接打开can0设备,并发送默认数据
ip link set can0 down ip link set can0 up type can bitrate 250000 loopback on ip link set can0 up while true do cansend can0 00000000#0000000000000000 done
上述可以直接测试can的回环
# 接收can信息 candump can0 > can_recv.log & # 发送can信息 cansend can0 123#1122334455 cansend can0 5A1#11.2233.44556677.88 cansend can0 1F334455#1122334455667788
因为can的数据通过net skb上报,所以解析skb数据即可确定接收和发送的can数据信息。如下函数
static void skb_dump(struct sk_buff *skb, struct net_device *dev){ int i = 0; if(!skb || !dev){ pr_err("%s: bad param\n",__FUNCTION__); goto out; } printk(KERN_CONT "Kylin:Can packets(len=%d): ",skb->len); for (i = 0; i < skb->len; i++){ printk(KERN_CONT "%02x", *(skb->data + i)); } printk(KERN_CONT "\n"); out: return; }
然后只需要将skb_dump函数加入接收可发送的处理中即可。
对于接收到的can中断,主要有如下中断
#define RX_FINISH_INT BIT(0) #define TX_FINISH_INT BIT(1) #define ERR_WARN_INT BIT(2) #define RX_BUF_OV_INT BIT(3) #define PASSIVE_ERR_INT BIT(4) #define TX_LOSTARB_INT BIT(5) #define BUS_ERR_INT BIT(6) #define RX_FIFO_FULL_INT BIT(7) #define RX_FIFO_OV_INT BIT(8) #define BUS_OFF_INT BIT(9) #define BUS_OFF_RECOVERY_INT BIT(10) #define TSC_OV_INT BIT(11) #define TXE_FIFO_OV_INT BIT(12) #define TXE_FIFO_FULL_INT BIT(13) #define WAKEUP_INT BIT(14)
而关于BUS_ERR的错误,有如下情况
#define BIT_ERR 0 #define STUFF_ERR 1 #define FORM_ERR 2 #define ACK_ERR 3 #define CRC_ERR 4
对应情况可以进去调试即可。
关于netdev_dbg的日志,可以通过如下步骤
a. #define DEBUG 在驱动内部定义 b. 设置日志等级为8
而其他的netdev_*日志,可直接设置日志等级为8即可查看,如
dmesg -n8
针对can的问题,可以先确定中断
cat /proc/interrupts | grep can
查看硬件中断是否正常
然后再确定软中断
cat /proc/softirqs CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 HI: 2834 1 1 3 0 0 0 0 TIMER: 7791 33602 36340 57451 22095 6063 31674 5003 NET_TX: 2596 9 7 2 1 3870 2639 3561 NET_RX: 33128 100 36 307 141 3876 2662 21700 BLOCK: 7154 17766 14324 14861 13713 2332 2492 1812 IRQ_POLL: 0 0 0 0 0 0 0 0 TASKLET: 160 2 1 2 1 1 75 3 SCHED: 85621 59461 52573 172213 82530 16771 32225 13479 HRTIMER: 0 0 0 0 0 0 0 0 RCU: 77962 44178 37468 71517 57343 36644 33600 27705
确定NET_RX是否正常,如不正常,则can驱动内的netif_rx调用太频繁
1)dts中去掉所有:cpu-idle-states = <&CPU_SLEEP>; 2)设置成性能模式:echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 3)绑定can的中断到一个特定的cpu上: irq_set_affinity_hint(ndev->irq, get_cpu_mask(num_online_cpus() - 1));
打开命令提示符,输入adb --version 如果正常出现 adb版本信息,则添加成功
apt-get install android-tools-adb 2207:0006
cd ~/.android touch adb_usb.ini
在adb_usb.ini文件中写入以下内容:
0x2207
添加权限:
创建权限文件:touch /etc/udev/rules.d/70-android.rules
在70-android.rules文件中写入一下内容:
UBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="d002", MODE="0666"
重启USB服务:
chmod 666 /etc/udev/rules.d/70-android.rules service udev restart
重启adb服务:
adb kill-server adb start-server
查看adb版本:
adb --version
adb devices List of devices attached 0123456789ABCDEF device
adb shell #
adb shell ls bin dev home lost+found mnt opt root sbin sys usr boot etc lib media '(null)' proc run srv tmp var
adb shell touch testfile adb pull testfile testfile: 1 file pulled.
adb push testfile /tmp/ testfile: 1 file pushed. adb shell ls /tmp/testfile /tmp/testfile
apt install lrzsz
输入rz ---> 选择文件发送
输入 sz /tmp/testfile ----> 选择需要存放文件的地址
apt-get install samba
[work] comment=this is Linux share directory path=/ public=yes create mode=0777 directory mode=0777 guest ok = yes browseable = yes writeable = yes
systemctl restart smbd
在文件管理器下输入IP 即可跳转,账号和密码为系统的账号密码
sftp -p root@172.25.80.124
主机: 172.25.80.124 用户 kylin 密码 qwe123 端口 22
win主机右击要共享的目录,选择属性,在共享页面选择共享模式
通过mount -t cifs //192.168.1.105/User /home/kylin/temp -o username=***,password=*,vers=2.0命令挂载目录。
其中192.168.1.105/User为win主机IP及共享目录,/home/kylin/temp为挂载目标目录。username后为win主机用户名,password为对应密码。
CONFIG_CIFS=y CONFIG_CIFS_STATS=y CONFIG_CIFS_STATS2=y # CONFIG_CIFS_WEAK_PW_HASH is not set # CONFIG_CIFS_UPCALL is not set CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_CIFS_ACL=y CONFIG_CIFS_DEBUG=y # CONFIG_CIFS_DEBUG2 is not set # CONFIG_CIFS_DFS_UPCALL is not set CONFIG_CIFS_SMB2=y CONFIG_CIFS_SMB311=y CONFIG_CIFS_FSCACHE=y CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V2=y CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y CONFIG_NFS_SWAP=y # CONFIG_NFS_V4_1 is not set CONFIG_NFS_FSCACHE=y # CONFIG_NFS_USE_LEGACY_DNS is not set CONFIG_NFS_USE_KERNEL_DNS=y # CONFIG_NFSD is not set
外网源能够让客户使用apt或yum命令一键安装我们制作的软件包,不需要手动解决繁琐的依赖问题。本文将介绍如何简单快速的搭建一个apt外网源。
因为需要通过外网访问,所以我们需要一个能联通到外网的服务器。目前市面上比较常见的有腾讯云、阿里云等,我们这里使用公司购买的阿里云服务器(公网ip为47.92.75.45)。
通过ssh登录上去之后,发现我们之前的同事已经通过apache服务搭建了一个后端代理服务器,所以我们本文就不多介绍代理服务器如何搭建,可以自行百度tomcat(或其他后端软件)服务器搭建教程。
这时我们通过在网页输入外网ip,就可以看到如下的网页

这时我们是通过ip去访问的网页,通过域名访问还需要我们去购买域名并绑定ip,这个步骤就不多做解释。公司已经购买了域名,所以我们也可以通过域名访问这个网页

随后我们进入服务器默认的一个文件夹可以看到

因此可以在网页上看到对应的文件信息

所以我们需要先在外网服务器上创建一个文件夹来存放我们的软件镜像仓库

创建之后网页上也会出现对应的文件夹(由于后端软件不同,可能部分软件需要重启一下服务才能看到所做的改动)

此时,外网源服务器上的工作就告一段落,我们在外网源服务器上面创建了一个用于外网用户访问的路径,后面将会把软件源镜像放入这个路径中。
我们可以通过apt-mirror工具制作我们需要的软件源镜像。首先下载apt-mirror软件

然后配置/etc/apt/mirror.list文件

随后使用apt-mirror命令即可拉去镜像到默认文件夹中

我们可以在默认路径中看到已经下载好的软件镜像文件

到此,我们就制作好了需要的软件源镜像
由于我们不停地在更新软件包,所以可以通过定时任务去同步软件源上的更新到镜像中,这里我们可以使用crontab工具,下面简单介绍crontab软件的用法
编辑/etc/crontab 将你想要执行的脚本按照提示给的格式匹配即可

这里我们添加了最后一行,意思是在每天的凌晨1点,执行apt-mirror命令,更新软件源镜像
我们使用rsync命令同步软件源镜像,将我们的镜像文件夹同步到外网服务器上我们创建的文件夹中

这时,我们刷新网页就可以看到两个软件源的文件夹

此时,我们只需要将 deb [trusted=yes] http://download.cs2c.com.cn/kylin/rk3399/3399-base-components/kylin-desktop/ 10.1 main 加入到/etc/apt/sources.list,update后即可下载软件源中的软件包。
注意必须要加入[trusted=yes]选项,如果不加则会出现gpg密钥验证错误的问题。这是由于我们的服务器密钥没有被添加到我们本地需要安装软件包机器的密钥列表中,长沙外网源是通过kylin-keyring软件包将密钥添加到系统当中的,我们目前通过添加信任即可。
更新:可以通过 gpg -o public-file.key --export keyId 命令将外网源服务器的gpg密钥导出到public-file.key文件,然后将这个文件拷贝到你要装软件包的机器上 /etc/apt/trusted.gpg.d/ 路径下即可,这样就不需要添加 [trusted=yes] 选项了。
keyId是通过 gpg --gen-key 命令生成,已经生成的可以通过 gpg --list-keys 命令查看当前环境的gpg key。
同时还需要在外网源服务器生成InRelease文件去验证gpg密钥,命令是 gpg --clearsign --no-tty --batch --personal-digest-preferences SHA256 -o InRelease Release 密码是gpg --gen-key生成的密码。
我们也可以把rsync加入到crontab中,每天或每周定时同步外网的软件源镜像。这里由于需要同时连接内网和外网不安全,所以就不使用定时同步。
本文档主要用于配合sysapp包实现V10嵌入式多模终端模块定制功能,主要说明如何将需要安装的deb包下载到一个特定系统,然后制作成该系统的本地apt源,最终实现在无需联网的情况下可自由安装、卸载本地源中的包,并自动解决依赖关系。
新建"/etc/apt/sources.list.d/sysapp-local.list" APT源配置文件,指定"/opt/sysapp/repo"为本地仓库地址
内容如下(注意最内层目录需要空格分开):
deb [trusted=yes] file:/opt/sysapp repo/
可以先在一个带有外网源的系统上一次下载完所有包,然后再拷贝到本地
apt-get -s install xdotool x11-utils evtest

apt-get download evtest libxdo3 x11-utils xdotool

dpkg-scanpackages repo /dev/null | gzip > repo/Packages.gz

apt-get update
新建"/etc/apt/preferences.d/sysapp-local.pref" APT源优先级配置文件,指定本地仓库地址优先级为2000
内容如下:
Package: * Pin: origin "" Pin-Priority: 2000
如下可以看到qtcreator共3个版本,其中最新的版本为4.11.0-3kylin5, 优先级为500, 本地源版本为4.11.0-3-kylin4, 优先级为2000

可以看到,即使本地源中的版本不是最新的但由于优先级更高还是安装了本地源中的版本

https://www.jianshu.com/p/3abea53e4d66