Add support for ADJ_SETOFFSET mode
This adjtimex mode allows precise stepping of the system clock.
This commit is contained in:
parent
b088b70f82
commit
6ab3d1daa3
4 changed files with 87 additions and 15 deletions
|
@ -40,6 +40,7 @@ struct timex {
|
||||||
#define ADJ_MAXERROR 0x0004 /* maximum time error */
|
#define ADJ_MAXERROR 0x0004 /* maximum time error */
|
||||||
#define ADJ_STATUS 0x0010 /* clock status */
|
#define ADJ_STATUS 0x0010 /* clock status */
|
||||||
#define ADJ_TIMECONST 0x0020 /* pll time constant */
|
#define ADJ_TIMECONST 0x0020 /* pll time constant */
|
||||||
|
#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
|
||||||
#define ADJ_NANO 0x2000 /* select nanosecond resolution */
|
#define ADJ_NANO 0x2000 /* select nanosecond resolution */
|
||||||
#define ADJ_TICK 0x4000 /* tick value */
|
#define ADJ_TICK 0x4000 /* tick value */
|
||||||
#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */
|
#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */
|
||||||
|
|
21
sys_linux.c
21
sys_linux.c
|
@ -115,6 +115,9 @@ static int have_readonly_adjtime;
|
||||||
adjustments. */
|
adjustments. */
|
||||||
static int have_nanopll;
|
static int have_nanopll;
|
||||||
|
|
||||||
|
/* Flag indicating whether adjtimex() can step the clock */
|
||||||
|
static int have_setoffset;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void handle_end_of_slew(void *anything);
|
static void handle_end_of_slew(void *anything);
|
||||||
|
@ -612,6 +615,11 @@ apply_step_offset(double offset)
|
||||||
abort_slew();
|
abort_slew();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_setoffset) {
|
||||||
|
if (TMX_ApplyStepOffset(-offset) < 0) {
|
||||||
|
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (gettimeofday(&old_time, NULL) < 0) {
|
if (gettimeofday(&old_time, NULL) < 0) {
|
||||||
LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
|
LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
|
||||||
}
|
}
|
||||||
|
@ -628,6 +636,7 @@ apply_step_offset(double offset)
|
||||||
|
|
||||||
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
|
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
|
||||||
lcl_InvokeDispersionNotifyHandlers(fabs(err));
|
lcl_InvokeDispersionNotifyHandlers(fabs(err));
|
||||||
|
}
|
||||||
|
|
||||||
initiate_slew();
|
initiate_slew();
|
||||||
|
|
||||||
|
@ -1010,6 +1019,13 @@ get_version_specific_details(void)
|
||||||
have_nanopll = 1;
|
have_nanopll = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ADJ_SETOFFSET support */
|
||||||
|
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
|
||||||
|
have_setoffset = 0;
|
||||||
|
} else {
|
||||||
|
have_setoffset = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Override freq_scale if it appears in conf file */
|
/* Override freq_scale if it appears in conf file */
|
||||||
CNF_GetLinuxFreqScale(&set_config_freq_scale, &config_freq_scale);
|
CNF_GetLinuxFreqScale(&set_config_freq_scale, &config_freq_scale);
|
||||||
if (set_config_freq_scale) {
|
if (set_config_freq_scale) {
|
||||||
|
@ -1049,6 +1065,11 @@ SYS_Linux_Initialise(void)
|
||||||
have_nanopll = 0;
|
have_nanopll = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_setoffset && TMX_TestStepOffset() < 0) {
|
||||||
|
LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
|
||||||
|
have_setoffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
TMX_SetSync(CNF_GetRTCSync());
|
TMX_SetSync(CNF_GetRTCSync());
|
||||||
|
|
||||||
/* Read current kernel frequency */
|
/* Read current kernel frequency */
|
||||||
|
|
|
@ -229,5 +229,53 @@ TMX_GetPLLOffsetLeft(long *offset)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TMX_TestStepOffset(void)
|
||||||
|
{
|
||||||
|
struct timex txc;
|
||||||
|
|
||||||
|
/* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
|
||||||
|
This seems to be the only way how to verify that the kernel really
|
||||||
|
supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
|
||||||
|
mode. */
|
||||||
|
|
||||||
|
txc.modes = ADJ_MAXERROR;
|
||||||
|
txc.maxerror = 0;
|
||||||
|
if (adjtimex(&txc) < 0 || txc.maxerror != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
txc.modes = ADJ_SETOFFSET;
|
||||||
|
txc.time.tv_sec = 0;
|
||||||
|
txc.time.tv_usec = 0;
|
||||||
|
if (adjtimex(&txc) < 0 || txc.maxerror < 100000)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TMX_ApplyStepOffset(double offset)
|
||||||
|
{
|
||||||
|
struct timex txc;
|
||||||
|
|
||||||
|
txc.modes = ADJ_SETOFFSET;
|
||||||
|
if (offset >= 0) {
|
||||||
|
txc.time.tv_sec = offset;
|
||||||
|
} else {
|
||||||
|
txc.time.tv_sec = offset - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADJ_NANO changes the status even with ADJ_SETOFFSET, use it only when
|
||||||
|
STA_NANO is already enabled */
|
||||||
|
if (status & STA_PLL) {
|
||||||
|
txc.modes |= ADJ_NANO;
|
||||||
|
txc.time.tv_usec = 1e9 * (offset - txc.time.tv_sec);
|
||||||
|
} else {
|
||||||
|
txc.time.tv_usec = 1e6 * (offset - txc.time.tv_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjtimex(&txc);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,8 @@ int TMX_SetSync(int sync);
|
||||||
int TMX_EnableNanoPLL(void);
|
int TMX_EnableNanoPLL(void);
|
||||||
int TMX_ApplyPLLOffset(long offset);
|
int TMX_ApplyPLLOffset(long offset);
|
||||||
int TMX_GetPLLOffsetLeft(long *offset);
|
int TMX_GetPLLOffsetLeft(long *offset);
|
||||||
|
int TMX_TestStepOffset(void);
|
||||||
|
int TMX_ApplyStepOffset(double offset);
|
||||||
|
|
||||||
#endif /* GOT_WRAP_ADJTIMEX_H */
|
#endif /* GOT_WRAP_ADJTIMEX_H */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue