Linux驅動之在驅動中校準系統時間——學習筆記(8)

最近寫了一個RTC驅動,然後打算在驅動內部定時校準系統時間,搜索了一圈實在是沒找到什麼有用的信息,就去內核的源碼裏找了一下還真的有。
這裏記錄一下這個功能。


一、函數介紹

(1)mktime

  • 聲明位置:kernel\include\linux\time.h
  • 源碼位置:kernel\kernel\time.c

這個函數的作用是將輸入的年月日時分秒轉換成距離 1970-01-01 00:00:00 的秒數。

/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
 * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
 *
 * [For the Julian calendar (which was used in Russia before 1917,
 * Britain & colonies before 1752, anywhere else before 1582,
 * and is still in use by some communities) leave out the
 * -year/100+year/400 terms, and add 10.]
 *
 * This algorithm was first published by Gauss (I think).
 *
 * WARNING: this function will overflow on 2106-02-07 06:28:16 on
 * machines where long is 32-bit! (However, as time_t is signed, we
 * will already get problems at other places on 2038-01-19 03:14:08)
 */
unsigned long
mktime(const unsigned int year0, const unsigned int mon0,
       const unsigned int day, const unsigned int hour,
       const unsigned int min, const unsigned int sec)
{
	unsigned int mon = mon0, year = year0;

	/* 1..12 -> 11,12,1..10 */
	if (0 >= (int) (mon -= 2)) {
		mon += 12;	/* Puts Feb last since it has leap day */
		year -= 1;
	}

	return ((((unsigned long)
		  (year/4 - year/100 + year/400 + 367*mon/12 + day) +
		  year*365 - 719499
	    )*24 + hour /* now have hours */
	  )*60 + min /* now have minutes */
	)*60 + sec; /* finally seconds */
}

(2)do_settimeofday

  • 聲明位置:kernel\include\linux\time.h
  • 源碼位置:kernel\kernel\time\timekeeping.c

這個函數的作用是將要設置的時間的秒數設置到系統時間。

/**
 * do_settimeofday - Sets the time of day
 * @tv:		pointer to the timespec variable containing the new time
 *
 * Sets the time of day to the new time and update NTP and notify hrtimers
 */
int do_settimeofday(const struct timespec *tv)
{
	struct timekeeper *tk = &timekeeper;
	struct timespec ts_delta, xt;
	unsigned long flags;

	if (!timespec_valid_strict(tv))
		return -EINVAL;

	raw_spin_lock_irqsave(&timekeeper_lock, flags);
	write_seqcount_begin(&timekeeper_seq);

	timekeeping_forward_now(tk);

	xt = tk_xtime(tk);
	ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
	ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;

	tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta));

	tk_set_xtime(tk, tv);

	timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);

	write_seqcount_end(&timekeeper_seq);
	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);

	/* signal hrtimers about time change */
	clock_was_set();

	return 0;
}

二、使用代碼

使用起來是挺簡單的

    struct timespec tv;
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
    
    tv.tv_sec = mktime(year, mon, day, hour - 8, min, sec);            /* 東八區 */
    ret = do_settimeofday(&tv);
    

由於當前是在東八區,所以需要在小時上減 8。

三、測試

測試可以使用 date 命令進行測試。

root@imx6qsabresd:/tmp# date
Tue Mar  3 12:23:33 CST 2020
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章