Драйвер устройства IOCTL Linux

Может ли кто-нибудь объяснить мне,

  • Что такое IOCTL?
  • Для чего он используется?
  • Как я могу использовать его?
  • Почему я не могу определить новую функцию, которая выполняет ту же работу, что и IOCTL?

Ответ 1

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.

Ответ 2

Функция ioctl полезна для реализации драйвера устройства для настройки конфигурации устройства. например, принтер, который имеет параметры конфигурации для проверки и установки семейства шрифтов, размера шрифта и т.д. ioctl может использоваться для получения текущего шрифта, а также для установки шрифта на новый. Пользовательское приложение использует ioctl для отправки кода на принтер с указанием вернуть текущий шрифт или установить новый шрифт.

int ioctl(int fd, int request, ...)
  1. fd - файловый дескриптор, возвращаемый open;
  2. request код запроса. например, GETFONT получит текущий шрифт от принтера, SETFONT установит шрифт на принтере;
  3. третий аргумент является 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 установлено значение SETFONT для установки шрифта на принтере, направление передачи данных будет от пользовательского приложения к модулю драйвера устройства (пользовательское приложение отправляет имя шрифта "Arial" на принтер). Если код запроса GETFONT, направление от принтера к пользовательскому приложению.

Чтобы сгенерировать код запроса, Linux предоставляет некоторые предопределенные макросы, подобные функциям.

1. _IO(MAGIC, SEQ_NO) оба 8- _IO(MAGIC, SEQ_NO), от 0 до 255, например, допустим, мы хотим поставить принтер на паузу. Это не требует передачи данных. Поэтому мы сгенерируем код запроса, как показано ниже

#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM) 

и теперь используйте ioctl как

ret_val = ioctl(fd, PAUSE_PRIN);

Соответствующий системный вызов в модуле драйвера получит код и приостановит работу принтера.

  1. __IOW(MAGIC, SEQ_NO, TYPE) MAGIC и SEQ_NO такие же, как указано выше, а TYPE дает тип следующего аргумента, напомним, что третий аргумент ioctl void *. W in __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) где поток данных будет __IORW(MAGIC, SEQ_NO, TYPE) из пространства ядра в пространство пользователя и в обе стороны соответственно.

Пожалуйста, дайте мне знать, если это поможет!