一、ASL基本准则
1.变量命名不超过四个字符,且不能以数字开头。
2、变量或者函数命名,不分大小写。
3.Scope形成作用域,概念类似于C++中的namespace,Java中的package。
4.Device定义也会形成自己的作用域,类似于C++中的Class的概念。
5.Method或者Function定义函数,函数可以定义在Device下或者Scope下,但是不能脱离Scope定义单独的函数,也就是说,函数必须依附于对象(Scope or Device).
6.以“_”字符开头的函数,都是系统保留的,不得给自己的函数起这样的名字。
7.ASL中没有运算符号(逻辑运算与数学运算都是如此),但是有与此等价的相应系统函数代替。
8.符号“\”引用根作用域,“^”引用父级或者上级作用域。
9.作用域,或者称路径,有相对与绝对之分。相对作用域从当前作用域开始,向上延伸。也就是说在当前作用域中使用函数和变量时,解析器会首先从当前作用域中寻找他的定义,如果找不到,则会从父级或从上级作用域中继续寻找,一直找到当前作用域的root为止。绝对路径,则从定义此变量或者函数的root作用域开始,一级级的写下去,一直写到此变量的作用域,作用域的引用使用符号“.”,例如:\SB.PCI0.ABCD。
10.函数最多可以传递8个参数,在函数里用Arg0~Arh7引用,不可以自己定义名字。
11.在函数里最多可以使用8个局部变量,用Local0~Local7表示,不用定义,但是在把局部变量的值赋值给其他变量之前,局部变量必须是有效的值,也就是说,至少有一次给局部变量赋值的操作。
12.声明变量时不需要显示声明其类型。
二、数据类型、赋值、基本运算
1.数据类型ASL中支持的数据类型有:
a)整型——Integer
b)字符串——String
c)事件——Event
d)数组——Buffer
e)对象集合——Package
2. 定义变量
Name(MYTS,0) //定义一个整数
Name(TSTR,"Hello ASL") //定义一个字符串
3.赋值
最常用的赋值函数只有一个,即Store(),如:
Store(0x1234,Local0)
Store("Hello ASL",Local0)
4.算数运算
算术运算的操作符如下图:
举例如下:
5.逻辑运算
ASL中逻辑运算的函数如下图所示,运算的方法和数学中的逻辑运算相同,不过要使用逻辑运算函数代替符号
三、函数、流程控制
1.定义函数
Method(TMED)
{}
2.定义有两个输入参数的函数
Method(TMED,2)
{
//Arg0
//Arg1
}
3.在函数中使用局部变量
Method(TMED,2)
{
Store(Arg0,Local0)
Store(Arg1,Local1)
Add(Local0,Local1,Local0)
}
4.在函数中使用返回值
Method(TMED,2)
{
Store(Arg0,Local0)
Store(Arg1,Local1)
Add(Local0,Local1,Local0)
return(Local0)
}
5.调用函数
TMED(3,5)
6.保存函数的返回值
Store(TMED(4,5),TMPD)
与常见的高级语言一样,ASL中也有与之对应的流程控制语句
分支控制If、ElseIf、Else 借用ACPI中的sample,展示如下
}
四、OperationRegion的使用,IO、Memory、PCI、EC读写
OperationRegion是ACPI定义的一种操作Register方式,可以操作的Register包括IO Memory PCI等等,ACPI4.0支持的OperationRegion共有以下几种。此外,用户还可以自己写一个ACPI驱动,注册自己的OperationRegion。
就当前来说,并不是上面所有的OperationRegion都受到支持且可以使用,一些OperationRegion,受限于编译器,OS下AML的Interpreter支持等等因素,是不能确定能够在当前ASL中使用的,类似的OperationRegion有CMOS、PCIBARTarget、IPMI、SMBUS。另外,对于其他的一些OperationRegion,ACPI Spec有一些特殊的规定。
1.OS必须保证SystemIO OperationRegion 在任何情况下都可以使用
2.OS必须保证PCI Root Bus下的PCI_Config OperationRegion一定可用
3.OS必须保证,SystemMemory OperationRegion在访问通过Memory Map Report的Memory时,一定可用。事实上,这一条就是说明,只要是在有效地址空间中的Memory访问,OS必须保证Memory OperationRegion可用
此外其他OperationRegion,必须通过_Reg Method去判断,如果OperationRegion已经connect,则此时OperationRegion可用,如果没有connect,或者已经Disconnect,则不可通过此OperationRegion去访问设备的地址空间。
IO OperationRegion
接下来,将展示一个IO OperationRegion的使用,我们使用定义的OperationRegion,将debug code输出到80 Port
OperationRegion(DBGP,SystemIO,0x80,4)
Field (DBGP,ByteAcc,Lock,Preserve)
{
P80L, 8
}
Store(0xA3,P80L) //输出A3到80 Port
Memory OperationRegion
接下来,我们通过Memory OperationRegion展示一个相当好用的函数,我们知道,对于PCIe设备来说,有两种访问方式,一种是通过传统的PCI兼容方式,另一种是通过MMIO,不但可以访问PCI的256 Byte 的PCI Space,而且可以访问全部的4K PCI Space,那么接下来,我们将展示一组函数,他们能够让你在ASL里面自由的访问PCI Express的Configuration Space.
#define PCIE_BASE 0xE0000000
Method(RDPB,1)
{
Add(Arg0,PCIE_BASE,Local0) //Add PCI Express MMIO base address
OperationRegion(PECF,SystemMemory,Local0,0x1)
Field (PECF,ByteAcc,Nolock,Preserve)
{
MCFG, 8
}
Return (MCFG)
}
上面展示的函数是直接通过MMIO访问PCIe 设备的configuration space,但是上面的函数只适合比较熟练的开发者使用,因为需要自己计算PCIe设备的地址,那么,我们把上面的函数稍微转换一下,变成下面的函数,这样就够直观了。
// Arg0 - bus NO
// Arg1 - dev NO
// Arg2 - func NO
// Arg3 - register offset
Merhod (RDPB , 4)
{
ShiftLeft(Arg0,20,Local0)
Or(ShiftLeft(Arg1,15),Local0,Local0)
Or(ShiftLeft(Arg2,12),Local0,Local0)
Or(Arg3,Local0,Local0)
Add(Arg0,PCIE_BASE,Local0)
OperationRegion(PECF,SystemMemory,Local0,0x1)
Field(PECF,ByteAcc,Nolock,Preserve)
{
MCFG,8
}
Return (MCFG)
}
有了上面的函数,我们可以在ASL读某个PCIe设备的一个byte,例如,我们读PCI设备,bus 0,dev 31, func 3,register 0x40,可以使用如下 语句
Store(RDPB(0,31,3,0x40),Local0)
上面是读PCIe Register的函数,接下来,我们将上面的函数稍加修改,写一个写byte 到PCIe register的函数。
// Arg0 - bus NO
// Arg0 - dev NO
// Arg0 - func NO
// Arg0 - register offset
Merhod (WRPB , 4)
{
ShiftLeft(Arg0,20,Local0)
Or(ShiftLeft(Arg1,15),Local0,Local0)
Or(ShiftLeft(Arg2,12),Local0,Local0)
Or(Arg3,Local0,Local0)
Add(Arg0,PCIE_BASE,Local0)
OperationRegion(PECF,SystemMemory,Local0,0x1)
Field(PECF,ByteAcc,Nolock,Preserve)
{
MCFG,8
}
Store (Arg4,MCFG)
}
然后可以通过以下语句,将一个Byte写入到 bus 0, dev 31, func 3 ,register 0x40
WRPD(0, 31, 3, 0x40, 5)
PCI_CONFIG Operation
可以通过定义PCI OperationRegion来访问PCI和PCIe设备的配置空间。不过,可以从PCI OperationRegion的定义看到,OperationRegion 本身只定义Register在Configuration space的位置和长度,但不能确定PCI设备bus number ,device number 和 function number,所以也就不能确定设备的地址。ACPI引入了其他一些Method来确定ACPI中PCI设备的地址。_SEG函数定义PCI设备的Segment,在x86架构中来说,一般不使用_SEG,所有的PCI设备默认都在Segment 0。_BBN函数定义PCI Root Bridge的bus number,一般来说在PCI Root Bridge下定义_BBN(0),
意指从bus 0开始。有了segment 和 bus,最后我们在PCI设备下定义一个_ADR属性,确定此PCI设备的Device number 和function number,_ADR的返回值中,高16位表示Device number,低16位表示function number,接下来将展示一段sample code,读写 bus 0, device 29, function 1 USB controller 的PCI Configuration Space。
Name(_ADR, 0x001d0001)
OperationRegion (USBR, PCI_Config, 0xC4, 1)
Field (USBR,ANYACC, NOLOCK, Preserve)
{
URSE, 8
}
Method (TEMD)
{
Store(3,URES)
}
EC OperationRegion
EC OperationRegion 是定义EC Space操作的,可以在ASL里面定义EC OperationRegion,直接读写EC OperationRegion, OS的ACPI或者EC Driver或将这些操作转换为对EC Space的读写。根据ACPI spec,对于一个读操作,driver会向EC发送0x80 command 读其中的value,对于写操作,driver 会向EC发送0x81 command将一个value写到EC space。接下来定义一个EC OPerationRegion,假设EC space offset 0x00是CPU的温度。
OperationRegion (ECF2,EmbeddedControl, 0x00,0xff)
Field(ECF2,ByteAcc,Lock,Preserve)
{
CTMP, 8
}
Store(CTMP,Local0) //Read CPU temperature from EC space
与上面其他OperationRegion不同的是,EC OperationRegion并不是任何时候都可以使用,所以我们要follow ACPI spec,在同一scope中定义一个_Reg属性,来判断EC OperationRegon是否可用
Name(ECON,0) //Variable to remember EC OperationRegion Status
Method(_REG,0x2)
{
if(LEqual(Arg0,0x03)) // Is it EC OperationRegion ? Yes EC=3
{
if(Arg1) // Is OperationRegion Connect ?
{
Store(0x01,ECON) //Available
}Else //OperationRegion Disconnect
{
Store(0x00,ECON) //unavailable
}
}
}