понедельник, 16 мая 2011 г.

14.5. Классы

Классы
Последней концепцией модели устройства, которую мы рассмотрим в этой главе, является класс. Класс является высокоуровневым  представлением устройство, которое резюмирует низкоуровневые детали реализации. Драйверы могут видеть SCSI диск или диск ATA, но на уровне класса они все просто диски. Классы позволяют пользовательскому пространству работать с устройствами, базируясь на том, что они делают, а не как они подключены или как они работают.

Почти все классы отображаются в sysfs в каталоге /sys/class. Так, например, все сетевые интерфейсы, независимо от типа интерфейса, могут быть найдены в /sys/class/net. Устройства ввода могут быть найдены в /sys/class/input, а последовательных устройства находятся в /sys/class/tty. Единственным исключением являются блочные устройства, которые можно найти в каталоге /sys/block по историческим причинам.

Членство в классе обычно обрабатывается высокоуровневым кодом без необходимости явной поддержки со стороны драйверов. Когда драйвер sbull (смотрите Главу 16) создаёт виртуальный диск устройства, он автоматически появляется в /sys/block. Сетевому драйверу snull (смотрите Главу 17) не требуется делать ничего специального для представления его интерфейсов в /sys/class/net. Однако, будут случаи, когда в конечном итоге драйверы будут иметь дело с классами напрямую.

Во многих случаях подсистема классов является наилучшим способом экспорта информации для пользовательского пространстве. Когда подсистема создаёт класс, она принадлежит классу полностью, поэтому нет необходимости беспокоиться, каким модулям принадлежат атрибуты, находящиеся там. Немного времени надо, чтобы побродив в более аппаратно-ориентированных частях sysfs понять, что это может быть недружественным местом для прямого просмотра. Пользователи более успешно находят информацию в /sys/class/some-widget, чем, скажем, в /sys/devices/pci0000:00/0000:00:10.0/usb2/2-0:1.0. Драйверное ядро экспортирует два различных интерфейса для управления классами. Процедуры class_simple разработаны, чтобы сделать добавление новых классов в систему как можно более простым; их основная цель обычно состоит в том, чтобы показать атрибуты, содержащие номера устройства, разрешить автоматическое создание узлов устройств. Формальный интерфейс класса более сложный, но предлагает также и больше функций. Мы начинаем с простой версии.
Интерфейс class_simple
Начиная с версии 2.6.13 интерфейс class_simple в ядрах больше не присутствует. Все упоминания его и его функций в данной книге устарели.

Интерфейс class_simple должен был стать настолько простым в использовании, чтобы никто не имел бы никакого предлога для отказа от экспорта, как минимум, атрибута, содержащего номер, назначенный устройству. Использование этого интерфейса является просто вопросом нескольких вызовов функций с небольшим количеством обычного шаблона, связанного с моделью устройства Linux.

Первым шагом является создание самого класса. Это осуществляется с помощью вызова class_simple_create:

struct class_simple *class_simple_create(struct module *owner, char *name);

Эта функция создаёт класс с заданным name (именем). Конечно, операция может потерпеть неудачу, так что возвращаемое значение всегда должно быть проверено (с использованием IS_ERR, описанного в разделе "Указатели и значения ошибок" в Главе 11), прежде чем продолжить.

Простой класс может быть уничтожен с помощью:

void class_simple_destroy(struct class_simple *cs);

Настоящая цель создания простого класса состоит в добавлении к нему устройства; эта задача решается с помощью:

struct class_device *class_simple_device_add(struct class_simple *cs,
                dev_t devnum,
                struct device *device,
                const char *fmt, ...);

Здесь, cs является ранее созданным простым классом, devnum является присвоенным номером устройства, device является struct device, представляющей данное устройство и остальными параметрами являются строка формата в стиле printk и аргументы для создания имени устройства. Этот вызов добавляет запись к классе, содержащую один атрибут, dev, который содержит номер устройства. Если параметр device не NULL, символическая ссылка (с названием из device) указывает на запись устройства в /sys/devices.

Можно добавить другие атрибуты к записи устройства. Это всего лишь вопрос использования class_device_create_file, которую мы обсудим в следующем разделе вместе с остальной частью подсистемы полного класса.

Классы генерируют события горячего подключения, когда устройства приходят и уходят. Если вашему драйверу необходимо добавить переменные в окружение для обработчика события в пространстве пользователя, можно создать обратный вызов горячего подключения с помощью:

int class_simple_set_hotplug(struct class_simple *cs,
                int (*hotplug)(struct class_device *dev,
                char **envp, int num_envp,
                char *buffer, int buffer_size));

Когда устройство уходит, запись класса должна быть удалена с помощью:

void class_simple_device_remove(dev_t dev);

Обратите внимание, что структура class_device, возвращаемая class_simple_device_add, здесь не требуется; достаточно номера устройства (который должен быть, конечно, уникальным).
Полный интерфейс класса
Для многих потребностей достаточно интерфейса class_simple, но иногда требуется больше гибкости. Следующее обсуждение описывает, как использовать полноценный механизм класса, на котором основан class_simple. Оно является кратким: функции и структуры класса следуют тем же шаблонам, как и остальная модель устройства, поэтому действительно нового здесь мало.
Управление классами
Класс определяется экземпляром struct class:

struct class {
    char *name;
    struct class_attribute *class_attrs;
    struct class_device_attribute *class_dev_attrs;
    int (*hotplug)(struct class_device *dev, char **envp,
                   int num_envp, char *buffer, int buffer_size);
    void (*release)(struct class_device *dev);
    void (*class_release)(struct class *class);
    /* Некоторые поля опущены */
};

Каждому классу требуется уникальное name (имя), которое определяет, как этот класс представляется в /sys/class. После регистрации класса, все атрибуты, перечисленные в (заканчивающемся NULL) массиве, указываемом class_attrs, созданы. Для каждого устройства, добавляемого в класс, существует также набор атрибутов по умолчанию; на них указывает class_dev_attrs. Существует обычная функция горячего подключения для добавления переменных в окружение при генерации события. Есть также два метода release: release вызывается, когда устройство удаляется из класса, а class_release вызывается, когда освобождается сам класс.

Функциями регистрации являются:

int class_register(struct class *cls);
void class_unregister(struct class *cls);

Интерфейс для работы с атрибутами не будет здесь ни для кого сюрпризом:

struct class_attribute {
    struct attribute attr;
    ssize_t (*show)(struct class *cls, char *buf);
    ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};

CLASS_ATTR(name, mode, show, store);

int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);
Устройства класса
Настоящая цель класса - служить контейнером для устройств, которые являются членами этого класса. Член представлен struct class_device:

struct class_device {
    struct kobject kobj;
    struct class *class;
    struct device *dev;
    void *class_data;
    char class_id[BUS_ID_SIZE];
};

Поле class_id содержит название этого устройства, как оно появляется в sysfs. Указатель class должен указывать на класс, содержащий это устройство и dev должен указывать на связанную с устройством структуру. Настройка dev не является обязательной; если он не NULL, он используется для создания символической ссылки из записи класса на соответствующую запись в /sys/devices, что позволяет легко найти запись устройства в пространстве пользователя. Класс может использовать class_data для содержания своего собственного указателя.

Представлены обычные функции регистрации:

int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);

Интерфейс устройств класса позволяет также переименование уже зарегистрированной записи:

int class_device_rename(struct class_device *cd, char *new_name);

Записи устройства класса имеют атрибуты:

struct class_device_attribute {
    struct attribute attr;
    ssize_t (*show)(struct class_device *cls, char *buf);
    ssize_t (*store)(struct class_device *cls, const char *buf, size_t count);
};

CLASS_DEVICE_ATTR(name, mode, show, store);

int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);

Когда регистрируется класс устройств, в поле класса class_dev_attrs создаётся набор атрибутов по умолчанию; для создания дополнительных атрибутов может быть использована class_device_create_file. Атрибуты также могут быть добавлены и к классу устройств, созданных с помощью интерфейса class_simple.
Интерфейсы класса
Подсистема классов имеет дополнительную концепцию, не встречающуюся в других частях модели устройства Linux. Этот механизм назван интерфейсом, но это, пожалуй, лучше думать о нём, как своего рода спусковом механизме, который может быть использован для получения уведомлений, когда устройство входит или покидает класс.

Интерфейс представлен следующим образом:

struct class_interface {
    struct class *class;
    int (*add) (struct class_device *cd);
    void (*remove) (struct class_device *cd);
};

Интерфейсы могут быть зарегистрированными и разрегистрированными с помощью:

int class_interface_register(struct class_interface *intf);
void class_interface_unregister(struct class_interface *intf);

Функционирование интерфейса является простым. Всякий раз, когда устройство класса добавляется в class, указанный в структуре class_interface, вызывается функция интерфейса add. Эта функции может выполнять любые дополнительные настройки, необходимые для этого устройства; эта настройка часто принимает форму добавления дополнительных атрибутов, но возможны и другие применения. Когда устройство удаляется из класса, для выполнения всей необходимой очистки вызывается метод remove.

Для класса могут быть зарегистрированы множество интерфейсов.

Комментариев нет:

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.