diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_poweroff.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 168 |
1 files changed, 88 insertions, 80 deletions
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index f951c30236c9..e82a96ba396b 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c | |||
@@ -42,7 +42,6 @@ | |||
42 | #include <linux/ipmi_smi.h> | 42 | #include <linux/ipmi_smi.h> |
43 | 43 | ||
44 | #define PFX "IPMI poweroff: " | 44 | #define PFX "IPMI poweroff: " |
45 | #define IPMI_POWEROFF_VERSION "v33" | ||
46 | 45 | ||
47 | /* Where to we insert our poweroff function? */ | 46 | /* Where to we insert our poweroff function? */ |
48 | extern void (*pm_power_off)(void); | 47 | extern void (*pm_power_off)(void); |
@@ -53,16 +52,17 @@ extern void (*pm_power_off)(void); | |||
53 | #define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ | 52 | #define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ |
54 | 53 | ||
55 | /* the IPMI data command */ | 54 | /* the IPMI data command */ |
56 | static int poweroff_control = IPMI_CHASSIS_POWER_DOWN; | 55 | static int poweroff_powercycle; |
57 | 56 | ||
58 | /* parameter definition to allow user to flag power cycle */ | 57 | /* parameter definition to allow user to flag power cycle */ |
59 | module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN); | 58 | module_param(poweroff_powercycle, int, 0); |
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."); | 59 | MODULE_PARM_DESC(poweroff_powercycles, " Set to non-zero to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down."); |
61 | 60 | ||
62 | /* Stuff from the get device id command. */ | 61 | /* Stuff from the get device id command. */ |
63 | static unsigned int mfg_id; | 62 | static unsigned int mfg_id; |
64 | static unsigned int prod_id; | 63 | static unsigned int prod_id; |
65 | static unsigned char capabilities; | 64 | static unsigned char capabilities; |
65 | static unsigned char ipmi_version; | ||
66 | 66 | ||
67 | /* We use our own messages for this operation, we don't let the system | 67 | /* We use our own messages for this operation, we don't let the system |
68 | allocate them, since we may be in a panic situation. The whole | 68 | allocate them, since we may be in a panic situation. The whole |
@@ -338,6 +338,25 @@ static void ipmi_poweroff_cpi1 (ipmi_user_t user) | |||
338 | } | 338 | } |
339 | 339 | ||
340 | /* | 340 | /* |
341 | * ipmi_dell_chassis_detect() | ||
342 | * Dell systems with IPMI < 1.5 don't set the chassis capability bit | ||
343 | * but they can handle a chassis poweroff or powercycle command. | ||
344 | */ | ||
345 | |||
346 | #define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} | ||
347 | static int ipmi_dell_chassis_detect (ipmi_user_t user) | ||
348 | { | ||
349 | const char ipmi_version_major = ipmi_version & 0xF; | ||
350 | const char ipmi_version_minor = (ipmi_version >> 4) & 0xF; | ||
351 | const char mfr[3]=DELL_IANA_MFR_ID; | ||
352 | if (!memcmp(mfr, &mfg_id, sizeof(mfr)) && | ||
353 | ipmi_version_major <= 1 && | ||
354 | ipmi_version_minor < 5) | ||
355 | return 1; | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | /* | ||
341 | * Standard chassis support | 360 | * Standard chassis support |
342 | */ | 361 | */ |
343 | 362 | ||
@@ -366,37 +385,34 @@ static void ipmi_poweroff_chassis (ipmi_user_t user) | |||
366 | 385 | ||
367 | powercyclefailed: | 386 | powercyclefailed: |
368 | printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", | 387 | printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", |
369 | ((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle")); | 388 | (poweroff_powercycle ? "cycle" : "down")); |
370 | 389 | ||
371 | /* | 390 | /* |
372 | * Power down | 391 | * Power down |
373 | */ | 392 | */ |
374 | send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; | 393 | send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; |
375 | send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; | 394 | send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; |
376 | data[0] = poweroff_control; | 395 | if (poweroff_powercycle) |
396 | data[0] = IPMI_CHASSIS_POWER_CYCLE; | ||
397 | else | ||
398 | data[0] = IPMI_CHASSIS_POWER_DOWN; | ||
377 | send_msg.data = data; | 399 | send_msg.data = data; |
378 | send_msg.data_len = sizeof(data); | 400 | send_msg.data_len = sizeof(data); |
379 | rv = ipmi_request_in_rc_mode(user, | 401 | rv = ipmi_request_in_rc_mode(user, |
380 | (struct ipmi_addr *) &smi_addr, | 402 | (struct ipmi_addr *) &smi_addr, |
381 | &send_msg); | 403 | &send_msg); |
382 | if (rv) { | 404 | if (rv) { |
383 | switch (poweroff_control) { | 405 | if (poweroff_powercycle) { |
384 | case IPMI_CHASSIS_POWER_CYCLE: | 406 | /* power cycle failed, default to power down */ |
385 | /* power cycle failed, default to power down */ | 407 | printk(KERN_ERR PFX "Unable to send chassis power " \ |
386 | printk(KERN_ERR PFX "Unable to send chassis power " \ | 408 | "cycle message, IPMI error 0x%x\n", rv); |
387 | "cycle message, IPMI error 0x%x\n", rv); | 409 | poweroff_powercycle = 0; |
388 | poweroff_control = IPMI_CHASSIS_POWER_DOWN; | 410 | goto powercyclefailed; |
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 | } | 411 | } |
397 | } | ||
398 | 412 | ||
399 | return; | 413 | printk(KERN_ERR PFX "Unable to send chassis power " \ |
414 | "down message, IPMI error 0x%x\n", rv); | ||
415 | } | ||
400 | } | 416 | } |
401 | 417 | ||
402 | 418 | ||
@@ -414,6 +430,9 @@ static struct poweroff_function poweroff_functions[] = { | |||
414 | { .platform_type = "CPI1", | 430 | { .platform_type = "CPI1", |
415 | .detect = ipmi_cpi1_detect, | 431 | .detect = ipmi_cpi1_detect, |
416 | .poweroff_func = ipmi_poweroff_cpi1 }, | 432 | .poweroff_func = ipmi_poweroff_cpi1 }, |
433 | { .platform_type = "chassis", | ||
434 | .detect = ipmi_dell_chassis_detect, | ||
435 | .poweroff_func = ipmi_poweroff_chassis }, | ||
417 | /* Chassis should generally be last, other things should override | 436 | /* Chassis should generally be last, other things should override |
418 | it. */ | 437 | it. */ |
419 | { .platform_type = "chassis", | 438 | { .platform_type = "chassis", |
@@ -499,10 +518,11 @@ static void ipmi_po_new_smi(int if_num) | |||
499 | prod_id = (halt_recv_msg.msg.data[10] | 518 | prod_id = (halt_recv_msg.msg.data[10] |
500 | | (halt_recv_msg.msg.data[11] << 8)); | 519 | | (halt_recv_msg.msg.data[11] << 8)); |
501 | capabilities = halt_recv_msg.msg.data[6]; | 520 | capabilities = halt_recv_msg.msg.data[6]; |
521 | ipmi_version = halt_recv_msg.msg.data[5]; | ||
502 | 522 | ||
503 | 523 | ||
504 | /* Scan for a poweroff method */ | 524 | /* Scan for a poweroff method */ |
505 | for (i=0; i<NUM_PO_FUNCS; i++) { | 525 | for (i = 0; i < NUM_PO_FUNCS; i++) { |
506 | if (poweroff_functions[i].detect(ipmi_user)) | 526 | if (poweroff_functions[i].detect(ipmi_user)) |
507 | goto found; | 527 | goto found; |
508 | } | 528 | } |
@@ -538,39 +558,35 @@ static struct ipmi_smi_watcher smi_watcher = | |||
538 | 558 | ||
539 | 559 | ||
540 | #ifdef CONFIG_PROC_FS | 560 | #ifdef CONFIG_PROC_FS |
541 | /* displays properties to proc */ | 561 | #include <linux/sysctl.h> |
542 | static int proc_read_chassctrl(char *page, char **start, off_t off, int count, | 562 | |
543 | int *eof, void *data) | 563 | static ctl_table ipmi_table[] = { |
544 | { | 564 | { .ctl_name = DEV_IPMI_POWEROFF_POWERCYCLE, |
545 | return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n", | 565 | .procname = "poweroff_powercycle", |
546 | poweroff_control); | 566 | .data = &poweroff_powercycle, |
547 | } | 567 | .maxlen = sizeof(poweroff_powercycle), |
568 | .mode = 0644, | ||
569 | .proc_handler = &proc_dointvec }, | ||
570 | { } | ||
571 | }; | ||
548 | 572 | ||
549 | /* process property writes from proc */ | 573 | static ctl_table ipmi_dir_table[] = { |
550 | static int proc_write_chassctrl(struct file *file, const char *buffer, | 574 | { .ctl_name = DEV_IPMI, |
551 | unsigned long count, void *data) | 575 | .procname = "ipmi", |
552 | { | 576 | .mode = 0555, |
553 | int rv = count; | 577 | .child = ipmi_table }, |
554 | unsigned int newval = 0; | 578 | { } |
555 | 579 | }; | |
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 | 580 | ||
572 | return rv; | 581 | static ctl_table ipmi_root_table[] = { |
573 | } | 582 | { .ctl_name = CTL_DEV, |
583 | .procname = "dev", | ||
584 | .mode = 0555, | ||
585 | .child = ipmi_dir_table }, | ||
586 | { } | ||
587 | }; | ||
588 | |||
589 | static struct ctl_table_header *ipmi_table_header; | ||
574 | #endif /* CONFIG_PROC_FS */ | 590 | #endif /* CONFIG_PROC_FS */ |
575 | 591 | ||
576 | /* | 592 | /* |
@@ -578,42 +594,32 @@ static int proc_write_chassctrl(struct file *file, const char *buffer, | |||
578 | */ | 594 | */ |
579 | static int ipmi_poweroff_init (void) | 595 | static int ipmi_poweroff_init (void) |
580 | { | 596 | { |
581 | int rv; | 597 | int rv; |
582 | struct proc_dir_entry *file; | ||
583 | 598 | ||
584 | printk ("Copyright (C) 2004 MontaVista Software -" | 599 | printk ("Copyright (C) 2004 MontaVista Software -" |
585 | " IPMI Powerdown via sys_reboot version " | 600 | " IPMI Powerdown via sys_reboot.\n"); |
586 | IPMI_POWEROFF_VERSION ".\n"); | 601 | |
587 | 602 | if (poweroff_powercycle) | |
588 | switch (poweroff_control) { | 603 | printk(KERN_INFO PFX "Power cycle is enabled.\n"); |
589 | case IPMI_CHASSIS_POWER_CYCLE: | 604 | |
590 | printk(KERN_INFO PFX "Power cycle is enabled.\n"); | 605 | #ifdef CONFIG_PROC_FS |
591 | break; | 606 | ipmi_table_header = register_sysctl_table(ipmi_root_table, 1); |
592 | 607 | if (!ipmi_table_header) { | |
593 | case IPMI_CHASSIS_POWER_DOWN: | 608 | printk(KERN_ERR PFX "Unable to register powercycle sysctl\n"); |
594 | default: | 609 | rv = -ENOMEM; |
595 | poweroff_control = IPMI_CHASSIS_POWER_DOWN; | 610 | goto out_err; |
596 | break; | ||
597 | } | 611 | } |
612 | #endif | ||
598 | 613 | ||
614 | #ifdef CONFIG_PROC_FS | ||
599 | rv = ipmi_smi_watcher_register(&smi_watcher); | 615 | rv = ipmi_smi_watcher_register(&smi_watcher); |
616 | #endif | ||
600 | if (rv) { | 617 | if (rv) { |
618 | unregister_sysctl_table(ipmi_table_header); | ||
601 | printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); | 619 | printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); |
602 | goto out_err; | 620 | goto out_err; |
603 | } | 621 | } |
604 | 622 | ||
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 | ||
616 | |||
617 | out_err: | 623 | out_err: |
618 | return rv; | 624 | return rv; |
619 | } | 625 | } |
@@ -624,7 +630,7 @@ static __exit void ipmi_poweroff_cleanup(void) | |||
624 | int rv; | 630 | int rv; |
625 | 631 | ||
626 | #ifdef CONFIG_PROC_FS | 632 | #ifdef CONFIG_PROC_FS |
627 | remove_proc_entry("poweroff_control", proc_ipmi_root); | 633 | unregister_sysctl_table(ipmi_table_header); |
628 | #endif | 634 | #endif |
629 | 635 | ||
630 | ipmi_smi_watcher_unregister(&smi_watcher); | 636 | ipmi_smi_watcher_unregister(&smi_watcher); |
@@ -642,3 +648,5 @@ module_exit(ipmi_poweroff_cleanup); | |||
642 | 648 | ||
643 | module_init(ipmi_poweroff_init); | 649 | module_init(ipmi_poweroff_init); |
644 | MODULE_LICENSE("GPL"); | 650 | MODULE_LICENSE("GPL"); |
651 | MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); | ||
652 | MODULE_DESCRIPTION("IPMI Poweroff extension to sys_reboot"); | ||