Tìm hiểu về ASL
Last updated
Last updated
Bài viết yêu cầu bảng đã có 1 chút nền tảng về lập trình
Bài viết này được viết dựa trên các tài liệu được công bố ở diễn đàng
Do bài viết được tổng hợp từ nhiều nguồn nên có gì sai sót mong các bạn thông cảm
DefinitionBlock
Ở đây chúng ta sẽ đến với một ví dụ
AMLFileName
""
Tên của file AML được tạo ra. Bạn có thể để trống mục này, và thường thì nó được để trống
TableSignature
"1234"
Loại bảng AML, ví dụ như DSDT
hoặc SSDT
. Hãy dùng 4
ký tự cho mục này.
ComplianceRevision
X
Phiên bản ACPI mà bảng này sử dụng. Nếu giá trị là 1
hoặc nhỏ hơn, bảng sẽ sử dụng bản 32 bit
. Ngược lại, giá trị từ 2
trở lên dành cho hệ thống 64 bit
. Hiện nay, giá trị mặc định là 2
.
OEM ID
"123456"
Mã nhận diện của nhà sản xuất thiết bị gốc (OEM
) tạo ra bảng ACPI
này. Hãy sử dụng 6
ký tự cho mục này.
OEM Table ID
"12345678"
Mã nhận diện riêng cho bảng này, do nhà sản xuất đặt ra. Hãy sử dụng 8 ký tự cho mục này.
OEMRevision
0x00000000
Số hiệu phiên bản của bảng, do nhà sản xuất đặt ra. (định dạng 32-bit)
Dưới đây là một số ví dụ
Nếu bạn viết bảng thay thế (ví dụ: cho khai báo cổng USB), bạn cần sử dụng cùng một OEM Table ID với bảng bạn muốn thay thế.
Nghe hơi khó hiểu nhưng cụ thể là nếu bạn muốn viết một SSDT thay thế cho một bảng ACPI nào khác ví dụ như một SSDT khác hay một DSDT khác thì phải cùng OEM Table ID
Mối quan hệ của Method và Scope
Một Method
phải luôn nằm bên trong một Scope
của một Device
nào đó.
Do đó, ví dụ dưới đây không hợp lệ vì
Method
được đặt ngay sauDefinitionBlock
, trong khiDefinitionBlock
không phải là mộtScope
Mối quan hệ của Method
và Device
Method
không chứa Device
Method
không thể định nghĩa hoặc khai báo Device
trực tiếp bên trong nó.
Device
phải được khai báo trong một Scope
.
Method
thường nằm trong Scope
của Device
Trong hầu hết trường hợp, Method
được đặt bên trong Scope
của một Device
cụ thể.
Điều này cho phép Method
truy cập và thao tác với các thuộc tính và chức năng của Device
đó một cách dễ dàng.
Method
thao tác với Device
thế nào:
Bên trong Method
, bạn có thể sử dụng tên của Device
Nếu nó nằm trong cùng Scope
hoặc Scope
cha để:
Đọc và ghi giá trị thuộc tính của Device
. Ví dụ, bạn có thể đọc trạng thái hiện tại của Device
hoặc thay đổi cấu hình của nó.
Gọi các Method
khác được định nghĩa trong Device
. Điều này cho phép bạn thực hiện các tác vụ phức tạp hơn với Device
.
Các Scope gốc
\_GPE,\_PR,\_SB,\_SI,\_TZ
thuộc phạm vị root scope /
Ví dụ:
Device (PCI0) (Thiết bị PCI0) sẽ được đặt bên trong Scope (_SB) (Phạm vi System Bus) vì PCI là một loại bus hệ thống.
Processor (CPU0, ...) (Bộ xử lý CPU0) sẽ được đặt bên trong Scope (_PR) (Phạm vi Processor).
Các phương thức xử lý ngắt từ bàn phím (Method (_KBD, ...) ) sẽ được đặt bên trong Scope (_GPE) (Phạm vi General Purpose Event).
Lợi ích của việc tổ chức theo Scope:
Dễ đọc, dễ hiểu: Mã nguồn rõ ràng, dễ dàng tìm kiếm và theo dõi các thành phần liên quan.
Tránh xung đột tên: Các thành phần có thể có cùng tên nhưng nằm trong Scope khác nhau, giúp tránh nhầm lẫn.
Quản lý quyền truy cập: Scope kiểm soát phạm vi nhìn thấy của các tên, giúp bảo vệ dữ liệu và ngăn chặn lỗi sửa đổi ngoài ý muốn.
Nhắc lại một lần nữa
Các phương thức (Methods) và biến (variables) bắt đầu bằng dấu gạch dưới (_) được dành riêng cho các hệ điều hành.
Device (xxxx) cũng có thể được nhận diện như một scope, nó chứa các mô tả về thiết bị, ví dụ: _ADR, _CID, _UID, _DSM, _STA.
Giải thích: Trong ASL, một thiết bị (Device) cũng có thể hoạt động như một phạm vi (scope). Bên trong phạm vi này, các mô tả chi tiết về thiết bị có thể được định nghĩa. Các mô tả này bao gồm:
_ADR: Địa chỉ của thiết bị.
_CID: Mã nhận diện tương thích của thiết bị.
_UID: Mã nhận diện duy nhất của thiết bị.
_DSM: Phương thức đặc biệt dành cho thiết bị.
_STA: Trạng thái của thiết bị.
Ký hiệu \ trích dẫn root scope
Ký hiệu ^ trích dẫn superior scope.
Tương tự, ^^ đại diện cho phạm vi cao hơn một cấp nữa so với ^
Giải thích: Trong ASL, ký hiệu \ được sử dụng để trích dẫn root scope
Tức là phạm vi gốc của toàn bộ hệ thống.
Ký hiệu ^ được dùng để trích dẫn superior scope
Tức là phạm vi cấp trên so với phạm vi hiện tại.
Khi sử dụng nhiều dấu ^ liên tiếp, mỗi dấu đại diện cho một cấp độ phạm vi cao hơn.
_
Ký hiệu _
không có ý nghĩa, nó chỉ để hoàn thành đủ 4 ký tự, ví dụ: _OSI.
Giải thích: Trong ASL, ký hiệu _ không có ý nghĩa đặc biệt và thường được sử dụng chỉ để hoàn thành đủ 4 ký tự cho tên biến hoặc phương thức.
Ví dụ: _OSI là một tên phương thức hoàn chỉnh với 4 ký tự.
Bảng ACPI 2.0, nó giới thiệu các toán tử của ngôn ngữ C như +-*/=, <<, >> và các phép so sánh logic ==, !=, v.v.
Giải thích: ASL+ (phiên bản 2.0 của ASL) mở rộng cú pháp của ASL, bao gồm việc giới thiệu các toán tử toán học và logic của ngôn ngữ lập trình C. Các toán tử này bao gồm:
Toán tử số học: +, -, *, /, = (cộng, trừ, nhân, chia, gán)
Toán tử dịch bit: <<, >> (dịch trái, dịch phải)
Toán tử logic: ==, != (bằng, không bằng)
Các phương thức (Methods) trong ASL có thể chấp nhận tối đa 7 tham số; chúng được biểu diễn bằng Arg0 đến Arg6 và không thể tùy chỉnh.
Giải thích: Trong ASL, một phương thức (method) có thể nhận đến 7 tham số đầu vào. Các tham số này được đặt tên từ Arg0 đến Arg6 và không thể đổi tên hoặc tùy chỉnh. Điều này có nghĩa là khi viết phương thức, bạn chỉ có thể sử dụng các tham số đã được định sẵn này.
Các biến cục bộ (Local variables) trong ASL có thể chấp nhận tối đa 8 tham số, được biểu diễn bằng Local0 đến Local7. Các định nghĩa không cần thiết, nhưng phải được khởi tạo, nói cách khác, cần có sự gán giá trị.
Giải thích: Trong ASL, các biến cục bộ có thể nhận đến 8 tham số, được đặt tên từ Local0 đến Local7. Không cần thiết phải định nghĩa các biến này trước khi sử dụng, nhưng chúng phải được khởi tạo bằng cách gán giá trị ban đầu. Điều này có nghĩa là bạn phải gán giá trị cho biến trước khi sử dụng nó trong phương thức.
External
_OSI
(Operating System Interfaces)Giá trị cho các Method _OSI phải được chọn từ bảng dưới đây
macOS
"Darwin"
Linux (và các hệ điều hành dựa trên Linux)
"Linux"
FreeBSD
"FreeBSD"
Windows
"Windows 20XX"
Lưu ý
Các phiên bản Windows khác nhau yêu cầu một chuỗi riêng biệt
_STA
(Status)Có hai loại _STA
Đừng nhầm lẫn với _STA từ PowerResource!
Bit [0]
Được đặt nếu thiết bị hiện diện.
Bit [1]
Được đặt nếu thiết bị được kích hoạt và giải mã tài nguyên của nó.
Bit [2]
Được đặt nếu thiết bị nên được hiển thị trong giao diện người dùng.
Bit [3]
Được đặt nếu thiết bị hoạt động đúng (xóa nếu thiết bị không vượt qua chẩn đoán).
Bit [4]
Được đặt nếu pin hiện diện.
_STA
Vị trí các bit:
Bit [0]
0
1
Được đặt (1) nếu thiết bị hiện diện.
Bit [1]
1
1
Được đặt (1) nếu thiết bị được kích hoạt và giải mã tài nguyên của nó.
Bit [2]
2
1
Được đặt (1) nếu thiết bị nên được hiển thị trong giao diện người dùng.
Bit [3]
3
1
Được đặt (1) nếu thiết bị hoạt động đúng (xóa nếu thiết bị không vượt qua chẩn đoán).
Bit [4]
4
0
Được đặt (1) nếu pin hiện diện.
Bit [5]
5
0
Không sử dụng trong ví dụ này.
Bit [6]
6
0
Không sử dụng trong ví dụ này.
Bit [7]
7
0
Không sử dụng trong ví dụ này.
_CRS
(Current Resource Settings)Integer
String
Event
Buffer
Package
Name (TEST, 0): Định nghĩa một biến số nguyên có tên là TEST
với giá trị ban đầu là 0
.
Name (MSTR, "ASL"): Định nghĩa một biến chuỗi có tên là MSTR
với giá trị ban đầu là "ASL"
.
Name (_PRW, Package (0x02) { ... }): Định nghĩa một biến gói (package) có tên là _PRW
với hai phần tử bên trong là 0x0D
và 0x03
.
Các số trong cập ( ) mình dùng ở đây là size của field
CreateBitField (1-Bit)
CreateBitField (AAAA, Zero, CCCC): Tạo một trường bộ đệm 1-bit từ bộ đệm AAAA
bắt đầu từ vị trí Zero
và đặt tên cho trường là CCCC
.
CreateByteField (8-Bit)
CreateByteField (DDDD, 0x01, EEEE): Tạo một trường bộ đệm 8-bit từ bộ đệm DDDD
bắt đầu từ vị trí 0x01
và đặt tên cho trường là EEEE
.
CreateWordField (16-Bit)
CreateWordField (FFFF, 0x05, GGGG): Tạo một trường bộ đệm 16-bit từ bộ đệm FFFF
bắt đầu từ vị trí 0x05
và đặt tên cho trường là GGGG
.
CreateDWordField (32-Bit)
CreateDWordField (HHHH, 0x06, IIII): Tạo một trường bộ đệm 32-bit từ bộ đệm HHHH
bắt đầu từ vị trí 0x06
và đặt tên cho trường là IIII
.
CreateQWordField (64-Bit)
CreateQWordField (JJJJ, 0x14, KKKK): Tạo một trường bộ đệm 64-bit từ bộ đệm JJJJ
bắt đầu từ vị trí 0x14
và đặt tên cho trường là KKKK
.
CreateField (Kích thước bất kỳ)
CreateField (LLLL, Local0, 0x38, MMMM): Tạo một trường bộ đệm với kích thước bất kỳ từ bộ đệm LLLL
, bắt đầu từ vị trí Local0
, có độ dài 0x38
và đặt tên cho trường là MMMM
.
+
Add
Local0 = 1 + 2
Add (1, 2, Local0)
Add (1, 2, Local0)
- Cộng hai số và gán kết quả vào Local0
.
*
Multiply
Local0 = 1 * 2
Multiply (1, 2, Local0)
Multiply (1, 2, Local0)
- Nhân hai số và gán kết quả vào Local0
.
/
Divide
Local0 = 10 / 9
Divide (10, 9, Local1(remainder), Local0(result))
Divide (10, 9, Local1(remainder), Local0(result))
- Chia 10 cho 9, gán kết quả vào Local0
và phần dư vào Local1
.
%
Mod
Local0 = 10 % 9
Mod (10, 9, Local0)
Mod (10, 9, Local0)
- Lấy phần dư của phép chia 10 cho 9 và gán vào Local0
.
<<
ShiftLeft
Local0 = 1 << 20
ShiftLeft (1, 20, Local0)
ShiftLeft (1, 20, Local0)
- Dịch chuyển bit của số 1 sang trái 20 vị trí và gán kết quả vào Local0
.
>>
ShiftRight
Local0 = 0x10000 >> 4
ShiftRight (0x10000, 4, Local0)
ShiftRight (0x10000, 4, Local0)
- Dịch chuyển bit của số 0x10000
sang phải 4 vị trí và gán kết quả vào Local0
.
--
Decrement
Local0--
Decrement (Local0)
Decrement (Local0)
- Giảm giá trị của Local0
đi 1.
++
Increment
Local0++
Increment (Local0)
Increment (Local0)
- Tăng giá trị của Local0
lên 1.
&
And
Local0 = 0x11 & 0x22
And (0x11, 0x22, Local0)
And (0x11, 0x22, Local0)
- Thực hiện phép toán AND giữa 0x11
và 0x22
và gán kết quả vào Local0
.
|
Or
Local0 = 0x01|0x02
Or (0x01, 0x02, Local0)
Or (0x01, 0x02, Local0)
- Thực hiện phép toán OR giữa 0x01
và 0x02
và gán kết quả vào Local0
.
~
Not
Local0 = ~(0x00)
Not (0x00,Local0)
Not (0x00, Local0)
- Thực hiện phép toán NOT (đảo bit) trên 0x00
và gán kết quả vào Local0
.
Nor
Nor (0x11, 0x22, Local0)
Nor (0x11, 0x22, Local0)
- Thực hiện phép toán NOR giữa 0x11
và 0x22
và gán kết quả vào Local0
.
&&
LAnd
If (BOL1 && BOL2)
If (LAnd(BOL1, BOL2))
Kiểm tra nếu cả BOL1
và BOL2
đều đúng (AND logic).
!
LNot
Local0 = !0
Store (LNot(0), Local0)
Thực hiện phép toán NOT trên giá trị 0
và gán kết quả vào Local0
.
|
LOr
Local0 = (0|1)
Store (LOr(0, 1), Local0)
Thực hiện phép toán OR giữa 0
và 1
và gán kết quả vào Local0
.
<
LLess
Local0 = (1 < 2)
Store (LLess(1, 2), Local0)
Kiểm tra nếu 1
nhỏ hơn 2
và gán kết quả vào Local0
.
<=
LLessEqual
Local0 = (1 <= 2)
Store (LLessEqual(1, 2), Local0)
Kiểm tra nếu 1
nhỏ hơn hoặc bằng 2
và gán kết quả vào Local0
.
>
LGreater
Local0 = (1 > 2)
Store (LGreater(1, 2), Local0)
Kiểm tra nếu 1
lớn hơn 2
và gán kết quả vào Local0
.
>=
LGreaterEqual
Local0 = (1 >= 2)
Store (LGreaterEqual(1, 2), Local0)
Kiểm tra nếu 1
lớn hơn hoặc bằng 2
và gán kết quả vào Local0
.
==
LEqual
Local0 = (Local0 == Local1)
If (LEqual(Local0, Local1))
Kiểm tra nếu Local0
bằng Local1
.
!=
LNotEqual
Local0 = (0 != 1) Store (LNotEqual(0, 1), Local0)
Kiểm tra nếu 0
không bằng 1
và gán kết quả vào Local0
.
Method (TEST): Định nghĩa một Method với tên TEST
.
Số lượng tham số mặc định là 0. Bạn có thể định nghĩa Method với các tham số và sử dụng các biến cục bộ từ Local0 đến Local7.
Method (MADD, 2): Định nghĩa một Method với tên MADD
và 2 tham số.
Local0, Local1: Sử dụng các biến cục bộ.
Arg0, Arg1: Tham số đầu vào của Method.
Return (Local0): Trả về giá trị của Local0.
Local0 = 1 + 2: Ví dụ trong ASL+.
Store (MADD (1, 2), Local0): Ví dụ trong Legacy ASL
Gọi Method MADD
với tham số 1 và 2,
Sau đó gán kết quả cho Local0.
Serialized: Chỉ định rằng Method được tuần tự hóa.
Điều này có nghĩa là chỉ một thể hiện (instance) của Method đó có thể tồn tại và thực thi trong bộ nhớ tại một thời điểm.
Nói cách khác, các lần gọi Method đó sẽ được thực hiện tuần tự, không đồng thời.
Nếu Method không được chỉ định là Serialized
Mặc định là NotSerialized
Các lần gọi Method này có thể thực thi đồng thời
Điều này có thể dẫn đến xung đột nếu các lần gọi này cùng tạo hoặc thay đổi cùng một tài nguyên.
Method TEST: Nếu gọi từ hai Method khác nhau:
Khi thực thi TEST
trong Dev1
TEST
trong Dev2
sẽ phải chờ cho đến khi TEST
trong Dev1
hoàn tất.
Method TEST: Nếu gọi từ hai Method khác nhau:
Nếu một trong các TEST
được gọi từ Devx
, một TEST
khác sẽ không thể tạo MSTR
, dẫn đến thất bại.
Cụ thể :
Khi cả hai phương thức TEST()
đang chạy đồng thời, cả hai đều cố gắng tạo biến MSTR
cùng một lúc.
Vì MSTR
không thể được tạo đồng thời trong hai Method không tuần tự hóa, dẫn đến xung đột.
Mã sau kiểm tra xem hệ thống có phải là Darwin không, nếu đúng thì gán OSYS = 0x2710
.
Mã sau kiểm tra xem hệ thống có phải là Darwin không
Nếu không phải thì kiểm tra hệ thống có phải là Linux không
Nếu đúng thì gán OSYS = 0x03E8
Nếu không thì gán OSYS = 0x07D0
.
Ví dụ sau sử dụng cấu trúc Switch
, Case
, Default
, và BreakPoint
để kiểm soát luồng.
Ví dụ sau sử dụng While
và Stall
để kiểm soát vòng lặp.
Local0 = 10
: Khởi tạo Local0
bằng 10.
While (Local0 >= 0x00)
: Thực hiện vòng lặp khi Local0
lớn hơn hoặc bằng 0.
Local0--
: Giảm giá trị của Local0
đi 1.
Stall (32)
: Tạm dừng 32 microsecond.
Cấu trúc for
trong ASL tương tự như trong C, Java.
Cấu trúc For
trên tương đương với While
dưới đây.
CondRefOf
hữu ích để kiểm tra xem đối tượng có tồn tại hay không.
Mã này được trích từ SSDT-I2CxConf.
Khi hệ thống không phải là MacOS, và XSCN
tồn tại dưới I2C0
, nó trả về giá trị gốc.
Trong ví dụ này SSDT-X phải được load trước SSDT-Y
B2: Nếu bạn muốn thay đổi thứ tự load của SSDT nào chỉ cần kéo "dòng" của SSDT đó lên trước
Hiểu chi tiết những gì mình nói xem
Do giới hạn phần mềm nên các bạn thông cảm cho mình phần này
Đọc thêm tại: .
B1: Mở Config.plist bằng
Source tham khảo:
UnknownObj
External (\_SB.EROR, UnknownObj
Tránh sử dụng
Đối tượng không xác định (UnknownObj) thường nên tránh sử dụng do không rõ ràng và có thể gây ra lỗi hoặc khó khăn trong việc xử lý.
IntObj
External (TEST, IntObj
Name (TEST, 0)
Đối tượng số nguyên (IntObj) có thể được tham chiếu bằng cách sử dụng tên và giá trị của nó.
StrObj
External (\_PR.MSTR, StrObj
Name (MSTR,"ASL")
Đối tượng chuỗi (StrObj) có thể được tham chiếu bằng tên và giá trị chuỗi của nó
BuffObj
External (\_SB.PCI0.I2C0.TPD0.SBFB, BuffObj
Name (SBFB, ResourceTemplate ()
Name (BUF0, Buffer() {"abcde"})
Đối tượng bộ đệm (BuffObj) có thể được tham chiếu bằng tên và nội dung của nó
PkgObj
External (_SB.PCI0.RP01._PRW, PkgObj
Name (_PRW, Package (0x02) { 0x0D, 0x03 })
Đối tượng gói (PkgObj) có thể được tham chiếu bằng tên và nội dung của gói
FieldUnitObj
External (OSYS, FieldUnitObj
OSYS, 16,
Đối tượng đơn vị trường (FieldUnitObj) có thể được tham chiếu bằng tên và kích thước của nó.
DeviceObj
External (\_SB.PCI0.I2C1.ETPD, DeviceObj
Device (ETPD)
Đối tượng thiết bị (DeviceObj) có thể được tham chiếu bằng tên của thiết bị
EventObj
External (XXXX, EventObj
Event (XXXX)
Đối tượng sự kiện (EventObj) có thể được tham chiếu bằng tên sự kiện.
MethodObj
External (\_SB.PCI0.GPI0._STA, MethodObj
Method (_STA, 0, NotSerialized)
Đối tượng phương thức (MethodObj) có thể được tham chiếu bằng tên và các thuộc tính của phương thức.
MutexObj
External (_SB.PCI0.LPCB.EC0.BATM, MutexObj
Mutex (BATM, 0x07)
Đối tượng mutex (MutexObj) có thể được tham chiếu bằng tên và giá trị của mutex.
OpRegionObj
External (GNVS, OpRegionObj
OperationRegion (GNVS, SystemMemory, 0x7A4E7000, 0x0866)
Đối tượng vùng hoạt động (OpRegionObj) có thể được tham chiếu bằng tên, loại bộ nhớ và địa chỉ của nó.
PowerResObj
External (\_SB.PCI0.XDCI, PowerResObj
PowerResource (USBC, 0, 0)
Đối tượng nguồn điện (PowerResObj) có thể được tham chiếu bằng tên và các thuộc tính của nguồn điện.
ProcessorObj
External (\_SB.PR00, ProcessorObj
Processor (PR00, 0x01, 0x00001810, 0x06)
Đối tượng bộ xử lý (ProcessorObj) có thể được tham chiếu bằng tên và các thuộc tính của bộ xử lý.
ThermalZoneObj
External (\_TZ.THRM, ThermalZoneObj
ThermalZone (THRM)
Đối tượng vùng nhiệt (ThermalZoneObj) có thể được tham chiếu bằng tên của vùng nhiệt.
BuffFieldObj
External (\_SB.PCI0._CRS.BBBB, BuffFieldObj
CreateField (AAAA, Zero, BBBB)
Đối tượng trường bộ đệm (BuffFieldObj) có thể được tham chiếu bằng tên và các thuộc tính của trường bộ đệm.