#include <types.h>
#include <mach/message.h>
#include <kern/thread.h>
#include <kern/clock.h>
#include <kern/spl.h>
#include <kern/processor.h>
#include <kern/misc_protos.h>
#include <i386/pio.h>
#include <i386/AT386/rtc.h>
#include <i386/AT386/bbclock_entries.h>
static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
extern char dectohexdec(
int n);
extern int hexdectodec(
char c);
extern int yeartoday(
int yr);
extern void rtcput(
struct rtc_st * regs);
extern int rtcget(
struct rtc_st * regs);
#define LOCK_BBC() splclock()
#define UNLOCK_BBC(s) splx(s)
int
bbc_config(void)
{
int BbcFlag;
struct rtc_st rtclk;
#if NCPUS > 1 && AT386
mp_disable_preemption();
if (cpu_number() != master_cpu) {
mp_enable_preemption();
return(1);
}
#endif
outb(RTC_ADDR, RTC_A);
outb(RTC_DATA, RTC_DIV2 | RTC_RATE6);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, RTC_HM);
BbcFlag = (rtcget(&rtclk) ? 0 : 1);
if (BbcFlag)
printf("battery clock configured\n");
else
printf("WARNING: Battery Clock Failure!\n");
#if NCPUS > 1 && AT386
mp_enable_preemption();
#endif
return (BbcFlag);
}
kern_return_t
bbc_gettime(
mach_timespec_t *cur_time)
{
struct rtc_st rtclk;
time_t n;
int sec, min, hr, dom, mon, yr;
int i, days = 0;
spl_t s;
thread_t thread;
#if NCPUS > 1 && AT386
if ((thread = current_thread()) != THREAD_NULL) {
thread_bind(thread, master_processor);
mp_disable_preemption();
if (current_processor() != master_processor) {
mp_enable_preemption();
thread_block(THREAD_CONTINUE_NULL);
} else {
mp_enable_preemption();
}
}
#endif
s = LOCK_BBC();
rtcget(&rtclk);
sec = hexdectodec(rtclk.rtc_sec);
min = hexdectodec(rtclk.rtc_min);
hr = hexdectodec(rtclk.rtc_hr);
dom = hexdectodec(rtclk.rtc_dom);
mon = hexdectodec(rtclk.rtc_mon);
yr = hexdectodec(rtclk.rtc_yr);
yr = (yr < 70) ? yr+100 : yr;
n = sec + 60 * min + 3600 * hr;
n += (dom - 1) * 3600 * 24;
if (yeartoday(yr) == 366)
month[1] = 29;
for (i = mon - 2; i >= 0; i--)
days += month[i];
month[1] = 28;
for (i = 70; i < yr; i++)
days += yeartoday(i);
n += days * 3600 * 24;
cur_time->tv_sec = n;
cur_time->tv_nsec = 0;
UNLOCK_BBC(s);
#if NCPUS > 1 && AT386
if (thread != THREAD_NULL)
thread_bind(thread, PROCESSOR_NULL);
#endif
return (KERN_SUCCESS);
}
kern_return_t
bbc_settime(
mach_timespec_t *new_time)
{
struct rtc_st rtclk;
time_t n;
int diff, i, j;
spl_t s;
thread_t thread;
#if NCPUS > 1 && AT386
if ((thread = current_thread()) != THREAD_NULL) {
thread_bind(thread, master_processor);
mp_disable_preemption();
if (current_processor() != master_processor) {
mp_enable_preemption();
thread_block(THREAD_CONTINUE_NULL);
} else {
mp_enable_preemption();
}
}
#endif
s = LOCK_BBC();
rtcget(&rtclk);
diff = 0;
n = (new_time->tv_sec - diff) % (3600 * 24);
rtclk.rtc_sec = dectohexdec(n%60);
n /= 60;
rtclk.rtc_min = dectohexdec(n%60);
rtclk.rtc_hr = dectohexdec(n/60);
n = (new_time->tv_sec - diff) / (3600 * 24);
rtclk.rtc_dow = (n + 4) % 7;
for (j = 1970; n >= (i = yeartoday(j)); j++)
n -= i;
rtclk.rtc_yr = dectohexdec(j % 100);
if (yeartoday(j) == 366)
month[1] = 29;
for (i = 0; n >= month[i]; i++)
n -= month[i];
month[1] = 28;
rtclk.rtc_mon = dectohexdec(++i);
rtclk.rtc_dom = dectohexdec(++n);
rtcput(&rtclk);
UNLOCK_BBC(s);
#if NCPUS > 1 && AT386
if (thread != THREAD_NULL)
thread_bind(current_thread(), PROCESSOR_NULL);
#endif
return (KERN_SUCCESS);
}
kern_return_t
bbc_getattr(
clock_flavor_t flavor,
clock_attr_t attr,
mach_msg_type_number_t *count)
{
if (*count != 1)
return (KERN_FAILURE);
switch (flavor) {
case CLOCK_GET_TIME_RES:
*(clock_res_t *) attr = NSEC_PER_SEC;
break;
case CLOCK_ALARM_CURRES:
case CLOCK_ALARM_MINRES:
case CLOCK_ALARM_MAXRES:
*(clock_res_t *) attr = 0;
break;
default:
return (KERN_INVALID_VALUE);
}
return (KERN_SUCCESS);
}
int
rtcget(
struct rtc_st * regs)
{
outb(RTC_ADDR, RTC_D);
if (inb(RTC_DATA) & RTC_VRT == 0)
return (-1);
outb(RTC_ADDR, RTC_A);
while (inb(RTC_DATA) & RTC_UIP)
outb(RTC_ADDR, RTC_A);
load_rtc((unsigned char *)regs);
return (0);
}
void
rtcput(
struct rtc_st * regs)
{
register unsigned char x;
outb(RTC_ADDR, RTC_B);
x = inb(RTC_DATA);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, x | RTC_SET);
save_rtc((unsigned char *)regs);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, x & ~RTC_SET);
}
int
yeartoday(
int year)
{
year += 1900;
return((year % 4) ? 365 :
((year % 100) ? 366 : ((year % 400) ? 365: 366)));
}
int
hexdectodec(
char n)
{
return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F));
}
char
dectohexdec(
int n)
{
return ((char)(((n / 10) << 4) & 0xF0) | ((n % 10) & 0x0F));
}