6b04ea7c5f0d5b64127b56179e96f2effd51138f
[reactos.git] / reactos / drivers / bus / acpi / ospm / busmgr / bmpower.c
1 /****************************************************************************
2 *
3 * Module Name: bmpower.c - Driver for ACPI Power Resource 'devices'
4 * $Revision: 1.1 $
5 *
6 ****************************************************************************/
7
8 /*
9 * Copyright (C) 2000, 2001 Andrew Grover
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 /*
27 * TODO:
28 * -----
29 * 1. Sequencing of power resource list transitions.
30 * 2. Global serialization of power resource transtions (see ACPI
31 * spec section 7.1.2/7.1.3).
32 * 3. Better error handling.
33 */
34
35
36 #include <acpi.h>
37 #include "bm.h"
38 #include "bmpower.h"
39
40
41 #define _COMPONENT ACPI_POWER_CONTROL
42 MODULE_NAME ("bmpower")
43
44
45 /****************************************************************************
46 * Function Prototypes
47 ****************************************************************************/
48
49 ACPI_STATUS
50 bm_pr_notify (
51 BM_NOTIFY notify_type,
52 BM_HANDLE device_handle,
53 void **context);
54
55 ACPI_STATUS
56 bm_pr_request (
57 BM_REQUEST *request,
58 void *context);
59
60
61 /****************************************************************************
62 * Internal Functions
63 ****************************************************************************/
64
65 /****************************************************************************
66 *
67 * FUNCTION: bm_pr_print
68 *
69 * PARAMETERS: <TBD>
70 *
71 * RETURN: <TBD>
72 *
73 * DESCRIPTION: <TBD>
74 *
75 ****************************************************************************/
76
77 ACPI_STATUS
78 bm_pr_print (
79 BM_POWER_RESOURCE *pr)
80 {
81 ACPI_BUFFER buffer;
82
83 if (!pr) {
84 return(AE_BAD_PARAMETER);
85 }
86
87 buffer.length = 256;
88 buffer.pointer = acpi_os_callocate(buffer.length);
89 if (!buffer.pointer) {
90 return(AE_NO_MEMORY);
91 }
92
93 acpi_get_name(pr->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
94
95 acpi_os_printf("Power Resource: found\n");
96
97 DEBUG_PRINT(ACPI_INFO, ("+------------------------------------------------------------\n"));
98 DEBUG_PRINT(ACPI_INFO, ("PowerResource[0x%02X]|[0x%08X] %s\n", pr->device_handle, pr->acpi_handle, buffer.pointer));
99 DEBUG_PRINT(ACPI_INFO, (" system_level[S%d] resource_order[%d]\n", pr->system_level, pr->resource_order));
100 DEBUG_PRINT(ACPI_INFO, (" state[D%d] reference_count[%d]\n", pr->state, pr->reference_count));
101 DEBUG_PRINT(ACPI_INFO, ("+------------------------------------------------------------\n"));
102
103 acpi_os_free(buffer.pointer);
104
105 return(AE_OK);
106 }
107
108
109 /****************************************************************************
110 *
111 * FUNCTION: bm_pr_get_state
112 *
113 * PARAMETERS: <TBD>
114 *
115 * RETURN: <TBD>
116 *
117 * DESCRIPTION: <TBD>
118 *
119 ****************************************************************************/
120
121 ACPI_STATUS
122 bm_pr_get_state (
123 BM_POWER_RESOURCE *pr)
124 {
125 ACPI_STATUS status = AE_OK;
126 BM_DEVICE_STATUS device_status = BM_STATUS_UNKNOWN;
127
128 FUNCTION_TRACE("bm_pr_get_state");
129
130 if (!pr) {
131 return_ACPI_STATUS(AE_BAD_PARAMETER);
132 }
133
134 pr->state = ACPI_STATE_UNKNOWN;
135
136 /*
137 * Evaluate _STA:
138 * --------------
139 * Evalute _STA to determine whether the power resource is ON or OFF.
140 * Note that if the power resource isn't present we'll get AE_OK but
141 * an unknown status.
142 */
143 status = bm_get_device_status(pr->device_handle, &device_status);
144 if (ACPI_FAILURE(status)) {
145 DEBUG_PRINT(ACPI_ERROR, ("Error reading status for power resource [0x%02x].\n", pr->device_handle));
146 return_ACPI_STATUS(status);
147 }
148 if (device_status == BM_STATUS_UNKNOWN) {
149 DEBUG_PRINT(ACPI_ERROR, ("Error reading status for power resource [0x%02x].\n", pr->device_handle));
150 return_ACPI_STATUS(AE_NOT_EXIST);
151 }
152
153 /*
154 * Mask off all bits but the first as some systems return non-standard
155 * values (e.g. 0x51).
156 */
157 switch (device_status & 0x01) {
158 case 0:
159 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02x] is OFF.\n", pr->device_handle));
160 pr->state = ACPI_STATE_D3;
161 break;
162 case 1:
163 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02x] is ON.\n", pr->device_handle));
164 pr->state = ACPI_STATE_D0;
165 break;
166 }
167
168 return_ACPI_STATUS(status);
169 }
170
171
172 /****************************************************************************
173 *
174 * FUNCTION: bm_pr_set_state
175 *
176 * PARAMETERS: <TBD>
177 *
178 * RETURN: <TBD>
179 *
180 * DESCRIPTION: <TBD>
181 *
182 ****************************************************************************/
183
184 ACPI_STATUS
185 bm_pr_set_state (
186 BM_POWER_RESOURCE *pr,
187 BM_POWER_STATE target_state)
188 {
189 ACPI_STATUS status = AE_OK;
190
191 FUNCTION_TRACE("bm_pr_set_state");
192
193 if (!pr) {
194 return_ACPI_STATUS(AE_BAD_PARAMETER);
195 }
196
197 status = bm_pr_get_state(pr);
198 if (ACPI_FAILURE(status)) {
199 return_ACPI_STATUS(status);
200 }
201
202 if (target_state == pr->state) {
203 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02X] already at target power state [D%d].\n", pr->device_handle, pr->state));
204 return_ACPI_STATUS(AE_OK);
205 }
206
207 switch (target_state) {
208
209 case ACPI_STATE_D0:
210 DEBUG_PRINT(ACPI_INFO, ("Turning power resource [0x%02X] ON.\n", pr->device_handle));
211 status = bm_evaluate_object(pr->acpi_handle, "_ON", NULL, NULL);
212 break;
213
214 case ACPI_STATE_D3:
215 DEBUG_PRINT(ACPI_INFO, ("Turning power resource [0x%02X] OFF.\n", pr->device_handle));
216 status = bm_evaluate_object(pr->acpi_handle, "_OFF", NULL, NULL);
217 break;
218
219 default:
220 status = AE_BAD_PARAMETER;
221 break;
222 }
223
224 status = bm_pr_get_state(pr);
225 if (ACPI_FAILURE(status)) {
226 return_ACPI_STATUS(status);
227 }
228
229 return_ACPI_STATUS(status);
230 }
231
232
233 /****************************************************************************
234 *
235 * FUNCTION: bm_pr_list_get_state
236 *
237 * PARAMETERS: <TBD>
238 *
239 * RETURN: <TBD>
240 *
241 * DESCRIPTION: <TBD>
242 *
243 ****************************************************************************/
244
245 ACPI_STATUS
246 bm_pr_list_get_state (
247 BM_HANDLE_LIST *pr_list,
248 BM_POWER_STATE *power_state)
249 {
250 ACPI_STATUS status = AE_OK;
251 BM_POWER_RESOURCE *pr = NULL;
252 u32 i = 0;
253
254 FUNCTION_TRACE("bm_pr_list_get_state");
255
256 if (!pr_list || !power_state) {
257 return_ACPI_STATUS(AE_BAD_PARAMETER);
258 }
259
260 if (pr_list->count < 1) {
261 pr->state = ACPI_STATE_UNKNOWN;
262 return_ACPI_STATUS(AE_ERROR);
263 }
264
265 (*power_state) = ACPI_STATE_D0;
266
267 /*
268 * Calculate Current power_state:
269 * -----------------------------
270 * The current state of a list of power resources is ON if all
271 * power resources are currently in the ON state. In other words,
272 * if any power resource in the list is OFF then the collection
273 * isn't fully ON.
274 */
275 for (i = 0; i < pr_list->count; i++) {
276
277 status = bm_get_device_context(pr_list->handles[i],
278 (BM_DRIVER_CONTEXT*)(&pr));
279 if (ACPI_FAILURE(status)) {
280 DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", pr_list->handles[i]));
281 (*power_state) = ACPI_STATE_UNKNOWN;
282 break;
283 }
284
285 status = bm_pr_get_state(pr);
286 if (ACPI_FAILURE(status)) {
287 (*power_state) = ACPI_STATE_UNKNOWN;
288 break;
289 }
290
291 if (pr->state != ACPI_STATE_D0) {
292 (*power_state) = pr->state;
293 break;
294 }
295 }
296
297 return_ACPI_STATUS(status);
298 }
299
300
301 /****************************************************************************
302 *
303 * FUNCTION: bm_pr_list_transition
304 *
305 * PARAMETERS: <TBD>
306 *
307 * RETURN: <TBD>
308 *
309 * DESCRIPTION: <TBD>
310 *
311 ****************************************************************************/
312
313 ACPI_STATUS
314 bm_pr_list_transition (
315 BM_HANDLE_LIST *current_list,
316 BM_HANDLE_LIST *target_list)
317 {
318 ACPI_STATUS status = AE_OK;
319 BM_POWER_RESOURCE *pr = NULL;
320 u32 i = 0;
321
322 FUNCTION_TRACE("bm_pr_list_transition");
323
324 if (!current_list || !target_list) {
325 return_ACPI_STATUS(AE_BAD_PARAMETER);
326 }
327
328 /*
329 * Reference Target:
330 * -----------------
331 * Reference all resources for the target power state first (so
332 * the device doesn't get turned off while transitioning). Power
333 * resources that aren't on (new reference count of 1) are turned on.
334 */
335 for (i = 0; i < target_list->count; i++) {
336
337 status = bm_get_device_context(target_list->handles[i],
338 (BM_DRIVER_CONTEXT*)(&pr));
339 if (ACPI_FAILURE(status)) {
340 DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", target_list->handles[i]));
341 continue;
342 }
343
344 if (++pr->reference_count == 1) {
345 /* TODO: Need ordering based upon resource_order */
346 status = bm_pr_set_state(pr, ACPI_STATE_D0);
347 if (ACPI_FAILURE(status)) {
348 /* TODO: How do we handle this? */
349 DEBUG_PRINT(ACPI_WARN, ("Unable to change power state for power resource [0x%02X].\n", target_list->handles[i]));
350 }
351 }
352 }
353
354 /*
355 * Dereference Current:
356 * --------------------
357 * Dereference all resources for the current power state. Power
358 * resources no longer referenced (new reference count of 0) are
359 * turned off.
360 */
361 for (i = 0; i < current_list->count; i++) {
362
363 status = bm_get_device_context(current_list->handles[i],
364 (BM_DRIVER_CONTEXT*)(&pr));
365 if (ACPI_FAILURE(status)) {
366 DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", target_list->handles[i]));
367 continue;
368 }
369
370 if (--pr->reference_count == 0) {
371 /* TODO: Need ordering based upon resource_order */
372 status = bm_pr_set_state(pr, ACPI_STATE_D3);
373 if (ACPI_FAILURE(status)) {
374 /* TODO: How do we handle this? */
375 DEBUG_PRINT(ACPI_ERROR, ("Unable to change power state for power resource [0x%02X].\n", current_list->handles[i]));
376 }
377 }
378 }
379
380 return_ACPI_STATUS(status);
381 }
382
383
384 /****************************************************************************
385 *
386 * FUNCTION: bm_pr_add_device
387 *
388 * PARAMETERS: <TBD>
389 *
390 * RETURN: <TBD>
391 *
392 * DESCRIPTION: <TBD>
393 *
394 ****************************************************************************/
395
396 ACPI_STATUS
397 bm_pr_add_device (
398 BM_HANDLE device_handle,
399 void **context)
400 {
401 ACPI_STATUS status = AE_OK;
402 BM_POWER_RESOURCE *pr = NULL;
403 BM_DEVICE *device = NULL;
404 ACPI_BUFFER buffer;
405 ACPI_OBJECT acpi_object;
406
407 FUNCTION_TRACE("bm_pr_add_device");
408
409 DEBUG_PRINT(ACPI_INFO, ("Adding power resource [0x%02X].\n", device_handle));
410
411 if (!context || *context) {
412 return_ACPI_STATUS(AE_BAD_PARAMETER);
413 }
414
415 buffer.length = sizeof(ACPI_OBJECT);
416 buffer.pointer = &acpi_object;
417
418 /*
419 * Get information on this device.
420 */
421 status = bm_get_device_info(device_handle, &device);
422 if (ACPI_FAILURE(status)) {
423 return_ACPI_STATUS(status);
424 }
425
426 /*
427 * Allocate a new BM_POWER_RESOURCE structure.
428 */
429 pr = acpi_os_callocate(sizeof(BM_POWER_RESOURCE));
430 if (!pr) {
431 return_ACPI_STATUS(AE_NO_MEMORY);
432 }
433
434 pr->device_handle = device->handle;
435 pr->acpi_handle = device->acpi_handle;
436
437 /*
438 * Get information on this power resource.
439 */
440 status = acpi_evaluate_object(pr->acpi_handle, NULL, NULL, &buffer);
441 if (ACPI_FAILURE(status)) {
442 goto end;
443 }
444
445 pr->system_level = acpi_object.power_resource.system_level;
446 pr->resource_order = acpi_object.power_resource.resource_order;
447 pr->state = ACPI_STATE_UNKNOWN;
448 pr->reference_count = 0;
449
450 /*
451 * Get the power resource's current state (ON|OFF).
452 */
453 status = bm_pr_get_state(pr);
454
455 end:
456 if (ACPI_FAILURE(status)) {
457 acpi_os_free(pr);
458 }
459 else {
460 *context = pr;
461 bm_pr_print(pr);
462 }
463
464 return_ACPI_STATUS(status);
465 }
466
467
468 /****************************************************************************
469 *
470 * FUNCTION: bm_pr_remove_device
471 *
472 * PARAMETERS: <TBD>
473 *
474 * RETURN: <TBD>
475 *
476 * DESCRIPTION: <TBD>
477 *
478 ****************************************************************************/
479
480 ACPI_STATUS
481 bm_pr_remove_device (
482 void **context)
483 {
484 ACPI_STATUS status = AE_OK;
485 BM_POWER_RESOURCE *pr = NULL;
486
487 FUNCTION_TRACE("bm_pr_remove_device");
488
489 if (!context || !*context) {
490 return_ACPI_STATUS(AE_BAD_PARAMETER);
491 }
492
493 pr = (BM_POWER_RESOURCE*)*context;
494
495 DEBUG_PRINT(ACPI_INFO, ("Removing power resource [0x%02X].\n", pr->device_handle));
496
497 acpi_os_free(pr);
498
499 return_ACPI_STATUS(status);
500 }
501
502
503 /****************************************************************************
504 * External Functions
505 ****************************************************************************/
506
507 /****************************************************************************
508 *
509 * FUNCTION: bm_pr_initialize
510 *
511 * PARAMETERS: <none>
512 *
513 * RETURN:
514 *
515 * DESCRIPTION: <TBD>
516 *
517 ****************************************************************************/
518
519 ACPI_STATUS
520 bm_pr_initialize (void)
521 {
522 ACPI_STATUS status = AE_OK;
523 BM_DEVICE_ID criteria;
524 BM_DRIVER driver;
525
526 FUNCTION_TRACE("bm_pr_initialize");
527
528 MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
529 MEMSET(&driver, 0, sizeof(BM_DRIVER));
530
531 criteria.type = BM_TYPE_POWER_RESOURCE;
532
533 driver.notify = &bm_pr_notify;
534 driver.request = &bm_pr_request;
535
536 status = bm_register_driver(&criteria, &driver);
537
538 return_ACPI_STATUS(status);
539 }
540
541
542 /****************************************************************************
543 *
544 * FUNCTION: bm_pr_terminate
545 *
546 * PARAMETERS: <TBD>
547 *
548 * RETURN: <TBD>
549 *
550 * DESCRIPTION: <TBD>
551 *
552 ****************************************************************************/
553
554 ACPI_STATUS
555 bm_pr_terminate (void)
556 {
557 ACPI_STATUS status = AE_OK;
558 BM_DEVICE_ID criteria;
559 BM_DRIVER driver;
560
561 FUNCTION_TRACE("bm_pr_terminate");
562
563 MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
564 MEMSET(&driver, 0, sizeof(BM_DRIVER));
565
566 criteria.type = BM_TYPE_POWER_RESOURCE;
567
568 driver.notify = &bm_pr_notify;
569 driver.request = &bm_pr_request;
570
571 status = bm_unregister_driver(&criteria, &driver);
572
573 return_ACPI_STATUS(status);
574 }
575
576
577 /****************************************************************************
578 *
579 * FUNCTION: bm_pr_notify
580 *
581 * PARAMETERS: <TBD>
582 *
583 * RETURN: <TBD>
584 *
585 * DESCRIPTION: <TBD>
586 *
587 ****************************************************************************/
588
589 ACPI_STATUS
590 bm_pr_notify (
591 BM_NOTIFY notify_type,
592 BM_HANDLE device_handle,
593 void **context)
594 {
595 ACPI_STATUS status = AE_OK;
596
597 FUNCTION_TRACE("bm_pr_notify");
598
599 switch (notify_type) {
600
601 case BM_NOTIFY_DEVICE_ADDED:
602 status = bm_pr_add_device(device_handle, context);
603 break;
604
605 case BM_NOTIFY_DEVICE_REMOVED:
606 status = bm_pr_remove_device(context);
607 break;
608
609 default:
610 status = AE_SUPPORT;
611 break;
612 }
613
614 return_ACPI_STATUS(status);
615 }
616
617
618 /****************************************************************************
619 *
620 * FUNCTION: bm_pr_request
621 *
622 * PARAMETERS: <TBD>
623 *
624 * RETURN: <TBD>
625 *
626 * DESCRIPTION: <TBD>
627 *
628 ****************************************************************************/
629
630 ACPI_STATUS
631 bm_pr_request (
632 BM_REQUEST *request,
633 void *context)
634 {
635 ACPI_STATUS status = AE_OK;
636 BM_POWER_RESOURCE *pr = NULL;
637
638 FUNCTION_TRACE("bm_pr_request");
639
640 /*
641 * Must have a valid request structure and context.
642 */
643 if (!request || !context) {
644 return_ACPI_STATUS(AE_BAD_PARAMETER);
645 }
646
647 /*
648 * context contains information specific to this power resource.
649 */
650 pr = (BM_POWER_RESOURCE*)context;
651
652 /*
653 * Handle request:
654 * ---------------
655 */
656 switch (request->command) {
657
658 default:
659 status = AE_SUPPORT;
660 break;
661 }
662
663 request->status = status;
664
665 return_ACPI_STATUS(status);
666 }
667
668
669