diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/ipmi/ipmi_poweroff.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/ipmi/ipmi_poweroff.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c new file mode 100644 index 000000000000..cb5cdc6f14bf --- /dev/null +++ b/drivers/char/ipmi/ipmi_poweroff.c | |||
@@ -0,0 +1,549 @@ | |||
1 | /* | ||
2 | * ipmi_poweroff.c | ||
3 | * | ||
4 | * MontaVista IPMI Poweroff extension to sys_reboot | ||
5 | * | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * Steven Dake <sdake@mvista.com> | ||
8 | * Corey Minyard <cminyard@mvista.com> | ||
9 | * source@mvista.com | ||
10 | * | ||
11 | * Copyright 2002,2004 MontaVista Software Inc. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | * | ||
18 | * | ||
19 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
20 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | ||
25 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | ||
27 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | ||
28 | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
29 | * | ||
30 | * You should have received a copy of the GNU General Public License along | ||
31 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
32 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
33 | */ | ||
34 | #include <asm/semaphore.h> | ||
35 | #include <linux/kdev_t.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/string.h> | ||
38 | #include <linux/ipmi.h> | ||
39 | #include <linux/ipmi_smi.h> | ||
40 | |||
41 | #define PFX "IPMI poweroff: " | ||
42 | #define IPMI_POWEROFF_VERSION "v33" | ||
43 | |||
44 | /* Where to we insert our poweroff function? */ | ||
45 | extern void (*pm_power_off)(void); | ||
46 | |||
47 | /* Stuff from the get device id command. */ | ||
48 | static unsigned int mfg_id; | ||
49 | static unsigned int prod_id; | ||
50 | static unsigned char capabilities; | ||
51 | |||
52 | /* We use our own messages for this operation, we don't let the system | ||
53 | allocate them, since we may be in a panic situation. The whole | ||
54 | thing is single-threaded, anyway, so multiple messages are not | ||
55 | required. */ | ||
56 | static void dummy_smi_free(struct ipmi_smi_msg *msg) | ||
57 | { | ||
58 | } | ||
59 | static void dummy_recv_free(struct ipmi_recv_msg *msg) | ||
60 | { | ||
61 | } | ||
62 | static struct ipmi_smi_msg halt_smi_msg = | ||
63 | { | ||
64 | .done = dummy_smi_free | ||
65 | }; | ||
66 | static struct ipmi_recv_msg halt_recv_msg = | ||
67 | { | ||
68 | .done = dummy_recv_free | ||
69 | }; | ||
70 | |||
71 | |||
72 | /* | ||
73 | * Code to send a message and wait for the reponse. | ||
74 | */ | ||
75 | |||
76 | static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) | ||
77 | { | ||
78 | struct semaphore *sem = recv_msg->user_msg_data; | ||
79 | |||
80 | if (sem) | ||
81 | up(sem); | ||
82 | } | ||
83 | |||
84 | static struct ipmi_user_hndl ipmi_poweroff_handler = | ||
85 | { | ||
86 | .ipmi_recv_hndl = receive_handler | ||
87 | }; | ||
88 | |||
89 | |||
90 | static int ipmi_request_wait_for_response(ipmi_user_t user, | ||
91 | struct ipmi_addr *addr, | ||
92 | struct kernel_ipmi_msg *send_msg) | ||
93 | { | ||
94 | int rv; | ||
95 | struct semaphore sem; | ||
96 | |||
97 | sema_init (&sem, 0); | ||
98 | |||
99 | rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, | ||
100 | &halt_smi_msg, &halt_recv_msg, 0); | ||
101 | if (rv) | ||
102 | return rv; | ||
103 | |||
104 | down (&sem); | ||
105 | |||
106 | return halt_recv_msg.msg.data[0]; | ||
107 | } | ||
108 | |||
109 | /* We are in run-to-completion mode, no semaphore is desired. */ | ||
110 | static int ipmi_request_in_rc_mode(ipmi_user_t user, | ||
111 | struct ipmi_addr *addr, | ||
112 | struct kernel_ipmi_msg *send_msg) | ||
113 | { | ||
114 | int rv; | ||
115 | |||
116 | rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, | ||
117 | &halt_smi_msg, &halt_recv_msg, 0); | ||
118 | if (rv) | ||
119 | return rv; | ||
120 | |||
121 | return halt_recv_msg.msg.data[0]; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * ATCA Support | ||
126 | */ | ||
127 | |||
128 | #define IPMI_NETFN_ATCA 0x2c | ||
129 | #define IPMI_ATCA_SET_POWER_CMD 0x11 | ||
130 | #define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01 | ||
131 | #define IPMI_PICMG_ID 0 | ||
132 | |||
133 | static int ipmi_atca_detect (ipmi_user_t user) | ||
134 | { | ||
135 | struct ipmi_system_interface_addr smi_addr; | ||
136 | struct kernel_ipmi_msg send_msg; | ||
137 | int rv; | ||
138 | unsigned char data[1]; | ||
139 | |||
140 | /* | ||
141 | * Configure IPMI address for local access | ||
142 | */ | ||
143 | smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | ||
144 | smi_addr.channel = IPMI_BMC_CHANNEL; | ||
145 | smi_addr.lun = 0; | ||
146 | |||
147 | /* | ||
148 | * Use get address info to check and see if we are ATCA | ||
149 | */ | ||
150 | send_msg.netfn = IPMI_NETFN_ATCA; | ||
151 | send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD; | ||
152 | data[0] = IPMI_PICMG_ID; | ||
153 | send_msg.data = data; | ||
154 | send_msg.data_len = sizeof(data); | ||
155 | rv = ipmi_request_wait_for_response(user, | ||
156 | (struct ipmi_addr *) &smi_addr, | ||
157 | &send_msg); | ||
158 | return !rv; | ||
159 | } | ||
160 | |||
161 | static void ipmi_poweroff_atca (ipmi_user_t user) | ||
162 | { | ||
163 | struct ipmi_system_interface_addr smi_addr; | ||
164 | struct kernel_ipmi_msg send_msg; | ||
165 | int rv; | ||
166 | unsigned char data[4]; | ||
167 | |||
168 | /* | ||
169 | * Configure IPMI address for local access | ||
170 | */ | ||
171 | smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | ||
172 | smi_addr.channel = IPMI_BMC_CHANNEL; | ||
173 | smi_addr.lun = 0; | ||
174 | |||
175 | printk(KERN_INFO PFX "Powering down via ATCA power command\n"); | ||
176 | |||
177 | /* | ||
178 | * Power down | ||
179 | */ | ||
180 | send_msg.netfn = IPMI_NETFN_ATCA; | ||
181 | send_msg.cmd = IPMI_ATCA_SET_POWER_CMD; | ||
182 | data[0] = IPMI_PICMG_ID; | ||
183 | data[1] = 0; /* FRU id */ | ||
184 | data[2] = 0; /* Power Level */ | ||
185 | data[3] = 0; /* Don't change saved presets */ | ||
186 | send_msg.data = data; | ||
187 | send_msg.data_len = sizeof (data); | ||
188 | rv = ipmi_request_in_rc_mode(user, | ||
189 | (struct ipmi_addr *) &smi_addr, | ||
190 | &send_msg); | ||
191 | if (rv) { | ||
192 | printk(KERN_ERR PFX "Unable to send ATCA powerdown message," | ||
193 | " IPMI error 0x%x\n", rv); | ||
194 | goto out; | ||
195 | } | ||
196 | |||
197 | out: | ||
198 | return; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * CPI1 Support | ||
203 | */ | ||
204 | |||
205 | #define IPMI_NETFN_OEM_1 0xf8 | ||
206 | #define OEM_GRP_CMD_SET_RESET_STATE 0x84 | ||
207 | #define OEM_GRP_CMD_SET_POWER_STATE 0x82 | ||
208 | #define IPMI_NETFN_OEM_8 0xf8 | ||
209 | #define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL 0x80 | ||
210 | #define OEM_GRP_CMD_GET_SLOT_GA 0xa3 | ||
211 | #define IPMI_NETFN_SENSOR_EVT 0x10 | ||
212 | #define IPMI_CMD_GET_EVENT_RECEIVER 0x01 | ||
213 | |||
214 | #define IPMI_CPI1_PRODUCT_ID 0x000157 | ||
215 | #define IPMI_CPI1_MANUFACTURER_ID 0x0108 | ||
216 | |||
217 | static int ipmi_cpi1_detect (ipmi_user_t user) | ||
218 | { | ||
219 | return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID) | ||
220 | && (prod_id == IPMI_CPI1_PRODUCT_ID)); | ||
221 | } | ||
222 | |||
223 | static void ipmi_poweroff_cpi1 (ipmi_user_t user) | ||
224 | { | ||
225 | struct ipmi_system_interface_addr smi_addr; | ||
226 | struct ipmi_ipmb_addr ipmb_addr; | ||
227 | struct kernel_ipmi_msg send_msg; | ||
228 | int rv; | ||
229 | unsigned char data[1]; | ||
230 | int slot; | ||
231 | unsigned char hotswap_ipmb; | ||
232 | unsigned char aer_addr; | ||
233 | unsigned char aer_lun; | ||
234 | |||
235 | /* | ||
236 | * Configure IPMI address for local access | ||
237 | */ | ||
238 | smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | ||
239 | smi_addr.channel = IPMI_BMC_CHANNEL; | ||
240 | smi_addr.lun = 0; | ||
241 | |||
242 | printk(KERN_INFO PFX "Powering down via CPI1 power command\n"); | ||
243 | |||
244 | /* | ||
245 | * Get IPMI ipmb address | ||
246 | */ | ||
247 | send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; | ||
248 | send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA; | ||
249 | send_msg.data = NULL; | ||
250 | send_msg.data_len = 0; | ||
251 | rv = ipmi_request_in_rc_mode(user, | ||
252 | (struct ipmi_addr *) &smi_addr, | ||
253 | &send_msg); | ||
254 | if (rv) | ||
255 | goto out; | ||
256 | slot = halt_recv_msg.msg.data[1]; | ||
257 | hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot); | ||
258 | |||
259 | /* | ||
260 | * Get active event receiver | ||
261 | */ | ||
262 | send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2; | ||
263 | send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER; | ||
264 | send_msg.data = NULL; | ||
265 | send_msg.data_len = 0; | ||
266 | rv = ipmi_request_in_rc_mode(user, | ||
267 | (struct ipmi_addr *) &smi_addr, | ||
268 | &send_msg); | ||
269 | if (rv) | ||
270 | goto out; | ||
271 | aer_addr = halt_recv_msg.msg.data[1]; | ||
272 | aer_lun = halt_recv_msg.msg.data[2]; | ||
273 | |||
274 | /* | ||
275 | * Setup IPMB address target instead of local target | ||
276 | */ | ||
277 | ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; | ||
278 | ipmb_addr.channel = 0; | ||
279 | ipmb_addr.slave_addr = aer_addr; | ||
280 | ipmb_addr.lun = aer_lun; | ||
281 | |||
282 | /* | ||
283 | * Send request hotswap control to remove blade from dpv | ||
284 | */ | ||
285 | send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; | ||
286 | send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL; | ||
287 | send_msg.data = &hotswap_ipmb; | ||
288 | send_msg.data_len = 1; | ||
289 | ipmi_request_in_rc_mode(user, | ||
290 | (struct ipmi_addr *) &ipmb_addr, | ||
291 | &send_msg); | ||
292 | |||
293 | /* | ||
294 | * Set reset asserted | ||
295 | */ | ||
296 | send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; | ||
297 | send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE; | ||
298 | send_msg.data = data; | ||
299 | data[0] = 1; /* Reset asserted state */ | ||
300 | send_msg.data_len = 1; | ||
301 | rv = ipmi_request_in_rc_mode(user, | ||
302 | (struct ipmi_addr *) &smi_addr, | ||
303 | &send_msg); | ||
304 | if (rv) | ||
305 | goto out; | ||
306 | |||
307 | /* | ||
308 | * Power down | ||
309 | */ | ||
310 | send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; | ||
311 | send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE; | ||
312 | send_msg.data = data; | ||
313 | data[0] = 1; /* Power down state */ | ||
314 | send_msg.data_len = 1; | ||
315 | rv = ipmi_request_in_rc_mode(user, | ||
316 | (struct ipmi_addr *) &smi_addr, | ||
317 | &send_msg); | ||
318 | if (rv) | ||
319 | goto out; | ||
320 | |||
321 | out: | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * Standard chassis support | ||
327 | */ | ||
328 | |||
329 | #define IPMI_NETFN_CHASSIS_REQUEST 0 | ||
330 | #define IPMI_CHASSIS_CONTROL_CMD 0x02 | ||
331 | |||
332 | static int ipmi_chassis_detect (ipmi_user_t user) | ||
333 | { | ||
334 | /* Chassis support, use it. */ | ||
335 | return (capabilities & 0x80); | ||
336 | } | ||
337 | |||
338 | static void ipmi_poweroff_chassis (ipmi_user_t user) | ||
339 | { | ||
340 | struct ipmi_system_interface_addr smi_addr; | ||
341 | struct kernel_ipmi_msg send_msg; | ||
342 | int rv; | ||
343 | unsigned char data[1]; | ||
344 | |||
345 | /* | ||
346 | * Configure IPMI address for local access | ||
347 | */ | ||
348 | smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | ||
349 | smi_addr.channel = IPMI_BMC_CHANNEL; | ||
350 | smi_addr.lun = 0; | ||
351 | |||
352 | printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); | ||
353 | |||
354 | /* | ||
355 | * Power down | ||
356 | */ | ||
357 | send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; | ||
358 | send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; | ||
359 | data[0] = 0; /* Power down */ | ||
360 | send_msg.data = data; | ||
361 | send_msg.data_len = sizeof(data); | ||
362 | rv = ipmi_request_in_rc_mode(user, | ||
363 | (struct ipmi_addr *) &smi_addr, | ||
364 | &send_msg); | ||
365 | if (rv) { | ||
366 | printk(KERN_ERR PFX "Unable to send chassis powerdown message," | ||
367 | " IPMI error 0x%x\n", rv); | ||
368 | goto out; | ||
369 | } | ||
370 | |||
371 | out: | ||
372 | return; | ||
373 | } | ||
374 | |||
375 | |||
376 | /* Table of possible power off functions. */ | ||
377 | struct poweroff_function { | ||
378 | char *platform_type; | ||
379 | int (*detect)(ipmi_user_t user); | ||
380 | void (*poweroff_func)(ipmi_user_t user); | ||
381 | }; | ||
382 | |||
383 | static struct poweroff_function poweroff_functions[] = { | ||
384 | { .platform_type = "ATCA", | ||
385 | .detect = ipmi_atca_detect, | ||
386 | .poweroff_func = ipmi_poweroff_atca }, | ||
387 | { .platform_type = "CPI1", | ||
388 | .detect = ipmi_cpi1_detect, | ||
389 | .poweroff_func = ipmi_poweroff_cpi1 }, | ||
390 | /* Chassis should generally be last, other things should override | ||
391 | it. */ | ||
392 | { .platform_type = "chassis", | ||
393 | .detect = ipmi_chassis_detect, | ||
394 | .poweroff_func = ipmi_poweroff_chassis }, | ||
395 | }; | ||
396 | #define NUM_PO_FUNCS (sizeof(poweroff_functions) \ | ||
397 | / sizeof(struct poweroff_function)) | ||
398 | |||
399 | |||
400 | /* Our local state. */ | ||
401 | static int ready = 0; | ||
402 | static ipmi_user_t ipmi_user; | ||
403 | static void (*specific_poweroff_func)(ipmi_user_t user) = NULL; | ||
404 | |||
405 | /* Holds the old poweroff function so we can restore it on removal. */ | ||
406 | static void (*old_poweroff_func)(void); | ||
407 | |||
408 | |||
409 | /* Called on a powerdown request. */ | ||
410 | static void ipmi_poweroff_function (void) | ||
411 | { | ||
412 | if (!ready) | ||
413 | return; | ||
414 | |||
415 | /* Use run-to-completion mode, since interrupts may be off. */ | ||
416 | ipmi_user_set_run_to_completion(ipmi_user, 1); | ||
417 | specific_poweroff_func(ipmi_user); | ||
418 | ipmi_user_set_run_to_completion(ipmi_user, 0); | ||
419 | } | ||
420 | |||
421 | /* Wait for an IPMI interface to be installed, the first one installed | ||
422 | will be grabbed by this code and used to perform the powerdown. */ | ||
423 | static void ipmi_po_new_smi(int if_num) | ||
424 | { | ||
425 | struct ipmi_system_interface_addr smi_addr; | ||
426 | struct kernel_ipmi_msg send_msg; | ||
427 | int rv; | ||
428 | int i; | ||
429 | |||
430 | if (ready) | ||
431 | return; | ||
432 | |||
433 | rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); | ||
434 | if (rv) { | ||
435 | printk(KERN_ERR PFX "could not create IPMI user, error %d\n", | ||
436 | rv); | ||
437 | return; | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * Do a get device ide and store some results, since this is | ||
442 | * used by several functions. | ||
443 | */ | ||
444 | smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | ||
445 | smi_addr.channel = IPMI_BMC_CHANNEL; | ||
446 | smi_addr.lun = 0; | ||
447 | |||
448 | send_msg.netfn = IPMI_NETFN_APP_REQUEST; | ||
449 | send_msg.cmd = IPMI_GET_DEVICE_ID_CMD; | ||
450 | send_msg.data = NULL; | ||
451 | send_msg.data_len = 0; | ||
452 | rv = ipmi_request_wait_for_response(ipmi_user, | ||
453 | (struct ipmi_addr *) &smi_addr, | ||
454 | &send_msg); | ||
455 | if (rv) { | ||
456 | printk(KERN_ERR PFX "Unable to send IPMI get device id info," | ||
457 | " IPMI error 0x%x\n", rv); | ||
458 | goto out_err; | ||
459 | } | ||
460 | |||
461 | if (halt_recv_msg.msg.data_len < 12) { | ||
462 | printk(KERN_ERR PFX "(chassis) IPMI get device id info too," | ||
463 | " short, was %d bytes, needed %d bytes\n", | ||
464 | halt_recv_msg.msg.data_len, 12); | ||
465 | goto out_err; | ||
466 | } | ||
467 | |||
468 | mfg_id = (halt_recv_msg.msg.data[7] | ||
469 | | (halt_recv_msg.msg.data[8] << 8) | ||
470 | | (halt_recv_msg.msg.data[9] << 16)); | ||
471 | prod_id = (halt_recv_msg.msg.data[10] | ||
472 | | (halt_recv_msg.msg.data[11] << 8)); | ||
473 | capabilities = halt_recv_msg.msg.data[6]; | ||
474 | |||
475 | |||
476 | /* Scan for a poweroff method */ | ||
477 | for (i=0; i<NUM_PO_FUNCS; i++) { | ||
478 | if (poweroff_functions[i].detect(ipmi_user)) | ||
479 | goto found; | ||
480 | } | ||
481 | |||
482 | out_err: | ||
483 | printk(KERN_ERR PFX "Unable to find a poweroff function that" | ||
484 | " will work, giving up\n"); | ||
485 | ipmi_destroy_user(ipmi_user); | ||
486 | return; | ||
487 | |||
488 | found: | ||
489 | printk(KERN_INFO PFX "Found a %s style poweroff function\n", | ||
490 | poweroff_functions[i].platform_type); | ||
491 | specific_poweroff_func = poweroff_functions[i].poweroff_func; | ||
492 | old_poweroff_func = pm_power_off; | ||
493 | pm_power_off = ipmi_poweroff_function; | ||
494 | ready = 1; | ||
495 | } | ||
496 | |||
497 | static void ipmi_po_smi_gone(int if_num) | ||
498 | { | ||
499 | /* This can never be called, because once poweroff driver is | ||
500 | registered, the interface can't go away until the power | ||
501 | driver is unregistered. */ | ||
502 | } | ||
503 | |||
504 | static struct ipmi_smi_watcher smi_watcher = | ||
505 | { | ||
506 | .owner = THIS_MODULE, | ||
507 | .new_smi = ipmi_po_new_smi, | ||
508 | .smi_gone = ipmi_po_smi_gone | ||
509 | }; | ||
510 | |||
511 | |||
512 | /* | ||
513 | * Startup and shutdown functions. | ||
514 | */ | ||
515 | static int ipmi_poweroff_init (void) | ||
516 | { | ||
517 | int rv; | ||
518 | |||
519 | printk ("Copyright (C) 2004 MontaVista Software -" | ||
520 | " IPMI Powerdown via sys_reboot version " | ||
521 | IPMI_POWEROFF_VERSION ".\n"); | ||
522 | |||
523 | rv = ipmi_smi_watcher_register(&smi_watcher); | ||
524 | if (rv) | ||
525 | printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); | ||
526 | |||
527 | return rv; | ||
528 | } | ||
529 | |||
530 | #ifdef MODULE | ||
531 | static __exit void ipmi_poweroff_cleanup(void) | ||
532 | { | ||
533 | int rv; | ||
534 | |||
535 | ipmi_smi_watcher_unregister(&smi_watcher); | ||
536 | |||
537 | if (ready) { | ||
538 | rv = ipmi_destroy_user(ipmi_user); | ||
539 | if (rv) | ||
540 | printk(KERN_ERR PFX "could not cleanup the IPMI" | ||
541 | " user: 0x%x\n", rv); | ||
542 | pm_power_off = old_poweroff_func; | ||
543 | } | ||
544 | } | ||
545 | module_exit(ipmi_poweroff_cleanup); | ||
546 | #endif | ||
547 | |||
548 | module_init(ipmi_poweroff_init); | ||
549 | MODULE_LICENSE("GPL"); | ||