diff options
Diffstat (limited to 'main/common/dallasdate.c')
-rw-r--r-- | main/common/dallasdate.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/main/common/dallasdate.c b/main/common/dallasdate.c new file mode 100644 index 0000000..31af955 --- /dev/null +++ b/main/common/dallasdate.c @@ -0,0 +1,362 @@ +/************************************************************************** + * + * Copyright (c) 2013 Alcatel-Lucent + * + * Alcatel Lucent licenses this file to You under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. A copy of the License is contained the + * file LICENSE at the top level of this repository. + * You may also obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************** + * + * dallasdate.c: + * + * Code to support the DALLAS DS1743W Timekeeping RAM plus the hooks to + * allow TFS to timestamp files. The function tfsTimeEnable() must be + * called to setup the connection between TFS and the DS1743W. + * + * Applicable to DS1743W and DS1746WP. Probably also works with other + * Dallas timekeepers, but the above two are the only ones I've tested + * it with. This code is assumed to be compiled with the DATEREGBASE + * value defined elsewhere. This value is the address at which the code + * is to access the 8-byte register map that contains the time/date + * information. + * + * + * Original author: Ed Sutter (ed.sutter@alcatel-lucent.com) + * + */ +#include "config.h" +#include "stddefs.h" +#include "genlib.h" +#include "tfs.h" +#include "tfsprivate.h" +#include "cli.h" + +#ifndef DATEREGBASE +#error DATEREGBASE not defined +#endif + +#define WRITE 0x80 /* part of 'cent' member */ +#define READ 0x40 /* part of 'cent' member */ +#define FT 0x40 /* part of 'wday' member */ +#define OSC_OFF 0x80 /* part of 'sec' member */ + +/* Masks for various portions of the time/date... */ +#define YEAR10_MASK 0xf0 +#define MONTH10_MASK 0x10 +#define DATE10_MASK 0x30 +#define DAY10_MASK 0x00 +#define HOUR10_MASK 0x30 +#define MINUTE10_MASK 0x70 +#define SECOND10_MASK 0x70 +#define CENTURY10_MASK 0x30 +#define YEAR_MASK 0x0f /* Year is 0-99 */ +#define MONTH_MASK 0x0f /* Month is 1-12 */ +#define DATE_MASK 0x0f /* Date is 1-31 */ +#define DAY_MASK 0x07 /* Day is 1-7 */ +#define HOUR_MASK 0x0f /* Hour is 0-23 */ +#define MINUTE_MASK 0x0f /* Minutes is 0-59 */ +#define SECOND_MASK 0x0f /* Seconds is 0-59 */ +#define CENTURY_MASK 0x0f /* Century is 0-39 */ + +struct dsdate { + uchar cent; /* B7=W, B6=R, B5-4=10cent, B3-0=cent (00-39) */ + uchar sec; /* B7=OSC; B6-4=10secs; B3-0=sec (0-59) */ + uchar min; /* B6-4=10mins; B3-0=min (0-59) */ + uchar hour; /* B4-5=10hour; B3-0=hour (0-23) */ + uchar wday; /* B6=FT B2-0=day (1-7) */ + uchar mday; /* B4-5=10date; B3-0=date (1-31) */ + uchar month; /* B4=10mo; B3-0=month (1-12) */ + uchar year; /* MS-nibble=10Year; LS-nibble=year (0-99) */ +}; + +#define DS1743REGS ((struct dsdate *)DATEREGBASE) +#define DS_century (DS1743REGS->cent) +#define DS_second (DS1743REGS->sec) +#define DS_minute (DS1743REGS->min) +#define DS_hour (DS1743REGS->hour) +#define DS_wday (DS1743REGS->wday) +#define DS_mday (DS1743REGS->mday) +#define DS_month (DS1743REGS->month) +#define DS_year (DS1743REGS->year) + +#define SECONDS_IN_DAY 86400 + +static char +DaysInMonth[] = { + 31, 28, 31, 30, 31, 30, /* Jan, Feb, Mar, Apr, May, Jun */ + 31, 31, 30, 31, 30, 31 /* Jul, Aug, Sep, Oct, Nov, Dec */ +}; + +/* rangecheck(): + Return 0 if value is outside the range of high and low; else 1. +*/ +static int +rangecheck(int value, char *name, int low, int high) +{ + if ((value < low) || (value > high)) { + printf("%s outside valid range of %d - %d\n", + name,low,high); + return(0); + } + return(1); +} + +/* to_dsdatefmt(): + * Take an incoming integer and convert it to the dallas time date + * format using a 10-multiplier for the upper nibble. + */ +static unsigned char +to_dsdatefmt(int value) +{ + int tens; + + tens = 0; + while (value >= 10) { + tens++; + value -= 10; + } + return((tens << 4) | value); +} + +static int +from_dsdatefmt(unsigned char value, unsigned char mask10) +{ + int newvalue; + + newvalue = value & 0x0f; + newvalue += 10 * ((value & mask10) >> 4); + return(newvalue); +} + +int +showDate(int center) +{ + int (*pfunc)(char *, ...); + int day, date, month, year, hour, minute, second, century; + + DS_century |= READ; /* Set READ bit */ + + century = from_dsdatefmt(DS_century,CENTURY10_MASK); + second = from_dsdatefmt(DS_second,SECOND10_MASK); + minute = from_dsdatefmt(DS_minute,MINUTE10_MASK); + hour = from_dsdatefmt(DS_hour,HOUR10_MASK); + day = from_dsdatefmt(DS_wday,DAY10_MASK); + date = from_dsdatefmt(DS_mday,DATE10_MASK); + month = from_dsdatefmt(DS_month,MONTH10_MASK); + year = (((DS_year & 0xf0) >> 4) * 10) + (DS_year & 0x0f); + + DS_century &= ~READ; /* Clear READ bit */ + + if (center) + pfunc = cprintf; + else + pfunc = printf; + + pfunc("%02d/%02d/%02d%02d @ %02d:%02d:%02d\n", + month,date,century,year,hour,minute,second); + + return(0); +} + +#if INCLUDE_TFS + +static int +YearIsLeap(int year) +{ + if (year % 4) /* if year not divisible by 4... */ + return(0); /* it's not leap */ + if (year < 1582) /* all years divisible by 4 were */ + return(1); /* leap prior to 1582 */ + if (year % 100) /* if year divisible by 4, */ + return(1); /* but not by 100, it's leap */ + if (year % 400) /* if year divisible by 100, */ + return(0); /* but not by 400, it's not leap */ + else + return(1); /* if divisible by 400, it's leap */ +} + +/* GetAtime(): + * Build a string based on the incoming long value as described in + * GetLtime()... + * Used by TFS to keep track of file time. + */ +static char * +GetAtime(long tval, char *buf, int bsize) +{ + int year; /* Actual year */ + int doy; /* Day of year */ + int sid; /* Seconds in day */ + int leapyear; /* Set if year is leap */ + int month, hour, minute; + + if ((bsize < 18) || (tval == TIME_UNDEFINED)) { + *buf = 0; + return(buf); + } + + /* Break down the basic bitfields: */ + year = 2000 + ((tval >> 26) & 0x3f); + leapyear = YearIsLeap(year); + doy = ((tval >> 17) & 0x1ff); + sid = tval & 0x1ffff; + + /* Now build the date from the bit fields: */ + hour = sid / 3600; + sid -= (hour*3600); + minute = sid/60; + sid -= (minute*60); + + month = 0; + while(doy > DaysInMonth[month]) { + doy -= DaysInMonth[month]; + if ((month == 1) && (leapyear)) + doy--; + month++; + } + sprintf(buf,"%02d/%02d/%02d@%02d:%02d:%02d", + month+1,doy,year,hour,minute,sid); + return(buf); +} + +/* GetLtime(): + * Build a long with the following format.... + * + * B31-26 year since 2000 (0-127 yep, this breaks in 2128) + * B25-17 day of year (1-365) + * B16-0 seconds in day (0-86400) + * + * Used by TFS to keep track of file time. + */ +static long +GetLtime(void) +{ + long tval; + int year, month, mday, seconds, doy, leapyear; + + DS_century |= READ; /* Set READ bit */ + + year = from_dsdatefmt(DS_year,YEAR10_MASK); /* 00=2000 */ + month = from_dsdatefmt(DS_month,MONTH10_MASK)-1; /* 0-11 */ + mday = from_dsdatefmt(DS_mday,DATE10_MASK); /* 1-31 */ + leapyear = YearIsLeap(year+2000); + + if ((month > 11) || (month < 0) || (mday < 1) || (mday > 31)) + return(TIME_UNDEFINED); + + /* Determine current day of year... */ + doy = mday; + while(month > 0) { + doy += DaysInMonth[month-1]; + if ((month == 1) && leapyear) + doy++; + month--; + } + + /* Determine current second of day... */ + seconds = (from_dsdatefmt(DS_hour,HOUR10_MASK) * 3600); + seconds += (from_dsdatefmt(DS_minute,MINUTE10_MASK) * 60); + seconds += from_dsdatefmt(DS_second,SECOND10_MASK); + + DS_century &= ~READ; /* Clear READ bit */ + + tval = (((year & 0x3f) << 26) | + ((doy & 0x1ff) << 17) | + (seconds & 0x1ffff)); + return(tval); +} + +/* tfsTimeEnable(): + * Hook the file timestamping in TFS to the DS1743 device... + */ +void +tfsTimeEnable(void) +{ + tfsctrl(TFS_TIMEFUNCS,(long)GetLtime,(long)GetAtime); +} +#endif + +char *DateHelp[] = { + "Display (mm/dd/yyyy@hh:mm:ss) or modify time and date.", + "[{day date month year hour min sec}]", +#if INCLUDE_VERBOSEHELP + "Where...", + " day: 1-7 (sun=1)", + " date: 1-31", + " month: 1-12", + " year: 0-3899", + " hour: 0-23", + " min: 0-59", + " sec: 0-59", + "Note: 'date off' disables the oscillator", +#endif + 0 +}; + +int +Date(int argc,char *argv[]) +{ + int day, date, month, year, hour, minute, second, century, rngchk; + + if (argc == 1) { + if (DS_second & OSC_OFF) + printf("Warning: oscillator disabled.\n"); + showDate(0); + return(CMD_SUCCESS); + } + else if ((argc == 2) && !strcmp(argv[1],"off")) { + DS_century |= WRITE; /* Disable the oscillator */ + DS_second = OSC_OFF; /* to save battery life. */ + DS_century &= ~WRITE; + return(CMD_SUCCESS); + } + else if (argc != 8) + return(CMD_PARAM_ERROR); + + day = atoi(argv[1]); + date = atoi(argv[2]); + month = atoi(argv[3]); + year = atoi(argv[4]); + hour = atoi(argv[5]); + minute = atoi(argv[6]); + second = atoi(argv[7]); + + rngchk = 0; + rngchk += rangecheck(day,"day",1,7); + rngchk += rangecheck(date,"date",1,31); + rngchk += rangecheck(month,"month",1,12); + rngchk += rangecheck(year,"year",0,3899); + rngchk += rangecheck(hour,"hour",0,23); + rngchk += rangecheck(minute,"minute",0,59); + rngchk += rangecheck(second,"second",0,59); + + if (rngchk != 7) + return(CMD_PARAM_ERROR); + + DS_century = WRITE; /* Set WRITE bit */ + DS_second = to_dsdatefmt(second) & ~OSC_OFF; + DS_minute = to_dsdatefmt(minute); + DS_hour = to_dsdatefmt(hour); + DS_wday = day; + DS_mday = to_dsdatefmt(date); + DS_month = to_dsdatefmt(month); + century = year / 100; + year = year % 100; + DS_year = to_dsdatefmt(year); + DS_century = (to_dsdatefmt(century)&(CENTURY_MASK|CENTURY10_MASK))|WRITE; + + DS_century &= ~WRITE; /* Clear WRITE bit */ + return(CMD_SUCCESS); +} + + |