RK3568 RTC驱动实验

RK3568 RTC驱动实验

1. RTC简介

​ RTC 也就是实时时钟,用于记录当前系统时间,对于 Linux 系统而言时间是非常重要的,使用 Linux 设备的时候也需要查看时间。RTC是Linux的时间系统。

​ RTC 设备驱动是一个标准的字符设备驱动,应用程序通过 open、 release、 read、 write 和 ioctl 等函数完成对 RTC 设备的操作。

2. RTC相关结构体

// Linux内核将 RTC 设备抽象为 rtc_device 结构体
struct rtc_device {struct device dev;                 // 设备struct module *owner;int id;                            // IDconst struct rtc_class_ops *ops;   // RTC 设备底层操作函数struct mutex ops_lock;struct cdev char_dev;             // 字符设备unsigned long flags;unsigned long irq_data;spinlock_t irq_lock;wait_queue_head_t irq_queue;struct fasync_struct *async_queue;int irq_freq;int max_user_freq;struct timerqueue_head timerqueue;struct rtc_timer aie_timer;struct rtc_timer uie_rtctimer;struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */int pie_enabled;struct work_struct irqwork;/* Some hardware can't support UIE mode */int uie_unsupported;/* Number of nsec it takes to set the RTC clock. This influences when* the set ops are called. An offset:*   - of 0.5 s will call RTC set for wall clock time 10.0 s at 9.5 s*   - of 1.5 s will call RTC set for wall clock time 10.0 s at 8.5 s*   - of -0.5 s will call RTC set for wall clock time 10.0 s at 10.5 s*/long set_offset_nsec;bool registered;struct nvmem_device *nvmem;/* Old ABI support */bool nvram_old_abi;struct bin_attribute *nvram;time64_t range_min;timeu64_t range_max;time64_t start_secs;time64_t offset_secs;bool set_start_time;#ifdef CONFIG_RTC_INTF_DEV_UIE_EMULstruct work_struct uie_task;struct timer_list uie_timer;/* Those fields are protected by rtc->irq_lock */unsigned int oldsecs;unsigned int uie_irq_active:1;unsigned int stop_uie_polling:1;unsigned int uie_task_active:1;unsigned int uie_timer_active:1;
#endif
};// RTC 设备的最底层操作函数集合,用户编写
// 只是最底层的 RTC 设备操作函数,并不是提供给应用层的file_operations 函数操作集。
struct rtc_class_ops {int (*ioctl)(struct device *, unsigned int, unsigned long);int (*read_time)(struct device *, struct rtc_time *);int (*set_time)(struct device *, struct rtc_time *);int (*read_alarm)(struct device *, struct rtc_wkalrm *);int (*set_alarm)(struct device *, struct rtc_wkalrm *);int (*proc)(struct device *, struct seq_file *);int (*set_mmss64)(struct device *, time64_t secs);int (*set_mmss)(struct device *, unsigned long secs);int (*read_callback)(struct device *, int data);int (*alarm_irq_enable)(struct device *, unsigned int enabled);int (*read_offset)(struct device *, long *offset);int (*set_offset)(struct device *, long offset);
};// 提供给应用层的file_operations 函数操作集 drivers/rtc/rtc-dev.c 
static const struct file_operations rtc_dev_fops = {.owner      = THIS_MODULE,.llseek     = no_llseek,.read       = rtc_dev_read,.poll       = rtc_dev_poll,.unlocked_ioctl = rtc_dev_ioctl,.open       = rtc_dev_open,.release    = rtc_dev_release,.fasync     = rtc_dev_fasync,
};  

3. RTC整体调用框架

在这里插入图片描述

4. RTC代码解析

涉及到的目录功能总结
class.c:向linux设备模型核心注册了一个类RTC,提供了RTC子系统的一些公共函数,让各个RTC驱动注册集成到我们的linux内核中,向驱动程序提供了注册/注销接口。
rtc-dev.c:定义了基本的设备文件操作函数,用户程序与RTC驱动的接口函数,这里定义了每个ioctl命令需要调用的函数,还有open,read等。
interface.c:提供了ioctl各个命令需要调用的函数。
rtc-sysfs.c:与sysfs有关,提供通过sys文件系统操作pcf8563。
rtc-proc.c:与proc文件系统有关,提供通过proc文件系统操作pcf8563。
hctosys.c:系统起来之后会调用到这个文件中的rtc_hctosys()函数,主要功能是系统起来的时候去读RTC硬件中的时间,然后更新我们的系统时间。
rtc.h:定义了与RTC有关的数据结构。

5. RTC驱动注册函数解析

// 驱动路径:drivers/rtc/class.c
struct class *rtc_class;
rtc_init-> rtc_class = class_create(THIS_MODULE, "rtc");      // 创建名为rtc的class-> rtc_class->pm = RTC_CLASS_DEV_PM_OPS;              // 提供休眠唤醒相关接口suspend/resume-> rtc_dev_init();                                    // 动态申请/dev/rtcN的设备号-> alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");    // RTC_DEV_MAX=16
subsys_initcall(rtc_init);                                // rtc_sysfs_init():rtc类具有的device_attribute属性rtc_device_register-> id = rtc_device_get_id(dev);           // Linux支持多个RTC设备,所以需要为每一个设备分配一个ID-> ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);     // 对应与/dev/rtc0  /dev/rtc1  /dev/rtcN-> rtc = rtc_allocate_device();-> struct rtc_device *rtc;-> rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);         // 创建rtc_device设备(对象)并初始化-> ...... rtc->dev.class = rtc_class;  ......       // rtc_init创建的rtc_class// rtc设备中相关锁,等待队列的初始化-> mutex_init(&rtc->ops_lock);-> spin_lock_init(&rtc->irq_lock);-> init_waitqueue_head(&rtc->irq_queue);// 初始化工作队列rtc_timer_do_work-> timerqueue_init_head(&rtc->timerqueue);-> INIT_WORK(&rtc->irqwork, rtc_timer_do_work);// 初始化rtc闹钟中断-> rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);// RTC更新中断-> rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);// RTC周期性中断-> hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);-> rtc->pie_timer.function = rtc_pie_update_irq;-> rtc->pie_enabled = 0;-> rtc->id = id;-> rtc->ops = ops;            // 对应RTC驱动填充的底层操作函数-> rtc->owner = owner;-> rtc->dev.parent = dev;-> dev_set_name(&rtc->dev, "rtc%d", id);   // 设置rtc的dev成员中的name域-> __rtc_read_alarm(rtc, &alrm);        // 检查是否设置闹钟-> if (!err && !rtc_valid_tm(&alrm.time))    // 如果RTC芯片中设置有效的Alarm,则初始化,加入队列中rtc_initialize_alarm(rtc, &alrm);-> rtc_dev_prepare(rtc);               // /dev/rtc0的rtc作为字符设备进行初始化-> cdev_init(&rtc->char_dev, &rtc_dev_fops);     // rtc_dev_fops接口操作函数结构体-> cdev->ops = fops;-> cdev_device_add(&rtc->char_dev, &rtc->dev);-> cdev_add(cdev, dev->devt, 1);   // rtc设备作为字符设备添加到系统    生成/dev/rtc0-> rtc_proc_add_device(rtc);        // /proc/rtc-> proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, rtc);// 提供给用户层的接口
static const struct file_operations rtc_dev_fops = {.owner      = THIS_MODULE,.llseek     = no_llseek,.read       = rtc_dev_read,.poll       = rtc_dev_poll,.unlocked_ioctl = rtc_dev_ioctl,.open       = rtc_dev_open,.release    = rtc_dev_release,.fasync     = rtc_dev_fasync,
};

kernel中__init类型函数都位于.init.text段中,对应的在.initcall.init段中保存相应的函数指针。系统在启动过程中,根据定义在段中的等级值(0~7)从低到高依次执行。定义:

// include/linux/init.h
#define pure_initcall(fn)       __define_initcall(fn, 0)
#define core_initcall(fn)       __define_initcall(fn, 1)
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)
#define postcore_initcall(fn)       __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
#define arch_initcall(fn)       __define_initcall(fn, 3)
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
#define subsys_initcall(fn)     __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
#define fs_initcall(fn)         __define_initcall(fn, 5)
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
#define rootfs_initcall(fn)     __define_initcall(fn, rootfs)
#define device_initcall(fn)     __define_initcall(fn, 6)
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)
#define late_initcall(fn)       __define_initcall(fn, 7)
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)

6. 应用层调用驱动流程解析

rtc_dev_ioctl (struct file *file, unsigned int cmd, unsigned long arg)           // drivers/rtc/rtc-dev.c   -> struct rtc_device *rtc = file->private_data;               // 获取到rtc设备-> switch (cmd) -> case RTC_RD_TIME:-> rtc_read_time     // drivers/rtc/interface.c-> __rtc_read_time-> rtc->ops->read_time(rtc->dev.parent, tm);-> case RTC_SET_TIME:-> rtc_set_time      // drivers/rtc/interface.c-> rtc_valid_tm(tm);       // 参数检查// 调用rtc_device中ops结构体的函数指针// ops结构体的函数指针已经在RTC驱动中被赋值-> if (!rtc->ops)err = -ENODEV;else if (rtc->ops->set_time)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);

7. RTC时间查看与设置

RK809 内部 RTC 的使能需要先使能 RK809,默认已经使能,我们打开设备树 rk3568-evb.dtsi:

    rk809: pmic@20 {compatible = "rockchip,rk809";reg = <0x20>;interrupt-parent = <&gpio0>;interrupts = <3 IRQ_TYPE_LEVEL_LOW>;pinctrl-names = "default", "pmic-sleep","pmic-power-off", "pmic-reset";pinctrl-0 = <&pmic_int>;pinctrl-1 = <&soc_slppin_slp>, <&rk817_slppin_slp>;pinctrl-2 = <&soc_slppin_gpio>, <&rk817_slppin_pwrdn>;pinctrl-3 = <&soc_slppin_gpio>, <&rk817_slppin_rst>;rockchip,system-power-controller;wakeup-source;#clock-cells = <1>;clock-output-names = "rk808-clkout1", "rk808-clkout2";//fb-inner-reg-idxs = <2>;/* 1: rst regs (default in codes), 0: rst the pmic */pmic-reset-func = <0>;/* not save the PMIC_POWER_EN register in uboot */not-save-power-en = <1>;vcc1-supply = <&vcc3v3_sys>;vcc2-supply = <&vcc3v3_sys>;vcc3-supply = <&vcc3v3_sys>;vcc4-supply = <&vcc3v3_sys>;vcc5-supply = <&vcc3v3_sys>;vcc6-supply = <&vcc3v3_sys>;vcc7-supply = <&vcc3v3_sys>;vcc8-supply = <&vcc3v3_sys>;vcc9-supply = <&vcc3v3_sys>;

​ 上面 status 状态没写,默认就是“okay”的。
​ 同时我们需要在 menuconfg 里对应的宏配置为 CONFIG RTC DRV RK808。> Device
Drivers > Real Time Clock 选中 CONFIG RTC DRV RK808。如下图。

在这里插入图片描述

8. 查看时间

如果要查看时间的话输入“date”命令即可,结果如图:

在这里插入图片描述
从上面可看到内核启动 RTC 时间采用的是 UTC 标准,而系统启动后采用的是 CST 标准
时间恰好相差8个小时。UTC(协调世界时)和CST(中部标准时间)是两个不同的时间标准,
在中国,CST 通常被解释为"China Standard Time"(中国标准时间),而不是"Central Standard Time
(中部标准时间)。中国 CST 与协调世界时(UTC)相差8小时,即 UTC+8。
RTC 时间设置也是使用的 date 命令,输入“date --help”命令即可査看 date 命令如何设置
系统时间,结果如图所示:

root@RK356X:/# date --help
Usage: date [OPTION]... [+FORMAT]or:  date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
Display the current time in the given FORMAT, or set the system date.Mandatory arguments to long options are mandatory for short options too.-d, --date=STRING          display time described by STRING, not 'now'--debug                annotate the parsed date,and warn about questionable usage to stderr-f, --file=DATEFILE        like --date; once for each line of DATEFILE-I[FMT], --iso-8601[=FMT]  output date/time in ISO 8601 format.FMT='date' for date only (the default),'hours', 'minutes', 'seconds', or 'ns'for date and time to the indicated precision.14T02:34:56-06:00-R, --rfc-email            output date and      Example: Mon, 14 Aug 2006 02:34:56 -0600--rfc-3339=              FMT='date', 'seconds', or 'ns'Example: 2006-08-14 02:34:56-06:00-r, -s, --set=STRING           set time described by STRING-u, -     --help     display this help and exit--version  outrpreted sequences are:%%   a literal %%a   locale's ab(e.g., Sunday)%b   locale's abbreviated month name (e.g., Ja date and time (e.g., Thu Mar  3 23:05:25 2005)%C   century;(e.g., 01)%D   date; same as %m/%d/%y%e   day of month, last two digits of year of ISO week number (see %G)%G   yearme as %b%H   hour (00..23)%I   hour (01..12)%j   day%l   hour, space padded ( 1..12); same as %_I%m   month ((000000000..999999999)%p   locale's equivalent of either AM rter of year (1..4)%r   locale's 12-hour clock time (e.g., 1conds since 1970-01-01 00:00:00 UTC%S   second (00..60)% is Monday%U   week number of year, with Sunday as first dayf week (01..53)%w   day of week (0..6); 0 is Sunday%W     locale's date representation (e.g., 12/31/99)%X   locale's(00..99)%Y   year%z   +hhmm numeric time zone (e.g., -04s numeric time zone (e.g., -04:00:00)%:::z  numeric time zon time zone abbreviation (e.g., EDT)By default, date pads num:-  (hyphen) do not pad the field_  (underscore) pad wle#  use opposite case if possibleAfter any flags comes er, which is either
E to use the locale's alternate representats if available.Examples:
Convert seconds since the epoch (1ime on the west coast of the US (use tzselect(1) to find TZ)iday on the west coast of the US$ date --date='TZ="America/Lww.gnu.org/software/coreutils/>
Report date translation bugs to://www.gnu.org/software/coreutils/date>

比如现在设置当前时间为2024年4月1日11:40:00,因此输入如下:

date -s "2024-04-01 11:40:00"

在这里插入图片描述

设置完成后再次使用date命令查看一下当前时间就会发现时间改过来了

大家注意我们使用date -s命令仅仅是修改了当前时间,此时间还没有写入到RK809内部 RTC 里面或其他的 RTC 芯片里面,因此系统重启以后时间又会丢失。我们需要将当前的时间写入到 RTC 里面,这里要用到hwclock命令,输入如下命令将系统时间写入到 RTC 里面:

hwclock -w     /将当前系统时间写入RTC里面/

时间写入到 RTC 里面以后就不怕系统重启以后时间丢失了

间隔时间输入如下命令:

hwclock -r    /读取当前系统时间/

在这里插入图片描述

发现当前系统时间在走动,系统时间正常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/790620.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Python的微博旅游情感分析、微博舆论可视化系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Python网络爬虫(三):Selenium--以携程酒店为例

1 Selenium简介 Selenium是一个用于网站应用程序自动化的工具&#xff0c;它可以直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。它相当于一个机器人&#xff0c;可以模拟人类在浏览器上的一些行为&#xff0c;比如输入文本、点击、回车等。Selenium支持多种浏览器&…

记录一次官网访问很慢的情况

客户查看云监控,带宽未超限,客户取的是1分钟的原生值,也就是1分钟也是个平均值。 但是客户的原始值&#xff0c;其实就是1分钟内的平均值。所以客户的瞬时超限&#xff0c;其实是看不出来的。但是后端同事从实时监控里面可以看到超限的情况。 客户升带宽后&#xff0c; 发现还…

Flutter 应用数据持久化指南

1. 介绍 1.1 什么是数据持久化&#xff1f; 数据持久化是指将应用程序中的数据保存在持久存储介质&#xff08;如硬盘、数据库等&#xff09;中的过程。在计算机科学领域&#xff0c;持久化数据是指数据在程序退出或系统关机后仍然存在的能力。这种持久性使得数据可以在不同的…

是德科技keysight 33621A波形发生器

181/2461/8938产品概述&#xff1a; 与上一代DDS波形发生器相比&#xff0c;采用独家Trueform技术的安捷伦HP 33621A波形发生器具有更高的性能、保真度和灵活性。安捷伦HP 33621A 120 MHz、单通道、Trueform arbs&#xff0c;带时序控制和64 MSa存储器&#xff0c;1 ps抖动&am…

go juc 线程中的子类

1.go test() 主死随从 package mainimport ("fmt""strconv""time" )func test() {for i : 1; i < 10; i {fmt.Println("hello " strconv.Itoa(i))//阻塞time.Sleep(time.Second)} } func main() {//开启协程go test()for i : 1; …

如何配置vite的proxy

1.前言 vite项目&#xff0c;本地开发环境可以通过配置proxy代理实现跨域请求。但是生产环境&#xff0c;该配置不生效&#xff0c;一般使用 nginx 转发&#xff0c;或者后端配置cors 2.解释 server: {port: 9000,proxy: { // 本地开发环境通过代理实现跨域&#xff0c;生产…

基于ssm的轻型卡车零部件销售平台(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的轻型卡车零部件销售平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 轻型卡车零部件销售平台…

C# 批量删除Excel重复项

当从不同来源导入Excel数据时&#xff0c;可能存在重复的记录。为了确保数据的准确性&#xff0c;通常需要删除这些重复的行。 手动查找并删除可能会非常耗费时间&#xff0c;而通过编程脚本则可以实现在短时间内处理大量数据。本文将提供一个使用C# 快速查找并删除Excel重复项…

【ArduinoQuartus】在小脚丫STEP CYC10上安装PulseRain Reindeer并在软核上运行基础功能

【Arduino&Quartus】在小脚丫STEP CYC10上安装PulseRain Reindeer并在软核上运行基础功能 一、将Reindeer软核下载到STEP CYC10&#xff08;一&#xff09;下载PulseRain Reindeer软核&#xff08;二&#xff09;配置Reindeer软核到开发板1.将sof文件转换为jic文件2.将jic文…

Centos7安装单机版Kafka

下载 链接&#xff1a;https://pan.baidu.com/s/1W8lVEF6Y-xlg6zr3l9QAbg?pwdhbkt 提取码&#xff1a;hbkt 上传到服务器/opt目录 安装 # kafka安装目录为 /opt/kafka cd /opt; mkdir kafka; mv kafka_2.13-2.7.0.tgz ./kafka;cd kafka; #解压 tar -zxvf kafka_2.13-2.7.0…

说一说Redis的Bitmaps和HyperLoLog?

本篇内容对应 “Redis高级数据类型”小节 和 “7.5 网站数据统计”小节 对应视频&#xff1a; Redis高级数据结构 网站数据统计 1 什么是UV和DAU&#xff1f; DAUUV英文全称Daily Active UserUnique Visotr中文全称日活跃用户量独立访客如何统计数据通过用户ID排重统计数据通…

上位机图像处理和嵌入式模块部署(qmacvisual图像清晰度)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 做过isp的同学都知道&#xff0c;图像处理里面有一个3A&#xff0c;即自动曝光、自动白平衡和自动对焦。其中自动对焦这个&#xff0c;就需要用输入…

绩效考核存在合理性、公平性、客观性吗?

目录 一、绩效考核流于形式&#xff1a;没有实际考核过 二、考核结果的确定: 主管一人说了算 三、考核结果&#xff1a; 与绩效奖金挂钩吗&#xff1f; 四、考核的滥用&#xff1a;成为公司排挤迫使员工离职的手段 五、公司说&#xff1a; 让你滚蛋&#xff0c;谁还会发你奖…

SpringBoot(48)-使用 SkyWalking 进行分布式链路追踪

Spring Boot&#xff08;48&#xff09;- 使用 SkyWalking 进行分布式链路追踪 介绍 在分布式系统中&#xff0c;了解各个服务之间的调用关系和性能表现是非常重要的。SkyWalking 是一款开源的分布式系统监控与分析平台&#xff0c;能够帮助我们实现分布式系统的链路追踪、性…

使用minikube安装使用单机版K8S(docker)

前置&#xff1a;作为一个开发&#xff0c;工作之余想玩一下k8s&#xff0c;但是搭建成本太高&#xff0c;所以就找到了minikube这个工具&#xff0c;快速搭建单机版k8s&#xff0c;下面是个人搭建流程&#xff0c;基于centos7&#xff0c;仅供参考。 1.下载kubectl&#xff0…

ES学习日记(十)-------Java操作ES之连接客户端

Elasticsearch有两种连接方式: transport、rest。transport 通过TCP方式访问ES(只支持iava)&#xff0c;rest 方式通过http API 访问ES(没有语言限制)。 ES官方建议使用Iest 方式&#xff0c;transport 在7.8 版本中不建议使用&#xff0c;在8.x的版本中废弃。你可以用Java客户…

Java23种设计模式

本文主要是对Java中一些常用的设计模式进行讲解 后期会进行不断的更新&#xff0c;欢迎浏览 23种设计模式 创建型模式&#xff0c;共五种&#xff1a;工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式。结构型模式&#xff0c;共七种&#xff1a;适配器模式、桥接…

使用 Flume 将 CSV 数据导入 Kafka:实现实时数据流

使用 Flume 将 CSV 数据导入 Kafka&#xff1a;实现实时数据流 文介绍了如何使用 Apache Flume 将 CSV 格式的数据从本地文件系统导入到 Apache Kafka 中&#xff0c;以实现实时数据流处理。通过 Flume 的配置和操作步骤&#xff0c;我们可以轻松地将数据从 CSV 文件中读取并发…

RT-Thread下使用NTP服务器获取时间并同步到硬件RTC

单片机:STM32F407VET6 实现功能:通过ntp服务器获取时间并同步到硬件RTC上 1.配置NTP相关参数 1.1打开netutils相关软件包 1.2 关闭软件RTC相关配置 参考资料:RT-Thread中使用NTP自动更新时间_rtthread ntp-CSDN博客 2.配置硬件RTC 2.1 在ENV里面使能硬件RTC 2.2使用STM32C…