Linux驱动开发项目BL5372--hwclock用法及实现过程详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xi_xix_i/article/details/134057998

一、hwclock命令的参数及用法

该命令的可选参数以及对应的功能很多博客已经说的很清楚了,这篇博客写的比较详细,并且展示了使用示例。

因为项目需求,需要搞清楚hwclock的具体实现过程,所以重点梳理一下该命令及对应功能是如何实现的。因为是用的busybox来构建根文件系统,所以去busybox根文件下去仔细研究研究hwclock这个命令是如何实现具体功能的,先看看实现这个命令的文件应该是在哪:

在这里插入图片描述
使用grep -nR "hwclcok"搜索一下,看看哪个文件中包含这个命令,感觉这个util-linux/hwclock.c最像,毕竟名字就叫hwclock.c了。其实这个util-linux就是一个工具包,里面有装有很多命令及其实现。

打开这个文件,开头的注释写到:

/* vi: set sw=4 ts=4: */
/*
 * Mini hwclock implementation for busybox
 *
 * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
 *
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
//config:config HWCLOCK
//config:	bool "hwclock (5.8 kb)"
//config:	default y
//config:	select PLATFORM_LINUX
//config:	help
//config:	The hwclock utility is used to read and set the hardware clock
//config:	on a system. This is primarily used to set the current time on
//config:	shutdown in the hardware clock, so the hardware will keep the
//config:	correct time when Linux is _not_ r

那就是这个文件没错了。找到这个文件了,可以来分析一下hwclock是怎么实现各个功能的,以及我应该如何编写我的rtc驱动才能适配这个命令。该文件所有代码放在末尾附录。

二、 busybox/util-linux/hwclock.c:处理hwclock命令的文件

分析一下该文件的主要函数,即可梳理该文件是如何实现功能的。

1. hwclock_main():主函数,负责解析命令并执行对应操作

该函数代码如:”

int hwclock_main(int argc UNUSED_PARAM, char **argv)
{
	const char *rtcname = NULL;
	unsigned opt;
	int utc;

#if ENABLE_LONG_OPTS
	static const char hwclock_longopts[] ALIGN1 =
		"localtime\0" No_argument "l" /* short opt is non-standard */
		"utc\0"       No_argument "u"
		"show\0"      No_argument "r"
		"hctosys\0"   No_argument "s"
		"systohc\0"   No_argument "w"
		"systz\0"     No_argument "t" /* short opt is non-standard */
		"rtc\0"       Required_argument "f"
		;
#endif

	/* Initialize "timezone" (libc global variable) */
	tzset();

	/* 下面这个函数解析命令,具体的解析过程暂时不深究,估计跟uboot或者linux内核里的一些解析过程差不多,有空再继续深究 */
	opt = getopt32long(argv,
		"^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l",
		hwclock_longopts,
		&rtcname
	);

	/* If -u or -l wasn't given check if we are using utc 
	 * 接下来根据解析的命令执行对应的操作(UTC是世界协调时间时)
	 */
	if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
		utc = (opt & HWCLOCK_OPT_UTC);
	else
		utc = rtc_adjtime_is_utc();

	if (opt & HWCLOCK_OPT_HCTOSYS)
		to_sys_clock(&rtcname, utc);			/* 执行to_sys_clock(硬件时钟写入系统时钟)*/
	else if (opt & HWCLOCK_OPT_SYSTOHC)
		from_sys_clock(&rtcname, utc);		    /* 将系统时钟写入硬件时钟 */
	else if (opt & HWCLOCK_OPT_SYSTZ)
		set_system_clock_timezone(utc);
	else
		/* default HWCLOCK_OPT_SHOW */
		show_clock(&rtcname, utc);				/* 没什么命令的话就显示硬件时间 */

	return 0;
}

主函数,命令行输入hwclock后执行,char **argv存储命令行输入的参数。该函数主要执行两个任务:

  1. 通过getopt32long()函数来解析命令行输入的参数。
  2. 根据参数来执行对应的函数,实现对应的功能。

重点在于根据不同参数而调用的那些实现函数中,以-s参数为例(将硬件时钟同步到系统时钟),分析一下功能是如何实现的,该参数及对应的执行函数:

if (opt & HWCLOCK_OPT_HCTOSYS)
		to_sys_clock(&rtcname, utc);			/* 执行to_sys_clock(硬件时钟写入系统时钟)*/

接着来看一下to_sys_clcok()函数:

static void to_sys_clock(const char **pp_rtcname, int utc)
{
	struct timeval tv;
	struct timezone tz;

	tz.tz_minuteswest = timezone/60;
	/* ^^^ used to also subtract 60*daylight, but it's wrong:
	 * daylight!=0 means "this timezone has some DST
	 * during the year", not "DST is in effect now".
	 */
	tz.tz_dsttime = 0;

	tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);			/* 与硬件rtc有关系的函数 */
	tv.tv_usec = 0;
	if (settimeofday(&tv, &tz))								/* 设置系统时间 */
		bb_perror_msg_and_die("settimeofday");
}

其中tv为将要写入到系统的时间,tz为当地时区信息。struct timeval这个结构体有两个成员变量,一个是秒,一个是微秒。在这个函数中,微秒设置为0,而秒则是通过调用read_rtc()函数来获取的,所以说,这个read_rtc()函数一定很重要。获取到秒之后,通过settimeofday来设置系统时间。这篇博客对这个函数进行了简单的讲解。

2. 核心函数之一:read_rtc(),用来获取硬件时钟,hwclock所有指令的操作都离不开这个函数

下面来看一下这个read_rtc()函数,代码如下:

static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
{
	struct tm tm_time;
	int fd;

	fd = rtc_xopen(pp_rtcname, O_RDONLY);	/* 这个rtc_xopen就是关键啊*/

	rtc_read_tm(&tm_time, fd);				/* 这就是驱动的接口了,驱动要实现这个read的接口 */

#if SHOW_HWCLOCK_DIFF
	{
		int before = tm_time.tm_sec;
		while (1) {
			rtc_read_tm(&tm_time, fd);	
			gettimeofday(sys_tv, NULL);
			if (before != (int)tm_time.tm_sec)
				break;
		}
	}
#endif

	if (ENABLE_FEATURE_CLEAN_UP)
		close(fd);

	return rtc_tm2time(&tm_time, utc);
}

其中SHOW_HWCLOCK_DIFF这个宏在源代码中是没有定义的,所以真正执行的就是两个函数:rtc_xopen()rtc_read_tm()

3. rtc_xopen()rtc_read_tm()

这俩函数在busybox/libbb/rtc.c中,代码如下:

/* Never fails */
int FAST_FUNC rtc_xopen(const char **default_rtc, int flags)
{
	int rtc;
	const char *name =						/* 最终会尝试打开这三个文件,如果都没打开则会报错 */
		"/dev/rtc""\0"
		"/dev/rtc0""\0"
		"/dev/misc/rtc""\0";

	if (!*default_rtc)
		goto try_name;
	name = ""; /*else: we have rtc name, don't try other names */

	for (;;) {
		rtc = open_loop_on_busy(*default_rtc, flags);	/* 不断尝试访问该设备(防止设备被占用) */
		if (rtc >= 0)
			return rtc;
		if (!name[0])
			return xopen(*default_rtc, flags);
 try_name:
		*default_rtc = name;
		name += strlen(name) + 1;
	}
}

void FAST_FUNC rtc_read_tm(struct tm *ptm, int fd)
{
	memset(ptm, 0, sizeof(*ptm));
	xioctl(fd, RTC_RD_TIME, ptm);
	ptm->tm_isdst = -1; /* "not known" */
}

rtc_xopen()函数实现的功能,就是打开我们的rtc设备,这里就跟我们的驱动有关联了,该函数会调用open_loop_on_busy()函数(该函数实现的功能就是在rtc设备被占用时,不断使用open,就是用户空间使用的open()尝试打开rtc字符设备,直到到达尝试的最大时间),来尝试打开const char *name定义的三个字符设备(按定义的顺序来):"/dev/rtc" ,"/dev/rtc0", "/dev/misc/rtc",获取设备操作符。如果都没打开,则会报错无法找到rtc设备。到这里就明白了,就是open()了上述三个设备中的一个,然后就可以使用read,write,ioctl等来访问设备。

rtc_read_tm()函数,则是调用了xioctl()函数(是一个宏,最终还是调用的ioctl()函数,只不过加了一些出错时的报错信息),并且要读取的参数类型为struct tm结构体。

所以到这里已经知道该如何编写驱动程序,来适配这个命令了:

  1. 编写驱动程序,并且注册设备时必须将设备名注册为三个文件名("/dev/rtc" ,"/dev/rtc0", "/dev/misc/rtc")之一
  2. 在驱动程序的file_ops字符设备操作集中实现.unlocked_ioctl对应的函数,该函数需要根据传入的参数执行对应的功能。以读硬件rtc时间来说,需要在驱动程序中定义相同的宏RTC_RD_TIME,而执行的对应的函数应该能够读取硬件rtc中的时间寄存器,并将其转化为struct tm结构体中各成员变量对应的值。

上述编写驱动程序的做法是当你不使用linux下的RTC框架时的做法,如果使用RTC框架,则更加简单,但是会有局限性,在我的另外一篇文章中提到了这个问题。

题外话

hwclock和date都可以用来显示时间,但是hwclock是获取硬件时钟的时间,而date获取的是系统时间,如果长时间没同步,或者系统时间是联网得来的,两者显示的时间是会有差异。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: bl5372 rtc i2c 驱动是指一种用于控制实时时钟的设备驱动程序。BL5372是实时时钟芯片的型号,它使用i2c总线进行通信。 i2c(Inter-Integrated Circuit)是一种用于芯片间通信的串行总线协议,能够在多个设备之间传输数据。通过i2c总线,实时时钟芯片可以与主控制器进行通信,实现时间数据的读取和修改。 bl5372 rtc i2c 驱动的作用是提供一组API接口,使得主控制器可以方便地调用这些接口来读取和设置实时时钟的时间信息。驱动程序会负责与实时时钟芯片进行通信,将读取到的时间数据返回给主控制器,或者将主控制器传递过来的时间信息写入实时时钟芯片。 bl5372 rtc i2c 驱动通常需要在嵌入式系统或其他使用实时时钟芯片的场景中使用。主控制器可以通过i2c总线与实时时钟芯片进行通信,并利用驱动程序提供的接口来操作实时时钟。 总的来说,bl5372 rtc i2c 驱动在嵌入式系统中起到了关键的作用,它简化了主控制器与实时时钟芯片的通信过程,提供了方便的接口供主控制器进行时间数据的读写操作。 ### 回答2: BL5372是一种I2C总线RTC(Real-Time Clock驱动芯片。RTC芯片是一种在电脑或嵌入式系统中用来提供实时时间和日期功能的芯片。 BL5372 RTC I2C驱动允许主机通过I2C总线与BL5372芯片进行通信。I2C总线是一种串行通信协议,它允许多个设备共享同一条总线,在同一时刻进行数据通信。通过I2C总线,主机可以向BL5372芯片发送命令和读取返回的数据。 BL5372芯片内置了一个实时时钟电路,它可以独立工作且不会受到主机系统时间的影响。它可以提供精准的时间和日期信息,在电源断电时也可以继续工作。主机可以使用BL5372 RTC I2C驱动通过I2C总线与BL5372芯片通信,设置或读取当前时间和日期,以及其他相关功能。 BL5372 RTC I2C驱动通常由硬件和软件两部分组成。硬件部分包括I2C总线和BL5372芯片的连接,主机通过I2C总线与芯片进行数据交换。软件部分则是主机系统中的设备驱动程序,它提供了访问BL5372芯片的接口和命令集,允许主机通过I2C总线与芯片进行通信。 BL5372 RTC I2C驱动的使用可以广泛应用于各种需要实时时钟功能的设备,比如计算机、嵌入式系统、物联网设备等。它可以方便主机通过I2C总线与BL5372芯片进行通信,实现精准的时间和日期功能,为各种应用提供准确的时间戳和时间同步功能。 ### 回答3: bl5372 rtc i2c 驱动是针对BL5372芯片使用I2C通信协议进行实时时钟驱动的软件程序。BL5372是一款集成了RTC(实时时钟)功能的芯片,可以用于记录时间和日期。 I2C(Inter-Integrated Circuit)是一种常用的串行通信协议,适用于短距离、低速率的通信。它使用两根线路:SDA(数据线)和SCL(时钟线)。 bl5372 rtc i2c驱动程序的主要功能是通过I2C总线和BL5372芯片进行通信,以读取和写入实时时钟相关的信息。它可以用来设置和读取当前的日期和时间,包括年份、月份、日期、小时、分钟和秒数。同时,它还可以设置闹钟功能,使得芯片能够根据预设的时间触发相应的动作。 通过bl5372 rtc i2c驱动,用户可以方便地控制BL5372芯片的实时时钟功能,实现各种时间相关的应用,比如日历、时钟、计时器等。例如,用户可以使用该驱动来设置一个闹钟,当到达预设的时间时,芯片会触发一个中断信号,从而实现提醒功能。 总之,bl5372 rtc i2c驱动程序是为了方便用户对BL5372芯片的实时时钟功能进行控制而设计的,通过使用I2C通信协议,用户可以设置和读取时间信息,实现各种时间相关的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值