1 /* 2 * bios-less APM driver for ARM Linux 3 * Jamey Hicks <jamey@crl.dec.com> 4 * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com) 5 * 6 * APM 1.2 Reference: 7 * Intel Corporation, Microsoft Corporation. Advanced Power Management 8 * (APM) BIOS Interface Specification, Revision 1.2, February 1996. 9 * 10 * [This document is available from Microsoft at: 11 * http://www.microsoft.com/hwdev/busbios/amp_12.htm] 12 */ 13 #include <linux/module.h> 14 #include <linux/poll.h> 15 #include <linux/slab.h> 16 #include <linux/proc_fs.h> 17 #include <linux/miscdevice.h> 18 #include <linux/apm_bios.h> 19 #include <linux/capability.h> 20 #include <linux/sched.h> 21 #include <linux/pm.h> 22 #include <linux/apm-emulation.h> 23 #include <linux/device.h> 24 #include <linux/kernel.h> 25 #include <linux/list.h> 26 #include <linux/init.h> 27 #include <linux/completion.h> 28 #include <linux/kthread.h> 29 #include <linux/delay.h> 30 31 #include <asm/system.h> 32 33 /* 34 * The apm_bios device is one of the misc char devices. 35 * This is its minor number. 36 */ 37 #define APM_MINOR_DEV 134 38 39 /* 40 * See Documentation/Config.help for the configuration options. 41 * 42 * Various options can be changed at boot time as follows: 43 * (We allow underscores for compatibility with the modules code) 44 * apm=on/off enable/disable APM 45 */ 46 47 /* 48 * Maximum number of events stored 49 */ 50 #define APM_MAX_EVENTS 16 51 52 struct apm_queue { 53 unsigned int event_head; 54 unsigned int event_tail; 55 apm_event_t events[APM_MAX_EVENTS]; 56 }; 57 58 /* 59 * The per-file APM data 60 */ 61 struct apm_user { 62 struct list_head list; 63 64 unsigned int suser: 1; 65 unsigned int writer: 1; 66 unsigned int reader: 1; 67 68 int suspend_result; 69 unsigned int suspend_state; 70 #define SUSPEND_NONE 0 /* no suspend pending */ 71 #define SUSPEND_PENDING 1 /* suspend pending read */ 72 #define SUSPEND_READ 2 /* suspend read, pending ack */ 73 #define SUSPEND_ACKED 3 /* suspend acked */ 74 #define SUSPEND_WAIT 4 /* waiting for suspend */ 75 #define SUSPEND_DONE 5 /* suspend completed */ 76 77 struct apm_queue queue; 78 }; 79 80 /* 81 * Local variables 82 */ 83 static int suspends_pending; 84 static int apm_disabled; 85 static struct task_struct *kapmd_tsk; 86 87 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); 88 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); 89 90 /* 91 * This is a list of everyone who has opened /dev/apm_bios 92 */ 93 static DECLARE_RWSEM(user_list_lock); 94 static LIST_HEAD(apm_user_list); 95 96 /* 97 * kapmd info. kapmd provides us a process context to handle 98 * "APM" events within - specifically necessary if we're going 99 * to be suspending the system. 100 */ 101 static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait); 102 static DEFINE_SPINLOCK(kapmd_queue_lock); 103 static struct apm_queue kapmd_queue; 104 105 static DEFINE_MUTEX(state_lock); 106 107 static const char driver_version[] = "1.13"; /* no spaces */ 108 109 110 111 /* 112 * Compatibility cruft until the IPAQ people move over to the new 113 * interface. 114 */ 115 static void __apm_get_power_status(struct apm_power_info *info) 116 { 117 } 118 119 /* 120 * This allows machines to provide their own "apm get power status" function. 121 */ 122 void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status; 123 EXPORT_SYMBOL(apm_get_power_status); 124 125 126 /* 127 * APM event queue management. 128 */ 129 static inline int queue_empty(struct apm_queue *q) 130 { 131 return q->event_head == q->event_tail; 132 } 133 134 static inline apm_event_t queue_get_event(struct apm_queue *q) 135 { 136 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS; 137 return q->events[q->event_tail]; 138 } 139 140 static void queue_add_event(struct apm_queue *q, apm_event_t event) 141 { 142 q->event_head = (q->event_head + 1) % APM_MAX_EVENTS; 143 if (q->event_head == q->event_tail) { 144 static int notified; 145 146 if (notified++ == 0) 147 printk(KERN_ERR "apm: an event queue overflowed\n"); 148 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS; 149 } 150 q->events[q->event_head] = event; 151 } 152 153 static void queue_event(apm_event_t event) 154 { 155 struct apm_user *as; 156 157 down_read(&user_list_lock); 158 list_for_each_entry(as, &apm_user_list, list) { 159 if (as->reader) 160 queue_add_event(&as->queue, event); 161 } 162 up_read(&user_list_lock); 163 wake_up_interruptible(&apm_waitqueue); 164 } 165 166 /* 167 * queue_suspend_event - queue an APM suspend event. 168 * 169 * Check that we're in a state where we can suspend. If not, 170 * return -EBUSY. Otherwise, queue an event to all "writer" 171 * users. If there are no "writer" users, return '1' to 172 * indicate that we can immediately suspend. 173 */ 174 static int queue_suspend_event(apm_event_t event, struct apm_user *sender) 175 { 176 struct apm_user *as; 177 int ret = 1; 178 179 mutex_lock(&state_lock); 180 down_read(&user_list_lock); 181 182 /* 183 * If a thread is still processing, we can't suspend, so reject 184 * the request. 185 */ 186 list_for_each_entry(as, &apm_user_list, list) { 187 if (as != sender && as->reader && as->writer && as->suser && 188 as->suspend_state != SUSPEND_NONE) { 189 ret = -EBUSY; 190 goto out; 191 } 192 } 193 194 list_for_each_entry(as, &apm_user_list, list) { 195 if (as != sender && as->reader && as->writer && as->suser) { 196 as->suspend_state = SUSPEND_PENDING; 197 suspends_pending++; 198 queue_add_event(&as->queue, event); 199 ret = 0; 200 } 201 } 202 out: 203 up_read(&user_list_lock); 204 mutex_unlock(&state_lock); 205 wake_up_interruptible(&apm_waitqueue); 206 return ret; 207 } 208 209 static void apm_suspend(void) 210 { 211 struct apm_user *as; 212 int err = pm_suspend(PM_SUSPEND_MEM); 213 214 /* 215 * Anyone on the APM queues will think we're still suspended. 216 * Send a message so everyone knows we're now awake again. 217 */ 218 queue_event(APM_NORMAL_RESUME); 219 220 /* 221 * Finally, wake up anyone who is sleeping on the suspend. 222 */ 223 mutex_lock(&state_lock); 224 down_read(&user_list_lock); 225 list_for_each_entry(as, &apm_user_list, list) { 226 if (as->suspend_state == SUSPEND_WAIT || 227 as->suspend_state == SUSPEND_ACKED) { 228 as->suspend_result = err; 229 as->suspend_state = SUSPEND_DONE; 230 } 231 } 232 up_read(&user_list_lock); 233 mutex_unlock(&state_lock); 234 235 wake_up(&apm_suspend_waitqueue); 236 } 237 238 static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) 239 { 240 struct apm_user *as = fp->private_data; 241 apm_event_t event; 242 int i = count, ret = 0; 243 244 if (count < sizeof(apm_event_t)) 245 return -EINVAL; 246 247 if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK) 248 return -EAGAIN; 249 250 wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue)); 251 252 while ((i >= sizeof(event)) && !queue_empty(&as->queue)) { 253 event = queue_get_event(&as->queue); 254 255 ret = -EFAULT; 256 if (copy_to_user(buf, &event, sizeof(event))) 257 break; 258 259 mutex_lock(&state_lock); 260 if (as->suspend_state == SUSPEND_PENDING && 261 (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)) 262 as->suspend_state = SUSPEND_READ; 263 mutex_unlock(&state_lock); 264 265 buf += sizeof(event); 266 i -= sizeof(event); 267 } 268 269 if (i < count) 270 ret = count - i; 271 272 return ret; 273 } 274 275 static unsigned int apm_poll(struct file *fp, poll_table * wait) 276 { 277 struct apm_user *as = fp->private_data; 278 279 poll_wait(fp, &apm_waitqueue, wait); 280 return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM; 281 } 282 283 /* 284 * apm_ioctl - handle APM ioctl 285 * 286 * APM_IOC_SUSPEND 287 * This IOCTL is overloaded, and performs two functions. It is used to: 288 * - initiate a suspend 289 * - acknowledge a suspend read from /dev/apm_bios. 290 * Only when everyone who has opened /dev/apm_bios with write permission 291 * has acknowledge does the actual suspend happen. 292 */ 293 static int 294 apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) 295 { 296 struct apm_user *as = filp->private_data; 297 unsigned long flags; 298 int err = -EINVAL; 299 300 if (!as->suser || !as->writer) 301 return -EPERM; 302 303 switch (cmd) { 304 case APM_IOC_SUSPEND: 305 mutex_lock(&state_lock); 306 307 as->suspend_result = -EINTR; 308 309 if (as->suspend_state == SUSPEND_READ) { 310 int pending; 311 312 /* 313 * If we read a suspend command from /dev/apm_bios, 314 * then the corresponding APM_IOC_SUSPEND ioctl is 315 * interpreted as an acknowledge. 316 */ 317 as->suspend_state = SUSPEND_ACKED; 318 suspends_pending--; 319 pending = suspends_pending == 0; 320 mutex_unlock(&state_lock); 321 322 /* 323 * If there are no further acknowledges required, 324 * suspend the system. 325 */ 326 if (pending) 327 apm_suspend(); 328 329 /* 330 * Wait for the suspend/resume to complete. If there 331 * are pending acknowledges, we wait here for them. 332 * 333 * Note: we need to ensure that the PM subsystem does 334 * not kick us out of the wait when it suspends the 335 * threads. 336 */ 337 flags = current->flags; 338 current->flags |= PF_NOFREEZE; 339 340 wait_event(apm_suspend_waitqueue, 341 as->suspend_state == SUSPEND_DONE); 342 } else { 343 as->suspend_state = SUSPEND_WAIT; 344 mutex_unlock(&state_lock); 345 346 /* 347 * Otherwise it is a request to suspend the system. 348 * Queue an event for all readers, and expect an 349 * acknowledge from all writers who haven't already 350 * acknowledged. 351 */ 352 err = queue_suspend_event(APM_USER_SUSPEND, as); 353 if (err < 0) { 354 /* 355 * Avoid taking the lock here - this 356 * should be fine. 357 */ 358 as->suspend_state = SUSPEND_NONE; 359 break; 360 } 361 362 if (err > 0) 363 apm_suspend(); 364 365 /* 366 * Wait for the suspend/resume to complete. If there 367 * are pending acknowledges, we wait here for them. 368 * 369 * Note: we need to ensure that the PM subsystem does 370 * not kick us out of the wait when it suspends the 371 * threads. 372 */ 373 flags = current->flags; 374 current->flags |= PF_NOFREEZE; 375 376 wait_event_interruptible(apm_suspend_waitqueue, 377 as->suspend_state == SUSPEND_DONE); 378 } 379 380 current->flags = flags; 381 382 mutex_lock(&state_lock); 383 err = as->suspend_result; 384 as->suspend_state = SUSPEND_NONE; 385 mutex_unlock(&state_lock); 386 break; 387 } 388 389 return err; 390 } 391 392 static int apm_release(struct inode * inode, struct file * filp) 393 { 394 struct apm_user *as = filp->private_data; 395 int pending = 0; 396 397 filp->private_data = NULL; 398 399 down_write(&user_list_lock); 400 list_del(&as->list); 401 up_write(&user_list_lock); 402 403 /* 404 * We are now unhooked from the chain. As far as new 405 * events are concerned, we no longer exist. However, we 406 * need to balance suspends_pending, which means the 407 * possibility of sleeping. 408 */ 409 mutex_lock(&state_lock); 410 if (as->suspend_state != SUSPEND_NONE) { 411 suspends_pending -= 1; 412 pending = suspends_pending == 0; 413 } 414 mutex_unlock(&state_lock); 415 if (pending) 416 apm_suspend(); 417 418 kfree(as); 419 return 0; 420 } 421 422 static int apm_open(struct inode * inode, struct file * filp) 423 { 424 struct apm_user *as; 425 426 as = kzalloc(sizeof(*as), GFP_KERNEL); 427 if (as) { 428 /* 429 * XXX - this is a tiny bit broken, when we consider BSD 430 * process accounting. If the device is opened by root, we 431 * instantly flag that we used superuser privs. Who knows, 432 * we might close the device immediately without doing a 433 * privileged operation -- cevans 434 */ 435 as->suser = capable(CAP_SYS_ADMIN); 436 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE; 437 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ; 438 439 down_write(&user_list_lock); 440 list_add(&as->list, &apm_user_list); 441 up_write(&user_list_lock); 442 443 filp->private_data = as; 444 } 445 446 return as ? 0 : -ENOMEM; 447 } 448 449 static struct file_operations apm_bios_fops = { 450 .owner = THIS_MODULE, 451 .read = apm_read, 452 .poll = apm_poll, 453 .ioctl = apm_ioctl, 454 .open = apm_open, 455 .release = apm_release, 456 }; 457 458 static struct miscdevice apm_device = { 459 .minor = APM_MINOR_DEV, 460 .name = "apm_bios", 461 .fops = &apm_bios_fops 462 }; 463 464 465 #ifdef CONFIG_PROC_FS 466 /* 467 * Arguments, with symbols from linux/apm_bios.h. 468 * 469 * 0) Linux driver version (this will change if format changes) 470 * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. 471 * 2) APM flags from APM Installation Check (0x00): 472 * bit 0: APM_16_BIT_SUPPORT 473 * bit 1: APM_32_BIT_SUPPORT 474 * bit 2: APM_IDLE_SLOWS_CLOCK 475 * bit 3: APM_BIOS_DISABLED 476 * bit 4: APM_BIOS_DISENGAGED 477 * 3) AC line status 478 * 0x00: Off-line 479 * 0x01: On-line 480 * 0x02: On backup power (BIOS >= 1.1 only) 481 * 0xff: Unknown 482 * 4) Battery status 483 * 0x00: High 484 * 0x01: Low 485 * 0x02: Critical 486 * 0x03: Charging 487 * 0x04: Selected battery not present (BIOS >= 1.2 only) 488 * 0xff: Unknown 489 * 5) Battery flag 490 * bit 0: High 491 * bit 1: Low 492 * bit 2: Critical 493 * bit 3: Charging 494 * bit 7: No system battery 495 * 0xff: Unknown 496 * 6) Remaining battery life (percentage of charge): 497 * 0-100: valid 498 * -1: Unknown 499 * 7) Remaining battery life (time units): 500 * Number of remaining minutes or seconds 501 * -1: Unknown 502 * 8) min = minutes; sec = seconds 503 */ 504 static int apm_get_info(char *buf, char **start, off_t fpos, int length) 505 { 506 struct apm_power_info info; 507 char *units; 508 int ret; 509 510 info.ac_line_status = 0xff; 511 info.battery_status = 0xff; 512 info.battery_flag = 0xff; 513 info.battery_life = -1; 514 info.time = -1; 515 info.units = -1; 516 517 if (apm_get_power_status) 518 apm_get_power_status(&info); 519 520 switch (info.units) { 521 default: units = "?"; break; 522 case 0: units = "min"; break; 523 case 1: units = "sec"; break; 524 } 525 526 ret = sprintf(buf, "%s 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", 527 driver_version, APM_32_BIT_SUPPORT, 528 info.ac_line_status, info.battery_status, 529 info.battery_flag, info.battery_life, 530 info.time, units); 531 532 return ret; 533 } 534 #endif 535 536 static int kapmd(void *arg) 537 { 538 do { 539 apm_event_t event; 540 int ret; 541 542 wait_event_interruptible(kapmd_wait, 543 !queue_empty(&kapmd_queue) || kthread_should_stop()); 544 545 if (kthread_should_stop()) 546 break; 547 548 spin_lock_irq(&kapmd_queue_lock); 549 event = 0; 550 if (!queue_empty(&kapmd_queue)) 551 event = queue_get_event(&kapmd_queue); 552 spin_unlock_irq(&kapmd_queue_lock); 553 554 switch (event) { 555 case 0: 556 break; 557 558 case APM_LOW_BATTERY: 559 case APM_POWER_STATUS_CHANGE: 560 queue_event(event); 561 break; 562 563 case APM_USER_SUSPEND: 564 case APM_SYS_SUSPEND: 565 ret = queue_suspend_event(event, NULL); 566 if (ret < 0) { 567 /* 568 * We were busy. Try again in 50ms. 569 */ 570 queue_add_event(&kapmd_queue, event); 571 msleep(50); 572 } 573 if (ret > 0) 574 apm_suspend(); 575 break; 576 577 case APM_CRITICAL_SUSPEND: 578 apm_suspend(); 579 break; 580 } 581 } while (1); 582 583 return 0; 584 } 585 586 static int __init apm_init(void) 587 { 588 int ret; 589 590 if (apm_disabled) { 591 printk(KERN_NOTICE "apm: disabled on user request.\n"); 592 return -ENODEV; 593 } 594 595 kapmd_tsk = kthread_create(kapmd, NULL, "kapmd"); 596 if (IS_ERR(kapmd_tsk)) { 597 ret = PTR_ERR(kapmd_tsk); 598 kapmd_tsk = NULL; 599 return ret; 600 } 601 kapmd_tsk->flags |= PF_NOFREEZE; 602 wake_up_process(kapmd_tsk); 603 604 #ifdef CONFIG_PROC_FS 605 create_proc_info_entry("apm", 0, NULL, apm_get_info); 606 #endif 607 608 ret = misc_register(&apm_device); 609 if (ret != 0) { 610 remove_proc_entry("apm", NULL); 611 kthread_stop(kapmd_tsk); 612 } 613 614 return ret; 615 } 616 617 static void __exit apm_exit(void) 618 { 619 misc_deregister(&apm_device); 620 remove_proc_entry("apm", NULL); 621 622 kthread_stop(kapmd_tsk); 623 } 624 625 module_init(apm_init); 626 module_exit(apm_exit); 627 628 MODULE_AUTHOR("Stephen Rothwell"); 629 MODULE_DESCRIPTION("Advanced Power Management"); 630 MODULE_LICENSE("GPL"); 631 632 #ifndef MODULE 633 static int __init apm_setup(char *str) 634 { 635 while ((str != NULL) && (*str != '\0')) { 636 if (strncmp(str, "off", 3) == 0) 637 apm_disabled = 1; 638 if (strncmp(str, "on", 2) == 0) 639 apm_disabled = 0; 640 str = strchr(str, ','); 641 if (str != NULL) 642 str += strspn(str, ", \t"); 643 } 644 return 1; 645 } 646 647 __setup("apm=", apm_setup); 648 #endif 649 650 /** 651 * apm_queue_event - queue an APM event for kapmd 652 * @event: APM event 653 * 654 * Queue an APM event for kapmd to process and ultimately take the 655 * appropriate action. Only a subset of events are handled: 656 * %APM_LOW_BATTERY 657 * %APM_POWER_STATUS_CHANGE 658 * %APM_USER_SUSPEND 659 * %APM_SYS_SUSPEND 660 * %APM_CRITICAL_SUSPEND 661 */ 662 void apm_queue_event(apm_event_t event) 663 { 664 unsigned long flags; 665 666 spin_lock_irqsave(&kapmd_queue_lock, flags); 667 queue_add_event(&kapmd_queue, event); 668 spin_unlock_irqrestore(&kapmd_queue_lock, flags); 669 670 wake_up_interruptible(&kapmd_wait); 671 } 672 EXPORT_SYMBOL(apm_queue_event); 673