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_STATUS 0x0010 /* clock status */
|
||||
#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_TICK 0x4000 /* tick value */
|
||||
#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. */
|
||||
static int have_nanopll;
|
||||
|
||||
/* Flag indicating whether adjtimex() can step the clock */
|
||||
static int have_setoffset;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void handle_end_of_slew(void *anything);
|
||||
|
@ -612,6 +615,11 @@ apply_step_offset(double offset)
|
|||
abort_slew();
|
||||
}
|
||||
|
||||
if (have_setoffset) {
|
||||
if (TMX_ApplyStepOffset(-offset) < 0) {
|
||||
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
|
||||
}
|
||||
} else {
|
||||
if (gettimeofday(&old_time, NULL) < 0) {
|
||||
LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
|
||||
}
|
||||
|
@ -628,6 +636,7 @@ apply_step_offset(double offset)
|
|||
|
||||
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
|
||||
lcl_InvokeDispersionNotifyHandlers(fabs(err));
|
||||
}
|
||||
|
||||
initiate_slew();
|
||||
|
||||
|
@ -1010,6 +1019,13 @@ get_version_specific_details(void)
|
|||
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 */
|
||||
CNF_GetLinuxFreqScale(&set_config_freq_scale, &config_freq_scale);
|
||||
if (set_config_freq_scale) {
|
||||
|
@ -1049,6 +1065,11 @@ SYS_Linux_Initialise(void)
|
|||
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());
|
||||
|
||||
/* Read current kernel frequency */
|
||||
|
|
|
@ -229,5 +229,53 @@ TMX_GetPLLOffsetLeft(long *offset)
|
|||
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
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@ int TMX_SetSync(int sync);
|
|||
int TMX_EnableNanoPLL(void);
|
||||
int TMX_ApplyPLLOffset(long offset);
|
||||
int TMX_GetPLLOffsetLeft(long *offset);
|
||||
int TMX_TestStepOffset(void);
|
||||
int TMX_ApplyStepOffset(double offset);
|
||||
|
||||
#endif /* GOT_WRAP_ADJTIMEX_H */
|
||||
|
||||
|
|
Loading…
Reference in a new issue