summaryrefslogblamecommitdiffstats
path: root/main/common/dallasdate.c
blob: 7ebf43f381cdfb202b0a99e9e194ebe35c8e7c58 (plain) (tree)
1
2
3
4


                                                                           
  






















                                                                           
                                                                      



















                                                                        



                                                       

                                                    















                                                     

               







                                                                    

  








                                                      
 
                             



                                                                  
                                                                  


                
                                                                   



                                                    





                                                     








                                                                  







                                




                                                         




                                             




                    

                                                              
 
                                          
 







                                                             
 
                                                
 




                        
 

                                                      
 
              






                    













                                                           


              


                                                                   



                                         

































                                                   


              




                                                                    
  
                                           



              


































                                                                      


                   
                                                             



                   
                                                         



                    

                                                             
                       








                                               
      
     




                           



















































                                                                             


 
/**************************************************************************
 *
 * 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);
}