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 52struct 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 */ 61struct 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 */ 83static int suspends_pending; 84static int apm_disabled; 85static struct task_struct *kapmd_tsk; 86 87static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); 88static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); 89 90/* 91 * This is a list of everyone who has opened /dev/apm_bios 92 */ 93static DECLARE_RWSEM(user_list_lock); 94static 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 */ 101static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait); 102static DEFINE_SPINLOCK(kapmd_queue_lock); 103static struct apm_queue kapmd_queue; 104 105static DEFINE_MUTEX(state_lock); 106 107static 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 */ 115static 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 */ 122void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status; 123EXPORT_SYMBOL(apm_get_power_status); 124 125 126/* 127 * APM event queue management. 128 */ 129static inline int queue_empty(struct apm_queue *q) 130{ 131 return q->event_head == q->event_tail; 132} 133 134static 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 140static 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 153static 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 */ 174static 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 209static 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 238static 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 275static 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 */ 293static int 294apm_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 392static 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 422static 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 as->suser = capable(CAP_SYS_ADMIN); 429 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE; 430 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ; 431 432 down_write(&user_list_lock); 433 list_add(&as->list, &apm_user_list); 434 up_write(&user_list_lock); 435 436 filp->private_data = as; 437 } 438 439 return as ? 0 : -ENOMEM; 440} 441 442static struct file_operations apm_bios_fops = { 443 .owner = THIS_MODULE, 444 .read = apm_read, 445 .poll = apm_poll, 446 .ioctl = apm_ioctl, 447 .open = apm_open, 448 .release = apm_release, 449}; 450 451static struct miscdevice apm_device = { 452 .minor = APM_MINOR_DEV, 453 .name = "apm_bios", 454 .fops = &apm_bios_fops 455}; 456 457 458#ifdef CONFIG_PROC_FS 459/* 460 * Arguments, with symbols from linux/apm_bios.h. 461 * 462 * 0) Linux driver version (this will change if format changes) 463 * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. 464 * 2) APM flags from APM Installation Check (0x00): 465 * bit 0: APM_16_BIT_SUPPORT 466 * bit 1: APM_32_BIT_SUPPORT 467 * bit 2: APM_IDLE_SLOWS_CLOCK 468 * bit 3: APM_BIOS_DISABLED 469 * bit 4: APM_BIOS_DISENGAGED 470 * 3) AC line status 471 * 0x00: Off-line 472 * 0x01: On-line 473 * 0x02: On backup power (BIOS >= 1.1 only) 474 * 0xff: Unknown 475 * 4) Battery status 476 * 0x00: High 477 * 0x01: Low 478 * 0x02: Critical 479 * 0x03: Charging 480 * 0x04: Selected battery not present (BIOS >= 1.2 only) 481 * 0xff: Unknown 482 * 5) Battery flag 483 * bit 0: High 484 * bit 1: Low 485 * bit 2: Critical 486 * bit 3: Charging 487 * bit 7: No system battery 488 * 0xff: Unknown 489 * 6) Remaining battery life (percentage of charge): 490 * 0-100: valid 491 * -1: Unknown 492 * 7) Remaining battery life (time units): 493 * Number of remaining minutes or seconds 494 * -1: Unknown 495 * 8) min = minutes; sec = seconds 496 */ 497static int apm_get_info(char *buf, char **start, off_t fpos, int length) 498{ 499 struct apm_power_info info; 500 char *units; 501 int ret; 502 503 info.ac_line_status = 0xff; 504 info.battery_status = 0xff; 505 info.battery_flag = 0xff; 506 info.battery_life = -1; 507 info.time = -1; 508 info.units = -1; 509 510 if (apm_get_power_status) 511 apm_get_power_status(&info); 512 513 switch (info.units) { 514 default: units = "?"; break; 515 case 0: units = "min"; break; 516 case 1: units = "sec"; break; 517 } 518 519 ret = sprintf(buf, "%s 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", 520 driver_version, APM_32_BIT_SUPPORT, 521 info.ac_line_status, info.battery_status, 522 info.battery_flag, info.battery_life, 523 info.time, units); 524 525 return ret; 526} 527#endif 528 529static int kapmd(void *arg) 530{ 531 do { 532 apm_event_t event; 533 int ret; 534 535 wait_event_interruptible(kapmd_wait, 536 !queue_empty(&kapmd_queue) || kthread_should_stop()); 537 538 if (kthread_should_stop()) 539 break; 540 541 spin_lock_irq(&kapmd_queue_lock); 542 event = 0; 543 if (!queue_empty(&kapmd_queue)) 544 event = queue_get_event(&kapmd_queue); 545 spin_unlock_irq(&kapmd_queue_lock); 546 547 switch (event) { 548 case 0: 549 break; 550 551 case APM_LOW_BATTERY: 552 case APM_POWER_STATUS_CHANGE: 553 queue_event(event); 554 break; 555 556 case APM_USER_SUSPEND: 557 case APM_SYS_SUSPEND: 558 ret = queue_suspend_event(event, NULL); 559 if (ret < 0) { 560 /* 561 * We were busy. Try again in 50ms. 562 */ 563 queue_add_event(&kapmd_queue, event); 564 msleep(50); 565 } 566 if (ret > 0) 567 apm_suspend(); 568 break; 569 570 case APM_CRITICAL_SUSPEND: 571 apm_suspend(); 572 break; 573 } 574 } while (1); 575 576 return 0; 577} 578 579static int __init apm_init(void) 580{ 581 int ret; 582 583 if (apm_disabled) { 584 printk(KERN_NOTICE "apm: disabled on user request.\n"); 585 return -ENODEV; 586 } 587 588 kapmd_tsk = kthread_create(kapmd, NULL, "kapmd"); 589 if (IS_ERR(kapmd_tsk)) { 590 ret = PTR_ERR(kapmd_tsk); 591 kapmd_tsk = NULL; 592 return ret; 593 } 594 kapmd_tsk->flags |= PF_NOFREEZE; 595 wake_up_process(kapmd_tsk); 596 597#ifdef CONFIG_PROC_FS 598 create_proc_info_entry("apm", 0, NULL, apm_get_info); 599#endif 600 601 ret = misc_register(&apm_device); 602 if (ret != 0) { 603 remove_proc_entry("apm", NULL); 604 kthread_stop(kapmd_tsk); 605 } 606 607 return ret; 608} 609 610static void __exit apm_exit(void) 611{ 612 misc_deregister(&apm_device); 613 remove_proc_entry("apm", NULL); 614 615 kthread_stop(kapmd_tsk); 616} 617 618module_init(apm_init); 619module_exit(apm_exit); 620 621MODULE_AUTHOR("Stephen Rothwell"); 622MODULE_DESCRIPTION("Advanced Power Management"); 623MODULE_LICENSE("GPL"); 624 625#ifndef MODULE 626static int __init apm_setup(char *str) 627{ 628 while ((str != NULL) && (*str != '\0')) { 629 if (strncmp(str, "off", 3) == 0) 630 apm_disabled = 1; 631 if (strncmp(str, "on", 2) == 0) 632 apm_disabled = 0; 633 str = strchr(str, ','); 634 if (str != NULL) 635 str += strspn(str, ", \t"); 636 } 637 return 1; 638} 639 640__setup("apm=", apm_setup); 641#endif 642 643/** 644 * apm_queue_event - queue an APM event for kapmd 645 * @event: APM event 646 * 647 * Queue an APM event for kapmd to process and ultimately take the 648 * appropriate action. Only a subset of events are handled: 649 * %APM_LOW_BATTERY 650 * %APM_POWER_STATUS_CHANGE 651 * %APM_USER_SUSPEND 652 * %APM_SYS_SUSPEND 653 * %APM_CRITICAL_SUSPEND 654 */ 655void apm_queue_event(apm_event_t event) 656{ 657 unsigned long flags; 658 659 spin_lock_irqsave(&kapmd_queue_lock, flags); 660 queue_add_event(&kapmd_queue, event); 661 spin_unlock_irqrestore(&kapmd_queue_lock, flags); 662 663 wake_up_interruptible(&kapmd_wait); 664} 665EXPORT_SYMBOL(apm_queue_event); 666