Получение ENOTTY на ioctl для модуля ядра Linux

Я определил следующий chardev:

.час

#define MAJOR_NUM 245
#define MINOR_NUM 0
#define IOCTL_MY_DEV1 _IOW(MAJOR_NUM, 0, unsigned long)
#define IOCTL_MY_DEV2 _IOW(MAJOR_NUM, 1, unsigned long)
#define IOCTL_MY_DEV3 _IOW(MAJOR_NUM, 2, unsigned long)

модуль.c

static long device_ioctl(
                  struct file*   file,
                  unsigned int   ioctl_num,
                  unsigned long  ioctl_param)
{
    ...
}

static int device_open(struct inode* inode, struct file* file)
{
    ...
}

static int device_release(struct inode* inode, struct file* file)
{
    ...
}

struct file_operations Fops = {
    .open=device_open,
    .unlocked_ioctl= device_ioctl,
    .release=device_release
};

static int __init my_dev_init(void)
{
    register_chrdev(MAJOR_NUM, "MY_DEV", &Fops);
    ...
}
module_init(my_dev_init);

Мой код пользователя

ioctl(fd, IOCTL_MY_DEV1, 1);

Всегда терпит неудачу с той же ошибкой: ENOTTY

Неподходящий ioctl для устройства

Я видел похожие вопросы: т.е.

Модуль ядра Linux - использование IOCTL возвращает ENOTTY

Модуль ядра Linux /IOCTL: неподходящий ioctl для устройства

Но их решения не сработали для меня

1 ответ

ENOTTY выдается ядром, когда драйвер вашего устройства не зарегистрировал функцию ioctl для вызова. Боюсь, ваша функция не зарегистрирована должным образом, вероятно, потому что вы зарегистрировали ее в .unlocked_ioctl поле struct file_operations состав.

Возможно, вы получите другой результат, если зарегистрируете его в заблокированной версии функции. Наиболее вероятная причина заключается в том, что inode заблокирован для вызова ioctl (как и должно быть, чтобы избежать условий гонки с одновременным read или же write операции на одном устройстве)

Извините, у меня нет доступа к исходному дереву linux для правильного имени используемого поля, но наверняка вы сможете найти его самостоятельно.

НОТА

Я наблюдаю, что вы использовали макрос _IOW, используя главный номер в качестве уникального идентификатора. Это, вероятно, не то, что вы хотите. Первый параметр для _IOW пытается убедиться, что вызовы ioctl получают уникальные идентификаторы. Нет общего способа получить такие идентификаторы, так как это интерфейсный контракт, который вы создаете между кодом приложения и кодом ядра. Поэтому использование старшего номера является плохой практикой по двум причинам:

  • Несколько устройств (по крайней мере, в linux) могут использовать один и тот же старший номер (это позволяет незначительное распределение в ядре linux), что делает возможным конфликт между ioctl устройств.
  • В случае, если вы меняете основной номер (вы настраиваете ядро, в котором этот номер уже выделен), вам придется перекомпилировать все программное обеспечение вашего уровня пользователя, чтобы справиться с новыми ioctl-идентификаторами устройств (все они изменятся, если вы сделаете это)

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

Древние ядра Unix (и ранние Linux) использовали разные символы для построения этих вызовов, так, например, tty драйвер используется 'T' в качестве параметра для _IO* макросы, scsi диски используются 'S', так далее.

Я предлагаю вам выбрать случайное число (не встречающееся в других местах в списках ядра linux), а затем использовать его на всех ваших устройствах (вероятно, будет меньше драйверов, которые вы пишете, чем драйверов в ядре) и выбрать разные идентификаторы ioctl для каждого ioctl вызов. Поддерживать локальный файл ioctl с зарегистрированными ioctl таким способом гораздо лучше, чем пытаться угадать значение, которое работает всегда.

Кроме того, посмотрите на определение _IO* макросы должны быть очень наглядными :)

Другие вопросы по тегам