📄 rtc.c
字号:
struct rtc_time alm_tm; if (copy_from_user(&alm_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) return -EFAULT; day = alm_tm.tm_mday; hrs = alm_tm.tm_hour; min = alm_tm.tm_min; sec = alm_tm.tm_sec; if (hrs >= 24) hrs = 0xff; if (min >= 60) min = 0xff; if (sec >= 60) sec = 0xff; spin_lock_irq(&rtc_lock); WRITEREG(DAYALARM, day); WRITEREG(ALRM_HM, (hrs << 8 | min)); WRITEREG(ALRM_SEC, sec); spin_unlock_irq(&rtc_lock); return 0; } case RTC_RD_TIME: /* Read the time/date from RTC */ { get_rtc_time(&wtime); break; } case RTC_SET_TIME: /* Set the RTC */ { struct rtc_time rtc_tm; unsigned char mon, day, hrs, min, sec, leap_yr; unsigned int yrs; if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) return -EFAULT; yrs = rtc_tm.tm_year + 1900; mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ day = rtc_tm.tm_mday; hrs = rtc_tm.tm_hour; min = rtc_tm.tm_min; sec = rtc_tm.tm_sec; if (yrs < 1970) return -EINVAL; leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); if ((mon > 12) || (day == 0)) return -EINVAL; if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) return -EINVAL; if ((hrs >= 24) || (min >= 60) || (sec >= 60)) return -EINVAL; if ((yrs -= epoch) > 255) /* They are unsigned */ return -EINVAL; spin_lock_irq(&rtc_lock); WRITEREG(DAYR, day); WRITEREG(HOURMIN, hrs << 8 | min); WRITEREG(SECONDS, sec); spin_unlock_irq(&rtc_lock); rtc_timer.tm_year = yrs; rtc_timer.tm_mon = mon; return 0; }#if RTC_IRQ case RTC_IRQP_READ: /* Read the periodic IRQ rate. */ { return put_user(rtc_freq, (unsigned long *)arg); } case RTC_IRQP_SET: /* Set periodic IRQ rate to rtc_freq */ { int tmp = 0; /* * The range we can do is 1,2,4,8.. 512Hz. */ if (((arg < 2) || (arg > 512))&& arg != 1) return -EINVAL; while (arg > (1<<tmp))/* get the power of arg */ tmp++; /* * Check that the input was really a power of 2. */ if (arg != (1<<tmp)) return -EINVAL; spin_lock_irq(&rtc_lock); rtc_freq = arg; spin_unlock_irq(&rtc_lock); return 0; }#elif !defined(CONFIG_DECSTATION) case RTC_EPOCH_READ: /* Read the epoch. */ { return put_user (epoch, (unsigned long *)arg); } case RTC_EPOCH_SET: /* Set the epoch. */ { /* * There were no RTC clocks before 1900. */ if (arg < 1900) return -EINVAL; if (!capable(CAP_SYS_TIME)) return -EACCES; epoch = arg; return 0; }#endif default: return -EINVAL; } return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;}/* * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean * up things on a close. *//* We use rtc_lock to protect against concurrent opens. So the BKL is not * needed here. Or anywhere else in this driver. */static int rtc_open(struct inode *inode, struct file *file){ spin_lock_irq (&rtc_lock); if(rtc_status & RTC_IS_OPEN) goto out_busy; rtc_status |= RTC_IS_OPEN; rtc_irq_data = 0; spin_unlock_irq (&rtc_lock); return 0;out_busy: spin_unlock_irq (&rtc_lock); return -EBUSY;}static int rtc_fasync (int fd, struct file *filp, int on){ return fasync_helper (fd, filp, on, &rtc_async_queue);}static int rtc_release(struct inode *inode, struct file *file){ #if RTC_IRQ /* * Turn off all interrupts once the device is no longer * in use, and clear the data. */ spin_lock_irq(&rtc_lock); if (rtc_status & RTC_TIMER_ON) { rtc_status &= ~RTC_TIMER_ON; del_timer(&rtc_irq_timer); } spin_unlock_irq(&rtc_lock); if (file->f_flags & FASYNC) { rtc_fasync (-1, file, 0); }#endif spin_lock_irq (&rtc_lock); rtc_irq_data = 0; spin_unlock_irq (&rtc_lock); /* No need for locking -- nobody else can do anything until this rmw is * committed, and no timer is running. */ rtc_status &= ~RTC_IS_OPEN; return 0;}#if RTC_IRQ/* Called without the kernel lock - fine */static unsigned int rtc_poll(struct file *file, poll_table *wait){ unsigned long l; poll_wait(file, &rtc_wait, wait); spin_lock_irq (&rtc_lock); l = rtc_irq_data; spin_unlock_irq (&rtc_lock); if (l != 0) return POLLIN | POLLRDNORM; return 0;}#endif/* * The various file operations we support. */static struct file_operations rtc_fops = { owner: THIS_MODULE, llseek: rtc_llseek, read: rtc_read,#if RTC_IRQ poll: rtc_poll,#endif ioctl: rtc_ioctl, open: rtc_open, release: rtc_release, fasync: rtc_fasync,};static struct miscdevice rtc_dev={ RTC_MINOR, "rtc", &rtc_fops};static int __init rtc_init(void){#ifdef KHz32768 WRITEREG(RCCTL, 0x01); WRITEREG(RCCTL, 0x80);#else WRITEREG(RCCTL, 0x01); WRITEREG(RCCTL, 0xa0);#endif rtc_timer.tm_mon = 0; rtc_timer.tm_year = 0;#if RTC_IRQ if(request_irq(RTC_IRQ, rtc_interrupt, SA_SHIRQ | SA_INTERRUPT, "rtc", "rtc")) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); return -EIO; } if(request_irq(RTC_IRQ + 1, rtc_interrupt, SA_SHIRQ |SA_INTERRUPT, "rtc smaple timer", "rtcst")) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); return -EIO; }//Karen modify#endif misc_register(&rtc_dev); create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);#if RTC_IRQ init_timer(&rtc_irq_timer); rtc_irq_timer.function = rtc_dropped_irq; spin_lock_irq(&rtc_lock); spin_unlock_irq(&rtc_lock); rtc_freq = 512;#endif#ifdef TRY_SLEEPON init_waitqueue_head(&rtc_wait);#endif printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n"); return 0;}static void __exit rtc_exit (void){ remove_proc_entry ("driver/rtc", NULL); misc_deregister(&rtc_dev);#if RTC_IRQ free_irq (RTC_IRQ, "rtc"); free_irq (RTC_IRQ + 1, "rtcst");#endif}module_init(rtc_init);module_exit(rtc_exit);EXPORT_NO_SYMBOLS;#if RTC_IRQstatic void rtc_dropped_irq(unsigned long data){ unsigned long freq; spin_lock_irq (&rtc_lock); /* Just in case someone disabled the timer from behind our back... */ if (rtc_status & RTC_TIMER_ON) mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); rtc_irq_data += ((rtc_freq/HZ)<<16);//((rtc_freq/HZ)<<8); rtc_irq_data &= ~0xffff; rtc_irq_data |= (READREG(RTCISR) & 0xffff); freq = rtc_freq; spin_unlock_irq(&rtc_lock);// printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", freq); /* Now we have new data */ wake_up_interruptible(&rtc_wait); kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); }#endif/* * Info exported via "/proc/driver/rtc". */static int rtc_proc_output (char *buf){ char *p; struct rtc_time tm; unsigned long freq; spin_lock_irq(&rtc_lock); freq = rtc_freq; spin_unlock_irq(&rtc_lock); p = buf; get_rtc_time(&tm); /* * There is no way to tell if the luser has the RTC set for local * time or for Universal Standard Time (GMT). Probably local though. */ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" "rtc_date\t: %04d-%02d-%02d\n" "rtc_epoch\t: %04lu\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); get_rtc_alm_time(&tm); /* * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will * match any value for that particular field. Values that are * greater than a valid time, but less than 0xc0 shouldn't appear. */ p += sprintf(p, "alarm\t\t: "); if (tm.tm_hour <= 24) p += sprintf(p, "%02d:", tm.tm_hour); else p += sprintf(p, "**:"); if (tm.tm_min <= 59) p += sprintf(p, "%02d:", tm.tm_min); else p += sprintf(p, "**:"); if (tm.tm_sec <= 59) p += sprintf(p, "%02d\n", tm.tm_sec); else p += sprintf(p, "**\n"); return p - buf;}static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ int len = rtc_proc_output (page); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len>count) len = count; if (len<0) len = 0; return len;}static void get_rtc_time(struct rtc_time *rtc_tm){ /* * Only the values that we read from the RTC are set. We leave * tm_wday, tm_yday and tm_isdst untouched. Even though the * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated * by the RTC when initially set to a non-zero value. */ spin_lock_irq(&rtc_lock); rtc_tm->tm_sec = READREG(SECONDS); rtc_tm->tm_min = READREG(HOURMIN) & 0x3f; rtc_tm->tm_hour = (READREG(HOURMIN) >> 8 ) & 0x1f; rtc_tm->tm_mday = READREG(DAYR); rtc_tm->tm_mon = rtc_timer.tm_mon; rtc_tm->tm_year = rtc_timer.tm_year; spin_unlock_irq(&rtc_lock); /* * Account for differences between how the RTC uses the values * and how they are defined in a struct rtc_time; */ if ((rtc_tm->tm_year += (epoch - 1900)) <= 69) rtc_tm->tm_year += 100; rtc_tm->tm_mon--; }static void get_rtc_alm_time(struct rtc_time *alm_tm){ /* * Only the values that we read from the RTC are set. That * means only tm_hour, tm_min, and tm_sec. */ spin_lock_irq(&rtc_lock); alm_tm->tm_sec = READREG(ALRM_SEC); alm_tm->tm_min = READREG(ALRM_HM) & 0x3f; alm_tm->tm_hour = (READREG(ALRM_HM) >> 8) & 0x1f; alm_tm->tm_mday = READREG(DAYALARM); spin_unlock_irq(&rtc_lock); }#if RTC_IRQ/* * Used to disable/enable interrupts for any one of UIE, AIE, PIE. * Rumour has it that if you frob the interrupt enable/disable * bits in RTC_CONTROL, you should read RTC_INTR_FLAGS, to * ensure you actually start getting interrupts. Probably for * compatibility with older/broken chipset RTC implementations. * We also clear out any old irq data after an ioctl() that * meddles with the interrupt enable/disable bits. */static void mask_rtc_irq_bit(unsigned int bit){ spin_lock_irq(&rtc_lock); /* for 2hz cant work well we use 4hz to simulate it */ if (bit == 0x80) //??? bit = 0x100;// WRITEREG(RTCIENR, READREG(RTCIENR) & ~bit); WRITEREG(RTCIENR, 0); //??? rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);}static void set_rtc_irq_bit(unsigned int bit){ spin_lock_irq(&rtc_lock); /* use 4 hz to simulate 2hz */#ifndef HW_2HZ if (bit == 0x80){ bit = 0x100; }#endif WRITEREG(RTCIENR, READREG(RTCIENR) | bit); rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -