diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_poweroff.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 137 |
1 files changed, 116 insertions, 21 deletions
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index cb5cdc6f14bf..f951c30236c9 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c | |||
@@ -31,10 +31,13 @@ | |||
31 | * with this program; if not, write to the Free Software Foundation, Inc., | 31 | * with this program; if not, write to the Free Software Foundation, Inc., |
32 | * 675 Mass Ave, Cambridge, MA 02139, USA. | 32 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
33 | */ | 33 | */ |
34 | #include <asm/semaphore.h> | 34 | #include <linux/config.h> |
35 | #include <linux/kdev_t.h> | ||
36 | #include <linux/module.h> | 35 | #include <linux/module.h> |
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/proc_fs.h> | ||
37 | #include <linux/string.h> | 38 | #include <linux/string.h> |
39 | #include <linux/completion.h> | ||
40 | #include <linux/kdev_t.h> | ||
38 | #include <linux/ipmi.h> | 41 | #include <linux/ipmi.h> |
39 | #include <linux/ipmi_smi.h> | 42 | #include <linux/ipmi_smi.h> |
40 | 43 | ||
@@ -44,6 +47,18 @@ | |||
44 | /* Where to we insert our poweroff function? */ | 47 | /* Where to we insert our poweroff function? */ |
45 | extern void (*pm_power_off)(void); | 48 | extern void (*pm_power_off)(void); |
46 | 49 | ||
50 | /* Definitions for controlling power off (if the system supports it). It | ||
51 | * conveniently matches the IPMI chassis control values. */ | ||
52 | #define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */ | ||
53 | #define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ | ||
54 | |||
55 | /* the IPMI data command */ | ||
56 | static int poweroff_control = IPMI_CHASSIS_POWER_DOWN; | ||
57 | |||
58 | /* parameter definition to allow user to flag power cycle */ | ||
59 | module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN); | ||
60 | MODULE_PARM_DESC(poweroff_control, " Set to 2 to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down."); | ||
61 | |||
47 | /* Stuff from the get device id command. */ | 62 | /* Stuff from the get device id command. */ |
48 | static unsigned int mfg_id; | 63 | static unsigned int mfg_id; |
49 | static unsigned int prod_id; | 64 | static unsigned int prod_id; |
@@ -75,10 +90,10 @@ static struct ipmi_recv_msg halt_recv_msg = | |||
75 | 90 | ||
76 | static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) | 91 | static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) |
77 | { | 92 | { |
78 | struct semaphore *sem = recv_msg->user_msg_data; | 93 | struct completion *comp = recv_msg->user_msg_data; |
79 | 94 | ||
80 | if (sem) | 95 | if (comp) |
81 | up(sem); | 96 | complete(comp); |
82 | } | 97 | } |
83 | 98 | ||
84 | static struct ipmi_user_hndl ipmi_poweroff_handler = | 99 | static struct ipmi_user_hndl ipmi_poweroff_handler = |
@@ -91,27 +106,27 @@ static int ipmi_request_wait_for_response(ipmi_user_t user, | |||
91 | struct ipmi_addr *addr, | 106 | struct ipmi_addr *addr, |
92 | struct kernel_ipmi_msg *send_msg) | 107 | struct kernel_ipmi_msg *send_msg) |
93 | { | 108 | { |
94 | int rv; | 109 | int rv; |
95 | struct semaphore sem; | 110 | struct completion comp; |
96 | 111 | ||
97 | sema_init (&sem, 0); | 112 | init_completion(&comp); |
98 | 113 | ||
99 | rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, | 114 | rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &comp, |
100 | &halt_smi_msg, &halt_recv_msg, 0); | 115 | &halt_smi_msg, &halt_recv_msg, 0); |
101 | if (rv) | 116 | if (rv) |
102 | return rv; | 117 | return rv; |
103 | 118 | ||
104 | down (&sem); | 119 | wait_for_completion(&comp); |
105 | 120 | ||
106 | return halt_recv_msg.msg.data[0]; | 121 | return halt_recv_msg.msg.data[0]; |
107 | } | 122 | } |
108 | 123 | ||
109 | /* We are in run-to-completion mode, no semaphore is desired. */ | 124 | /* We are in run-to-completion mode, no completion is desired. */ |
110 | static int ipmi_request_in_rc_mode(ipmi_user_t user, | 125 | static int ipmi_request_in_rc_mode(ipmi_user_t user, |
111 | struct ipmi_addr *addr, | 126 | struct ipmi_addr *addr, |
112 | struct kernel_ipmi_msg *send_msg) | 127 | struct kernel_ipmi_msg *send_msg) |
113 | { | 128 | { |
114 | int rv; | 129 | int rv; |
115 | 130 | ||
116 | rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, | 131 | rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, |
117 | &halt_smi_msg, &halt_recv_msg, 0); | 132 | &halt_smi_msg, &halt_recv_msg, 0); |
@@ -349,26 +364,38 @@ static void ipmi_poweroff_chassis (ipmi_user_t user) | |||
349 | smi_addr.channel = IPMI_BMC_CHANNEL; | 364 | smi_addr.channel = IPMI_BMC_CHANNEL; |
350 | smi_addr.lun = 0; | 365 | smi_addr.lun = 0; |
351 | 366 | ||
352 | printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); | 367 | powercyclefailed: |
368 | printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", | ||
369 | ((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle")); | ||
353 | 370 | ||
354 | /* | 371 | /* |
355 | * Power down | 372 | * Power down |
356 | */ | 373 | */ |
357 | send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; | 374 | send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; |
358 | send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; | 375 | send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; |
359 | data[0] = 0; /* Power down */ | 376 | data[0] = poweroff_control; |
360 | send_msg.data = data; | 377 | send_msg.data = data; |
361 | send_msg.data_len = sizeof(data); | 378 | send_msg.data_len = sizeof(data); |
362 | rv = ipmi_request_in_rc_mode(user, | 379 | rv = ipmi_request_in_rc_mode(user, |
363 | (struct ipmi_addr *) &smi_addr, | 380 | (struct ipmi_addr *) &smi_addr, |
364 | &send_msg); | 381 | &send_msg); |
365 | if (rv) { | 382 | if (rv) { |
366 | printk(KERN_ERR PFX "Unable to send chassis powerdown message," | 383 | switch (poweroff_control) { |
367 | " IPMI error 0x%x\n", rv); | 384 | case IPMI_CHASSIS_POWER_CYCLE: |
368 | goto out; | 385 | /* power cycle failed, default to power down */ |
386 | printk(KERN_ERR PFX "Unable to send chassis power " \ | ||
387 | "cycle message, IPMI error 0x%x\n", rv); | ||
388 | poweroff_control = IPMI_CHASSIS_POWER_DOWN; | ||
389 | goto powercyclefailed; | ||
390 | |||
391 | case IPMI_CHASSIS_POWER_DOWN: | ||
392 | default: | ||
393 | printk(KERN_ERR PFX "Unable to send chassis power " \ | ||
394 | "down message, IPMI error 0x%x\n", rv); | ||
395 | break; | ||
396 | } | ||
369 | } | 397 | } |
370 | 398 | ||
371 | out: | ||
372 | return; | 399 | return; |
373 | } | 400 | } |
374 | 401 | ||
@@ -430,7 +457,8 @@ static void ipmi_po_new_smi(int if_num) | |||
430 | if (ready) | 457 | if (ready) |
431 | return; | 458 | return; |
432 | 459 | ||
433 | rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); | 460 | rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, |
461 | &ipmi_user); | ||
434 | if (rv) { | 462 | if (rv) { |
435 | printk(KERN_ERR PFX "could not create IPMI user, error %d\n", | 463 | printk(KERN_ERR PFX "could not create IPMI user, error %d\n", |
436 | rv); | 464 | rv); |
@@ -509,21 +537,84 @@ static struct ipmi_smi_watcher smi_watcher = | |||
509 | }; | 537 | }; |
510 | 538 | ||
511 | 539 | ||
540 | #ifdef CONFIG_PROC_FS | ||
541 | /* displays properties to proc */ | ||
542 | static int proc_read_chassctrl(char *page, char **start, off_t off, int count, | ||
543 | int *eof, void *data) | ||
544 | { | ||
545 | return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n", | ||
546 | poweroff_control); | ||
547 | } | ||
548 | |||
549 | /* process property writes from proc */ | ||
550 | static int proc_write_chassctrl(struct file *file, const char *buffer, | ||
551 | unsigned long count, void *data) | ||
552 | { | ||
553 | int rv = count; | ||
554 | unsigned int newval = 0; | ||
555 | |||
556 | sscanf(buffer, "%d", &newval); | ||
557 | switch (newval) { | ||
558 | case IPMI_CHASSIS_POWER_CYCLE: | ||
559 | printk(KERN_INFO PFX "power cycle is now enabled\n"); | ||
560 | poweroff_control = newval; | ||
561 | break; | ||
562 | |||
563 | case IPMI_CHASSIS_POWER_DOWN: | ||
564 | poweroff_control = IPMI_CHASSIS_POWER_DOWN; | ||
565 | break; | ||
566 | |||
567 | default: | ||
568 | rv = -EINVAL; | ||
569 | break; | ||
570 | } | ||
571 | |||
572 | return rv; | ||
573 | } | ||
574 | #endif /* CONFIG_PROC_FS */ | ||
575 | |||
512 | /* | 576 | /* |
513 | * Startup and shutdown functions. | 577 | * Startup and shutdown functions. |
514 | */ | 578 | */ |
515 | static int ipmi_poweroff_init (void) | 579 | static int ipmi_poweroff_init (void) |
516 | { | 580 | { |
517 | int rv; | 581 | int rv; |
582 | struct proc_dir_entry *file; | ||
518 | 583 | ||
519 | printk ("Copyright (C) 2004 MontaVista Software -" | 584 | printk ("Copyright (C) 2004 MontaVista Software -" |
520 | " IPMI Powerdown via sys_reboot version " | 585 | " IPMI Powerdown via sys_reboot version " |
521 | IPMI_POWEROFF_VERSION ".\n"); | 586 | IPMI_POWEROFF_VERSION ".\n"); |
522 | 587 | ||
588 | switch (poweroff_control) { | ||
589 | case IPMI_CHASSIS_POWER_CYCLE: | ||
590 | printk(KERN_INFO PFX "Power cycle is enabled.\n"); | ||
591 | break; | ||
592 | |||
593 | case IPMI_CHASSIS_POWER_DOWN: | ||
594 | default: | ||
595 | poweroff_control = IPMI_CHASSIS_POWER_DOWN; | ||
596 | break; | ||
597 | } | ||
598 | |||
523 | rv = ipmi_smi_watcher_register(&smi_watcher); | 599 | rv = ipmi_smi_watcher_register(&smi_watcher); |
524 | if (rv) | 600 | if (rv) { |
525 | printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); | 601 | printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); |
602 | goto out_err; | ||
603 | } | ||
604 | |||
605 | #ifdef CONFIG_PROC_FS | ||
606 | file = create_proc_entry("poweroff_control", 0, proc_ipmi_root); | ||
607 | if (!file) { | ||
608 | printk(KERN_ERR PFX "Unable to create proc power control\n"); | ||
609 | } else { | ||
610 | file->nlink = 1; | ||
611 | file->read_proc = proc_read_chassctrl; | ||
612 | file->write_proc = proc_write_chassctrl; | ||
613 | file->owner = THIS_MODULE; | ||
614 | } | ||
615 | #endif | ||
526 | 616 | ||
617 | out_err: | ||
527 | return rv; | 618 | return rv; |
528 | } | 619 | } |
529 | 620 | ||
@@ -532,6 +623,10 @@ static __exit void ipmi_poweroff_cleanup(void) | |||
532 | { | 623 | { |
533 | int rv; | 624 | int rv; |
534 | 625 | ||
626 | #ifdef CONFIG_PROC_FS | ||
627 | remove_proc_entry("poweroff_control", proc_ipmi_root); | ||
628 | #endif | ||
629 | |||
535 | ipmi_smi_watcher_unregister(&smi_watcher); | 630 | ipmi_smi_watcher_unregister(&smi_watcher); |
536 | 631 | ||
537 | if (ready) { | 632 | if (ready) { |