Регистрация символьных устройств

Предыдущая  Содержание  Следующая V*D*V

Как уже упоминалось, ядро использует структуры типа cdev для внутреннего представления символьных устройств. Перед тем, как ядро вызовет операции устройства, вы должны выделить и зарегистрировать одну или больше этих структур. (* Существует старый механизм, который избегает применения структур cdev (которые мы обсудим в разделе "Старый способ"). Однако, новый код должен использовать новую технику.) Чтобы сделать это, ваш код должен подключить <linux/cdev.h>, где определены структура и связанные с ней вспомогательные функции. Есть два способа создания и инициализации каждой из этих структур. Если вы хотите получить автономную структуру cdev во время выполнения, вы можете сделать это таким кодом:

 

struct cdev *my_cdev = cdev_alloc( );

my_cdev->ops = &my_fops;

 

Однако, скорее всего, вы захотите вставлять свои собственные устройство-зависимые структуры cdev; это то, что делает scull. В этом случае вы должны проинициализировать те структуры, что уже созданы с помощью:

 

void cdev_init(struct cdev *cdev, struct file_operations *fops);

 

В любом случае есть ещё одно поле структуры cdev, которое необходимо проинициализировать. Так же как в структуре file_operations, структура cdev имеет поле owner (владелец), которое должно быть установлено в THIS_MODULE.

 

Последний шаг после создания структуры cdev  - сказать об этом ядру вызовом:

 

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

 

Здесь, dev является структурой cdev, num - первый номер устройства, на который реагирует данное устройство, а count - количество номеров устройств, которые должны быть связаны с устройством. Часто count единица, но бывают ситуации, когда имеет смысл иметь более одного номера устройства, соответствующего определённому устройству. Рассмотрим, например, драйвер ленточного SCSI накопителя, который позволяет пользовательскому пространству выбирать режимы работы (например, плотность) путём назначения нескольких младших номеров для каждого физического устройства.

 

При использовании cdev_add необходимо иметь в виду несколько важных вещей. Первым является то, что этот вызов может потерпеть неудачу. Если он вернул код ошибки, ваше устройство не было добавлено в систему. Однако, это почти всегда удаётся, что вызывает другой момент: как только cdev_add возвращается, устройство становится "живым" и его операции могут быть вызваны ядром. Вы не должны вызывать cdev_add, пока драйвер не готов полностью проводить операции на устройстве.

 

Чтобы удалить символьное устройство из системы, вызовите:

 

void cdev_del(struct cdev *dev);

 

Очевидно, что вы не должны обращаться к структуре cdev после передачи её в cdev_del.

Регистрация устройства в scull

Внутри scull представляет каждое устройство структурой типа scull_dev. Эта структура определена как:

 

struct scull_dev {

    struct scull_qset *data; /* Указатель, установленный на первый квант */

    int quantum;             /* размер текущего кванта */

    int qset;                /* размер текущего массива */

    unsigned long size;      /* количество данных, хранимых здесь */

    unsigned int access_key; /* используется sculluid и scullpriv */

    struct semaphore sem;    /* семафор взаимного исключения */

    struct cdev cdev;        /* структура символьного устройства */

};

 

Мы обсуждаем различные поля этой структуры, когда приходим к ним, но сейчас мы обращаем внимание на cdev, структуру типа cdev, которая является интерфейсами нашего устройства к ядру. Эта структура должна быть проинициализирована и добавлена в систему, как описано выше; код scull, который решает эту задачу:

 

static void scull_setup_cdev(struct scull_dev *dev, int index)

{

    int err, devno = MKDEV(scull_major, scull_minor + index);

 

    cdev_init(&dev->cdev, &scull_fops);

    dev->cdev.owner = THIS_MODULE;

    dev->cdev.ops = &scull_fops;

    err = cdev_add (&dev->cdev, devno, 1);

    /* Терпите неудачу изящно, если это необходимо */

    if (err)

        printk(KERN_NOTICE "Error %d adding scull%d", err, index);

}

 

Поскольку структуры cdev внедрены в структуру scull_dev, чтобы выполнить инициализацию этой структуры должна быть вызвана cdev_init .

Старый способ

Если вы пороетесь во многих драйверных кодах в ядре версии 2.6, то сможете заметить, что довольно много символьных драйверов не используют интерфейс cdev, который мы только что описали. То, что вы видите, является старым кодом, который ещё не был обновлён до интерфейса версии 2.6. Так как этот код работает, это обновление может не произойти в течение длительного времени. Для полноты картины мы описываем старый интерфейс регистрации символьных устройств, но новый код не должен его использовать; этот механизм, вероятно, уйдёт в будущем ядре.

 

Классический способ зарегистрировать символьный драйвер устройства:

 

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

 

Здесь major является запрашиваемым старшим номером, name - это имя драйвера (оно появляется в /proc/devices) и fops является структурой по умолчанию file_operations. Вызов register_chrdev регистрирует младшие номера 0 - 255 для данного старшего и устанавливает для каждого структуру по умолчанию cdev. Драйверы, использующие этот интерфейс, должны быть готовы для обработки вызовов open по всем 256 младшим номерам (независимо от того, соответствуют ли они реальным устройствам или нет) и они не могут использовать старший или младший номера больше 255.

 

Если вы используете register_chrdev, правильная функция удаления устройств(а) из системы:

 

int unregister_chrdev(unsigned int major, const char *name);

 

major и name должны быть такими же, как передаваемые в register_chrdev, или вызов будет ошибочным.

Предыдущая  Содержание  Следующая