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— логическая память, выделенная через kmallocUIO_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 может быть лучшим выбором.