Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12 507 lines 11 kB view raw
1/* 2 * linux/arch/arm/common/rtctime.c 3 * 4 * Copyright (C) 2003 Deep Blue Solutions Ltd. 5 * Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre. 6 * Based on rtc.c by Paul Gortmaker 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12#include <linux/module.h> 13#include <linux/kernel.h> 14#include <linux/time.h> 15#include <linux/rtc.h> 16#include <linux/poll.h> 17#include <linux/proc_fs.h> 18#include <linux/miscdevice.h> 19#include <linux/spinlock.h> 20#include <linux/device.h> 21 22#include <asm/rtc.h> 23#include <asm/semaphore.h> 24 25static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); 26static struct fasync_struct *rtc_async_queue; 27 28/* 29 * rtc_lock protects rtc_irq_data 30 */ 31static DEFINE_SPINLOCK(rtc_lock); 32static unsigned long rtc_irq_data; 33 34/* 35 * rtc_sem protects rtc_inuse and rtc_ops 36 */ 37static DECLARE_MUTEX(rtc_sem); 38static unsigned long rtc_inuse; 39static struct rtc_ops *rtc_ops; 40 41#define rtc_epoch 1900UL 42 43static const unsigned char days_in_month[] = { 44 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 45}; 46 47#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) 48#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) 49 50static int month_days(unsigned int month, unsigned int year) 51{ 52 return days_in_month[month] + (LEAP_YEAR(year) && month == 1); 53} 54 55/* 56 * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. 57 */ 58void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) 59{ 60 int days, month, year; 61 62 days = time / 86400; 63 time -= days * 86400; 64 65 tm->tm_wday = (days + 4) % 7; 66 67 year = 1970 + days / 365; 68 days -= (year - 1970) * 365 69 + LEAPS_THRU_END_OF(year - 1) 70 - LEAPS_THRU_END_OF(1970 - 1); 71 if (days < 0) { 72 year -= 1; 73 days += 365 + LEAP_YEAR(year); 74 } 75 tm->tm_year = year - 1900; 76 tm->tm_yday = days + 1; 77 78 for (month = 0; month < 11; month++) { 79 int newdays; 80 81 newdays = days - month_days(month, year); 82 if (newdays < 0) 83 break; 84 days = newdays; 85 } 86 tm->tm_mon = month; 87 tm->tm_mday = days + 1; 88 89 tm->tm_hour = time / 3600; 90 time -= tm->tm_hour * 3600; 91 tm->tm_min = time / 60; 92 tm->tm_sec = time - tm->tm_min * 60; 93} 94EXPORT_SYMBOL(rtc_time_to_tm); 95 96/* 97 * Does the rtc_time represent a valid date/time? 98 */ 99int rtc_valid_tm(struct rtc_time *tm) 100{ 101 if (tm->tm_year < 70 || 102 tm->tm_mon >= 12 || 103 tm->tm_mday < 1 || 104 tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) || 105 tm->tm_hour >= 24 || 106 tm->tm_min >= 60 || 107 tm->tm_sec >= 60) 108 return -EINVAL; 109 110 return 0; 111} 112EXPORT_SYMBOL(rtc_valid_tm); 113 114/* 115 * Convert Gregorian date to seconds since 01-01-1970 00:00:00. 116 */ 117int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) 118{ 119 *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 120 tm->tm_hour, tm->tm_min, tm->tm_sec); 121 122 return 0; 123} 124EXPORT_SYMBOL(rtc_tm_to_time); 125 126/* 127 * Calculate the next alarm time given the requested alarm time mask 128 * and the current time. 129 * 130 * FIXME: for now, we just copy the alarm time because we're lazy (and 131 * is therefore buggy - setting a 10am alarm at 8pm will not result in 132 * the alarm triggering.) 133 */ 134void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm) 135{ 136 next->tm_year = now->tm_year; 137 next->tm_mon = now->tm_mon; 138 next->tm_mday = now->tm_mday; 139 next->tm_hour = alrm->tm_hour; 140 next->tm_min = alrm->tm_min; 141 next->tm_sec = alrm->tm_sec; 142} 143 144static inline int rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm) 145{ 146 memset(tm, 0, sizeof(struct rtc_time)); 147 return ops->read_time(tm); 148} 149 150static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm) 151{ 152 int ret; 153 154 ret = rtc_valid_tm(tm); 155 if (ret == 0) 156 ret = ops->set_time(tm); 157 158 return ret; 159} 160 161static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) 162{ 163 int ret = -EINVAL; 164 if (ops->read_alarm) { 165 memset(alrm, 0, sizeof(struct rtc_wkalrm)); 166 ret = ops->read_alarm(alrm); 167 } 168 return ret; 169} 170 171static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) 172{ 173 int ret = -EINVAL; 174 if (ops->set_alarm) 175 ret = ops->set_alarm(alrm); 176 return ret; 177} 178 179void rtc_update(unsigned long num, unsigned long events) 180{ 181 spin_lock(&rtc_lock); 182 rtc_irq_data = (rtc_irq_data + (num << 8)) | events; 183 spin_unlock(&rtc_lock); 184 185 wake_up_interruptible(&rtc_wait); 186 kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); 187} 188EXPORT_SYMBOL(rtc_update); 189 190 191static ssize_t 192rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 193{ 194 DECLARE_WAITQUEUE(wait, current); 195 unsigned long data; 196 ssize_t ret; 197 198 if (count < sizeof(unsigned long)) 199 return -EINVAL; 200 201 add_wait_queue(&rtc_wait, &wait); 202 do { 203 __set_current_state(TASK_INTERRUPTIBLE); 204 205 spin_lock_irq(&rtc_lock); 206 data = rtc_irq_data; 207 rtc_irq_data = 0; 208 spin_unlock_irq(&rtc_lock); 209 210 if (data != 0) { 211 ret = 0; 212 break; 213 } 214 if (file->f_flags & O_NONBLOCK) { 215 ret = -EAGAIN; 216 break; 217 } 218 if (signal_pending(current)) { 219 ret = -ERESTARTSYS; 220 break; 221 } 222 schedule(); 223 } while (1); 224 set_current_state(TASK_RUNNING); 225 remove_wait_queue(&rtc_wait, &wait); 226 227 if (ret == 0) { 228 ret = put_user(data, (unsigned long __user *)buf); 229 if (ret == 0) 230 ret = sizeof(unsigned long); 231 } 232 return ret; 233} 234 235static unsigned int rtc_poll(struct file *file, poll_table *wait) 236{ 237 unsigned long data; 238 239 poll_wait(file, &rtc_wait, wait); 240 241 spin_lock_irq(&rtc_lock); 242 data = rtc_irq_data; 243 spin_unlock_irq(&rtc_lock); 244 245 return data != 0 ? POLLIN | POLLRDNORM : 0; 246} 247 248static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 249 unsigned long arg) 250{ 251 struct rtc_ops *ops = file->private_data; 252 struct rtc_time tm; 253 struct rtc_wkalrm alrm; 254 void __user *uarg = (void __user *)arg; 255 int ret = -EINVAL; 256 257 switch (cmd) { 258 case RTC_ALM_READ: 259 ret = rtc_read_alarm(ops, &alrm); 260 if (ret) 261 break; 262 ret = copy_to_user(uarg, &alrm.time, sizeof(tm)); 263 if (ret) 264 ret = -EFAULT; 265 break; 266 267 case RTC_ALM_SET: 268 ret = copy_from_user(&alrm.time, uarg, sizeof(tm)); 269 if (ret) { 270 ret = -EFAULT; 271 break; 272 } 273 alrm.enabled = 0; 274 alrm.pending = 0; 275 alrm.time.tm_mday = -1; 276 alrm.time.tm_mon = -1; 277 alrm.time.tm_year = -1; 278 alrm.time.tm_wday = -1; 279 alrm.time.tm_yday = -1; 280 alrm.time.tm_isdst = -1; 281 ret = rtc_set_alarm(ops, &alrm); 282 break; 283 284 case RTC_RD_TIME: 285 ret = rtc_read_time(ops, &tm); 286 if (ret) 287 break; 288 ret = copy_to_user(uarg, &tm, sizeof(tm)); 289 if (ret) 290 ret = -EFAULT; 291 break; 292 293 case RTC_SET_TIME: 294 if (!capable(CAP_SYS_TIME)) { 295 ret = -EACCES; 296 break; 297 } 298 ret = copy_from_user(&tm, uarg, sizeof(tm)); 299 if (ret) { 300 ret = -EFAULT; 301 break; 302 } 303 ret = rtc_set_time(ops, &tm); 304 break; 305 306 case RTC_EPOCH_SET: 307#ifndef rtc_epoch 308 /* 309 * There were no RTC clocks before 1900. 310 */ 311 if (arg < 1900) { 312 ret = -EINVAL; 313 break; 314 } 315 if (!capable(CAP_SYS_TIME)) { 316 ret = -EACCES; 317 break; 318 } 319 rtc_epoch = arg; 320 ret = 0; 321#endif 322 break; 323 324 case RTC_EPOCH_READ: 325 ret = put_user(rtc_epoch, (unsigned long __user *)uarg); 326 break; 327 328 case RTC_WKALM_SET: 329 ret = copy_from_user(&alrm, uarg, sizeof(alrm)); 330 if (ret) { 331 ret = -EFAULT; 332 break; 333 } 334 ret = rtc_set_alarm(ops, &alrm); 335 break; 336 337 case RTC_WKALM_RD: 338 ret = rtc_read_alarm(ops, &alrm); 339 if (ret) 340 break; 341 ret = copy_to_user(uarg, &alrm, sizeof(alrm)); 342 if (ret) 343 ret = -EFAULT; 344 break; 345 346 default: 347 if (ops->ioctl) 348 ret = ops->ioctl(cmd, arg); 349 break; 350 } 351 return ret; 352} 353 354static int rtc_open(struct inode *inode, struct file *file) 355{ 356 int ret; 357 358 down(&rtc_sem); 359 360 if (rtc_inuse) { 361 ret = -EBUSY; 362 } else if (!rtc_ops || !try_module_get(rtc_ops->owner)) { 363 ret = -ENODEV; 364 } else { 365 file->private_data = rtc_ops; 366 367 ret = rtc_ops->open ? rtc_ops->open() : 0; 368 if (ret == 0) { 369 spin_lock_irq(&rtc_lock); 370 rtc_irq_data = 0; 371 spin_unlock_irq(&rtc_lock); 372 373 rtc_inuse = 1; 374 } 375 } 376 up(&rtc_sem); 377 378 return ret; 379} 380 381static int rtc_release(struct inode *inode, struct file *file) 382{ 383 struct rtc_ops *ops = file->private_data; 384 385 if (ops->release) 386 ops->release(); 387 388 spin_lock_irq(&rtc_lock); 389 rtc_irq_data = 0; 390 spin_unlock_irq(&rtc_lock); 391 392 module_put(rtc_ops->owner); 393 rtc_inuse = 0; 394 395 return 0; 396} 397 398static int rtc_fasync(int fd, struct file *file, int on) 399{ 400 return fasync_helper(fd, file, on, &rtc_async_queue); 401} 402 403static struct file_operations rtc_fops = { 404 .owner = THIS_MODULE, 405 .llseek = no_llseek, 406 .read = rtc_read, 407 .poll = rtc_poll, 408 .ioctl = rtc_ioctl, 409 .open = rtc_open, 410 .release = rtc_release, 411 .fasync = rtc_fasync, 412}; 413 414static struct miscdevice rtc_miscdev = { 415 .minor = RTC_MINOR, 416 .name = "rtc", 417 .fops = &rtc_fops, 418}; 419 420 421static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) 422{ 423 struct rtc_ops *ops = data; 424 struct rtc_wkalrm alrm; 425 struct rtc_time tm; 426 char *p = page; 427 428 if (rtc_read_time(ops, &tm) == 0) { 429 p += sprintf(p, 430 "rtc_time\t: %02d:%02d:%02d\n" 431 "rtc_date\t: %04d-%02d-%02d\n" 432 "rtc_epoch\t: %04lu\n", 433 tm.tm_hour, tm.tm_min, tm.tm_sec, 434 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 435 rtc_epoch); 436 } 437 438 if (rtc_read_alarm(ops, &alrm) == 0) { 439 p += sprintf(p, "alrm_time\t: "); 440 if ((unsigned int)alrm.time.tm_hour <= 24) 441 p += sprintf(p, "%02d:", alrm.time.tm_hour); 442 else 443 p += sprintf(p, "**:"); 444 if ((unsigned int)alrm.time.tm_min <= 59) 445 p += sprintf(p, "%02d:", alrm.time.tm_min); 446 else 447 p += sprintf(p, "**:"); 448 if ((unsigned int)alrm.time.tm_sec <= 59) 449 p += sprintf(p, "%02d\n", alrm.time.tm_sec); 450 else 451 p += sprintf(p, "**\n"); 452 453 p += sprintf(p, "alrm_date\t: "); 454 if ((unsigned int)alrm.time.tm_year <= 200) 455 p += sprintf(p, "%04d-", alrm.time.tm_year + 1900); 456 else 457 p += sprintf(p, "****-"); 458 if ((unsigned int)alrm.time.tm_mon <= 11) 459 p += sprintf(p, "%02d-", alrm.time.tm_mon + 1); 460 else 461 p += sprintf(p, "**-"); 462 if ((unsigned int)alrm.time.tm_mday <= 31) 463 p += sprintf(p, "%02d\n", alrm.time.tm_mday); 464 else 465 p += sprintf(p, "**\n"); 466 p += sprintf(p, "alrm_wakeup\t: %s\n", 467 alrm.enabled ? "yes" : "no"); 468 p += sprintf(p, "alrm_pending\t: %s\n", 469 alrm.pending ? "yes" : "no"); 470 } 471 472 if (ops->proc) 473 p += ops->proc(p); 474 475 return p - page; 476} 477 478int register_rtc(struct rtc_ops *ops) 479{ 480 int ret = -EBUSY; 481 482 down(&rtc_sem); 483 if (rtc_ops == NULL) { 484 rtc_ops = ops; 485 486 ret = misc_register(&rtc_miscdev); 487 if (ret == 0) 488 create_proc_read_entry("driver/rtc", 0, NULL, 489 rtc_read_proc, ops); 490 } 491 up(&rtc_sem); 492 493 return ret; 494} 495EXPORT_SYMBOL(register_rtc); 496 497void unregister_rtc(struct rtc_ops *rtc) 498{ 499 down(&rtc_sem); 500 if (rtc == rtc_ops) { 501 remove_proc_entry("driver/rtc", NULL); 502 misc_deregister(&rtc_miscdev); 503 rtc_ops = NULL; 504 } 505 up(&rtc_sem); 506} 507EXPORT_SYMBOL(unregister_rtc);