Небольшой TTY драйвер

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

Чтобы объяснить, как работает ядро tty, мы создаём небольшой tty драйвер, который может быть загружен, может записать и прочитать данные и быть выгружен. Основной структурой данных любого tty драйвера является struct tty_driver. Она используется для регистрации и отмены регистрации tty драйвера в ядре tty и описана в заголовочном файле ядра <linux/tty_driver.h>.

 

Чтобы создать struct tty_driver, должна быть вызвана функция alloc_tty_driver с количеством поддерживаемых этим драйвером tty устройств в качестве параметра. Это может быть сделано следующим коротким кодом:

 

/* создаём tty драйвер */

tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);

if (!tiny_tty_driver)

    return -ENOMEM;

 

После успешного вызова функции alloc_tty_driver, struct tty_driver должна быть проинициализирована соответствующей информацией на основе потребностей tty драйвера. Эта структура содержит множество разных полей, но не все из них должны быть проинициализированы, чтобы получить работающий tty драйвер. Вот пример, который показывает, как проинициализировать эту структуру, и устанавливает достаточное число полей, чтобы создать работающий tty драйвер. Чтобы помочь скопировать набор назначенных операций, которые определены в драйвере, он использует функцию tty_set_operations:

 

static struct tty_operations serial_ops = {

    .open = tiny_open,

    .close = tiny_close,

    .write = tiny_write,

    .write_room = tiny_write_room,

    .set_termios = tiny_set_termios,

};

 

...

 

    /* инициализация tty драйвера */

    tiny_tty_driver->owner = THIS_MODULE;

    tiny_tty_driver->driver_name = "tiny_tty";

    tiny_tty_driver->name = "ttty";

    tiny_tty_driver->devfs_name = "tts/ttty%d";

    tiny_tty_driver->major = TINY_TTY_MAJOR,

    tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,

    tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,

    tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,

    tiny_tty_driver->init_termios = tty_std_termios;

    tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;

    tty_set_operations(tiny_tty_driver, &serial_ops);

 

Переменные и функции, перечисленные выше, и как использовать эту структуру объяснено в остальной части главы.

 

Чтобы зарегистрировать этот драйвер в ядре tty, struct tty_driver должна быть передана в функцию tty_register_driver:

 

/* регистрируем этот tty драйвер */

retval = tty_register_driver(tiny_tty_driver);

if (retval) {

    printk(KERN_ERR "failed to register tiny tty driver");

    put_tty_driver(tiny_tty_driver);

    return retval;

}

 

При вызове tty_register_driver ядро создаёт в sysfs все различные tty файлы на весь диапазон младших номеров tty устройств, которые может иметь этот tty драйвер. Если вы используете devfs (не описанную в этой книге) и если не указан флаг TTY_DRIVER_NO_DEVFS, также создаются файлы в devfs. Этот флаг может быть указан, если вы хотите вызвать tty_register_device только для тех устройств, которые реально существуют в системе, чтобы пользователь всегда имел достоверный список устройств, присутствующих в ядре, что и ожидают пользователи devfs.

 

После регистрации самого себя, драйвер через функцию tty_register_device регистрирует устройства, которыми он управляет. Эта функция имеет три аргумента:

 

Указатель на struct tty_driver, к которой принадлежит это устройство.

Младший номер этого устройства.

Указатель на struct device, с которой связано это tty устройство. Если это tty устройства не связано ни с какой struct device, этот аргумент может быть установлен NULL.

 

Наш драйвер регистрирует все tty устройства одновременно, поскольку они являются виртуальными и не связаны с какими-либо физическими устройствами:

 

for (i = 0; i < TINY_TTY_MINORS; ++i)

    tty_register_device(tiny_tty_driver, i, NULL);

 

Чтобы отменить регистрацию драйвера в ядре tty, все tty устройства, которые были зарегистрированы вызовом tty_register_device, необходимо освободить вызовом tty_unregister_device. Затем вызовом tty_unregister_driver должна быть отменена регистрация struct tty_driver:

 

for (i = 0; i < TINY_TTY_MINORS; ++i)

    tty_unregister_device(tiny_tty_driver, i);

tty_unregister_driver(tiny_tty_driver);

struct termios

Переменной init_termios в struct tty_driver является struct termios. Эта переменная используется, чтобы обеспечить разумный набор настроек линии, если порт используется до того, как он проинициализирован пользователем. Драйвер инициализирует переменную с помощью стандартного набора значений, которые скопированы из переменной tty_std_termios. tty_std_termios определена в ядре tty следующим образом:

 

struct termios tty_std_termios = {

    .c_iflag = ICRNL | IXON,

    .c_oflag = OPOST | ONLCR,

    .c_cflag = B38400 | CS8 | CREAD | HUPCL,

    .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |

               ECHOCTL | ECHOKE | IEXTEN,

    .c_cc = INIT_C_CC

};

 

Структура struct termios используется для хранения всех текущих настроек линии для данного порта tty устройства. Эти параметры линии управляют текущей скоростью передачи, размером данных, настройками потока данных и многими другими значениями. Полями этой структуры являются:

 

tcflag_t c_iflag;

Флаги режима ввода.

 

tcflag_t c_oflag;

Флаги режима вывода.

 

tcflag_t c_cflag;

Флаги режима управления.

 

tcflag_t c_lflag;

Флаги местного режима.

 

cc_t c_line;

Тип дисциплины линии.

 

cc_t c_cc[NCCS];

Массив управляющих символов.

 

Все флаги режимов определены как большое битовое поле. Различные значения режимов и для чего они используются можно увидеть в руководстве по termios, доступного в любом дистрибутиве Linux. Для получения на различных битов ядро предоставляет набор полезных макросов. Эти макросы определены в заголовочном файле include/linux/tty.h.

 

Все поля, которые были определены в переменной tiny_tty_driver, необходимы для получения рабочего tty драйвера. Поле owner необходимо для предотвращения выгрузки tty драйвера во время открытия tty порта. В предыдущих версиях ядра это выполнялось самим tty драйвером с помощью логики подсчёта числа ссылок на модуль. Но программисты ядра решили, что было бы сложно разрешить все различные возможные состояния гонок, и поэтому за tty драйвер всё это управление выполняет ядро tty.

 

Поля driver_name и name выглядят очень похожими, но всё же используются для различных целей. Переменная driver_name должна быть установлена во что-то краткое, наглядное и уникальные среди всех tty драйверов в ядре. Так происходит потому, что оно показывается в /proc/tty/drivers для описания драйвера для пользователя и в sysfs в каталоге для tty класса, куда в настоящее время загружаются драйверы. Поле name используется, чтобы задать имя отдельным tty узлам, назначенных этому tty драйверу в дереве /dev. Эта строка используется для создания tty устройства путём добавления используемого номера tty устройства в конец строки. Оно также используется для создания имени устройства в каталоге sysfs /sys/class/tty/. Если в ядре разрешена devfs, это имя должно включать любой подкаталог, в котором хочет быть помещён tty драйвер. Так, например, драйвер последовательного порта в ядре устанавливает поле name в tts/, если devfs разрешена, и в ttyS, если это не так. Эта строка также отображается в файле /proc/tty/drivers.

 

Как мы уже упоминали, файл /proc/tty/drivers показывает все в настоящее время зарегистрированные tty драйверы. При использовании для регистрации в ядре драйвера tiny_tty и отсутствии devfs этот файл выглядит похожим на следующий:

 

$ cat /proc/tty/drivers

tiny_tty             /dev/ttty    240     0-3 serial

usbserial            /dev/ttyUSB  188   0-254 serial

serial               /dev/ttyS      4  64-107 serial

pty_slave            /dev/pts     136   0-255 pty:slave

pty_master           /dev/ptm     128   0-255 pty:master

pty_slave            /dev/ttyp      3   0-255 pty:slave

pty_master           /dev/pty       2   0-255 pty:master

unknown              /dev/vc/       4    1-63 console

/dev/vc/0            /dev/vc/0      4       0 system:vtmaster

/dev/ptmx            /dev/ptmx      5       2 system

/dev/console         /dev/console   5       1 system:console

/dev/tty             /dev/tty       5       0 system:/dev/tty

 

Кроме того, когда драйвер tiny_tty зарегистрирован в ядре tty, каталог /sys/class/tty в sysfs выглядит следующим образом:

 

$ tree /sys/class/tty/ttty*

/sys/class/tty/ttty0

`-- dev

/sys/class/tty/ttty1

`-- dev

/sys/class/tty/ttty2

`-- dev

/sys/class/tty/ttty3

`-- dev

 

$ cat /sys/class/tty/ttty0/dev

240:0

 

Старшее значение показывает старший номер для этого драйвер. Переменные типа и подтипа показывают каким типом tty драйвера является этот драйвер. В нашем примере последовательный драйвер является последовательным драйвером "нормального" типа. Единственным другим подтипом tty драйвера будет тип "с выноской" (управления) (“callout”). Устройства с выноской (управления) традиционно использовались для управления настройками линии устройства. В этом случае данные бы отправлялись и получались через один узел устройства, а любые изменения настройки линии, отправлялись бы на другой узел устройства, который бы был вынесенным устройством. Это требует использования двух младших номеров для каждого tty устройства. К счастью, почти все драйверы обрабатывают и данные и настройки линии используя один и тот же узел устройства, и тип с выноской в новых драйверах используются редко.

 

Переменная flags используется как tty драйвером, так и tty ядром для указания текущего состояние драйвера и типа, к которому  принадлежит tty драйвер. Определено несколько макросов битовых масок, которые вы должны использовать при проверке или манипулировании этими флагами. Драйвером могут быть установлены три бита в переменной flags:

 

TTY_DRIVER_RESET_TERMIOS

Этот флаг сообщает, что ядро tty сбрасывает настройки termios всякий раз, когда последний процесс закрыл это устройство. Это полезно для консольных и pty драйверов. Например, предположим, что пользователь покидает терминал в странном состоянии. Когда этот флаг установлен, терминал сбрасывается в нормальное значение, когда пользователь выходит из системы, или процесс, который управлял этой сессией, "убит".

 

TTY_DRIVER_REAL_RAW

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

 

TTY_DRIVER_NO_DEVFS

Этот флаг указывает, что когда производится вызов tty_register_driver, для данного tty устройства ядро tty не создаёт никаких записей в devfs. Это полезно для любого драйвера, который динамически создаёт и уничтожает устройства с младшими номерами. Примерами драйверов, которые устанавливают его, являются являются драйверы преобразования USB в последовательный порт, драйвер USB модема, tty драйвер для USB Bluetooth, а также ряд стандартных драйверов последовательного порта.

Если позже tty драйвер захочет зарегистрировать данное tty устройство в ядре tty, он должен вызвать tty_register_device с указателем на tty драйвер и младший номер устройства, которое было создано. Если этого не сделать, ядро tty по-прежнему передаёт все вызовы в этот tty драйвер, но некоторой внутренней, относящейся к tty функциональности может и не быть. Это включает уведомление /sbin/hotplug о новых tty устройствах и представление tty устройства в sysfs. Когда зарегистрированное tty устройство удаляется из машины, tty драйвер должен вызвать tty_unregister_device.

 

Последний оставшийся бит в этой переменной управляется ядром tty и называется TTY_DRIVER_INSTALLED. Этот флаг устанавливается ядром tty после того, как драйвер был зарегистрирован, и никогда не должен быть устанавливаться tty драйвером.

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