📄 cmos.c
字号:
}static inlineunsigned long cmos_write(unsigned long reg, unsigned long val){ if (use_dev_port) { unsigned char v = reg | 0x80; lseek(dev_port_fd, clock_ctl_addr, 0); write(dev_port_fd, &v, 1); v = (val & 0xff); lseek(dev_port_fd, clock_data_addr, 0); write(dev_port_fd, &v, 1); } else { outb (reg, clock_ctl_addr); outb (val, clock_data_addr); } return 0;}unsigned long cmos_set_time(unsigned long arg){ unsigned char save_control, save_freq_select, pmbit = 0; struct tm tm = *(struct tm *) arg; unsigned int century;/* * CMOS byte 10 (clock status register A) has 3 bitfields: * bit 7: 1 if data invalid, update in progress (read-only bit) * (this is raised 224 us before the actual update starts) * 6-4 select base frequency * 010: 32768 Hz time base (default) * 111: reset * all other combinations are manufacturer-dependent * (e.g.: DS1287: 010 = start oscillator, anything else = stop) * 3-0 rate selection bits for interrupt * 0000 none (may stop RTC) * 0001, 0010 give same frequency as 1000, 1001 * 0011 122 microseconds (minimum, 8192 Hz) * .... each increase by 1 halves the frequency, doubles the period * 1111 500 milliseconds (maximum, 2 Hz) * 0110 976.562 microseconds (default 1024 Hz) */ save_control = cmos_read (11); /* tell the clock it's being set */ cmos_write (11, (save_control | 0x80)); save_freq_select = cmos_read (10); /* stop and reset prescaler */ cmos_write (10, (save_freq_select | 0x70)); tm.tm_year += TM_EPOCH; century = tm.tm_year/100; tm.tm_year -= cmos_epoch; tm.tm_year %= 100; tm.tm_mon += 1; tm.tm_wday += 1; if (!(save_control & 0x02)) { /* 12hr mode; the default is 24hr mode */ if (tm.tm_hour == 0) tm.tm_hour = 24; if (tm.tm_hour > 12) { tm.tm_hour -= 12; pmbit = 0x80; } } if (!(save_control & 0x04)) { /* BCD mode - the default */ BIN_TO_BCD(tm.tm_sec); BIN_TO_BCD(tm.tm_min); BIN_TO_BCD(tm.tm_hour); BIN_TO_BCD(tm.tm_wday); BIN_TO_BCD(tm.tm_mday); BIN_TO_BCD(tm.tm_mon); BIN_TO_BCD(tm.tm_year); BIN_TO_BCD(century); } cmos_write (0, tm.tm_sec); cmos_write (2, tm.tm_min); cmos_write (4, tm.tm_hour | pmbit); cmos_write (6, tm.tm_wday); cmos_write (7, tm.tm_mday); cmos_write (8, tm.tm_mon); cmos_write (9, tm.tm_year); if (century_byte) cmos_write (century_byte, century); /* The kernel sources, linux/arch/i386/kernel/time.c, have the following comment: The following flags have to be released exactly in this order, otherwise the DS12887 (popular MC146818A clone with integrated battery and quartz) will not reset the oscillator and will not update precisely 500 ms later. You won't find this mentioned in the Dallas Semiconductor data sheets, but who believes data sheets anyway ... -- Markus Kuhn */ cmos_write (11, save_control); cmos_write (10, save_freq_select); return 0;}static inthclock_read(unsigned long reg) { return atomic("clock read", cmos_read, (reg));}static voidhclock_set_time(const struct tm *tm) { atomic("set time", cmos_set_time, (unsigned long)(tm));}static inline intcmos_clock_busy(void) { return#ifdef __alpha__ /* poll bit 4 (UF) of Control Register C */ funkyTOY ? (hclock_read(12) & 0x10) :#endif /* poll bit 7 (UIP) of Control Register A */ (hclock_read(10) & 0x80);}static intsynchronize_to_clock_tick_cmos(void) { int i; /* Wait for rise. Should be within a second, but in case something weird happens, we have a limit on this loop to reduce the impact of this failure. */ for (i = 0; !cmos_clock_busy(); i++) if (i >= 10000000) return 1; /* Wait for fall. Should be within 2.228 ms. */ for (i = 0; cmos_clock_busy(); i++) if (i >= 1000000) return 1; return 0;}static intread_hardware_clock_cmos(struct tm *tm) {/*---------------------------------------------------------------------------- Read the hardware clock and return the current time via <tm> argument. Assume we have an ISA machine and read the clock directly with CPU I/O instructions. This function is not totally reliable. It takes a finite and unpredictable amount of time to execute the code below. During that time, the clock may change and we may even read an invalid value in the middle of an update. We do a few checks to minimize this possibility, but only the kernel can actually read the clock properly, since it can execute code in a short and predictable amount of time (by turning of interrupts). In practice, the chance of this function returning the wrong time is extremely remote.-----------------------------------------------------------------------------*/ bool got_time = FALSE; unsigned char status, pmbit; status = pmbit = 0; /* just for gcc */ while (!got_time) { /* Bit 7 of Byte 10 of the Hardware Clock value is the Update In Progress (UIP) bit, which is on while and 244 uS before the Hardware Clock updates itself. It updates the counters individually, so reading them during an update would produce garbage. The update takes 2mS, so we could be spinning here that long waiting for this bit to turn off. Furthermore, it is pathologically possible for us to be in this code so long that even if the UIP bit is not on at first, the clock has changed while we were running. We check for that too, and if it happens, we start over. */ if (!cmos_clock_busy()) { /* No clock update in progress, go ahead and read */ tm->tm_sec = hclock_read(0); tm->tm_min = hclock_read(2); tm->tm_hour = hclock_read(4); tm->tm_wday = hclock_read(6); tm->tm_mday = hclock_read(7); tm->tm_mon = hclock_read(8); tm->tm_year = hclock_read(9); status = hclock_read(11);#if 0 if (century_byte) century = hclock_read(century_byte);#endif /* Unless the clock changed while we were reading, consider this a good clock read . */ if (tm->tm_sec == hclock_read (0)) got_time = TRUE; } /* Yes, in theory we could have been running for 60 seconds and the above test wouldn't work! */ } if (!(status & 0x04)) { /* BCD mode - the default */ BCD_TO_BIN(tm->tm_sec); BCD_TO_BIN(tm->tm_min); pmbit = (tm->tm_hour & 0x80); tm->tm_hour &= 0x7f; BCD_TO_BIN(tm->tm_hour); BCD_TO_BIN(tm->tm_wday); BCD_TO_BIN(tm->tm_mday); BCD_TO_BIN(tm->tm_mon); BCD_TO_BIN(tm->tm_year);#if 0 BCD_TO_BIN(century);#endif } /* We don't use the century byte of the Hardware Clock since we don't know its address (usually 50 or 55). Here, we follow the advice of the X/Open Base Working Group: "if century is not specified, then values in the range [69-99] refer to years in the twentieth century (1969 to 1999 inclusive), and values in the range [00-68] refer to years in the twenty-first century (2000 to 2068 inclusive)." */ tm->tm_wday -= 1; tm->tm_mon -= 1; tm->tm_year += (cmos_epoch - TM_EPOCH); if (tm->tm_year < 69) tm->tm_year += 100; if (pmbit) { tm->tm_hour += 12; if (tm->tm_hour == 24) tm->tm_hour = 0; } tm->tm_isdst = -1; /* don't know whether it's daylight */ return 0;}static intset_hardware_clock_cmos(const struct tm *new_broken_time) { hclock_set_time(new_broken_time); return 0;}static inti386_iopl(const int level) {#if defined(__i386__) || defined(__alpha__) extern int iopl(const int lvl); return iopl(level);#else return -2;#endif}static intget_permissions_cmos(void) { int rc; if (use_dev_port) { if ((dev_port_fd = open("/dev/port", O_RDWR)) < 0) { int errsv = errno; fprintf(stderr, _("Cannot open /dev/port: %s"), strerror(errsv)); rc = 1; } else rc = 0; } else { rc = i386_iopl(3); if (rc == -2) { fprintf(stderr, _("I failed to get permission because I didn't try.\n")); } else if (rc != 0) { rc = errno; fprintf(stderr, _("%s is unable to get I/O port access: " "the iopl(3) call failed.\n"), progname); if(rc == EPERM && geteuid()) fprintf(stderr, _("Probably you need root privileges.\n")); } } return rc ? 1 : 0;}static struct clock_ops cmos = { "direct I/O instructions to ISA clock", get_permissions_cmos, read_hardware_clock_cmos, set_hardware_clock_cmos, synchronize_to_clock_tick_cmos,};/* return &cmos if cmos clock present, NULL otherwise *//* choose this construction to avoid gcc messages about unused variables */struct clock_ops *probe_for_cmos_clock(void){ int have_cmos =#if defined(__i386__) || defined(__alpha__) TRUE;#else FALSE;#endif return have_cmos ? &cmos : NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -