5.14. MMIO (Memory-Mapped I/O) Device Access ============================================ Введение -------- MMIO (Memory-Mapped I/O) — это механизм доступа к аппаратным устройствам через адресное пространство памяти. Он позволяет обращаться к регистрам периферийных устройств (GPIO, UART, SPI и др.) **точно так же, как к обычным переменным**, если известен базовый физический адрес устройства и структура его регистров. На Raspberry Pi это особенно актуально при работе с низкоуровневыми устройствами, не обёрнутыми в готовые драйверы. --- Как это работает ---------------- 1. Аппаратные регистры устройств отображаются в память через физические адреса (например, `0x3F200000` для GPIO). 2. Драйвер резервирует и отображает (remap) эти адреса в виртуальное пространство ядра с помощью `ioremap()`. 3. Доступ к значениям регистров производится через функции: - `readl()` / `writel()` — 32-битный доступ - `readb()` / `writeb()` — байтовый доступ - `readw()` / `writew()` — 16-битный доступ 4. По завершении работы память освобождается функцией `iounmap()`. --- Пример использования MMIO -------------------------- Простой пример драйвера, который включает/выключает GPIO21 напрямую через регистры: .. code-block:: c #define GPIO_BASE_PHYS 0x3F200000 #define GPIO_LEN 0xB4 static void __iomem *gpio_base; static int __init mmio_example_init(void) { gpio_base = ioremap(GPIO_BASE_PHYS, GPIO_LEN); if (!gpio_base) { pr_err("Failed to map GPIO MMIO region\n"); return -ENOMEM; } // Настройка GPIO21 как выход (GPFSEL2) u32 val = readl(gpio_base + 0x08); val &= ~(7 << 3); // очистить биты FSEL21 val |= (1 << 3); // установить как output (001) writel(val, gpio_base + 0x08); // Включаем GPIO21 (GPSET0) writel(1 << 21, gpio_base + 0x1C); return 0; } static void __exit mmio_example_exit(void) { // Выключаем GPIO21 (GPCLR0) writel(1 << 21, gpio_base + 0x28); iounmap(gpio_base); } module_init(mmio_example_init); module_exit(mmio_example_exit); --- Важно ----- - `ioremap()` возвращает **виртуальный адрес**, через который производится доступ - **Нельзя** просто использовать `(volatile uint32_t *)0x3F200000` — это нарушает принципы безопасности ядра - Всегда вызывайте `iounmap()` при выгрузке модуля - Не забывайте про `MODULE_LICENSE()` и права доступа --- Когда использовать MMIO ------------------------ MMIO нужен, когда: - Нет стандартного драйвера для устройства - Требуется максимальная производительность - Вы реализуете **прототип** низкоуровневой периферии (например, на FPGA или нестандартной плате) - Нужно изучить поведение контроллера "вживую" --- Вывод ----- MMIO — это мощный и универсальный способ взаимодействия с железом на Linux. Но он требует **точного знания адресов**, регистров и правильной работы с памятью ядра. Мы будем использовать MMIO при разработке собственного драйвера для RGB LED и других устройств, где важно получить прямой доступ к битам конфигурации GPIO.