OSDev.org https://forum.osdev.org/ |
|
ACPI poweroff https://forum.osdev.org/viewtopic.php?f=1&t=16990 |
Page 1 of 3 |
Author: | kaworu [ Tue May 13, 2008 6:34 am ] |
Post subject: | ACPI poweroff |
I have written a "simple" acpi poweroff routine and wanted to share my knowledge since it seems here isn't that much acpi related stuff available. Maybe this could be added to the wiki? ################################### The ACPI shutdown is technically a really simple thing all that is needed is a outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and the computer is powered off. The problem lies in the gathering of these values especialy since the SLP_TYPa is in the \_S5 object which is in the DSDT and therefore AML encoded. Below is a simple "map" of where to find these fields. Code: "RSD PTR " || RsdtAddress pointer at offset 16 || \/ "RSDT" || pointer at offset 36 + 4 * n (check the target for the sig "FACP" to get the right n) || \/ "FACP" || ||=====\ || || || PM1a_CNT_BLK; offset: 64 (see section 4.7.3.2) || PM1b_CNT_BLK; offset: 68 || || || \/ || SLP_TYPx; bit 10-12 || SLP_EN; bit 13 || DSDT pointer at offset 40 || \/ "DSDT" (export the \_S5 object somehow.) To export the \_S5 object one would normally use an AML interpreter but thats obviously not an option considering we're building a hobby os. The simple solution is to scan the DSDT manually. The AML language specifies that \_... objects are defined only once which makes it very simple to find the \_S5 object since a simple memcmp() is enough. Once found the SLP_TYPx values are extracted. Code: bytecode of the \_S5 object ----------------------------------------- | (optional) | | | | NameOP | \ | _ | S | 5 | _ 08 | 5A | 5F | 53 | 35 | 5F ----------------------------------------------------------------------------------------------------------- | | | ( SLP_TYPa ) | ( SLP_TYPb ) | ( Reserved ) | (Reserved ) PackageOP | PkgLength | NumElements | byteprefix Num | byteprefix Num | byteprefix Num | byteprefix Num 12 | 0A | 04 | 0A 05 | 0A 05 | 0A 05 | 0A 05 ----this-structure-was-also-seen---------------------- PackageOP | PkgLength | NumElements | 12 | 06 | 04 | 00 00 00 00 The gatering of the informations is best performed at OS initialization because after that you can reuse the ram and don't need to worry about corrupting it. Now all that remains is outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and your'e gone. If PM1b_CNT is != 0 you need to repeat it with b. If that was a little too abstract here is some code to look at Code: // // here is the slighlty complicated ACPI poweroff code // #include <stddef.h> #include <print.h> #include <string.h> #include <io.h> #include <time.h> dword *SMI_CMD; byte ACPI_ENABLE; byte ACPI_DISABLE; dword *PM1a_CNT; dword *PM1b_CNT; word SLP_TYPa; word SLP_TYPb; word SLP_EN; word SCI_EN; byte PM1_CNT_LEN; struct RSDPtr { byte Signature[8]; byte CheckSum; byte OemID[6]; byte Revision; dword *RsdtAddress; }; struct FACP { byte Signature[4]; dword Length; byte unneded1[40 - 8]; dword *DSDT; byte unneded2[48 - 44]; dword *SMI_CMD; byte ACPI_ENABLE; byte ACPI_DISABLE; byte unneded3[64 - 54]; dword *PM1a_CNT_BLK; dword *PM1b_CNT_BLK; byte unneded4[89 - 72]; byte PM1_CNT_LEN; }; // check if the given address has a valid header unsigned int *acpiCheckRSDPtr(unsigned int *ptr) { char *sig = "RSD PTR "; struct RSDPtr *rsdp = (struct RSDPtr *) ptr; byte *bptr; byte check = 0; int i; if (memcmp(sig, rsdp, 8) == 0) { // check checksum rsdpd bptr = (byte *) ptr; for (i=0; i<sizeof(struct RSDPtr); i++) { check += *bptr; bptr++; } // found valid rsdpd if (check == 0) { /* if (desc->Revision == 0) wrstr("acpi 1"); else wrstr("acpi 2"); */ return (unsigned int *) rsdp->RsdtAddress; } } return NULL; } // finds the acpi header and returns the address of the rsdt unsigned int *acpiGetRSDPtr(void) { unsigned int *addr; unsigned int *rsdp; // search below the 1mb mark for RSDP signature for (addr = (unsigned int *) 0x000E0000; (int) addr<0x00100000; addr += 0x10/sizeof(addr)) { rsdp = acpiCheckRSDPtr(addr); if (rsdp != NULL) return rsdp; } // at address 0x40:0x0E is the RM segment of the ebda int ebda = *((short *) 0x40E); // get pointer ebda = ebda*0x10 &0x000FFFFF; // transform segment into linear address // search Extended BIOS Data Area for the Root System Description Pointer signature for (addr = (unsigned int *) ebda; (int) addr<ebda+1024; addr+= 0x10/sizeof(addr)) { rsdp = acpiCheckRSDPtr(addr); if (rsdp != NULL) return rsdp; } return NULL; } // checks for a given header and validates checksum int acpiCheckHeader(unsigned int *ptr, char *sig) { if (memcmp(ptr, sig, 4) == 0) { char *checkPtr = (char *) ptr; int len = *(ptr + 1); char check = 0; while (0<len--) { check += *checkPtr; checkPtr++; } if (check == 0) return 0; } return -1; } int acpiEnable(void) { // check if acpi is enabled if ( (inw((unsigned int) PM1a_CNT) &SCI_EN) == 0 ) { // check if acpi can be enabled if (SMI_CMD != 0 && ACPI_ENABLE != 0) { outb((unsigned int) SMI_CMD, ACPI_ENABLE); // send acpi enable command // give 3 seconds time to enable acpi int i; for (i=0; i<300; i++ ) { if ( (inw((unsigned int) PM1a_CNT) &SCI_EN) == 1 ) break; sleep(10); } if (PM1b_CNT != 0) for (; i<300; i++ ) { if ( (inw((unsigned int) PM1b_CNT) &SCI_EN) == 1 ) break; sleep(10); } if (i<300) { wrstr("enabled acpi.\n"); return 0; } else { wrstr("couldn't enable acpi.\n"); return -1; } } else { wrstr("no known way to enable acpi.\n"); return -1; } } else { //wrstr("acpi was already enabled.\n"); return 0; } } // // bytecode of the \_S5 object // ----------------------------------------- // | (optional) | | | | // NameOP | \ | _ | S | 5 | _ // 08 | 5A | 5F | 53 | 35 | 5F // // ----------------------------------------------------------------------------------------------------------- // | | | ( SLP_TYPa ) | ( SLP_TYPb ) | ( Reserved ) | (Reserved ) // PackageOP | PkgLength | NumElements | byteprefix Num | byteprefix Num | byteprefix Num | byteprefix Num // 12 | 0A | 04 | 0A 05 | 0A 05 | 0A 05 | 0A 05 // //----this-structure-was-also-seen---------------------- // PackageOP | PkgLength | NumElements | // 12 | 06 | 04 | 00 00 00 00 // // (Pkglength bit 6-7 encode additional PkgLength bytes [shouldn't be the case here]) // int initAcpi(void) { unsigned int *ptr = acpiGetRSDPtr(); // check if address is correct ( if acpi is available on this pc ) if (ptr != NULL && acpiCheckHeader(ptr, "RSDT") == 0) { // the RSDT contains an unknown number of pointers to acpi tables int entrys = *(ptr + 1); entrys = (entrys-36) /4; ptr += 36/4; // skip header information while (0<entrys--) { // check if the desired table is reached if (acpiCheckHeader((unsigned int *) *ptr, "FACP") == 0) { entrys = -2; struct FACP *facp = (struct FACP *) *ptr; if (acpiCheckHeader((unsigned int *) facp->DSDT, "DSDT") == 0) { // search the \_S5 package in the DSDT char *S5Addr = (char *) facp->DSDT +36; // skip header int dsdtLength = *(facp->DSDT+1) -36; while (0 < dsdtLength--) { if ( memcmp(S5Addr, "_S5_", 4) == 0) break; S5Addr++; } // check if \_S5 was found if (dsdtLength > 0) { // check for valid AML structure if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\\') ) && *(S5Addr+4) == 0x12 ) { S5Addr += 5; S5Addr += ((*S5Addr &0xC0)>>6) +2; // calculate PkgLength size if (*S5Addr == 0x0A) S5Addr++; // skip byteprefix SLP_TYPa = *(S5Addr)<<10; S5Addr++; if (*S5Addr == 0x0A) S5Addr++; // skip byteprefix SLP_TYPb = *(S5Addr)<<10; SMI_CMD = facp->SMI_CMD; ACPI_ENABLE = facp->ACPI_ENABLE; ACPI_DISABLE = facp->ACPI_DISABLE; PM1a_CNT = facp->PM1a_CNT_BLK; PM1b_CNT = facp->PM1b_CNT_BLK; PM1_CNT_LEN = facp->PM1_CNT_LEN; SLP_EN = 1<<13; SCI_EN = 1; return 0; } else { wrstr("\\_S5 parse error.\n"); } } else { wrstr("\\_S5 not present.\n"); } } else { wrstr("DSDT invalid.\n"); } } ptr++; } wrstr("no valid FACP present.\n"); } else { wrstr("no acpi.\n"); } return -1; } void acpiPowerOff(void) { // SCI_EN is set to 1 if acpi shutdown is possible if (SCI_EN == 0) return; acpiEnable(); // send the shutdown command outw((unsigned int) PM1a_CNT, SLP_TYPa | SLP_EN ); if ( PM1b_CNT != 0 ) outw((unsigned int) PM1b_CNT, SLP_TYPb | SLP_EN ); wrstr("acpi poweroff failed.\n"); } For further information read the corresponding sections of the acpi 1.0a specification Code: 9.1.7 Transitioning from the Working to the Soft Off State
7.5.2 \_Sx states 7.4.1 \_S5 4.7.2.3 Sleeping/Wake Control 16.3 AML Byte Streeam Byte Values 16.2.3 Package Length Encoding ############################################# This works on all my machines bochs and qemu. but i noticed that one needn't enable acpi for the pc to power down. Tough i dont know if this is always the case. If you yust want to play a little. For bochs and qemu it's outw( 0xB004, 0x0 | 0x2000 ); |
Author: | Cognition [ Tue May 13, 2008 7:11 am ] |
Post subject: | |
Technically I think there's one method you're suppose to execute first as well \_PTS. There's an optional method \_GTS too, but I think what you're describing should be fine for an emulator. Method execution would definitely require the use of interpreter as well. I don't know if a real system would require you todo an SMI->OS handoff of the resources first as well. |
Author: | kaworu [ Tue May 13, 2008 10:12 am ] |
Post subject: | |
youre right the _PTS should be executed but as far as i could see that only makes a aml local? thing and for \_S4 some pci things. so dosnt realy matter. me thinks and actually all of my real hardware even powers down when acpi wasnt even enabled so there seems to be no problem. though maybe there a lot of machines where this dosnt apply |
Author: | lukem95 [ Tue May 13, 2008 12:33 pm ] |
Post subject: | |
That looks very useful, do you think it could be added to the wiki? |
Author: | piranha [ Tue May 13, 2008 8:17 pm ] |
Post subject: | |
Say, is this supposed to work in QEMU, or is there a flag I need to set in the Qemu command? Or am I just calling the enable function wrong? Code: initAcpi(); Is all you need, right?
-JL |
Author: | kaworu [ Wed May 14, 2008 5:49 am ] |
Post subject: | |
No initAcpi();just retrieves everything needed and stores it so that you can use the ram used by acpi its basically a function you want to add into your kernel initialization the actual shutdown is performed by a later call to acpiPowerOff(); And i am okay with it being added to the wiki actually i strove to write it in a fashion which would enable a simple copy and paste. Hope i was able to acomplish it. |
Author: | piranha [ Wed May 14, 2008 8:35 am ] |
Post subject: | |
Yeah, I meant just to enable ACPI or start it or whatever, but the Init call doesn't work, it returns 'no acpi'. Is this a problem only in Qemu? -JL |
Author: | kaworu [ Wed May 14, 2008 4:46 pm ] |
Post subject: | |
You usually get the no acpi message when the RSDT wasnt found which is the case when acpi isnt available tough a disabled a20 gate could also be the cause since the header may lay in an area above 1m its certainly not a qemu problem as it works in my qemu. i have qemu v0.9.1 tough i didnt compile it myself but used the default ubuntu hardy package. |
Author: | piranha [ Wed May 14, 2008 6:05 pm ] |
Post subject: | |
Well, it doesn't work for me. Maybe it's my memcmp function, thats the only one I didn't have already: Code: int memcmp(char *s1, char *s2, int n)
{ int i; for(i=0;i<n;i++) { if(s1[n] != s2[n]) return 1; } return 0; } Does it need more or is it just completly messed up? -JL |
Author: | Brynet-Inc [ Wed May 14, 2008 6:53 pm ] |
Post subject: | |
piranha wrote: Well, it doesn't work for me. Maybe it's my memcmp function, thats the only one I didn't have already: That's not even a standards compliant implementation.. heck, it's not even a working implementation.
Code: int memcmp(char *s1, char *s2, int n) { int i; for(i=0;i<n;i++) { if(s1[n] != s2[n]) return 1; } return 0; } Does it need more or is it just completly messed up? -JL Do you even read standards and stress test the functions you write? |
Author: | piranha [ Wed May 14, 2008 7:20 pm ] |
Post subject: | |
That one I wrote quickly because I had about 5 min before I had to leave and I did want to test the ACPI implementation, so I didn't bother spending a while researching it, but I'll do that now. EDIT: Oops, yeah, that was the problem, sorry about these posts, they were stupid. -JL |
Author: | frazzledjazz [ Sun Mar 08, 2009 4:50 am ] |
Post subject: | Re: ACPI poweroff |
can someone check the above code to this please? I have that code above already and need it in 32bits assembler or 32bits pascal for FPC. I THINK I ported it correctly. The assembler cannot be inline, this is a FPC limitation, however I can accept either ATT or intel styles. All examples I otherwise have are 16bit calls.[which wont work with FPC] I have yet to see any real world examples on dropping down to real mode for shutdown. Code: //
// here is the slighlty complicated ACPI poweroff code // uses x86,strings; type record RSDPtr begin Signature:byte=8; CheckSum:byte; OemID:byte=6; Revision:byte; ^RsdtAddress:dword; end; record FACP begin Signature:byte=4; Length:dword; unneded1:byte=32; ^DSDT:dword; unneded2:byte=4; ^SMI_CMD:dword; ACPI_ENABLE:byte; ACPI_DISABLE:byte; unneded3:byte=10; ^PM1a_CNT_BLK:dword; ^PM1b_CNT_BLK:dword; unneded4:byte=67; PM1_CNT_LEN:byte; end; var ^SMI_CMD:dword; ACPI_ENABLE:byte; ACPI_DISABLE:byte; ^PM1a_CNT:dword; ^PM1b_CNT:dword; SLP_TYPa:word; SLP_TYPb:word; SLP_EN:word; SCI_EN:word; PM1_CNT_LEN:byte; // check if the given address has a valid header function ^acpiCheckRSDPtr(^ptr:integer); ^sig:string; rsdp:rsdPtr; bptr:integer; check:byte; i:integer; begin ^sig:="RSD PTR"; rsdp := ^ ptr; check:=0; rsdp:=^ptr if (memcmp(sig, rsdp, 8) = 0) then begin // check checksum rsdpd bptr := ptr; i:=0; repeat check += ^bptr; //huh?? inc(bptr); inc(i); until sizeof(rsdp);) >i // found valid rsdpd if (check == 0) { /* if (desc->Revision == 0) wrstr("acpi 1"); else wrstr("acpi 2"); */ return rsdp->RsdtAddress; end; end; return NULL; end; // finds the acpi header and returns the address of the rsdt procedure ^acpiGetRSDPtr ^addr:integer; ^rsdp:integer; begin // search below the 1mb mark for RSDP signature addr = 0x000E0000; repeat addr += 0x10/sizeof(addr)); {is this inc() or dec() im confused...} rsdp = acpiCheckRSDPtr(addr); if (rsdp != NULL) return rsdp; until addr > 0x00100000; // at address 0x40:0x0E is the RM segment of the ebda int ebda = *((short *) 0x40E); // get pointer ebda = ebda*0x10 &0x000FFFFF; // transform segment into linear address // search Extended BIOS Data Area for the Root System Description Pointer signature for (addr = (unsigned int *) ebda; (int) addr<ebda+1024; addr+= 0x10/sizeof(addr)) { rsdp = acpiCheckRSDPtr(addr); if (rsdp != NULL) return rsdp; } return NULL; end; // checks for a given header and validates checksum procedure acpiCheckHeader(^ptr:integer; ^sig:char); var ^checkPtr,len:integer; check:char; begin if (memcmp(ptr, sig, 4) = 0) then begin ^checkPtr = ptr; len = ^(ptr + 1); check = 0; while (len >0) do begin check += ^checkPtr; //huh ?? inc(checkPtr); end; if (check = 0) return 0; end; return -1; end; procedure acpiEnable; var i:integer; begin // check if acpi is enabled if ((readportw (PM1a_CNT) and SCI_EN) = 0) then begin // check if acpi can be enabled if (SMI_CMD <> 0 & ACPI_ENABLE <> 0) then begin writeportb( SMI_CMD, ACPI_ENABLE); // send acpi enable command --writeportb // give 3 seconds time to enable acpi i:=0; repeat if ((readportw( PM1a_CNT) and SCI_EN) = 1) then break; delay(3000); //about 3 secs inc(i); until i > 300; if (PM1b_CNT <> 0) then i:=0; repeat if ((readportw( PM1b_CNT) and SCI_EN) = 1) then //if readportw () and ()=1 break; delay(3000); inc(i); until i > 300; if (i>300) then begin writeln("enabled acpi."); return 0; end; else begin writeln("couldn"t enable acpi."); return -1; end; else begin writeln("no known way to enable acpi.\n"); return -1; end; else begin writeln("acpi was already enabled.\n"); return 0; end; end; // // bytecode of the \_S5 object // ----------------------------------------- // | (optional) | | | | // NameOP | \ | _ | S | 5 | _ // 08 | 5A | 5F | 53 | 35 | 5F // // ----------------------------------------------------------------------------------------------------------- // | | | ( SLP_TYPa ) | ( SLP_TYPb ) | ( Reserved ) | (Reserved ) // PackageOP | PkgLength | NumElements | byteprefix Num | byteprefix Num | byteprefix Num | byteprefix Num // 12 | 0A | 04 | 0A 05 | 0A 05 | 0A 05 | 0A 05 // //----this-structure-was-also-seen---------------------- // PackageOP | PkgLength | NumElements | // 12 | 06 | 04 | 00 00 00 00 // // (Pkglength bit 6-7 encode additional PkgLength bytes [shouldn't be the case here]) // procedure initAcpi(); var ^ptr,entries,dsdtlength:integer; ^S5Addr,facp:pointer of char; begin ^ptr = acpiGetRSDPtr(); // check if address is correct ( if acpi is available on this pc ) if (ptr <> NULL & acpiCheckHeader(ptr, "RSDT") = 0)then begin // the RSDT contains an unknown number of pointers to acpi tables entrys = ^(ptr + 1); entrys = (entrys-36) /4; ptr += 36/4; // skip header information while (entrys>0) do begin // looks like there is a decrement courter weirdly implemented here. // check if the desired table is reached if (acpiCheckHeader( ^ptr, "FACP") = 0) then begin entrys = -2; ^facp = ^ptr; // i think this is right.. if (acpiCheckHeader(facp->DSDT, "DSDT") = 0) then begin // search the \_S5 package in the DSDT ^S5Addr = facp->DSDT +36; // skip header dsdtLength= ^(facp->DSDT+1) -36; while (dsdtLength >0) do begin // again with the dec counter if (memcmp(S5Addr, "_S5_", 4) = 0) break; inc(S5Addr); } // check if \_S5 was found if (dsdtLength > 0) begin // check for valid AML structure if ((^(S5Addr-1) = 0x08 or {||} (^(S5Addr-2) == 0x08 and ^(S5Addr-1) = '\\')) and ^(S5Addr+4) = 0x12) then begin S5Addr += 5; S5Addr += ((^S5Addr &0xC0)>>6) +2; // calculate PkgLength size shr(6)? if (^S5Addr = 0x0A) inc(S5Addr); // skip byteprefix SLP_TYPa = ^(S5Addr)<<10; // shl (10)?? inc (S5Addr); if (^S5Addr = 0x0A) inc(S5Addr); // skip byteprefix SLP_TYPb = ^(S5Addr)<<10; //shl(10); SMI_CMD = facp->SMI_CMD; ACPI_ENABLE = facp->ACPI_ENABLE; ACPI_DISABLE = facp->ACPI_DISABLE; PM1a_CNT = facp->PM1a_CNT_BLK; PM1b_CNT = facp->PM1b_CNT_BLK; PM1_CNT_LEN = facp->PM1_CNT_LEN; SLP_EN = 1<<13; // shl(13) ?? SCI_EN = 1; return 0; end; else begin writeln("_S5 parse error."); end; end; else begin writeln("_S5 not present."); end; end; else begin writeln("DSDT invalid."); end; end; inc(ptr); end; writeln("no valid FACP present."); end; else begin writeln("no acpi."); end; return -1; end; procedure acpiPowerOff; begin // SCI_EN is set to 1 if acpi shutdown is possible if (SCI_EN = 0) return; acpiEnable(); // send the shutdown command writeportw(PM1a_CNT, SLP_TYPa | SLP_EN); // what the hell does | (single pipe) mean? there can be only ONE command. //IE: writeport[b,w,l](port num ,byte[or word]) if (PM1b_CNT <> 0) writeportw( PM1b_CNT, SLP_TYPb | SLP_EN); writeln("acpi poweroff failed."); end; end. |
Author: | jal [ Mon Mar 09, 2009 4:45 am ] |
Post subject: | Re: ACPI poweroff |
frazzledjazz wrote: can someone check the above code to this please? I think you've been told before, but we are not here to do your homework. Also, please stop copy/pasting with double linewidth, it's quite annoying! JAL |
Author: | andymc [ Sun Feb 27, 2011 8:18 pm ] |
Post subject: | Re: ACPI poweroff |
Sorry to revive an old thread, but I found this really useful and I have a couple of questions. For my operating system, Visopsys, I was really only interested in implementing ACPI power off for the time being, but I started reading the spec and implementing the basic stuff, gathering the structures, etc., then I hit the namespace stuff and the bytecode stuff and my head just started spinning. I haven't seen a spec this computer science wanker-ish since I read the USB human interface spec (and I say that as a CompSci guy). This post really helped distill the essential elements, thanks. I like to give credit when I follow an example like this (normally full name, email address) - can you tell me how I should credit this? Now to the questions: On my laptop system, it successfully powers off to the soft-off state, but when the power button is subsequently pressed, all I see is a small cursor, and it requires a hard reset to boot normally. It's almost as if the system expects to wake from that state. Is there something (such as the wakeup vector) that I need to clear, in order to prevent this? Could this relate to the things Cognition mentioned? Second, on the same laptop system, enabling ACPI as shown seems to interfere with other devices. It almost looks as if there are unserviced interrupts causing problems with (for example) my ATA driver. Should I be implementing an interrupt handler to at least acknowledge any ACPI event interrupts? Cheers for any help |
Author: | Brendan [ Sun Feb 27, 2011 9:41 pm ] |
Post subject: | Re: ACPI poweroff |
Hi, andymc wrote: On my laptop system, it successfully powers off to the soft-off state, but when the power button is subsequently pressed, all I see is a small cursor, and it requires a hard reset to boot normally. It's almost as if the system expects to wake from that state. Is there something (such as the wakeup vector) that I need to clear, in order to prevent this? Could this relate to the things Cognition mentioned? Cognition is right, but that's only the beginning. You'd have to decipher the entire ACPI specification to get a full picture of how many different ways kaworu's code can break in lots of different situations. For example, the "\_S5" method alone could call a bunch of different methods elsewhere, and can even do different things depending on the current state, or which OS you told ACPI you are, or whatever; and none of that is taken into account. You need a full AML interpreter. You can't extract a few values and use them directly and hope that will work. andymc wrote: Second, on the same laptop system, enabling ACPI as shown seems to interfere with other devices. It almost looks as if there are unserviced interrupts causing problems with (for example) my ATA driver. Should I be implementing an interrupt handler to at least acknowledge any ACPI event interrupts? In "default mode" the firmware takes care of all the power management stuff for you. This is like having a chauffeur drive your car - you get less control over how things are done, but don't need to worry about the details. When you enable ACPI you're effectively telling the firmware that you will take control over all the power management stuff (with the assistance of the AML). This is like telling the chauffeur that you'll drive yourself (with the assistance of a TomTom navigation system or something). When you enable ACPI and don't take control of all the power management stuff then things go wrong. This is like a car rolling down the street with its cruise control on and nobody behind the wheel. When any ACPI events occur, you have to execute the corresponding AML. Cheers, Brendan |
Page 1 of 3 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |