Merge branch 'vendor/FILE'
[dragonfly.git] / usr.bin / doscmd / cmos.c
1 /*
2  * Copyright (c) 1992, 1993, 1996
3  *      Berkeley Software Design, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Berkeley Software
16  *      Design, Inc.
17  *
18  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *      BSDI cmos.c,v 2.3 1996/04/08 19:32:20 bostic Exp
31  *
32  * $FreeBSD: src/usr.bin/doscmd/cmos.c,v 1.2.2.2 2002/04/25 11:04:50 tg Exp $
33  * $DragonFly: src/usr.bin/doscmd/cmos.c,v 1.2 2003/06/17 04:29:25 dillon Exp $
34  */
35
36 #include "doscmd.h"
37
38 #define ALARM_ON     ((unsigned char) 0x20)
39 #define FAST_TIMER   ((unsigned char) 0x40)
40 #define SEC_SIZE     1
41 #define MIN_SIZE     60
42 #define HOUR_SIZE    (MIN_SIZE * 60)
43 #define DAY_SIZE     (HOUR_SIZE * 24)
44 #define YEAR_DAY     365
45
46 #define SEC_MS 1000000
47 #define FAST_TICK_BSD 0x3D00
48
49 #define Jan 31
50 #define Feb 28
51 #define Mar 31
52 #define Apr 30
53 #define May 31
54 #define Jun 30
55 #define Jul 31
56 #define Aug 31
57 #define Sep 31
58 #define Oct 31
59 #define Nov 30
60 #define Dec 31
61
62 static unsigned char cmos_last_port_70 = 0;
63 static unsigned char cmos_data[0x40] = {
64     0x00,                /* 0x00 Current Second */
65     0x00,                /* 0x01 Alarm Second */
66     0x00,                /* 0x02 Current minute */
67     0x00,                /* 0x03 Alarm minute */
68     0x00,                /* 0x04 Current hour */
69     0x00,                /* 0x05 Alarm hour */
70     0x00,                /* 0x06 Current week day */
71     0x00,                /* 0x07 Current day */
72     0x00,                /* 0x08 Current month */
73     0x00,                /* 0x09 Current year */
74     0x26,                /* 0x0A Status register A */
75     0x02,                /* 0x0B Status register B */
76     0x00,                /* 0x0C Status register C */
77     0x80,                /* 0x0D Status register D */
78     0x00,                /* 0x0E Diagnostic status */
79     0x00,                /* 0x0F Shutdown Code */
80     0x00,                /* 0x10 Drive types (1 FDHD disk) */
81     0x00,                /* 0x11 Fixed disk 0 type */
82     0x00,                /* 0x12 Fixed disk 1 type */
83     0x00,
84     0x00,                /* Installed equipment */
85 };
86
87 int day_in_year [12] = {
88     0, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov
89 };
90
91 /* consumed by dos.c */
92 time_t                  delta_clock = 0;
93
94 /* locals */
95 static struct timeval   fast_clock;
96 static int              fast_tick;
97
98 static struct timeval   glob_clock;
99 static int              cmos_alarm_time = 0;
100 static int              cmos_alarm_daytime = 0;
101
102 static inline int
103 day_in_mon_year(int mon, int year)
104 {
105     return day_in_year[mon] + (mon > 2 && (year % 4 == 0));
106 }
107
108 static inline int
109 to_BCD (int n)
110 {
111     n &= 0xFF;
112     return n%10 + ((n/10)<<4);
113 }
114
115 static inline int
116 from_BCD (int n)
117 {
118     n &= 0xFF;
119     return (n & 0xF) + (n >> 4) * 10;
120 }
121
122 /*
123 ** inb() from clock ports.
124 **
125 ** 0x70 is scratchpad/register select
126 ** 0x71 is data
127 */
128 static unsigned char
129 cmos_inb(int portnum)
130 {
131     unsigned char ret_val = 0;
132     int cmos_reg;
133     struct timezone tz;
134     struct tm tm = { .tm_sec = 0 };
135     time_t now;
136
137     switch (portnum) {
138     case 0x70:
139         ret_val = cmos_last_port_70;
140         break;
141     case 0x71:
142         cmos_reg = cmos_last_port_70 & 0x3f;
143         if (cmos_reg < 0xa) {
144             gettimeofday(&glob_clock, &tz);
145             now = glob_clock.tv_sec + delta_clock;
146             tm = *localtime(&now);
147         }
148         switch (cmos_reg) {
149         case 0:
150             ret_val = to_BCD(tm.tm_sec);
151             break;
152         case 2:
153             ret_val = to_BCD(tm.tm_min);
154             break;
155         case 4:
156             ret_val = to_BCD(tm.tm_hour);
157             break;
158         case 6:
159             ret_val = to_BCD(tm.tm_wday);
160             break;
161         case 7:
162             ret_val = to_BCD(tm.tm_mday);
163             break;
164         case 8:
165             ret_val = to_BCD(tm.tm_mon + 1);
166             break;
167         case 9:
168             ret_val = to_BCD((tm.tm_year + 1900) % 100);
169             break;
170         default:
171             ret_val = cmos_data[cmos_reg];
172             break;
173         }
174         break;
175     }
176     return (ret_val);
177 }
178
179 static void
180 cmos_outb(int portnum, unsigned char byte)
181 {
182     int cmos_reg;
183     int year;
184     int time00;
185     struct timezone tz;
186     struct tm tm = { .tm_sec = 0 };
187     time_t now;
188
189     switch (portnum) {
190     case 0x70:
191         cmos_last_port_70 = byte;
192         break;
193     case 0x71:
194         cmos_reg = cmos_last_port_70 & 0x3f;
195         if (cmos_reg < 0xa) {
196             gettimeofday(&glob_clock, &tz);
197             now = glob_clock.tv_sec + delta_clock;
198             tm = *localtime(&now);
199         }
200         switch (cmos_reg) {
201         case 0:
202             delta_clock += SEC_SIZE * (from_BCD(byte) - tm.tm_sec);
203             break;
204         case 1:
205             cmos_alarm_daytime +=
206                 SEC_SIZE * (from_BCD(byte) - from_BCD(cmos_data[1]));
207             break;
208         case 2:
209             delta_clock += MIN_SIZE * (from_BCD(byte) - tm.tm_min);
210             break;
211         case 3:
212             cmos_alarm_daytime +=
213                 MIN_SIZE * (from_BCD(byte) - from_BCD(cmos_data[3]));
214             break;
215         case 4:
216             delta_clock += HOUR_SIZE * (from_BCD(byte) - tm.tm_hour);
217             break;
218         case 5:
219             cmos_alarm_daytime += 
220                 HOUR_SIZE * (from_BCD(byte) - from_BCD(cmos_data[5]));
221             break;
222         case 7:
223             delta_clock += DAY_SIZE * (from_BCD(byte) - tm.tm_mday);
224             break;
225         case 8:
226             delta_clock += DAY_SIZE *
227                 (day_in_mon_year(from_BCD(byte), tm.tm_year) -
228                  day_in_mon_year(tm.tm_mon + 1, tm.tm_year));
229             break;
230         case 9:
231             year = from_BCD(byte);
232             delta_clock += DAY_SIZE * (YEAR_DAY * (year - tm.tm_year)
233                                        + (year/4 - tm.tm_year/4));
234             break;
235         case 0xB:
236             cmos_data[0xc] = byte;
237             if (byte & ALARM_ON) {
238                 debug(D_ALWAYS, "Alarm turned on\n");
239                 time00 = glob_clock.tv_sec + delta_clock -
240                     (tm.tm_sec + MIN_SIZE * tm.tm_min
241                      + HOUR_SIZE * tm.tm_hour);
242                 cmos_alarm_time = time00 + cmos_alarm_daytime;
243                 if (cmos_alarm_time < (glob_clock.tv_sec + delta_clock))
244                     cmos_alarm_time += DAY_SIZE;
245             }
246             if (byte & FAST_TIMER) {
247                 debug(D_ALWAYS, "Fast timer turned on\n");
248                 fast_clock = glob_clock;
249                 fast_tick = 0;
250             }
251             break;
252         }
253         cmos_data[cmos_reg] = byte;
254         break;
255     }
256 }
257
258
259 void
260 cmos_init(void)
261 {
262     int numflops = 0;
263     int checksum = 0;
264     int i;
265
266     cmos_data[0x0e] = 0;
267
268     numflops = nfloppies;
269     cmos_data[0x10] = (search_floppy(0) << 4) | search_floppy(1);
270
271     if (numflops)                       /* floppy drives present + numflops */
272         cmos_data[0x14] = ((numflops - 1) << 6) | 1;
273
274     cmos_data[0x15] = 0x80;             /* base memory 640k */
275     cmos_data[0x16] = 0x2;
276     for (i=0x10; i<=0x2d; i++)
277         checksum += cmos_data[i];
278     cmos_data[0x2e] = checksum >>8;     /* High byte */
279     cmos_data[0x2f] = checksum & 0xFF;  /* Low    byte */
280
281     cmos_data[0x32] = 0x19;             /* Century in BCD ; temporary */
282
283     for (i = 1; i < 12; i++){
284         day_in_year[i] += day_in_year[i-1];
285     }
286     
287     define_input_port_handler(0x70, cmos_inb);
288     define_input_port_handler(0x71, cmos_inb);
289     define_output_port_handler(0x70, cmos_outb);
290     define_output_port_handler(0x71, cmos_outb);
291 }