31. Kernel UIO API — ядровой интерфейс

31.1. Введение

UIO API — это минималистичный и понятный интерфейс ядра Linux, позволяющий разработчику создать простой драйвер устройства с пользовательской логикой в пространстве пользователя. В ядре нужно только описать устройство, его память и (опционально) обработку прерываний — всё остальное можно реализовать в user space.

—

31.2. Основная структура: struct uio_info

UIO-ядро работает через структуру struct uio_info, в которую вы записываете параметры вашего устройства.

Обязательные поля:

  • const char *name Название драйвера. Отображается в /sys/class/uio/uioX/name. Лучше использовать имя модуля.

  • const char *version Версия драйвера. Показывается в /sys/class/uio/uioX/version.

  • struct uio_mem mem[MAX_UIO_MAPS] Массив отображаемых в user space областей памяти. Каждый элемент описывает одну mmap-область.

  • long irq Номер IRQ. Может быть:

    • Конкретный номер (например, 42)

    • UIO_IRQ_CUSTOM — если вы хотите вручную вызывать прерывание

    • UIO_IRQ_NONE — если прерывания не используются (редко)

  • unsigned long irq_flags Флаги для request_irq(), если используется аппаратное прерывание (например, IRQF_SHARED)

—

31.3. Дополнительные (опциональные) поля

  • int (*mmap)(struct uio_info *info, struct vm_area_struct *vma) Своё поведение при mmap(). Если не задано — используется стандартное поведение.

  • int (*open)(struct uio_info *info, struct inode *inode) Вызывается при open() на /dev/uioX. Полезно, например, чтобы включать прерывания при использовании.

  • int (*release)(struct uio_info *info, struct inode *inode) Парный close() — для освобождения ресурсов.

  • int (*irqcontrol)(struct uio_info *info, s32 irq_on) Позволяет управлять прерываниями из user space: запись в /dev/uioX включает/выключает IRQ.

—

31.4. Память: struct uio_mem

Каждую область памяти, доступную через mmap, нужно описать через struct uio_mem.

Основные поля:

  • int memtype Тип области памяти:

    • UIO_MEM_PHYS — физическая память, доступная пользователю (через mmap)

    • UIO_MEM_LOGICAL — логическая память, выделенная через kmalloc

    • UIO_MEM_VIRTUAL — виртуальная память (редко)

  • unsigned long size Размер области. Если 0 — mapping не используется (нужно обнулять все неиспользуемые элементы mem[])

  • phys_addr_t addr Адрес физической памяти. Только для UIO_MEM_PHYS.

  • void *internal_addr Адрес внутри ядра, например, из ioremap(). Хранится отдельно от addr, потому что ioremap() не может быть mmap-нут в userspace.

—

31.5. Регистрация UIO-устройства

После инициализации struct uio_info, модуль должен зарегистрировать устройство:

ret = uio_register_device(&pdev->dev, &info);

Это:

  • создаёт /dev/uioX

  • создаёт все соответствующие sysfs-файлы в /sys/class/uio/uioX/

  • начинает работу UIO-драйвера

—

31.6. Удаление UIO

При выгрузке модуля нужно обязательно вызвать:

uio_unregister_device(&info);

Это удалит /dev/uioX и все связанные sysfs-файлы.

—

31.7. Минимальный пример

static struct uio_info my_uio_info = {
    .name = "my_uio",
    .version = "1.0",
    .irq = 42,
    .irq_flags = IRQF_SHARED,
    .mem[0] = {
        .memtype = UIO_MEM_PHYS,
        .addr = 0x3F200000,
        .size = 0x1000,
    },
};

static int my_probe(struct platform_device *pdev)
{
    return uio_register_device(&pdev->dev, &my_uio_info);
}

—

31.8. Полезные определения

#define MAX_UIO_MAPS 5
#define UIO_IRQ_NONE    (-1)
#define UIO_IRQ_CUSTOM  (-2)

—

31.9. Заключение

UIO API позволяет легко интегрировать простые устройства в Linux, используя ядро только как «мост», а всю основную логику реализуя в user space. Это снижает сложность, облегчает отладку и идеально подходит для экспериментов, FPGA, custom boards и embedded-разработки.

Если вам не нужна сложная интеграция с подсистемами ядра — UIO может быть лучшим выбором.