Пример кода, который вам нужен, можно найти в drivers/watchdog/softdog.c
(из Linux 2.6.33 на момент написания этой статьи), который иллюстрирует правильные файловые операции, а также как разрешить пользователю заполнять структуру с помощью ioctl ().
На самом деле это отличный, рабочий учебник для тех, кому нужно писать тривиальные драйверы символьных устройств.
Я анализировал интерфейс ioctl softdog, когда отвечал на мой собственный вопрос , который может быть вам полезен.
Вот суть этого (хотя и далеко не исчерпывающего) ...
В softdog_ioctl()
вы видите простую инициализацию struct watchdog_info, которая рекламирует функциональность, версию и информацию об устройстве:
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "Software Watchdog",
};
Затем мы рассмотрим простой случай, когда пользователь просто хочет получить эти возможности:
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
... что, конечно, заполнит соответствующее пользовательское пространство watchdog_info инициализированными значениями выше. Если copy_to_user () завершается ошибкой, возвращается -EFAULT, что приводит к тому, что соответствующий вызов ioctl () из пользовательского пространства возвращает -1 с установленным значащим значением errno.
Обратите внимание, магические запросы на самом деле определены в linux / watchdog.h, так что ядро и пользовательское пространство разделяют их:
#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
WDIOC явно означает "Watchdog ioctl"
Вы можете легко сделать этот шаг дальше, если ваш драйвер что-то сделает, поместит результат этого чего-либо в структуру и скопирует его в пространство пользователя. Например, если struct watchdog_info также имеет член __u32 result_code
. Обратите внимание, __u32
- это просто версия ядра uint32_t
.
С помощью ioctl () пользователь передает адрес объекта, будь то структура, целое число, ядру, ожидающему, что ядро запишет свой ответ в идентичном объекте, и скопирует результаты по указанному адресу.
Второе, что вам нужно сделать, это убедиться, что ваше устройство знает, что делать, когда кто-то открывает, читает из него, пишет в него или использует хук, такой как ioctl (), который вы легко можете увидеть, изучив softdog.
Интерес представляет:
static const struct file_operations softdog_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = softdog_write,
.unlocked_ioctl = softdog_ioctl,
.open = softdog_open,
.release = softdog_release,
};
Где вы видите, что обработчик unlocked_ioctl собирается ... вы уже догадались, softdog_ioctl ().
Я думаю, вы могли бы сопоставить слой сложности, которого на самом деле не существует при работе с ioctl (), это действительно так просто. По этой же причине большинство разработчиков ядра не одобряют добавление новых интерфейсов ioctl, если только они не являются абсолютно необходимыми. Просто слишком легко потерять след того типа, который ioctl () собирается заполнить, по сравнению с магией, которую вы используете, чтобы сделать это, что является основной причиной, по которой copy_to_user () часто дает сбой, приводящий к гниению ядра с застрявшими в нем ордами процессов пользовательского пространства диск спит.
Для таймера, я согласен, ioctl () - кратчайший путь к здравомыслию.