hwclock -w
报错
问题现象
sdm660_64:/ # hwclock -w
hwclock: ioctl 4024700a: Invalid argument
分析
1. hwclock命令分析
hwclock命令是toybox的内建命令,可以修改toybox代码来调试
进入external/toybox
目录下修改toybox/toys/other/hwclock.c
diff --git a/toybox/toys/other/hwclock.c b/toybox/toys/other/hwclock.c
index 1d313e3..5d907aa 100644
--- a/toybox/toys/other/hwclock.c
+++ b/toybox/toys/other/hwclock.c
@@ -82,13 +82,25 @@ void hwclock_main()
dirtree_read("/sys/class/rtc", rtc_find);
if (!TT.fname) TT.fname = "/dev/misc/rtc";
}
+ xprintf("fd=%d,TT.fname%s\n", fd, TT.fname);
if (fd == -1) fd = xopen(TT.fname, flag);
+ xprintf("fd=%d,TT.fname%s\n", fd, TT.fname);
// Get current time in seconds from rtc device. todo: get subsecond time
if (!w) {
char *s = s;
+ xprintf("RTC_RD_TIME\n");
xioctl(fd, RTC_RD_TIME, &tm);
+ xprintf("tm_sec=%d\n", tm.tm_sec);
+ xprintf("tm_min=%d\n", tm.tm_min);
+ xprintf("tm_hour=%d\n", tm.tm_hour);
+ xprintf("tm_mday=%d\n", tm.tm_mday);
+ xprintf("tm_mon=%d\n", tm.tm_mon);
+ xprintf("tm_year=%d\n", tm.tm_year);
+ xprintf("tm_wday=%d\n", tm.tm_wday);
+ xprintf("tm_yday=%d\n", tm.tm_yday);
+ xprintf("tm_isdst=%d\n", tm.tm_isdst);
if (TT.utc) s = xtzset("UTC0");
if ((time = mktime(&tm)) < 0) error_exit("mktime failed");
if (TT.utc) {
@@ -108,7 +120,17 @@ void hwclock_main()
/* The value of tm_isdst is positive if daylight saving time is in effect,
* zero if it is not and negative if the information is not available.
* todo: so why isn't this negative...? */
+ xprintf("RTC_SET_TIME\n");
tm.tm_isdst = 0;
+ xprintf("tm_sec=%d\n", tm.tm_sec);
+ xprintf("tm_min=%d\n", tm.tm_min);
+ xprintf("tm_hour=%d\n", tm.tm_hour);
+ xprintf("tm_mday=%d\n", tm.tm_mday);
+ xprintf("tm_mon=%d\n", tm.tm_mon);
+ xprintf("tm_year=%d\n", tm.tm_year);
+ xprintf("tm_wday=%d\n", tm.tm_wday);
+ xprintf("tm_yday=%d\n", tm.tm_yday);
+ xprintf("tm_isdst=%d\n", tm.tm_isdst);
xioctl(fd, RTC_SET_TIME, &tm);
} else if (toys.optflags & FLAG_s) {
tzone.tz_minuteswest = timezone / 60 - 60 * daylight;
执行编译命令
make toybox -j4
将本地编译的toybox push到手机里面
# adb push toybox /system/bin/toybox2
# adb shell
sdm660_64:/ # toybox2 hwclock -w
fd=-1,TT.fname/dev/rtc0
fd=3,TT.fname/dev/rtc0
RTC_SET_TIME
tm_sec=49
tm_min=45
tm_hour=19
tm_mday=24
tm_mon=7
tm_year=121
tm_wday=2
tm_yday=235
tm_isdst=0
hwclock: ioctl 4024700a: Invalid argument
由此可以判定是/dev/rtc0
这个驱动节点有问题。并且是在进行ioctl RTC_SET_TIME
时发生的问题。
2. /dev/rtc0
驱动节点分析
观察进行ioctl RTC_SET_TIME
时发生了什么。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qa8YNNY6-1629809627988)(https://note.youdao.com/yws/res/b/WEBRESOURCE7b9c259a0e11b0e7db40166ff22225eb)]
这里可以看到,调用到了rtc_set_time
函数
修改如下代码,增加debug log
diff --git a/msm-4.4/drivers/rtc/interface.c b/msm-4.4/drivers/rtc/interface.c
index 9cad172..7bf4d88 100644
--- a/msm-4.4/drivers/rtc/interface.c
+++ b/msm-4.4/drivers/rtc/interface.c
@@ -61,19 +61,25 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
+ dev_warn(&rtc->dev, "tm value: %d-%d-%d %d:%d:%d\n",
+ tm->tm_year + 1900, tm->tm_mon + 1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec);
err = rtc_valid_tm(tm);
if (err != 0)
return err;
err = mutex_lock_interruptible(&rtc->ops_lock);
+ dev_warn(&rtc->dev, "mutex_lock_interruptible,err=%d\n", err);
if (err)
return err;
if (!rtc->ops)
err = -ENODEV;
- else if (rtc->ops->set_time)
+ else if (rtc->ops->set_time) {
+ dev_warn(&rtc->dev, "err = rtc->ops->set_time(rtc->dev.parent, tm);\n");
err = rtc->ops->set_time(rtc->dev.parent, tm);
- else if (rtc->ops->set_mmss64) {
+ } else if (rtc->ops->set_mmss64) {
time64_t secs64 = rtc_tm_to_time64(tm);
err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
@@ -82,6 +88,7 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
err = rtc->ops->set_mmss(rtc->dev.parent, secs64);
} else
err = -EINVAL;
+ dev_warn(&rtc->dev,"ifxxx err=%d\n", err);
pm_stay_awake(rtc->dev.parent);
mutex_unlock(&rtc->ops_lock);
diff --git a/msm-4.4/drivers/rtc/qpnp-rtc.c b/msm-4.4/drivers/rtc/qpnp-rtc.c
index bafcebb..8baf15d 100644
--- a/msm-4.4/drivers/rtc/qpnp-rtc.c
+++ b/msm-4.4/drivers/rtc/qpnp-rtc.c
@@ -113,6 +113,7 @@ qpnp_rtc_set_time(struct device *dev, struct rtc_time *tm)
value[2] = (secs >> 16) & 0xFF;
value[3] = (secs >> 24) & 0xFF;
+ dev_err(dev, "Seconds value to be written to RTC = %lu\n", secs);
dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
修改后的rtc_set_time
函数
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
dev_warn(&rtc->dev, "tm value: %d-%d-%d %d:%d:%d\n",
tm->tm_year + 1900, tm->tm_mon + 1,
tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec);
err = rtc_valid_tm(tm);
if (err != 0)
return err;
err = mutex_lock_interruptible(&rtc->ops_lock);
dev_warn(&rtc->dev, "mutex_lock_interruptible,err=%d\n", err);
if (err)
return err;
if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_time) {
dev_warn(&rtc->dev, "err = rtc->ops->set_time(rtc->dev.parent, tm);\n");
err = rtc->ops->set_time(rtc->dev.parent, tm);
} else if (rtc->ops->set_mmss64) {
time64_t secs64 = rtc_tm_to_time64(tm);
err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
} else if (rtc->ops->set_mmss) {
time64_t secs64 = rtc_tm_to_time64(tm);
err = rtc->ops->set_mmss(rtc->dev.parent, secs64);
} else
err = -EINVAL;
dev_warn(&rtc->dev,"ifxxx err=%d\n", err);
pm_stay_awake(rtc->dev.parent);
mutex_unlock(&rtc->ops_lock);
/* A timer might have just expired */
schedule_work(&rtc->irqwork);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);
刷机后复现问题,抓取log
# adb shell
sdm660_64:/ # logcat -b kernel | grep -i rtc
01-01 07:28:27.330 0 0 I msm_serial c170000.serial: uartclk = 1843200
01-01 07:28:27.364 0 0 I msm_serial c16f000.serial: uartclk = 19200000
01-01 07:28:27.384 0 0 I msm_serial c1b0000.serial: uartclk = 19200000
01-01 07:28:28.790 0 0 I qcom,qpnp-rtc 800f000.qcom,spmi: qcom,pm660@0:qcom,pm660_rtc: rtc core: registered qpnp_rtc as rtc0
01-01 07:28:29.515 0 0 I qcom,qpnp-rtc 800f000.qcom,spmi: qcom,pm660@0:qcom,pm660_rtc: setting system clock to 1970-01-01 07:28:29 UTC (26909)
08-24 20:07:46.101 0 0 W rtc0 : tm value: 2021-8-24 20:7:46
08-24 20:07:46.101 0 0 W rtc0 : mutex_lock_interruptible,err=0
08-24 20:07:46.105 0 0 W rtc0 : ifxxx err=-22
08-24 20:15:03.147 0 0 W rtc0 : tm value: 2021-8-24 20:15:3
08-24 20:15:03.147 0 0 W rtc0 : mutex_lock_interruptible,err=0
有mutex_lock_interruptible,err=0
和ifxxx err=-22
这两个log,但是没有err = rtc->ops->set_time(rtc->dev.parent, tm);
。
说明没有走这个if语句
else if (rtc->ops->set_time) {
dev_warn(&rtc->dev, "err = rtc->ops->set_time(rtc->dev.parent, tm);\n");
err = rtc->ops->set_time(rtc->dev.parent, tm);
这个rtc->ops->set_time
似乎是空的
但是根据上面log中的qcom,qpnp-rtc
两行log(这里再列在下面方便表述),高通的qcom,qpnp-rtc
驱动已经注册了rtc0
01-01 07:28:28.790 0 0 I qcom,qpnp-rtc 800f000.qcom,spmi: qcom,pm660@0:qcom,pm660_rtc: rtc core: registered qpnp_rtc as rtc0
01-01 07:28:29.515 0 0 I qcom,qpnp-rtc 800f000.qcom,spmi: qcom,pm660@0:qcom,pm660_rtc: setting system clock to 1970-01-01 07:28:29 UTC (26909)
根据高通的qpnp_rtc
驱动代码看
static int qpnp_rtc_probe(struct platform_device *pdev)
{
const struct rtc_class_ops *rtc_ops = &qpnp_rtc_ro_ops;
...//省略
if (rtc_dd->rtc_write_enable == true)
rtc_ops = &qpnp_rtc_rw_ops;
...//省略
/* Register the RTC device */
rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
rtc_ops, THIS_MODULE);
if (IS_ERR(rtc_dd->rtc)) {
dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
__func__, PTR_ERR(rtc_dd->rtc));
rc = PTR_ERR(rtc_dd->rtc);
goto fail_rtc_enable;
}
...//省略
}
这个rtc_dd->rtc_write_enable
很关键,如果是true
,.set_time = qpnp_rtc_set_time
才会被注册
根据上面的代码可以看出,当设备树中的qcom,qpnp-rtc-write
被设置为true
时,满足rtc_dd->rtc_write_enable == true
的条件。
按如下方式修改设备树,即可
diff --git a/msm-4.4/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/msm-4.4/arch/arm/boot/dts/qcom/msm-pm660.dtsi
index 56f8bfb..09b64eb 100755
--- a/msm-4.4/arch/arm/boot/dts/qcom/msm-pm660.dtsi
+++ b/msm-4.4/arch/arm/boot/dts/qcom/msm-pm660.dtsi
@@ -168,7 +168,7 @@
compatible = "qcom,qpnp-rtc";
#address-cells = <1>;
#size-cells = <1>;
- qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-write = <1>;
qcom,qpnp-rtc-alarm-pwrup = <0>;
qcom,pm660_rtc_rw@6000 {