it-roy-ru.com

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

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

  1. Что такое IOCTLname__?
  2. Для чего его используют?
  3. Как я могу использовать это?
  4. Почему я не могу определить новую функцию, которая работает так же, как IOCTLname__?
113
flashdisk

ioctl, что означает "управление вводом-выводом", является своего рода системным вызовом для конкретного устройства. В Linux существует всего несколько системных вызовов (300-400), которых недостаточно для выражения всех уникальных функций, которые могут иметь устройства. Таким образом, драйвер может определить ioctl, который позволяет приложению пользовательского пространства отправлять ему заказы. Тем не менее, ioctl не очень гибки и имеют тенденцию становиться немного загроможденными (десятки "магических чисел", которые просто работают ... или нет), а также могут быть небезопасными, поскольку вы передаете буфер в ядро ​​- плохая обработка может привести к поломке все легко.

Альтернативой является интерфейс 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.

85
Inductiveload

Функция 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, направление передачи данных будет от пользовательского приложения к модулю драйвера устройства (пользовательское приложение отправляет имя шрифта "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);

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

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

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

140
anukalp