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.30-rc7 421 lines 9.9 kB view raw
1/* 2 * EFI Time Services Driver for Linux 3 * 4 * Copyright (C) 1999 Hewlett-Packard Co 5 * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> 6 * 7 * Based on skeleton from the drivers/char/rtc.c driver by P. Gortmaker 8 * 9 * This code provides an architected & portable interface to the real time 10 * clock by using EFI instead of direct bit fiddling. The functionalities are 11 * quite different from the rtc.c driver. The only way to talk to the device 12 * is by using ioctl(). There is a /proc interface which provides the raw 13 * information. 14 * 15 * Please note that we have kept the API as close as possible to the 16 * legacy RTC. The standard /sbin/hwclock program should work normally 17 * when used to get/set the time. 18 * 19 * NOTES: 20 * - Locking is required for safe execution of EFI calls with regards 21 * to interrupts and SMP. 22 * 23 * TODO (December 1999): 24 * - provide the API to set/get the WakeUp Alarm (different from the 25 * rtc.c alarm). 26 * - SMP testing 27 * - Add module support 28 */ 29 30 31#include <linux/smp_lock.h> 32#include <linux/types.h> 33#include <linux/errno.h> 34#include <linux/miscdevice.h> 35#include <linux/module.h> 36#include <linux/init.h> 37#include <linux/rtc.h> 38#include <linux/proc_fs.h> 39#include <linux/efi.h> 40#include <linux/uaccess.h> 41 42#include <asm/system.h> 43 44#define EFI_RTC_VERSION "0.4" 45 46#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) 47/* 48 * EFI Epoch is 1/1/1998 49 */ 50#define EFI_RTC_EPOCH 1998 51 52static DEFINE_SPINLOCK(efi_rtc_lock); 53 54static long efi_rtc_ioctl(struct file *file, unsigned int cmd, 55 unsigned long arg); 56 57#define is_leap(year) \ 58 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) 59 60static const unsigned short int __mon_yday[2][13] = 61{ 62 /* Normal years. */ 63 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 64 /* Leap years. */ 65 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 66}; 67 68/* 69 * returns day of the year [0-365] 70 */ 71static inline int 72compute_yday(efi_time_t *eft) 73{ 74 /* efi_time_t.month is in the [1-12] so, we need -1 */ 75 return __mon_yday[is_leap(eft->year)][eft->month-1]+ eft->day -1; 76} 77/* 78 * returns day of the week [0-6] 0=Sunday 79 * 80 * Don't try to provide a year that's before 1998, please ! 81 */ 82static int 83compute_wday(efi_time_t *eft) 84{ 85 int y; 86 int ndays = 0; 87 88 if ( eft->year < 1998 ) { 89 printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n"); 90 return -1; 91 } 92 93 for(y=EFI_RTC_EPOCH; y < eft->year; y++ ) { 94 ndays += 365 + (is_leap(y) ? 1 : 0); 95 } 96 ndays += compute_yday(eft); 97 98 /* 99 * 4=1/1/1998 was a Thursday 100 */ 101 return (ndays + 4) % 7; 102} 103 104static void 105convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) 106{ 107 108 eft->year = wtime->tm_year + 1900; 109 eft->month = wtime->tm_mon + 1; 110 eft->day = wtime->tm_mday; 111 eft->hour = wtime->tm_hour; 112 eft->minute = wtime->tm_min; 113 eft->second = wtime->tm_sec; 114 eft->nanosecond = 0; 115 eft->daylight = wtime->tm_isdst ? EFI_ISDST: 0; 116 eft->timezone = EFI_UNSPECIFIED_TIMEZONE; 117} 118 119static void 120convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) 121{ 122 memset(wtime, 0, sizeof(*wtime)); 123 wtime->tm_sec = eft->second; 124 wtime->tm_min = eft->minute; 125 wtime->tm_hour = eft->hour; 126 wtime->tm_mday = eft->day; 127 wtime->tm_mon = eft->month - 1; 128 wtime->tm_year = eft->year - 1900; 129 130 /* day of the week [0-6], Sunday=0 */ 131 wtime->tm_wday = compute_wday(eft); 132 133 /* day in the year [1-365]*/ 134 wtime->tm_yday = compute_yday(eft); 135 136 137 switch (eft->daylight & EFI_ISDST) { 138 case EFI_ISDST: 139 wtime->tm_isdst = 1; 140 break; 141 case EFI_TIME_ADJUST_DAYLIGHT: 142 wtime->tm_isdst = 0; 143 break; 144 default: 145 wtime->tm_isdst = -1; 146 } 147} 148 149static long efi_rtc_ioctl(struct file *file, unsigned int cmd, 150 unsigned long arg) 151{ 152 153 efi_status_t status; 154 unsigned long flags; 155 efi_time_t eft; 156 efi_time_cap_t cap; 157 struct rtc_time wtime; 158 struct rtc_wkalrm __user *ewp; 159 unsigned char enabled, pending; 160 161 switch (cmd) { 162 case RTC_UIE_ON: 163 case RTC_UIE_OFF: 164 case RTC_PIE_ON: 165 case RTC_PIE_OFF: 166 case RTC_AIE_ON: 167 case RTC_AIE_OFF: 168 case RTC_ALM_SET: 169 case RTC_ALM_READ: 170 case RTC_IRQP_READ: 171 case RTC_IRQP_SET: 172 case RTC_EPOCH_READ: 173 case RTC_EPOCH_SET: 174 return -EINVAL; 175 176 case RTC_RD_TIME: 177 lock_kernel(); 178 spin_lock_irqsave(&efi_rtc_lock, flags); 179 180 status = efi.get_time(&eft, &cap); 181 182 spin_unlock_irqrestore(&efi_rtc_lock,flags); 183 unlock_kernel(); 184 if (status != EFI_SUCCESS) { 185 /* should never happen */ 186 printk(KERN_ERR "efitime: can't read time\n"); 187 return -EINVAL; 188 } 189 190 convert_from_efi_time(&eft, &wtime); 191 192 return copy_to_user((void __user *)arg, &wtime, 193 sizeof (struct rtc_time)) ? - EFAULT : 0; 194 195 case RTC_SET_TIME: 196 197 if (!capable(CAP_SYS_TIME)) return -EACCES; 198 199 if (copy_from_user(&wtime, (struct rtc_time __user *)arg, 200 sizeof(struct rtc_time)) ) 201 return -EFAULT; 202 203 convert_to_efi_time(&wtime, &eft); 204 205 lock_kernel(); 206 spin_lock_irqsave(&efi_rtc_lock, flags); 207 208 status = efi.set_time(&eft); 209 210 spin_unlock_irqrestore(&efi_rtc_lock,flags); 211 unlock_kernel(); 212 213 return status == EFI_SUCCESS ? 0 : -EINVAL; 214 215 case RTC_WKALM_SET: 216 217 if (!capable(CAP_SYS_TIME)) return -EACCES; 218 219 ewp = (struct rtc_wkalrm __user *)arg; 220 221 if ( get_user(enabled, &ewp->enabled) 222 || copy_from_user(&wtime, &ewp->time, sizeof(struct rtc_time)) ) 223 return -EFAULT; 224 225 convert_to_efi_time(&wtime, &eft); 226 227 lock_kernel(); 228 spin_lock_irqsave(&efi_rtc_lock, flags); 229 /* 230 * XXX Fixme: 231 * As of EFI 0.92 with the firmware I have on my 232 * machine this call does not seem to work quite 233 * right 234 */ 235 status = efi.set_wakeup_time((efi_bool_t)enabled, &eft); 236 237 spin_unlock_irqrestore(&efi_rtc_lock,flags); 238 unlock_kernel(); 239 240 return status == EFI_SUCCESS ? 0 : -EINVAL; 241 242 case RTC_WKALM_RD: 243 244 lock_kernel(); 245 spin_lock_irqsave(&efi_rtc_lock, flags); 246 247 status = efi.get_wakeup_time((efi_bool_t *)&enabled, (efi_bool_t *)&pending, &eft); 248 249 spin_unlock_irqrestore(&efi_rtc_lock,flags); 250 unlock_kernel(); 251 252 if (status != EFI_SUCCESS) return -EINVAL; 253 254 ewp = (struct rtc_wkalrm __user *)arg; 255 256 if ( put_user(enabled, &ewp->enabled) 257 || put_user(pending, &ewp->pending)) return -EFAULT; 258 259 convert_from_efi_time(&eft, &wtime); 260 261 return copy_to_user(&ewp->time, &wtime, 262 sizeof(struct rtc_time)) ? -EFAULT : 0; 263 } 264 return -ENOTTY; 265} 266 267/* 268 * We enforce only one user at a time here with the open/close. 269 * Also clear the previous interrupt data on an open, and clean 270 * up things on a close. 271 */ 272 273static int efi_rtc_open(struct inode *inode, struct file *file) 274{ 275 /* 276 * nothing special to do here 277 * We do accept multiple open files at the same time as we 278 * synchronize on the per call operation. 279 */ 280 cycle_kernel_lock(); 281 return 0; 282} 283 284static int efi_rtc_close(struct inode *inode, struct file *file) 285{ 286 return 0; 287} 288 289/* 290 * The various file operations we support. 291 */ 292 293static const struct file_operations efi_rtc_fops = { 294 .owner = THIS_MODULE, 295 .unlocked_ioctl = efi_rtc_ioctl, 296 .open = efi_rtc_open, 297 .release = efi_rtc_close, 298}; 299 300static struct miscdevice efi_rtc_dev= { 301 EFI_RTC_MINOR, 302 "efirtc", 303 &efi_rtc_fops 304}; 305 306/* 307 * We export RAW EFI information to /proc/driver/efirtc 308 */ 309static int 310efi_rtc_get_status(char *buf) 311{ 312 efi_time_t eft, alm; 313 efi_time_cap_t cap; 314 char *p = buf; 315 efi_bool_t enabled, pending; 316 unsigned long flags; 317 318 memset(&eft, 0, sizeof(eft)); 319 memset(&alm, 0, sizeof(alm)); 320 memset(&cap, 0, sizeof(cap)); 321 322 spin_lock_irqsave(&efi_rtc_lock, flags); 323 324 efi.get_time(&eft, &cap); 325 efi.get_wakeup_time(&enabled, &pending, &alm); 326 327 spin_unlock_irqrestore(&efi_rtc_lock,flags); 328 329 p += sprintf(p, 330 "Time : %u:%u:%u.%09u\n" 331 "Date : %u-%u-%u\n" 332 "Daylight : %u\n", 333 eft.hour, eft.minute, eft.second, eft.nanosecond, 334 eft.year, eft.month, eft.day, 335 eft.daylight); 336 337 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 338 p += sprintf(p, "Timezone : unspecified\n"); 339 else 340 /* XXX fixme: convert to string? */ 341 p += sprintf(p, "Timezone : %u\n", eft.timezone); 342 343 344 p += sprintf(p, 345 "Alarm Time : %u:%u:%u.%09u\n" 346 "Alarm Date : %u-%u-%u\n" 347 "Alarm Daylight : %u\n" 348 "Enabled : %s\n" 349 "Pending : %s\n", 350 alm.hour, alm.minute, alm.second, alm.nanosecond, 351 alm.year, alm.month, alm.day, 352 alm.daylight, 353 enabled == 1 ? "yes" : "no", 354 pending == 1 ? "yes" : "no"); 355 356 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 357 p += sprintf(p, "Timezone : unspecified\n"); 358 else 359 /* XXX fixme: convert to string? */ 360 p += sprintf(p, "Timezone : %u\n", alm.timezone); 361 362 /* 363 * now prints the capabilities 364 */ 365 p += sprintf(p, 366 "Resolution : %u\n" 367 "Accuracy : %u\n" 368 "SetstoZero : %u\n", 369 cap.resolution, cap.accuracy, cap.sets_to_zero); 370 371 return p - buf; 372} 373 374static int 375efi_rtc_read_proc(char *page, char **start, off_t off, 376 int count, int *eof, void *data) 377{ 378 int len = efi_rtc_get_status(page); 379 if (len <= off+count) *eof = 1; 380 *start = page + off; 381 len -= off; 382 if (len>count) len = count; 383 if (len<0) len = 0; 384 return len; 385} 386 387static int __init 388efi_rtc_init(void) 389{ 390 int ret; 391 struct proc_dir_entry *dir; 392 393 printk(KERN_INFO "EFI Time Services Driver v%s\n", EFI_RTC_VERSION); 394 395 ret = misc_register(&efi_rtc_dev); 396 if (ret) { 397 printk(KERN_ERR "efirtc: can't misc_register on minor=%d\n", 398 EFI_RTC_MINOR); 399 return ret; 400 } 401 402 dir = create_proc_read_entry ("driver/efirtc", 0, NULL, 403 efi_rtc_read_proc, NULL); 404 if (dir == NULL) { 405 printk(KERN_ERR "efirtc: can't create /proc/driver/efirtc.\n"); 406 misc_deregister(&efi_rtc_dev); 407 return -1; 408 } 409 return 0; 410} 411 412static void __exit 413efi_rtc_exit(void) 414{ 415 /* not yet used */ 416} 417 418module_init(efi_rtc_init); 419module_exit(efi_rtc_exit); 420 421MODULE_LICENSE("GPL");