Ответы:
An ioctl
, что означает «управление вводом-выводом», является разновидностью системного вызова для конкретного устройства. В Linux всего несколько системных вызовов (300-400), которых недостаточно, чтобы выразить все уникальные функции, которые могут иметь устройства. Таким образом, драйвер может определить ioctl, который позволяет приложению пользовательского пространства отправлять ему заказы. Однако ioctls не очень гибкие и имеют тенденцию быть немного загроможденными (десятки «магических чисел», которые просто работают ... или нет), а также могут быть небезопасными, поскольку вы передаете буфер в ядро - плохая обработка может нарушить вещи легко.
Альтернативой является sysfs
интерфейс, в котором вы настраиваете файл /sys/
и читаете / записываете его для получения информации от драйвера и для него. Пример того, как это настроить:
static ssize_t mydrvr_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", DRIVER_RELEASE);
}
static DEVICE_ATTR(version, S_IRUGO, mydrvr_version_show, NULL);
И во время установки драйвера:
device_create_file(dev, &dev_attr_version);
Тогда у вас будет файл для вашего устройства /sys/
, например, /sys/block/myblk/version
для блочного драйвера.
Другой способ более интенсивного использования - это netlink, который представляет собой метод IPC (межпроцессного взаимодействия) для связи с вашим драйвером через интерфейс сокета BSD. Это используется, например, драйверами WiFi. Затем вы общаетесь с ним из пользовательского пространства, используя библиотеки libnl
или libnl3
.
ioctl
Функция полезна для реализации драйвера устройства для установки конфигурации на устройстве. например, принтер, который имеет параметры конфигурации для проверки и установки семейства шрифтов, размера шрифта и т. д., ioctl
может использоваться для получения текущего шрифта, а также для установки нового шрифта. Пользовательское приложение использует ioctl
для отправки на принтер кода, сообщающего ему вернуть текущий шрифт или установить новый шрифт.
int ioctl(int fd, int request, ...)
fd
- дескриптор файла, возвращаемый функцией open
;request
это код запроса. например GETFONT
, получит текущий шрифт с принтера, SETFONT
установит шрифт на принтере;void *
. В зависимости от второго аргумента третий может присутствовать или отсутствовать, например, если второй аргумент есть SETFONT
, третьим аргументом может быть имя шрифта, например "Arial"
;int request
это не просто макрос. Требуется пользовательское приложение для генерации кода запроса и модуль драйвера устройства, чтобы определить, с какой конфигурацией на устройстве нужно играть. Приложение отправляет код запроса, используя, ioctl
а затем использует код запроса в модуле драйвера устройства, чтобы определить, какое действие выполнить.
Код запроса состоит из 4 основных частей.
1. A Magic number - 8 bits
2. A sequence number - 8 bits
3. Argument type (typically 14 bits), if any.
4. Direction of data transfer (2 bits).
Если код запроса предназначен SETFONT
для установки шрифта на принтере, направление передачи данных будет от пользовательского приложения к модулю драйвера устройства (пользовательское приложение отправляет имя шрифта "Arial"
на принтер). Если код запроса - это GETFONT
направление от принтера к пользовательскому приложению.
Для генерации кода запроса Linux предоставляет несколько предопределенных макросов, подобных функциям.
1. _IO(MAGIC, SEQ_NO)
оба 8 бит, от 0 до 255, например, допустим, мы хотим приостановить работу принтера. Это не требует передачи данных. Итак, мы сгенерируем код запроса, как показано ниже
#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)
и теперь используйте ioctl
как
ret_val = ioctl(fd, PAUSE_PRIN);
Соответствующий системный вызов в модуле драйвера получит код и приостановит работу принтера.
__IOW(MAGIC, SEQ_NO, TYPE)
MAGIC
и SEQ_NO
такие же, как указано выше, и TYPE
задает тип следующего аргумента, вспомните третий аргумент ioctl
is void *
. W __IOW
указывает, что поток данных идет от пользовательского приложения к модулю драйвера. В качестве примера предположим, что мы хотим установить для шрифта принтера значение "Arial"
.#define PRIN_MAGIC 'S'
#define SEQ_NO 1
#define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)
дальше,
char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font);
Теперь font
это указатель, что означает, что это адрес, лучше всего представленный как unsigned long
, следовательно, третья часть _IOW
упоминает тип как таковой. Кроме того, этот адрес шрифта передается соответствующему системному вызову, реализованному в модуле драйвера устройства, unsigned long
и нам нужно привести его к правильному типу перед его использованием. Пространство ядра может получить доступ к пространству пользователя, и, следовательно, это работает. другие два функционально-подобных макроса - это __IOR(MAGIC, SEQ_NO, TYPE)
и, __IORW(MAGIC, SEQ_NO, TYPE)
где поток данных будет из пространства ядра в пространство пользователя и в обоих направлениях соответственно.
Пожалуйста, дайте мне знать, если это поможет!