ACPI: Добавление устройств без перекомпиляции ядра

habr.png

Как выясняется, далеко немногие знают о существовании режима оверлеев в ACPICA и их поддержки в ОС Linux. Я хочу восполнить этот пробел на примере добавления ведомых устройств I2C в систему без перекомпиляции.

Начальные условия


Допустим, при запуске

i2cdetect -y -r 0

у нас выдаётся такая картина:

Вывод i2cdetect
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- 53 -- -- -- 57 -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         

где по адресу 0×53 обнаруживается акселерометер ADXL345, а по адресу 0×57 — микросхема EEPROM памяти 24c128. Описания этих устройств отсутствуют в ACPI, а именно в таблице DSDT.

Добавляем акселерометер ADXL345


Всё, что нам необходимо знать здесь — это адрес, по которому отзывается устройство, его ID, поддерживаемые драйвером, частота шины, на которой он должен работать. Обратите внимание, что частота I2C шины со стороны драйвера часто устанавливается в ту минимальную, которая поддерживается всеми ведомыми устройствами на данной шине!

Ах, да, было время, когда подсистемы IIO не существовало, а драйвер ADXL345 уже был. Так вот, мы используем новый, который доступен через подсистему IIO.

Итого,

  • Адрес: 0×53
  • Частота шины: 400кГц
  • Ссылка на ведущее (контроллер) устройство: \_SB.PCI0.I2C1
  • Идентификатор: adi, adxl345

Следует обратить внимание, что мы используем здесь специальный идентификатор, который предназначен для систем с OF. В качестве прослойки в ACPI был добавлен специальный идентификатор PRP0001, который и обеспечивает совместимость с драйверами, написанными ранее для OF.

Переводим полученную информацию на язык ASL:

ASL код для акселерометра ADXL345
DefinitionBlock ("adxl345.aml", "SSDT", 5, "", "ADXL345", 1)
{
    External (_SB_.PCI0.I2C1, DeviceObj)

    Scope (\_SB.PCI0.I2C1)
    {
        Device (ACL0) {
            Name (_HID, "PRP0001")
            Name (_DDN, "Analog Devices ADXL345 3-axis accelerometer")
            Name (_CRS, ResourceTemplate () {
                I2cSerialBusV2 (
                    0x0053,              // I2C Slave Address
                    ControllerInitiated,
                    400000,              // Bus speed
                    AddressingMode7Bit,
                    "\\_SB.PCI0.I2C1",   // Link to ACPI I2C host controller
                    0
                )
            })

            Name (_DSD, Package () {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package () {
                    Package () { "compatible", "adi,adxl345" },
                }
            })
        }
    }
}

Добавляем EEPROM 24c128


Так же, как и в предыдущем случае получаем необходимую информацию про устройство и его драйвер:

  • Адрес: 0×57
  • Частота шины: 400кГц
  • Ссылка на ведущее (контроллер) устройство: \_SB.PCI0.I2C1
  • Идентификатор: INT3499
  • Объём: 1024
  • Размер страницы: 32
ASL код для EEPROM 24c128
DefinitionBlock ("at24.aml", "SSDT", 5, "", "AT24", 1)
{
    External (_SB_.PCI0.I2C1, DeviceObj)

    Scope (\_SB.PCI0.I2C1)
    {
        Device (EEP0) {
            Name (_HID, "INT3499")
            Name (_DDN, "Atmel AT24 compatible EEPROM")
            Name (_CRS, ResourceTemplate () {
                I2cSerialBusV2 (
                    0x0057,              // I2C Slave Address
                    ControllerInitiated,
                    400000,              // Bus speed
                    AddressingMode7Bit,
                    "\\_SB.PCI0.I2C1",   // Link to ACPI I2C host controller
                    0
                )
            })

            Name (_DSD, Package () {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package () {
                    Package () { "size", 1024 },
                    Package () { "pagesize", 32 },
                }
            })
        }
    }
}

Заметьте разницу с предыдущим вариантом. Здесь используется напрямую ACPI ID, который выделен в пространстве, контроллируемом Intel, спасибо платформе Intel Galileo. Второе отличие, мы передаём дополнительные параметры устройства в виде строк ключ-значение.

Возможные варианты инициализации


Что теперь с этим всем делать? Алгоритм прост. Во-первых, необходимо откомпилировать полученные файлы в ASL байт-код. Достигается с помощью вызова команды

iasl adxl345.asl

и по аналогии для EEPROM. Во-вторых, выбрать способ инициализации новоиспечённой таблицы. Их собственно три: 1) присоединение к initramfs, 2) загрузка на рабочей системе через ConfigFS, 3) загрузка таблицы из переменной EFI. Рассмотрим первые два из них ниже.Присоединение к initramfs
Ничего с самим архивом initramfs мы делать не будем, однако рекомендуется сохранить оригинал где-нибудь в сторонке.


# Добавляем ACPI таблицы в некомпрессированный cpio архив.
# Они обязаны находится в подкаталоге /kernel/firmware/acpi внутри архива.
# Некомпрессированные архивы должны идти первыми в цепочке.
mkdir -p kernel/firmware/acpi
cp adxl345.aml kernel/firmware/acpi
cp at24.aml kernel/firmware/acpi

# Создаём архив и цепочкой к нему присоединяем оригинальный initramfs:
find kernel | cpio -H newc --create > /boot/instrumented_initramfs-vX.Y
cat /boot/initramfs-vX.Y >> /boot/instrumented_initramfs-vX.Y


После этой процедуры старый архив можно заменить новым и перезагрузить компьютер.
Должно появиться в выводе dmesg что-то типа:

[    0.000000] ACPI: Table Upgrade: install [SSDT-      - ADXL345]
[    0.000000] ACPI: SSDT 0x000000003F4FF5C4 0000A6 (v05       ADXL345   00000001 INTL 20170303)

Учтите, что ядро поддерживает только цепочку до 64 таких архивов.

Загрузка через ConfigFS
Такая возможность доступна, когда ядро собрано с опцией CONFIG_ACPI_CONFIGFS и ConfigFS примонтирована. Если предположить, что она примонтирована в подкаталог /sys/kernel/config, то нижеследующий пример показывает как загрузить таблицу.


cd /sys/kernel/config/acpi/table
mkdir adxl345
cat ~/adxl354.aml > adxl345/aml

Заключение


Хотя язык ASL требует больше скобочек, чем аналоги, тем не менее он предоставляет не меньшие возможности для описания устройств. Так, существует некоторое количество примеров в проекте meta-acpi, где в частности можно найти описания светодиодов и кнопок, подключенных к GPIO линиям, микросхем памяти, и даже описание модуля Adafruit 2.8» — TFT дисплея с сенсорным экраном!

© Habrahabr.ru