Index: sys/kern/subr_clock.c =================================================================== --- sys/kern/subr_clock.c (revision 327438) +++ sys/kern/subr_clock.c (working copy) @@ -199,6 +199,52 @@ clock_ct_to_ts(struct clocktime *ct, struct timesp return (0); } +int +clock_bcd_to_ts(struct bcd_clocktime *bct, struct timespec *ts) +{ + struct clocktime ct; + + /* + * Year may come in as 2-digit or 4-digit. clock_ct_to_ts() handles the + * century for 2-digit, but we need to decode the century for 4-digit. + */ + if (validbcd(bct->year)) { + if (bct->year <= 0x99) + ct.year = FROMBCD(bct->year); + else + ct.year = FROMBCD(bct->year >> 8) * 100 + + FROMBCD(bct->year & 0xff); + } else + ct.year = -1; + + /* + * Ensure that all values are valid BCD numbers, to avoid assertions in + * the BCD-to-binary conversion routines. clock_ct_to_ts() will further + * validate the field ranges (such as 0 <= min <= 59) during conversion. + */ + if (!validbcd(bct->sec) || !validbcd(bct->min) || + !validbcd(bct->hour) || !validbcd(bct->day) || + !validbcd(bct->mon) || ct.year == -1) { + if (ct_debug) { + printf("clock_bcd_to_ts: bad BCD: " + "[%04x-%02x-%02x %02x:%02x:%02x]\n", + bct->year, bct->mon, bct->day, + bct->hour, bct->min, bct->sec); + } + return (EINVAL); + } + + ct.mon = FROMBCD(bct->mon); + ct.day = FROMBCD(bct->day); + ct.hour = FROMBCD(bct->hour); + ct.min = FROMBCD(bct->min); + ct.sec = FROMBCD(bct->sec); + ct.dow = bct->dow; + ct.nsec = bct->nsec; + + return (clock_ct_to_ts(&ct, ts)); +} + void clock_ts_to_ct(struct timespec *ts, struct clocktime *ct) { @@ -260,6 +306,23 @@ clock_ts_to_ct(struct timespec *ts, struct clockti ("seconds %d not in 0-60", ct->sec)); } +void +clock_ts_to_bcd(struct timespec *ts, struct bcd_clocktime *bct) +{ + struct clocktime ct; + + clock_ts_to_ct(ts, &ct); + + bct->year = TOBCD(ct.year % 100) | (TOBCD(ct.year / 100) << 8); + bct->mon = TOBCD(ct.mon); + bct->day = TOBCD(ct.day); + bct->hour = TOBCD(ct.hour); + bct->min = TOBCD(ct.min); + bct->sec = TOBCD(ct.sec); + bct->dow = ct.dow; + bct->nsec = ct.nsec; +} + int utc_offset(void) { Index: sys/sys/clock.h =================================================================== --- sys/sys/clock.h (revision 327438) +++ sys/sys/clock.h (working copy) @@ -60,9 +60,22 @@ extern int tz_dsttime; int utc_offset(void); /* - * Structure to hold the values typically reported by time-of-day clocks. - * This can be passed to the generic conversion functions to be converted - * to a struct timespec. + * Structure to hold the values typically reported by time-of-day clocks, + * expressed as binary integers (see below for a BCD version). This can be + * passed to the conversion functions to be converted to/from a struct timespec. + * + * On input, the year is interpreted as follows: + * 0 - 69 = 2000 - 2069 + * 70 - 99 = 1970 - 1999 + * 100 - 199 = 2000 - 2099 (Supports hardware "century bit".) + * 200 - 1969 = Invalid. + * 1970 - 9999 = Full 4-digit century+year. + * + * The dow field is ignored (not even validated) on input, but is always + * populated with day-of-week on output. + * + * clock_ct_to_ts() returns EINVAL if any values are out of range. The year + * field will always be 4-digit on output. */ struct clocktime { int year; /* year (4 digit year) */ @@ -79,6 +92,36 @@ int clock_ct_to_ts(struct clocktime *, struct time void clock_ts_to_ct(struct timespec *, struct clocktime *); /* + * Structure to hold the values typically reported by time-of-day clocks, + * expressed as BCD. This can be passed to the conversion functions to be + * converted to/from a struct timespec. + * + * The clock_bcd_to_ts() function interprets the values in the year through sec + * fields as BCD numbers, and returns EINVAL if any BCD values are out of range. + * After conversion to binary, the values are passed to clock_ct_to_ts() and + * undergo further validation as described above. Year may be 2 or 4-digit BCD, + * interpreted as described above. The nsec field is binary. + * + * The clock_ts_to_bcd() function converts the timespec to BCD values stored + * into year through sec. The value in year will be 4-digit BCD (e.g., + * 0x2017). The mon through sec values will be 2-digit BCD. The nsec field will + * be binary, and the range of dow makes its binary and BCD values identical. + */ +struct bcd_clocktime { + int year; /* year (4 digit year) */ + int mon; /* month (1 - 12) */ + int day; /* day (1 - 31) */ + int hour; /* hour (0 - 23) */ + int min; /* minute (0 - 59) */ + int sec; /* second (0 - 59) */ + int dow; /* day of week (0 - 6; 0 = Sunday) */ + long nsec; /* nano seconds */ +}; + +int clock_bcd_to_ts(struct bcd_clocktime *, struct timespec *); +void clock_ts_to_bcd(struct timespec *, struct bcd_clocktime *); + +/* * Time-of-day clock functions and flags. These functions might sleep. * * clock_register and clock_unregister() do what they say. Upon return from