zoukankan      html  css  js  c++  java
  • Qcom平台RTC驱动分析

    相关文件list:

    pm8998.dtsi ---RTC dts配置
    qpnp-rtc.c  ---qcom RTC驱动
    class.c    ---RTC相关class
    interface.c  ---相关RTC功能的接口定义
    hctosys.c    ---一开机通过RTC设置系统时间
    rtc-dev.c   ---RTC device fops接口:open、close、ioctl、poll等

    简述:

    所谓RTC(Real Time Clock),用于关机时继续计算系统日期和时间。是基于硬件的功能。也可以RTC做Alarm来设置power on/off。

    驱动分析:

    首先在dts的Document中看到两个配置项:

    - qcom,qpnp-rtc-write: This property enables/disables rtc write
                           0 = Disable rtc writes.
                           1 = Enable rtc writes.
    - qcom,qpnp-rtc-alarm-pwrup:	This property enables/disables 
                                    feature of powering up phone (from 
                                    power down state) through alarm 
                                    interrupt.
                                    If not mentioned rtc driver will 
                                    disable feature of powring-up     
                                    phone through alarm.
                                    0 = Disable powering up of phone 
                                        through alarm interrupt.
                                    1 = Enable powering up of phone 
                                        through alarm interrupt.            

    一个是是否使能写RTC时间的功能。另一个是是否支持RTC alarm开机的功能。

    接下来,再看RTC的驱动部分,从qpnp-rtc.c:

    其中根据.compatible = "qcom,qpnp-rtc"匹配成功后走到probe,probe中获取了dts中上面两条配置参数,从而进行初始化。获取RTC/Alarm相关的寄存器和资源,并通过操作寄存器enable RTC相关功能。注册了RTC中断并配置了RTC相关操作的api。

    通过dts的配置,使用不同的ops,从而实现支持write RTC与否。

    //这个是不支持write RTC的ops
    static const struct rtc_class_ops qpnp_rtc_ro_ops = {
        .read_time = qpnp_rtc_read_time,
        .set_alarm = qpnp_rtc_set_alarm,
        .read_alarm = qpnp_rtc_read_alarm,
        .alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
    };
    
    //这个是支持读写 RTC的ops,就是多了个write的接口
    static const struct rtc_class_ops qpnp_rtc_rw_ops = {
        .read_time = qpnp_rtc_read_time,
        .set_alarm = qpnp_rtc_set_alarm,
        .read_alarm = qpnp_rtc_read_alarm,
        .alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
        .set_time = qpnp_rtc_set_time,
    };
    static int qpnp_rtc_probe(struct platform_device *pdev)
    {
        const struct rtc_class_ops *rtc_ops = &qpnp_rtc_ro_ops; //默认使用不支持RTC write的ops
        int rc;
        u8 subtype;
        struct qpnp_rtc *rtc_dd;
        unsigned int base;
        struct device_node *child;
    
        rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
        if (rtc_dd == NULL)
            return -ENOMEM;
    
        rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
        if (!rtc_dd->regmap) {
            dev_err(&pdev->dev, "Couldn't get parent's regmap
    ");
            return -EINVAL;
        }
    
        /* Get the rtc write property */
        rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
                            &rtc_dd->rtc_write_enable); //读取dts中的RTC write配置
        if (rc && rc != -EINVAL) {
            dev_err(&pdev->dev,
                "Error reading rtc_write_enable property %d
    ", rc);
            return rc;
        }
    
        rc = of_property_read_u32(pdev->dev.of_node,
                            "qcom,qpnp-rtc-alarm-pwrup",
                            &rtc_dd->rtc_alarm_powerup); //读取dts中RTC alarm powerup的配置
        if (rc && rc != -EINVAL) {
            dev_err(&pdev->dev,
                "Error reading rtc_alarm_powerup property %d
    ", rc);
            return rc;
        }
    
        /* Initialise spinlock to protect RTC control register */
        spin_lock_init(&rtc_dd->alarm_ctrl_lock);
    
        rtc_dd->rtc_dev = &(pdev->dev);
        rtc_dd->pdev = pdev;
    
    
        if (of_get_available_child_count(pdev->dev.of_node) == 0) {
            pr_err("no child nodes
    ");
            rc = -ENXIO;
            goto fail_rtc_enable;
        }
    
        /* Get RTC/ALARM resources */
        for_each_available_child_of_node(pdev->dev.of_node, child) {
            rc = of_property_read_u32(child, "reg", &base);
            if (rc < 0) {
                dev_err(&pdev->dev,
                    "Couldn't find reg in node = %s rc = %d
    ",
                    child->full_name, rc);
                goto fail_rtc_enable;
            }
    
            rc = qpnp_read_wrapper(rtc_dd, &subtype,
                    base + REG_OFFSET_PERP_SUBTYPE, 1);
            if (rc) {
                dev_err(&pdev->dev,
                    "Peripheral subtype read failed
    ");
                goto fail_rtc_enable;
            }
    
            switch (subtype) {
            case RTC_PERPH_SUBTYPE:
                rtc_dd->rtc_base = base;
                break;
            case ALARM_PERPH_SUBTYPE:
                rtc_dd->alarm_base = base;
                rtc_dd->rtc_alarm_irq = of_irq_get(child, 0);
                if (rtc_dd->rtc_alarm_irq < 0) {
                    dev_err(&pdev->dev, "ALARM IRQ absent
    ");
                    rc = -ENXIO;
                    goto fail_rtc_enable;
                }
                break;
            default:
                dev_err(&pdev->dev, "Invalid peripheral subtype
    ");
                rc = -EINVAL;
                goto fail_rtc_enable;
            }
        }
    
        rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
                    rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
        if (rc) {
            dev_err(&pdev->dev, "Read from RTC control reg failed
    ");
            goto fail_rtc_enable;
        }
    
        if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
            dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered
    ");
            goto fail_rtc_enable;
        }
    
        rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
                    rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
        if (rc) {
            dev_err(&pdev->dev, "Read from  Alarm control reg failed
    ");
            goto fail_rtc_enable;
        }
        /* Enable abort enable feature */
        rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
        rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
                rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
        if (rc) {
            dev_err(&pdev->dev, "SPMI write failed!
    ");
            goto fail_rtc_enable;
        }
    
        if (rtc_dd->rtc_write_enable == true) //判断dts中配置如果要支持RTC write功能,那就重新赋值,使用将RTC RW都支持的ops接口
            rtc_ops = &qpnp_rtc_rw_ops;
    
        dev_set_drvdata(&pdev->dev, rtc_dd);
    
        /* 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)
    ",
                        __func__, PTR_ERR(rtc_dd->rtc));
            rc = PTR_ERR(rtc_dd->rtc);
            goto fail_rtc_enable;
        }
    
        /* Request the alarm IRQ */
        rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
                     qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
                     "qpnp_rtc_alarm", rtc_dd);
        if (rc) {
            dev_err(&pdev->dev, "Request IRQ failed (%d)
    ", rc);
            goto fail_req_irq;
        }
    
        device_init_wakeup(&pdev->dev, 1);
        enable_irq_wake(rtc_dd->rtc_alarm_irq);
    
        dev_dbg(&pdev->dev, "Probe success !!
    ");
    
        return 0;
    
    fail_req_irq:
        rtc_device_unregister(rtc_dd->rtc);
    fail_rtc_enable:
        dev_set_drvdata(&pdev->dev, NULL);
    
        return rc;
    }

    而在RTC shutdown时,会根据是否要支持RTC alarm开机,进行中断和寄存器的配置。

    static void qpnp_rtc_shutdown(struct platform_device *pdev)
    {
        u8 value[4] = {0};
        u8 reg;
        int rc;
        unsigned long irq_flags;
        struct qpnp_rtc *rtc_dd;
        bool rtc_alarm_powerup;
    
        if (!pdev) {
            pr_err("qpnp-rtc: spmi device not found
    ");
            return;
        }
        rtc_dd = dev_get_drvdata(&pdev->dev);
        if (!rtc_dd) {
            pr_err("qpnp-rtc: rtc driver data not found
    ");
            return;
        }
        rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
        if (!rtc_alarm_powerup && !poweron_alarm) { //根据flag设置是否disbale RTC alarm的中断,以及通过设置reg,控制是否disable RTC alarm
            spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
            dev_dbg(&pdev->dev, "Disabling alarm interrupts
    ");
    
            /* Disable RTC alarms */
            reg = rtc_dd->alarm_ctrl_reg1;
            reg &= ~BIT_RTC_ALARM_ENABLE;
            rc = qpnp_write_wrapper(rtc_dd, &reg,
                rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
            if (rc) {
                dev_err(rtc_dd->rtc_dev, "SPMI write failed
    ");
                goto fail_alarm_disable;
            }
    
            /* Clear Alarm register */
            rc = qpnp_write_wrapper(rtc_dd, value,
                    rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
                    NUM_8_BIT_RTC_REGS);
            if (rc)
                dev_err(rtc_dd->rtc_dev, "SPMI write failed
    ");
    
    fail_alarm_disable:
            spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
        }
    }

    内核的开机时间首先是从RTC读取的时间作为基准,之后会通过QCOM time daemon进行corection。QCOM time daemon的代码非open source,所以暂不分析。这里我们仅分析RTC的部分。

    而RTC的时间,我们知道第一次开机,这里指的是RTC断电后的第一次开机,RTC时间是1970-01-01 00:00:00(初始时间),这个值就是从RTC中读取出来的。而在使用一段时间之后,再重启手机,这时RTC的时间为=初始时间+使用的时间。也就是说,这个时间会随着使用而不断累计,除非RTC掉电,重置为初始时间。RTC也支持将同步后的时间再次写入RTC,用于校准当前的正确日期和时间。

    Case 1 开机系统内核时间设置 from RTC

    这里主要分析RTC驱动部分的RTC时间读取,并设置内核系统时间。

    log:

    [    0.897142] qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@0:qcom,pm8998_rtc: rtc core: registered qpnp_rtc as rtc0
    [    1.808755] hctosys: [ljj]open rtc device (rtc0)
    [    1.808819] qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@0:qcom,pm8998_rtc: setting system clock to 1970-01-05 00:01:06 UTC (345666)

    上面的代码,可以看到kernel log的最前面是距离开机的时间累计(CLOCK_MONOTONIC),后面的则是RTC读取的UTC时间(CLOCK_REALTIME)。可以看到一开机,就会从RTC中读取UTC时间。

    对应代码在hcttosys.c中:

    static int __init rtc_hctosys(void)
    {
        int err = -ENODEV;
        struct rtc_time tm;
        struct timespec64 tv64 = {
            .tv_nsec = NSEC_PER_SEC >> 1,
        };
        struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
            pr_info("[ljj]open rtc device (%s)
    ",
                CONFIG_RTC_HCTOSYS_DEVICE);
        if (rtc == NULL) {
            pr_info("unable to open rtc device (%s)
    ",
                CONFIG_RTC_HCTOSYS_DEVICE);
            goto err_open;
        }
        //api 调用
        err = rtc_read_time(rtc, &tm);
        if (err) {
            dev_err(rtc->dev.parent,
                "hctosys: unable to read the hardware clock
    ");
            goto err_read;
    
        }
    
        tv64.tv_sec = rtc_tm_to_time64(&tm);
    
    #if BITS_PER_LONG == 32
        if (tv64.tv_sec > INT_MAX)
            goto err_read;
    #endif
        //设置内核系统时间
        err = do_settimeofday64(&tv64);
    
        dev_info(rtc->dev.parent,
            "setting system clock to "
            "%d-%02d-%02d %02d:%02d:%02d UTC (%lld)
    ",
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec,
            (long long) tv64.tv_sec);
    
    err_read:
        rtc_class_close(rtc);
    
    err_open:
        rtc_hctosys_ret = err;
    
        return err;
    }
    
    late_initcall(rtc_hctosys);
    int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
    {
        int err;
    
        err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        err = __rtc_read_time(rtc, tm); //调用内部__rtc_read_time
        mutex_unlock(&rtc->ops_lock);
        return err;
    }
    EXPORT_SYMBOL_GPL(rtc_read_time);
    static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) { int err; if (!rtc->ops) err = -ENODEV; else if (!rtc->ops->read_time) err = -EINVAL; else { memset(tm, 0, sizeof(struct rtc_time)); err = rtc->ops->read_time(rtc->dev.parent, tm); //调用了qpnp-rtc.c中的read_time ops,最后就是通过读取寄存器,来获取RTC硬件计算的时间,具体代码就不贴了 if (err < 0) { dev_dbg(&rtc->dev, "read_time: fail to read: %d ", err); return err; } err = rtc_valid_tm(tm); if (err < 0) dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid "); } return err; }

    Case 2 kernel RTC时间与system时间进行同步

    实际是因为RTC中的时间并不一定准,所以会用android system中的时间与RTC时间进行对比同步和更新。也就是上面可能会牵扯到QCOM time Daemon的部分,所以避开这块闭源代码。可以参考如下链接,了解开源代码中内核时间与system时间同步。

    kernel log中时间同步的流程,参考自:http://blog.chinaunix.net/uid-23141914-id-5715368.html

    Case 3 更新并设置RTC时间

    当时间与网络、或者setting同步后,可能需要将当前正确的时间再写入RTC中,以保证RTC中的时间准确。这块我们往上看多一点,从jni开始分析。如下:

    路径:frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp

    static const JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
        {"init", "()J", (void*)android_server_AlarmManagerService_init},
        {"close", "(J)V", (void*)android_server_AlarmManagerService_close},
        {"set", "(JIJJ)I", (void*)android_server_AlarmManagerService_set},
        {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
        {"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime}, //设置时间的JNI
        {"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
    };
    
    static jint android_server_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis)
    {
        AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
        struct timeval tv;
        int ret;
    
        if (millis <= 0 || millis / 1000LL >= INT_MAX) {
            return -1;
        }
    
        tv.tv_sec = (time_t) (millis / 1000LL);
        tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL);
    
        ALOGD("Setting time of day to sec=%d
    ", (int) tv.tv_sec);
    
        ret = impl->setTime(&tv); //调用setTime
    
        if(ret < 0) {
            ALOGW("Unable to set rtc to %ld: %s
    ", tv.tv_sec, strerror(errno));
            ret = -1;
        }
        return ret;
    }
    
    int AlarmImpl::setTime(struct timeval *tv)
    {
        struct rtc_time rtc;
        struct tm tm, *gmtime_res;
        int fd;
        int res;
    
        res = settimeofday(tv, NULL);
        if (res < 0) {
            ALOGV("settimeofday() failed: %s
    ", strerror(errno));
            return -1;
        }
    
        if (rtc_id < 0) {
            ALOGV("Not setting RTC because wall clock RTC was not found");
            errno = ENODEV;
            return -1;
        }
    
        android::String8 rtc_dev = String8::format("/dev/rtc%d", rtc_id);
        fd = open(rtc_dev.string(), O_RDWR); //打开了RTC设备
        if (fd < 0) {
            ALOGV("Unable to open %s: %s
    ", rtc_dev.string(), strerror(errno));
            return res;
        }
    
        gmtime_res = gmtime_r(&tv->tv_sec, &tm);
        if (!gmtime_res) {
            ALOGV("gmtime_r() failed: %s
    ", strerror(errno));
            res = -1;
            goto done;
        }
    
        memset(&rtc, 0, sizeof(rtc));
        rtc.tm_sec = tm.tm_sec;
        rtc.tm_min = tm.tm_min;
        rtc.tm_hour = tm.tm_hour;
        rtc.tm_mday = tm.tm_mday;
        rtc.tm_mon = tm.tm_mon;
        rtc.tm_year = tm.tm_year;
        rtc.tm_wday = tm.tm_wday;
        rtc.tm_yday = tm.tm_yday;
        rtc.tm_isdst = tm.tm_isdst;
        res = ioctl(fd, RTC_SET_TIME, &rtc); //通过ioctl设置RTC时间
        if (res < 0)
            ALOGV("RTC_SET_TIME ioctl failed: %s
    ", strerror(errno));
    done:
        close(fd);
        return res;
    }

    接下来就是驱动部分,rtc-dev.c文件中的ioctl接口,最后会调用至qpnp-rtc.c中的set接口,从而将准确的时间写入对应寄存器:

    static long rtc_dev_ioctl(struct file *file,
            unsigned int cmd, unsigned long arg)
    {
        int err = 0;
        struct rtc_device *rtc = file->private_data;
        const struct rtc_class_ops *ops = rtc->ops;
        struct rtc_time tm;
        struct rtc_wkalrm alarm;
        void __user *uarg = (void __user *) arg;
    
        err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        /* check that the calling task has appropriate permissions
         * for certain ioctls. doing this check here is useful
         * to avoid duplicate code in each driver.
         */
        switch (cmd) {
        case RTC_EPOCH_SET:
        case RTC_SET_TIME:
            if (!capable(CAP_SYS_TIME)) //如上面注释所说,设置时间的api必须要有CAP_SYS_TIME的权限
                err = -EACCES;
            break;
    
        case RTC_IRQP_SET:
            if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
                err = -EACCES;
            break;
    
        case RTC_PIE_ON:
            if (rtc->irq_freq > rtc->max_user_freq &&
                    !capable(CAP_SYS_RESOURCE))
                err = -EACCES;
            break;
        }
    
        if (err)
            goto done;
    
        /*
         * Drivers *SHOULD NOT* provide ioctl implementations
         * for these requests.  Instead, provide methods to
         * support the following code, so that the RTC's main
         * features are accessible without using ioctls.
         *
         * RTC and alarm times will be in UTC, by preference,
         * but dual-booting with MS-Windows implies RTCs must
         * use the local wall clock time.
         */
    
        switch (cmd) {
        case RTC_ALM_READ:
            mutex_unlock(&rtc->ops_lock);
    
            err = rtc_read_alarm(rtc, &alarm);
            if (err < 0)
                return err;
    
            if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
                err = -EFAULT;
            return err;
    
        case RTC_ALM_SET:
            mutex_unlock(&rtc->ops_lock);
    
            if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
                return -EFAULT;
    
            alarm.enabled = 0;
            alarm.pending = 0;
            alarm.time.tm_wday = -1;
            alarm.time.tm_yday = -1;
            alarm.time.tm_isdst = -1;
    
            /* RTC_ALM_SET alarms may be up to 24 hours in the future.
             * Rather than expecting every RTC to implement "don't care"
             * for day/month/year fields, just force the alarm to have
             * the right values for those fields.
             *
             * RTC_WKALM_SET should be used instead.  Not only does it
             * eliminate the need for a separate RTC_AIE_ON call, it
             * doesn't have the "alarm 23:59:59 in the future" race.
             *
             * NOTE:  some legacy code may have used invalid fields as
             * wildcards, exposing hardware "periodic alarm" capabilities.
             * Not supported here.
             */
            {
                time64_t now, then;
    
                err = rtc_read_time(rtc, &tm);
                if (err < 0)
                    return err;
                now = rtc_tm_to_time64(&tm);
    
                alarm.time.tm_mday = tm.tm_mday;
                alarm.time.tm_mon = tm.tm_mon;
                alarm.time.tm_year = tm.tm_year;
                err  = rtc_valid_tm(&alarm.time);
                if (err < 0)
                    return err;
                then = rtc_tm_to_time64(&alarm.time);
    
                /* alarm may need to wrap into tomorrow */
                if (then < now) {
                    rtc_time64_to_tm(now + 24 * 60 * 60, &tm);
                    alarm.time.tm_mday = tm.tm_mday;
                    alarm.time.tm_mon = tm.tm_mon;
                    alarm.time.tm_year = tm.tm_year;
                }
            }
    
            return rtc_set_alarm(rtc, &alarm);
    
        case RTC_RD_TIME:
            mutex_unlock(&rtc->ops_lock);
    
            err = rtc_read_time(rtc, &tm);
            if (err < 0)
                return err;
    
            if (copy_to_user(uarg, &tm, sizeof(tm)))
                err = -EFAULT;
            return err;
    
        case RTC_SET_TIME:
            mutex_unlock(&rtc->ops_lock);
    
            if (copy_from_user(&tm, uarg, sizeof(tm))) //获取上层传下来的tm结构
                return -EFAULT;
    
            return rtc_set_time(rtc, &tm); //通过interface.c的接口,从而调用qpnp-rtc.c的设置RTC寄存器函数,完成RTC时间写入。
    
        case RTC_PIE_ON:
            err = rtc_irq_set_state(rtc, NULL, 1);
            break;
    
        case RTC_PIE_OFF:
            err = rtc_irq_set_state(rtc, NULL, 0);
            break;
    
        case RTC_AIE_ON:
            mutex_unlock(&rtc->ops_lock);
            return rtc_alarm_irq_enable(rtc, 1);
    
        case RTC_AIE_OFF:
            mutex_unlock(&rtc->ops_lock);
            return rtc_alarm_irq_enable(rtc, 0);
    
        case RTC_UIE_ON:
            mutex_unlock(&rtc->ops_lock);
            return rtc_update_irq_enable(rtc, 1);
    
        case RTC_UIE_OFF:
            mutex_unlock(&rtc->ops_lock);
            return rtc_update_irq_enable(rtc, 0);
    
        case RTC_IRQP_SET:
            err = rtc_irq_set_freq(rtc, NULL, arg);
            break;
    
        case RTC_IRQP_READ:
            err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
            break;
    
        case RTC_WKALM_SET:
            mutex_unlock(&rtc->ops_lock);
            if (copy_from_user(&alarm, uarg, sizeof(alarm)))
                return -EFAULT;
    
            return rtc_set_alarm(rtc, &alarm);
    
        case RTC_WKALM_RD:
            mutex_unlock(&rtc->ops_lock);
            err = rtc_read_alarm(rtc, &alarm);
            if (err < 0)
                return err;
    
            if (copy_to_user(uarg, &alarm, sizeof(alarm)))
                err = -EFAULT;
            return err;
    
        default:
            /* Finally try the driver's ioctl interface */
            if (ops->ioctl) {
                err = ops->ioctl(rtc->dev.parent, cmd, arg);
                if (err == -ENOIOCTLCMD)
                    err = -ENOTTY;
            } else
                err = -ENOTTY;
            break;
        }
    
    done:
        mutex_unlock(&rtc->ops_lock);
        return err;
    }
  • 相关阅读:
    关于计算机改名无法连接TFS的问题
    配置 SQL Server 2008 Email 发送以及 Job 的 Notification通知功能
    javascript原型链中 this 的指向
    JavaScript中的构造函数 renturn
    Javascript中valueOf与toString区别
    JavaScript 中this与Dom中的注意
    SQL优化 CREATE STATISTICS
    Maven根据不同的环境打包不同的配置
    Maven build标签
    解决Maven报Plugin execution not covered by lifecycle configuration问题
  • 原文地址:https://www.cnblogs.com/lingjiajun/p/11104368.html
Copyright © 2011-2022 走看看