freebsd-skq/usr.bin/doscmd/cmos.c

291 lines
7.6 KiB
C

/*
* Copyright (c) 1992, 1993, 1996
* Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Berkeley Software
* Design, Inc.
*
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* BSDI cmos.c,v 2.3 1996/04/08 19:32:20 bostic Exp
*
* $FreeBSD$
*/
#include "doscmd.h"
#define ALARM_ON ((unsigned char) 0x20)
#define FAST_TIMER ((unsigned char) 0x40)
#define SEC_SIZE 1
#define MIN_SIZE 60
#define HOUR_SIZE (MIN_SIZE * 60)
#define DAY_SIZE (HOUR_SIZE * 24)
#define YEAR_DAY 365
#define SEC_MS 1000000
#define FAST_TICK_BSD 0x3D00
#define Jan 31
#define Feb 28
#define Mar 31
#define Apr 30
#define May 31
#define Jun 30
#define Jul 31
#define Aug 31
#define Sep 31
#define Oct 31
#define Nov 30
#define Dec 31
static unsigned char cmos_last_port_70 = 0;
static unsigned char cmos_data[0x40] = {
0x00, /* 0x00 Current Second */
0x00, /* 0x01 Alarm Second */
0x00, /* 0x02 Current minute */
0x00, /* 0x03 Alarm minute */
0x00, /* 0x04 Current hour */
0x00, /* 0x05 Alarm hour */
0x00, /* 0x06 Current week day */
0x00, /* 0x07 Current day */
0x00, /* 0x08 Current month */
0x00, /* 0x09 Current year */
0x26, /* 0x0A Status register A */
0x02, /* 0x0B Status register B */
0x00, /* 0x0C Status register C */
0x80, /* 0x0D Status register D */
0x00, /* 0x0E Diagnostic status */
0x00, /* 0x0F Shutdown Code */
0x00, /* 0x10 Drive types (1 FDHD disk) */
0x00, /* 0x11 Fixed disk 0 type */
0x00, /* 0x12 Fixed disk 1 type */
0x00,
0x00, /* Installed equipment */
};
int day_in_year [12] = {
0, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov
};
/* consumed by dos.c */
time_t delta_clock = 0;
/* locals */
static struct timeval fast_clock;
static int fast_tick;
static struct timeval glob_clock;
static int cmos_alarm_time = 0;
static int cmos_alarm_daytime = 0;
static inline int
day_in_mon_year(int mon, int year)
{
return day_in_year[mon] + (mon > 2 && (year % 4 == 0));
}
static inline int
to_BCD (int n)
{
n &= 0xFF;
return n%10 + ((n/10)<<4);
}
static inline int
from_BCD (int n)
{
n &= 0xFF;
return (n & 0xF) + (n >> 4) * 10;
}
/*
** inb() from clock ports.
**
** 0x70 is scratchpad/register select
** 0x71 is data
*/
static unsigned char
cmos_inb(int portnum)
{
unsigned char ret_val;
int cmos_reg;
struct timezone tz;
struct tm tm;
time_t now;
switch (portnum) {
case 0x70:
ret_val = cmos_last_port_70;
break;
case 0x71:
cmos_reg = cmos_last_port_70 & 0x3f;
if (cmos_reg < 0xa) {
gettimeofday(&glob_clock, &tz);
now = glob_clock.tv_sec + delta_clock;
tm = *localtime(&now);
}
switch (cmos_reg) {
case 0:
ret_val = to_BCD(tm.tm_sec);
break;
case 2:
ret_val = to_BCD(tm.tm_min);
break;
case 4:
ret_val = to_BCD(tm.tm_hour);
break;
case 6:
ret_val = to_BCD(tm.tm_wday);
break;
case 7:
ret_val = to_BCD(tm.tm_mday);
break;
case 8:
ret_val = to_BCD(tm.tm_mon + 1);
break;
case 9:
ret_val = to_BCD((tm.tm_year + 1900) % 100);
break;
default:
ret_val = cmos_data[cmos_reg];
break;
}
break;
}
return (ret_val);
}
static void
cmos_outb(int portnum, unsigned char byte)
{
int cmos_reg;
int year;
int time00;
struct timezone tz;
struct tm tm;
time_t now;
switch (portnum) {
case 0x70:
cmos_last_port_70 = byte;
break;
case 0x71:
cmos_reg = cmos_last_port_70 & 0x3f;
if (cmos_reg < 0xa) {
gettimeofday(&glob_clock, &tz);
now = glob_clock.tv_sec + delta_clock;
tm = *localtime(&now);
}
switch (cmos_reg) {
case 0:
delta_clock += SEC_SIZE * (from_BCD(byte) - tm.tm_sec);
break;
case 1:
cmos_alarm_daytime +=
SEC_SIZE * (from_BCD(byte) - from_BCD(cmos_data[1]));
break;
case 2:
delta_clock += MIN_SIZE * (from_BCD(byte) - tm.tm_min);
break;
case 3:
cmos_alarm_daytime +=
MIN_SIZE * (from_BCD(byte) - from_BCD(cmos_data[3]));
break;
case 4:
delta_clock += HOUR_SIZE * (from_BCD(byte) - tm.tm_hour);
break;
case 5:
cmos_alarm_daytime +=
HOUR_SIZE * (from_BCD(byte) - from_BCD(cmos_data[5]));
break;
case 7:
delta_clock += DAY_SIZE * (from_BCD(byte) - tm.tm_mday);
break;
case 8:
delta_clock += DAY_SIZE *
(day_in_mon_year(from_BCD(byte), tm.tm_year) -
day_in_mon_year(tm.tm_mon + 1, tm.tm_year));
break;
case 9:
year = from_BCD(byte);
delta_clock += DAY_SIZE * (YEAR_DAY * (year - tm.tm_year)
+ (year/4 - tm.tm_year/4));
break;
case 0xB:
cmos_data[0xc] = byte;
if (byte & ALARM_ON) {
debug(D_ALWAYS, "Alarm turned on\n");
time00 = glob_clock.tv_sec + delta_clock -
(tm.tm_sec + MIN_SIZE * tm.tm_min
+ HOUR_SIZE * tm.tm_hour);
cmos_alarm_time = time00 + cmos_alarm_daytime;
if (cmos_alarm_time < (glob_clock.tv_sec + delta_clock))
cmos_alarm_time += DAY_SIZE;
}
if (byte & FAST_TIMER) {
debug(D_ALWAYS, "Fast timer turned on\n");
fast_clock = glob_clock;
fast_tick = 0;
}
break;
}
cmos_data[cmos_reg] = byte;
break;
}
}
void
cmos_init(void)
{
int numflops = 0;
int checksum = 0;
int i;
cmos_data[0x0e] = 0;
numflops = nfloppies;
cmos_data[0x10] = (search_floppy(0) << 4) | search_floppy(1);
if (numflops) /* floppy drives present + numflops */
cmos_data[0x14] = ((numflops - 1) << 6) | 1;
cmos_data[0x15] = 0x80; /* base memory 640k */
cmos_data[0x16] = 0x2;
for (i=0x10; i<=0x2d; i++)
checksum += cmos_data[i];
cmos_data[0x2e] = checksum >>8; /* High byte */
cmos_data[0x2f] = checksum & 0xFF; /* Low byte */
cmos_data[0x32] = 0x19; /* Century in BCD ; temporary */
for (i = 1; i < 12; i++){
day_in_year[i] += day_in_year[i-1];
}
define_input_port_handler(0x70, cmos_inb);
define_input_port_handler(0x71, cmos_inb);
define_output_port_handler(0x70, cmos_outb);
define_output_port_handler(0x71, cmos_outb);
}