📄 cmos.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
Abstract:
This file implements the NK kernel interfaces for the real time clock.
NOTE: This file is also used by bootloader, so don't put any NK specific
code in here
Functions:
Notes:
--*/
#include <windows.h>
#include <pc.h>
// NOTE: A problem has been found with some chipsets such that
// setting the time to 23:59:59 on the 29th or 30th day of a month which
// has less than 31 days causes the clock to roll over incorrectly.
// Uncomment the following line to fix this problem. However, be aware
// that the fix consists of responding to calls that set the time to
// HH:MM:59 by instead setting the time to HH:MM:58.
//#define HARDWARE_TIME_SET_PROBLEM 1
SYSTEMTIME RTC_AlarmTime;
CRITICAL_SECTION RTC_critsect;
void CMOS_Write( BYTE offset, BYTE value )
{
BYTE cAddr;
// Remember, we only change the low order 5 bits in address register
cAddr = _inp( CMOS_ADDR );
_outp( CMOS_ADDR, (cAddr & RTC_ADDR_MASK) | offset );
_outp( CMOS_DATA, value );
//
// NOTE : If we ever update bytes 16-45, we should also recaculate
// the checksum & store it. However, We don't currently use any of
// those bytes so I'm not going to worry about it here.
//
DEBUGCHK(offset < 16);
}
BYTE CMOS_Read( BYTE offset )
{
BYTE cAddr, cResult;
// Remember, we only change the low order 5 bits in address register
cAddr = _inp( CMOS_ADDR );
_outp( CMOS_ADDR, (cAddr & RTC_ADDR_MASK) | offset );
cResult = _inp( CMOS_DATA );
return (cResult);
}
BOOL IsTimeEqual(LPSYSTEMTIME lpst1, LPSYSTEMTIME lpst2)
{
if (lpst1->wYear != lpst2->wYear) return(FALSE);
if (lpst1->wMonth != lpst2->wMonth) return(FALSE);
if (lpst1->wDayOfWeek != lpst2->wDayOfWeek) return(FALSE);
if (lpst1->wDay != lpst2->wDay) return(FALSE);
if (lpst1->wHour != lpst2->wHour) return(FALSE);
if (lpst1->wMinute != lpst2->wMinute) return(FALSE);
if (lpst1->wSecond != lpst2->wSecond) return(FALSE);
return (TRUE);
}
// NOTE : the RTC routines are not thread safe. But the ether debug
// functions don't want me mucking about with interrupts, etc. So I provide
// the Bare_ functions for callers who run with interrupts off, etc. Then
// the real functions jsut call into the bare functions after turning off
// interrupts.
BOOL
Bare_GetRealTime(LPSYSTEMTIME lpst)
{
SYSTEMTIME st;
LPSYSTEMTIME lpst1 = &st, lpst2 = lpst, lptmp;
lpst1->wSecond = 61; // initialize to an invalid value
lpst2->wSecond = 62; // initialize to an invalid value
do {
// exchange lpst1 and lpst2
lptmp = lpst1;
lpst1 = lpst2;
lpst2 = lptmp;
// wait until not updating
while (CMOS_Read(RTC_STATUS_A) & RTC_SRA_UIP);
// Read all the values.
lpst1->wYear = CMOS_Read(RTC_YEAR);
lpst1->wMonth = CMOS_Read(RTC_MONTH);
// RTC clock stores DO_WEEK 1-7, SYSTEMTIME uses 0-6
lpst1->wDayOfWeek = CMOS_Read(RTC_DO_WEEK)-1;
lpst1->wDay = CMOS_Read(RTC_DO_MONTH);
lpst1->wHour = CMOS_Read(RTC_HOUR);
lpst1->wMinute = CMOS_Read(RTC_MINUTE);
lpst1->wSecond = CMOS_Read(RTC_SECOND);
} while (!IsTimeEqual (lpst1, lpst2));
lpst->wMilliseconds = 0; // Not sure how we would get this
if (!(CMOS_Read (RTC_STATUS_B) & RTC_SRB_DM)) {
// Values returned in BCD.
lpst->wSecond = DECODE_BCD(lpst->wSecond);
lpst->wMinute = DECODE_BCD(lpst->wMinute);
lpst->wHour = DECODE_BCD(lpst->wHour);
lpst->wDay = DECODE_BCD(lpst->wDay);
lpst->wDayOfWeek = DECODE_BCD(lpst->wDayOfWeek);
lpst->wMonth = DECODE_BCD(lpst->wMonth);
lpst->wYear = DECODE_BCD(lpst->wYear);
}
// OK - PC RTC returns 2009 as 09.
lpst->wYear += 2000;
#ifdef NOTDEF
NKDbgPrintfW(TEXT("\r\nReal Time %d, %d, %d, %d, %d, %d, %d, %d\r\n"),
lpst->wYear,
lpst->wMonth,
lpst->wDayOfWeek,
lpst->wDay,
lpst->wHour,
lpst->wMinute,
lpst->wSecond,
lpst->wMilliseconds );
#endif
return (TRUE);
}
BOOL
Bare_SetRealTime(LPSYSTEMTIME lpst)
{
BYTE cStatusRegA, cStatusRegB, Year;
#ifdef NOTDEF
NKDbgPrintfW(TEXT("\r\nSet Real Time %d, %d, %d, %d, %d, %d, %d, %d\r\n"),
lpst->wYear,
lpst->wMonth,
lpst->wDayOfWeek,
lpst->wDay,
lpst->wHour,
lpst->wMinute,
lpst->wSecond,
lpst->wMilliseconds );
#endif
Year = lpst->wYear % 100;
#ifdef HARDWARE_TIME_SET_PROBLEM
if (lpst->wSecond == 59) {
lpst->wSecond = 58;
}
#endif
// Read the update in progress bit, wait for it to be clear. This bit will
// be set once per second for about 2us (Undoc. PC, page 897)
do {
cStatusRegA = CMOS_Read( RTC_STATUS_A);
} while ( cStatusRegA & RTC_SRA_UIP );
// Disable updates while we change the values
cStatusRegB = CMOS_Read( RTC_STATUS_B );
cStatusRegB |= RTC_SRB_UPDT;
CMOS_Write( RTC_STATUS_B, cStatusRegB );
if ( !(cStatusRegB & RTC_SRB_DM) ) {
// BCD Mode
CMOS_Write( RTC_YEAR, (BYTE)(CREATE_BCD(Year)));
CMOS_Write( RTC_MONTH, (BYTE)(CREATE_BCD(lpst->wMonth)));
// RTC clock stores DO_WEEK 1-7, SYSTEMTIME uses 0-6
CMOS_Write( RTC_DO_WEEK, (BYTE)(CREATE_BCD(lpst->wDayOfWeek+1)));
CMOS_Write( RTC_DO_MONTH, (BYTE)(CREATE_BCD(lpst->wDay)));
CMOS_Write( RTC_HOUR, (BYTE)(CREATE_BCD(lpst->wHour)));
CMOS_Write( RTC_MINUTE, (BYTE)(CREATE_BCD(lpst->wMinute)));
CMOS_Write( RTC_SECOND, (BYTE)(CREATE_BCD(lpst->wSecond)));
// Not sure how we can do lpst->wMilliseconds;
} else {
// Binary mode
CMOS_Write( RTC_YEAR, (UCHAR)Year);
CMOS_Write( RTC_MONTH, (UCHAR)lpst->wMonth);
// RTC clock stores DO_WEEK 1-7, SYSTEMTIME uses 0-6
CMOS_Write( RTC_DO_WEEK, (UCHAR)(lpst->wDayOfWeek+1));
CMOS_Write( RTC_DO_MONTH, (UCHAR)lpst->wDay);
CMOS_Write( RTC_HOUR, (UCHAR)lpst->wHour);
CMOS_Write( RTC_MINUTE, (UCHAR)lpst->wMinute);
CMOS_Write( RTC_SECOND, (UCHAR)lpst->wSecond);
// Not sure how we can do lpst->wMilliseconds;
}
// Reenable updates
cStatusRegB &= ~RTC_SRB_UPDT;
CMOS_Write( RTC_STATUS_B, cStatusRegB );
return (TRUE);
}
BOOL
Bare_SetAlarmTime(LPSYSTEMTIME lpst)
{
BYTE cStatusRegA, cStatusRegB, Year;
#ifdef NOTDEF
NKDbgPrintfW(TEXT("\r\nSet Alarm Time %d, %d, %d, %d, %d, %d, %d, %d\r\n"),
lpst->wYear,
lpst->wMonth,
lpst->wDayOfWeek,
lpst->wDay,
lpst->wHour,
lpst->wMinute,
lpst->wSecond,
lpst->wMilliseconds );
#endif
// NOTE : Our alarm only has a 1 day rollover. So we need to store the full
// alarm time and compare on alarm interrupts to see if we are at the correct
// day.
RTC_AlarmTime = *lpst;
Year = lpst->wYear % 100;
// Read the update in progress bit, wait for it to be clear. This bit will
// be set once per second for about 2us (Undoc. PC, page 897)
do {
cStatusRegA = CMOS_Read( RTC_STATUS_A);
} while ( cStatusRegA & RTC_SRA_UIP );
// Disable updates while we change the values
cStatusRegB = CMOS_Read( RTC_STATUS_B );
cStatusRegB |= RTC_SRB_UPDT;
CMOS_Write( RTC_STATUS_B, cStatusRegB );
if ( !(cStatusRegB & RTC_SRB_DM) ) {
// BCD Mode
CMOS_Write( RTC_ALRM_HOUR, (BYTE)(CREATE_BCD(lpst->wHour)));
CMOS_Write( RTC_ALRM_MINUTE, (BYTE)(CREATE_BCD(lpst->wMinute)));
CMOS_Write( RTC_ALRM_SECOND, (BYTE)(CREATE_BCD(lpst->wSecond)));
} else {
// Binary mode
CMOS_Write( RTC_ALRM_HOUR, (UCHAR)lpst->wHour);
CMOS_Write( RTC_ALRM_MINUTE, (UCHAR)lpst->wMinute);
CMOS_Write( RTC_ALRM_SECOND, (UCHAR)lpst->wSecond);
}
// Enable alarm interrupt and reenable updates
cStatusRegB = (cStatusRegB | RTC_SRB_AI) & ~RTC_SRB_UPDT;
CMOS_Write( RTC_STATUS_B, cStatusRegB );
return( TRUE );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -