Hãy áp dụng các nguyên tắc sau để tăng tính mạnh mẽ và độ tin cậy của các mô-đun nhà cung cấp. Khi tuân thủ nhiều nguyên tắc, bạn có thể dễ dàng xác định đúng thứ tự tải mô-đun và thứ tự mà trình điều khiển phải thăm dò thiết bị.
Một mô-đun có thể là một thư viện hoặc một trình điều khiển.
Mô-đun thư viện là những thư viện cung cấp API để các mô-đun khác sử dụng. Những mô-đun như vậy thường không dành riêng cho phần cứng. Ví dụ về các mô-đun thư viện bao gồm mô-đun mã hoá AES, khung
remoteproc
được biên dịch dưới dạng một mô-đun và mô-đun logbuffer. Mã mô-đun trongmodule_init()
chạy để thiết lập cấu trúc dữ liệu, nhưng không có mã nào khác chạy trừ phi được kích hoạt bởi một mô-đun bên ngoài.Các mô-đun trình điều khiển là những trình điều khiển thăm dò hoặc liên kết với một loại thiết bị cụ thể. Các mô-đun như vậy dành riêng cho phần cứng. Ví dụ về các mô-đun trình điều khiển bao gồm phần cứng UART, PCIe và bộ mã hoá video. Các mô-đun trình điều khiển chỉ kích hoạt khi thiết bị liên kết của chúng có trên hệ thống.
Nếu không có thiết bị, mã mô-đun duy nhất chạy là mã
module_init()
đăng ký trình điều khiển với khung lõi trình điều khiển.Nếu thiết bị có mặt và trình điều khiển thăm dò hoặc liên kết thành công với thiết bị đó, thì mã mô-đun khác có thể chạy.
Sử dụng đúng cách quá trình khởi động và thoát mô-đun
Các mô-đun trình điều khiển phải đăng ký trình điều khiển trong module_init()
và huỷ đăng ký trình điều khiển trong module_exit()
. Một cách để thực thi những hạn chế này là sử dụng macro trình bao bọc. Cách này giúp tránh việc sử dụng trực tiếp macro module_init()
, *_initcall()
hoặc module_exit()
.
Đối với các mô-đun có thể được gỡ tải, hãy sử dụng
module_subsystem_driver()
. Ví dụ:module_platform_driver()
,module_i2c_driver()
vàmodule_pci_driver()
.Đối với những mô-đun không thể huỷ tải, hãy sử dụng
builtin_subsystem_driver()
Ví dụ:builtin_platform_driver()
,builtin_i2c_driver()
vàbuiltin_pci_driver()
.
Một số mô-đun trình điều khiển sử dụng module_init()
và module_exit()
vì chúng đăng ký nhiều trình điều khiển. Đối với một mô-đun trình điều khiển sử dụng module_init()
và module_exit()
để đăng ký nhiều trình điều khiển, hãy thử kết hợp các trình điều khiển thành một trình điều khiển duy nhất. Ví dụ: bạn có thể phân biệt bằng cách sử dụng chuỗi compatible
hoặc dữ liệu phụ của thiết bị thay vì đăng ký các trình điều khiển riêng biệt.
Ngoài ra, bạn có thể chia mô-đun trình điều khiển thành 2 mô-đun.
Ngoại lệ của hàm khởi động và thoát
Các mô-đun thư viện không đăng ký trình điều khiển và được miễn các quy định hạn chế đối với module_init()
và module_exit()
vì có thể cần các hàm này để thiết lập cấu trúc dữ liệu, hàng đợi công việc hoặc luồng hạt nhân.
Sử dụng macro MODULE_DEVICE_TABLE
Các mô-đun trình điều khiển phải bao gồm macro MODULE_DEVICE_TABLE
, cho phép không gian người dùng xác định các thiết bị mà một mô-đun trình điều khiển hỗ trợ trước khi tải mô-đun. Android có thể sử dụng dữ liệu này để tối ưu hoá việc tải mô-đun, chẳng hạn như tránh tải các mô-đun cho những thiết bị không có trong hệ thống. Để biết ví dụ về cách sử dụng macro, hãy tham khảo mã nguồn.
Tránh trường hợp không khớp CRC do các kiểu dữ liệu được khai báo trước
Đừng thêm tệp tiêu đề để xem các kiểu dữ liệu được khai báo trước.
Một số cấu trúc, liên kết và các kiểu dữ liệu khác được xác định trong tệp tiêu đề (header-A.h
) có thể được khai báo chuyển tiếp trong một tệp tiêu đề khác (header-B.h
) thường sử dụng con trỏ đến các kiểu dữ liệu đó. Mẫu mã này có nghĩa là hạt nhân đang cố ý giữ cấu trúc dữ liệu ở chế độ riêng tư đối với người dùng header-B.h
.
Người dùng header-B.h
không nên thêm header-A.h
để truy cập trực tiếp vào nội bộ của các cấu trúc dữ liệu được khai báo chuyển tiếp này. Việc này gây ra các vấn đề về sự không khớp CRC CONFIG_MODVERSIONS
(tạo ra các vấn đề về việc tuân thủ ABI) khi một nhân khác (chẳng hạn như nhân GKI) cố gắng tải mô-đun.
Ví dụ: struct fwnode_handle
được xác định trong include/linux/fwnode.h
, nhưng được khai báo chuyển tiếp dưới dạng struct fwnode_handle;
trong include/linux/device.h
vì hạt nhân đang cố gắng giữ bí mật thông tin chi tiết của struct fwnode_handle
đối với người dùng include/linux/device.h
. Trong trường hợp này, đừng thêm #include <linux/fwnode.h>
vào một mô-đun để có quyền truy cập vào các thành phần của struct fwnode_handle
. Mọi thiết kế mà bạn phải đưa các tệp tiêu đề như vậy vào đều cho thấy một mẫu thiết kế không phù hợp.
Không truy cập trực tiếp vào các cấu trúc kernel cốt lõi
Việc truy cập hoặc sửa đổi trực tiếp các cấu trúc dữ liệu nhân cốt lõi có thể dẫn đến hành vi không mong muốn, bao gồm cả rò rỉ bộ nhớ, sự cố và khả năng tương thích bị hỏng với các bản phát hành nhân trong tương lai. Cấu trúc dữ liệu là cấu trúc dữ liệu cốt lõi của nhân khi đáp ứng bất kỳ điều kiện nào sau đây:
Cấu trúc dữ liệu được xác định trong
KERNEL-DIR/include/
. Ví dụ:struct device
vàstruct dev_links_info
. Các cấu trúc dữ liệu được xác định tronginclude/linux/soc
được miễn.Cấu trúc dữ liệu được phân bổ hoặc khởi tạo bởi mô-đun nhưng được hiển thị cho nhân bằng cách truyền gián tiếp (thông qua con trỏ trong một cấu trúc) hoặc trực tiếp, làm đầu vào trong một hàm do nhân xuất. Ví dụ: mô-đun trình điều khiển
cpufreq
khởi chạystruct cpufreq_driver
rồi truyền mô-đun này làm đầu vào chocpufreq_register_driver()
. Sau thời điểm này, mô-đun trình điều khiểncpufreq
không được sửa đổi trực tiếpstruct cpufreq_driver
vì việc gọicpufreq_register_driver()
sẽ làm chostruct cpufreq_driver
hiển thị với nhân.Cấu trúc dữ liệu không được mô-đun của bạn khởi tạo. Ví dụ:
struct regulator_dev
doregulator_register()
trả về.
Chỉ truy cập vào các cấu trúc dữ liệu hạt nhân cốt lõi thông qua các hàm do hạt nhân xuất hoặc thông qua các tham số được truyền rõ ràng dưới dạng dữ liệu đầu vào cho các hook của nhà cung cấp. Nếu không có API hoặc hook của nhà cung cấp để sửa đổi các phần của cấu trúc dữ liệu nhân cốt lõi, thì có thể đó là ý định và bạn không nên sửa đổi cấu trúc dữ liệu từ các mô-đun. Ví dụ: không sửa đổi bất kỳ trường nào bên trong struct device
hoặc struct device.links
.
Để sửa đổi
device.devres_head
, hãy sử dụng hàmdevm_*()
, chẳng hạn nhưdevm_clk_get()
,devm_regulator_get()
hoặcdevm_kzalloc()
.Để sửa đổi các trường bên trong
struct device.links
, hãy sử dụng một API liên kết thiết bị nhưdevice_link_add()
hoặcdevice_link_del()
.
Không phân tích cú pháp các nút devicetree bằng thuộc tính tương thích
Nếu một nút cây thiết bị (DT) có thuộc tính compatible
, thì struct device
sẽ được phân bổ cho nút đó một cách tự động hoặc khi of_platform_populate()
được gọi trên nút DT mẹ (thường là do trình điều khiển thiết bị của thiết bị mẹ). Kỳ vọng mặc định (ngoại trừ một số thiết bị được khởi động sớm cho trình lập lịch) là một nút DT có thuộc tính compatible
có struct device
và một trình điều khiển thiết bị phù hợp. Tất cả các trường hợp ngoại lệ khác đều đã được mã nguồn ở thượng nguồn xử lý.
Ngoài ra, fw_devlink
(trước đây gọi là of_devlink
) coi các nút DT có thuộc tính compatible
là các thiết bị có struct device
được phân bổ do trình điều khiển thăm dò. Nếu một nút DT có thuộc tính compatible
nhưng struct device
được phân bổ không được kiểm tra, thì fw_devlink
có thể chặn các thiết bị tiêu dùng của nút đó kiểm tra hoặc có thể chặn các lệnh gọi sync_state()
được gọi cho các thiết bị của nhà cung cấp.
Nếu trình điều khiển của bạn sử dụng một hàm of_find_*()
(chẳng hạn như of_find_node_by_name()
hoặc of_find_compatible_node()
) để trực tiếp tìm một nút DT có thuộc tính compatible
rồi phân tích cú pháp nút DT đó, hãy sửa đổi mô-đun bằng cách viết một trình điều khiển thiết bị có thể kiểm tra thiết bị hoặc xoá thuộc tính compatible
(chỉ có thể thực hiện nếu thuộc tính đó chưa được truyền lên). Để thảo luận về các giải pháp thay thế, hãy liên hệ với Nhóm nhân Android theo địa chỉ kernel-team@android.com và chuẩn bị sẵn sàng để biện minh cho các trường hợp sử dụng của bạn.
Sử dụng DT phandle để tra cứu nhà cung cấp
Tham chiếu đến một nhà cung cấp bằng cách sử dụng phandle (một tham chiếu hoặc con trỏ đến nút DT) trong DT bất cứ khi nào có thể. Việc sử dụng các liên kết DT tiêu chuẩn và phandle để tham chiếu đến các nhà cung cấp cho phép fw_devlink
(trước đây là of_devlink
) tự động xác định các phần phụ thuộc giữa các thiết bị bằng cách phân tích cú pháp DT tại thời gian chạy. Sau đó, nhân có thể tự động kiểm tra các thiết bị theo đúng thứ tự, loại bỏ nhu cầu về việc sắp xếp thứ tự tải mô-đun hoặc MODULE_SOFTDEP()
.
Trường hợp cũ (không hỗ trợ DT trong nhân ARM)
Trước đây, trước khi tính năng hỗ trợ DT được thêm vào nhân ARM, các thiết bị tiêu dùng như thiết bị cảm ứng đã tra cứu các nhà cung cấp như bộ điều chỉnh bằng cách sử dụng các chuỗi duy nhất trên toàn cầu.
Ví dụ: trình điều khiển PMIC của ACME có thể đăng ký hoặc quảng cáo nhiều bộ điều chỉnh (chẳng hạn như acme-pmic-ldo1
đến acme-pmic-ldo10
) và trình điều khiển cảm ứng có thể tra cứu bộ điều chỉnh bằng regulator_get(dev, "acme-pmic-ldo10")
.
Tuy nhiên, trên một bảng khác, LDO8 có thể cung cấp thiết bị cảm ứng, tạo ra một hệ thống phức tạp, trong đó cùng một trình điều khiển cảm ứng cần xác định chuỗi tra cứu chính xác cho bộ điều chỉnh của từng bảng mà thiết bị cảm ứng được dùng.
Tình huống hiện tại (hỗ trợ DT trong nhân ARM)
Sau khi thêm chế độ hỗ trợ DT vào các nhân ARM, người tiêu dùng có thể xác định nhà cung cấp trong DT bằng cách tham chiếu đến nút cây thiết bị của nhà cung cấp bằng cách sử dụng phandle.
Người tiêu dùng cũng có thể đặt tên cho tài nguyên dựa trên mục đích sử dụng thay vì nhà cung cấp. Ví dụ: trình điều khiển cảm ứng trong ví dụ trước có thể sử dụng regulator_get(dev, "core")
và regulator_get(dev, "sensor")
để lấy các nhà cung cấp cung cấp năng lượng cho lõi và cảm biến của thiết bị cảm ứng. DT được liên kết cho thiết bị như vậy tương tự như đoạn mã mẫu sau:
touch-device {
compatible = "fizz,touch";
...
core-supply = <&acme_pmic_ldo4>;
sensor-supply = <&acme_pmic_ldo10>;
};
acme-pmic {
compatible = "acme,super-pmic";
...
acme_pmic_ldo4: ldo4 {
...
};
...
acme_pmic_ldo10: ldo10 {
...
};
};
Trường hợp xấu nhất
Một số trình điều khiển được chuyển từ các nhân cũ hơn có hành vi cũ trong DT, lấy phần tệ nhất của lược đồ cũ và buộc phần đó vào lược đồ mới hơn nhằm mục đích giúp mọi thứ trở nên dễ dàng hơn. Trong các trình điều khiển như vậy, trình điều khiển người dùng sẽ đọc chuỗi để dùng cho việc tra cứu bằng cách sử dụng một thuộc tính DT dành riêng cho thiết bị, nhà cung cấp sẽ dùng một thuộc tính khác dành riêng cho nhà cung cấp để xác định tên sẽ dùng để đăng ký tài nguyên của nhà cung cấp, sau đó người dùng và nhà cung cấp sẽ tiếp tục dùng cùng một lược đồ cũ là dùng các chuỗi để tra cứu nhà cung cấp. Trong trường hợp xấu nhất này:
Trình điều khiển cảm ứng sử dụng mã tương tự như mã sau:
str = of_property_read(np, "fizz,core-regulator"); core_reg = regulator_get(dev, str); str = of_property_read(np, "fizz,sensor-regulator"); sensor_reg = regulator_get(dev, str);
DT sử dụng mã tương tự như sau:
touch-device { compatible = "fizz,touch"; ... fizz,core-regulator = "acme-pmic-ldo4"; fizz,sensor-regulator = "acme-pmic-ldo4"; }; acme-pmic { compatible = "acme,super-pmic"; ... ldo4 { regulator-name = "acme-pmic-ldo4" ... }; ... acme_pmic_ldo10: ldo10 { ... regulator-name = "acme-pmic-ldo10" }; };
Không sửa đổi các lỗi API khung
Các API khung, chẳng hạn như regulator
, clocks
, irq
, gpio
, phys
và extcon
, trả về -EPROBE_DEFER
làm giá trị trả về lỗi để cho biết rằng một thiết bị đang cố gắng dò nhưng không thể vào lúc này và nhân nên thử lại việc dò sau. Để đảm bảo chức năng .probe()
của thiết bị gặp lỗi như dự kiến trong những trường hợp như vậy, đừng thay thế hoặc ánh xạ lại giá trị lỗi.
Việc thay thế hoặc ánh xạ lại giá trị lỗi có thể khiến -EPROBE_DEFER
bị loại bỏ và dẫn đến việc thiết bị của bạn không bao giờ được kiểm tra.
Sử dụng các biến thể API devm_*()
Khi thiết bị nhận được một tài nguyên bằng cách sử dụng API devm_*()
, tài nguyên đó sẽ tự động được nhân giải phóng nếu thiết bị không thăm dò được hoặc thăm dò thành công và sau đó được huỷ liên kết. Khả năng này giúp mã xử lý lỗi trong hàm probe()
trở nên gọn gàng hơn vì không yêu cầu goto
nhảy để giải phóng các tài nguyên mà devm_*()
thu được và đơn giản hoá các thao tác huỷ liên kết trình điều khiển.
Xử lý việc huỷ liên kết trình điều khiển thiết bị
Hãy cố ý huỷ liên kết trình điều khiển thiết bị và không để quá trình huỷ liên kết không xác định vì không xác định không có nghĩa là không được phép. Bạn phải triển khai đầy đủ việc huỷ liên kết trình điều khiển thiết bị hoặc tắt rõ ràng việc huỷ liên kết trình điều khiển thiết bị.
Triển khai việc huỷ liên kết trình điều khiển thiết bị
Khi chọn triển khai hoàn toàn việc huỷ liên kết trình điều khiển thiết bị, hãy huỷ liên kết trình điều khiển thiết bị một cách rõ ràng để tránh rò rỉ bộ nhớ hoặc tài nguyên và các vấn đề về bảo mật. Bạn có thể liên kết một thiết bị với một trình điều khiển bằng cách gọi hàm probe()
của trình điều khiển và huỷ liên kết một thiết bị bằng cách gọi hàm remove()
của trình điều khiển. Nếu không có hàm remove()
, thì nhân vẫn có thể huỷ liên kết thiết bị; lõi trình điều khiển giả định rằng trình điều khiển không cần thực hiện công việc dọn dẹp khi huỷ liên kết với thiết bị. Trình điều khiển không liên kết với thiết bị không cần thực hiện bất kỳ công việc dọn dẹp rõ ràng nào khi cả hai điều kiện sau đều đúng:
Tất cả tài nguyên mà hàm
probe()
của trình điều khiển thu được đều thông qua APIdevm_*()
.Thiết bị phần cứng không cần trình tự tắt hoặc tạm dừng.
Trong trường hợp này, lõi trình điều khiển sẽ xử lý việc giải phóng tất cả các tài nguyên thu được thông qua API devm_*()
. Nếu một trong hai câu lệnh trước đó không đúng, thì trình điều khiển cần thực hiện dọn dẹp (giải phóng tài nguyên và tắt hoặc tạm dừng phần cứng) khi huỷ liên kết với một thiết bị. Để đảm bảo rằng một thiết bị có thể huỷ liên kết một mô-đun trình điều khiển một cách rõ ràng, hãy sử dụng một trong các lựa chọn sau:
Nếu phần cứng không cần trình tự tắt hoặc tạm dừng, hãy thay đổi mô-đun thiết bị để nhận tài nguyên bằng cách sử dụng API
devm_*()
.Triển khai thao tác trình điều khiển
remove()
trong cùng một cấu trúc với hàmprobe()
, sau đó thực hiện các bước dọn dẹp bằng hàmremove()
.
Tắt rõ ràng tính năng huỷ liên kết trình điều khiển thiết bị (không nên làm)
Khi chọn tắt rõ ràng việc huỷ liên kết trình điều khiển thiết bị, bạn cần không cho phép huỷ liên kết và không cho phép huỷ tải mô-đun.
Để không cho phép huỷ liên kết, hãy đặt cờ
suppress_bind_attrs
thànhtrue
trongstruct device_driver
của trình điều khiển; chế độ cài đặt này ngăn các tệpbind
vàunbind
xuất hiện trong thư mụcsysfs
của trình điều khiển. Tệpunbind
là tệp cho phép không gian người dùng kích hoạt quá trình huỷ liên kết trình điều khiển khỏi thiết bị.Để không cho phép huỷ tải mô-đun, hãy đảm bảo mô-đun có
[permanent]
tronglsmod
. Nếu không sử dụngmodule_exit()
hoặcmodule_XXX_driver()
, mô-đun sẽ được đánh dấu là[permanent]
.
Không tải chương trình cơ sở từ bên trong hàm kiểm tra
Trình điều khiển không được tải chương trình cơ sở từ bên trong hàm .probe()
vì có thể trình điều khiển không truy cập được vào chương trình cơ sở nếu trình điều khiển thăm dò trước khi hệ thống tệp dựa trên bộ nhớ flash hoặc bộ nhớ vĩnh viễn được gắn kết. Trong những trường hợp như vậy, API request_firmware*()
có thể chặn trong một thời gian dài rồi gặp lỗi. Điều này có thể làm chậm quá trình khởi động một cách không cần thiết. Thay vào đó, hãy hoãn việc tải chương trình cơ sở cho đến khi một ứng dụng bắt đầu sử dụng thiết bị. Ví dụ: trình điều khiển màn hình có thể tải chương trình cơ sở khi thiết bị hiển thị được mở.
Trong một số trường hợp, bạn có thể sử dụng .probe()
để tải chương trình cơ sở, chẳng hạn như trong trình điều khiển đồng hồ cần chương trình cơ sở để hoạt động nhưng thiết bị không được hiển thị cho không gian người dùng. Bạn có thể sử dụng trong các trường hợp thích hợp khác.
Triển khai tính năng kiểm tra không đồng bộ
Hỗ trợ và sử dụng tính năng kiểm tra không đồng bộ để tận dụng các điểm cải tiến trong tương lai, chẳng hạn như tải mô-đun song song hoặc kiểm tra thiết bị để tăng tốc thời gian khởi động, có thể được thêm vào Android trong các bản phát hành sau này. Các mô-đun trình điều khiển không sử dụng tính năng thăm dò không đồng bộ có thể làm giảm hiệu quả của các hoạt động tối ưu hoá như vậy.
Để đánh dấu một trình điều khiển là hỗ trợ và ưu tiên việc thăm dò không đồng bộ, hãy đặt trường probe_type
trong thành phần struct device_driver
của trình điều khiển. Ví dụ sau đây cho thấy tính năng hỗ trợ như vậy được bật cho một trình điều khiển nền tảng:
static struct platform_driver acme_driver = {
.probe = acme_probe,
...
.driver = {
.name = "acme",
...
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
Để trình điều khiển hoạt động với tính năng thăm dò không đồng bộ, bạn không cần mã đặc biệt. Tuy nhiên, hãy lưu ý những điều sau đây khi thêm tính năng hỗ trợ thăm dò không đồng bộ.
Đừng giả định về các phần phụ thuộc đã được kiểm tra trước đó. Kiểm tra trực tiếp hoặc gián tiếp (hầu hết các lệnh gọi khung) và trả về
-EPROBE_DEFER
nếu một hoặc nhiều nhà cung cấp chưa sẵn sàng.Nếu bạn thêm thiết bị của trẻ vào chức năng dò tìm của thiết bị cha mẹ, đừng cho rằng thiết bị của trẻ sẽ được dò tìm ngay lập tức.
Nếu một lệnh kiểm tra không thành công, hãy thực hiện quy trình xử lý lỗi và dọn dẹp thích hợp (xem phần Sử dụng các biến thể API devm_*()).
Không sử dụng MODULE_SOFTDEP để sắp xếp các lệnh dò tìm thiết bị
Hàm MODULE_SOFTDEP()
không phải là một giải pháp đáng tin cậy để đảm bảo thứ tự của các lệnh kiểm tra thiết bị và không được dùng vì những lý do sau.
Thăm dò hoãn lại. Khi một mô-đun tải, quá trình kiểm tra thiết bị có thể bị hoãn lại vì một trong các nhà cung cấp của mô-đun đó chưa sẵn sàng. Điều này có thể dẫn đến sự không khớp giữa thứ tự tải mô-đun và thứ tự kiểm tra thiết bị.
Một trình điều khiển, nhiều thiết bị. Một mô-đun trình điều khiển có thể quản lý một loại thiết bị cụ thể. Nếu hệ thống có nhiều phiên bản của một loại thiết bị và mỗi thiết bị đó có một yêu cầu khác nhau về thứ tự thăm dò, thì bạn không thể tuân thủ các yêu cầu đó bằng cách sử dụng thứ tự tải mô-đun.
Thăm dò không đồng bộ. Các mô-đun trình điều khiển thực hiện hoạt động thăm dò không đồng bộ sẽ không thăm dò thiết bị ngay khi mô-đun được tải. Thay vào đó, một luồng song song sẽ xử lý việc dò tìm thiết bị, điều này có thể dẫn đến sự không khớp giữa thứ tự tải mô-đun và thứ tự dò tìm thiết bị. Ví dụ: khi một mô-đun trình điều khiển chính I2C thực hiện quá trình thăm dò không đồng bộ và một mô-đun trình điều khiển cảm ứng phụ thuộc vào PMIC trên bus I2C, ngay cả khi trình điều khiển cảm ứng và trình điều khiển PMIC tải theo đúng thứ tự, thì quá trình thăm dò của trình điều khiển cảm ứng có thể được thực hiện trước quá trình thăm dò của trình điều khiển PMIC.
Nếu bạn có các mô-đun trình điều khiển sử dụng hàm MODULE_SOFTDEP()
, hãy sửa chúng để chúng không sử dụng hàm đó. Để hỗ trợ bạn, nhóm Android đã chuyển các thay đổi lên nguồn trên để cho phép nhân xử lý các vấn đề về thứ tự mà không cần dùng MODULE_SOFTDEP()
. Cụ thể, bạn có thể dùng fw_devlink
để đảm bảo thứ tự thăm dò và (sau khi tất cả người dùng của một thiết bị đã thăm dò) dùng lệnh gọi lại sync_state()
để thực hiện mọi tác vụ cần thiết.
Sử dụng #if IS_ENABLED() thay vì #ifdef cho cấu hình
Sử dụng #if IS_ENABLED(CONFIG_XXX)
thay vì #ifdef CONFIG_XXX
để đảm bảo mã bên trong khối #if
tiếp tục biên dịch nếu cấu hình thay đổi thành cấu hình có 3 trạng thái trong tương lai. Sau đây là những điểm khác biệt:
#if IS_ENABLED(CONFIG_XXX)
đánh giá thànhtrue
khiCONFIG_XXX
được đặt thành mô-đun (=m
) hoặc tích hợp (=y
).#ifdef CONFIG_XXX
đánh giá thànhtrue
khiCONFIG_XXX
được đặt thành tích hợp sẵn (=y
) , nhưng không đánh giá thànhtrue
khiCONFIG_XXX
được đặt thành mô-đun (=m
). Chỉ sử dụng tham số này khi bạn chắc chắn muốn làm điều tương tự khi cấu hình được đặt thành mô-đun hoặc bị vô hiệu hoá.
Sử dụng đúng macro để biên dịch có điều kiện
Nếu CONFIG_XXX
được đặt thành mô-đun (=m
), hệ thống bản dựng sẽ tự động xác định CONFIG_XXX_MODULE
. Nếu trình điều khiển của bạn được kiểm soát bởi CONFIG_XXX
và bạn muốn kiểm tra xem trình điều khiển của mình có đang được biên dịch dưới dạng một mô-đun hay không, hãy làm theo các nguyên tắc sau:
Trong tệp C (hoặc bất kỳ tệp nguồn nào không phải là tệp tiêu đề) cho trình điều khiển, đừng sử dụng
#ifdef CONFIG_XXX_MODULE
vì tệp này hạn chế không cần thiết và sẽ bị hỏng nếu cấu hình được đổi tên thànhCONFIG_XYZ
. Đối với mọi tệp nguồn không phải tiêu đề được biên dịch thành một mô-đun, hệ thống xây dựng sẽ tự động xác địnhMODULE
cho phạm vi của tệp đó. Do đó, để kiểm tra xem một tệp C (hoặc bất kỳ tệp nguồn không phải tiêu đề nào) có đang được biên dịch như một phần của mô-đun hay không, hãy sử dụng#ifdef MODULE
(không có tiền tốCONFIG_
).Trong các tệp tiêu đề, việc kiểm tra tương tự sẽ khó khăn hơn vì các tệp tiêu đề không được biên dịch trực tiếp thành tệp nhị phân mà được biên dịch như một phần của tệp C (hoặc các tệp nguồn khác). Hãy sử dụng các quy tắc sau cho tệp tiêu đề:
Đối với tệp tiêu đề sử dụng
#ifdef MODULE
, kết quả sẽ thay đổi tuỳ thuộc vào tệp nguồn đang sử dụng tệp tiêu đề đó. Điều này có nghĩa là cùng một tệp tiêu đề trong cùng một bản dựng có thể có các phần mã khác nhau được biên dịch cho các tệp nguồn khác nhau (mô-đun so với tích hợp hoặc bị vô hiệu hoá). Điều này có thể hữu ích khi bạn muốn xác định một macro cần mở rộng theo một cách cho mã tích hợp và mở rộng theo một cách khác cho một mô-đun.Đối với một tệp tiêu đề cần biên dịch trong một đoạn mã khi
CONFIG_XXX
cụ thể được đặt thành mô-đun (bất kể tệp nguồn bao gồm tệp đó có phải là mô-đun hay không), tệp tiêu đề phải sử dụng#ifdef CONFIG_XXX_MODULE
.