Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 18.2. tty_driver Function Pointers

Оглавление
Английский оригинал Перевод на русский

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

18.2. Указатели на функции tty-драйвера

hardorsoft 26.10.10 в 06:18

0

В конце, драйвер tiny_tty декларирует четыре указателя на
функции.
18.2.1. Функции оpen и close

hardorsoft 26.10.10 в 06:18

0

Функция open вызывается, когда происходит открытие устройства
пользователем. Ядро tty-устройств вызывает метод open, передавая ему
указатель на структуру tty_struct, назначенную данному устройству, и
file-указатель. Драйвер должен корректно инициализировать поле open,
иначе при открытии пользователь получит ошибку -ENODEV.

hardorsoft 26.10.10 в 06:19

0

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

hardorsoft 26.10.10 в 06:19

0

В приведенном коде, драйвер tiny_tty сохраняет указатель в
структуре tty:

hardorsoft 26.10.10 в 06:19

0

static int tiny_open(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny;
struct timer_list *timer;
int index;
/* инициализирует указатель на случай возникновения ошибки */
tty->driver_data = NULL;
/* получает обьект ассоциированный с указателем tty*/
index = tty->index;
tiny = tiny_table[index];
if (tiny = = NULL) {
/* создаем устройство при первом доступе к нему */
tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
if (!tiny)
return -ENOMEM;
init_MUTEX(&tiny->sem);
tiny->open_count = 0;
tiny->timer = NULL;
tiny_table[index] = tiny;
}
down(&tiny->sem);
/* сохраняем нашу структуру в структуру tty */
tty->driver_data = tiny;
tiny->tty = tty;
В этом коде структура tiny_serial сохраняется в структуре tty, что
позволяет функциям tiny_write, tiny_write_room, и tiny_close находить
структуру tiny_serial и управлять ее свойствами.

hardorsoft 26.10.10 в 06:19

0

Структура tiny_serial определена так:

hardorsoft 26.10.10 в 06:20

0

struct tiny_serial {
struct tty_struct *tty; /* указатель на tty данного устройства */
int open_count; /* сколько раз порт открывался */
struct semaphore sem; /* для блокировки этой структуры */
struct timer_list *timer;
};

hardorsoft 26.10.10 в 06:20

0

Как мы видим, при первом вызове open переменная open_count
инициализируется нулем. Это типичный счетчик ссылок, который
необходим т.к. open и close могут неоднократно вызываться для одного и
того же устройства в условиях многозадачности при чтении и записи
данных. Для корректной работы необходимо вести подсчет, сколько раз
порт открывался и закрывался. Драйвер инкрементирует и
декрементирует счетчик в процессе использования порта. При первом
открытии порта инициализируется аппаратное обеспечение, выделяется
память. При последнем закрытии происходит выключение аппаратного
обеспечения и освобождение памяти.

hardorsoft 26.10.10 в 06:20

0

Оставшаяся часть функции tiny_open учитывает, сколько раз
устройство было открыто:

hardorsoft 26.10.10 в 06:20

0

++tiny->open_count;
if (tiny->open_count = = 1) {
/* это первое открытие устройства*/
/* выполняется инициализация аппаратного обеспечения */

hardorsoft 26.10.10 в 06:21

0

Функция open должна возвращать отрицательное число при
возникновении ошибки, или ноль в случае успешного завершения.

hardorsoft 26.10.10 в 06:21

0

Функция close вызывается ядром tty-устройств, когда пользователь
вызывает метод close для дескриптора открытого устройства. Поскольку
устройство может быть открыто несколько раз, то и функция close так
же вызывается несколько раз. Функция ведет учет того, сколько раз
производилось закрытие, и при необходимости выключает аппаратное
обеспечение. Вот производящий это код:

hardorsoft 26.10.10 в 06:21

0

static void do_close(struct tiny_serial *tiny)
{
down(&tiny->sem);
if (!tiny->open_count) {
/* порт не открывался */
goto exit;
}
--tiny->open_count;
if (tiny->open_count <= 0) {
/* порт был закрыт последним пользователем*/
/* выполняем действия специфичные для аппаратного обеспечения*/
/* выключаем таймер */
del_timer(tiny->timer);
}
exit:
up(&tiny->sem);
}
static void tiny_close(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_data;
if (tiny)
do_close(tiny);
}

hardorsoft 26.10.10 в 06:21

0

Функция tiny_close вызывает do_close, которая и выполняет всю
работу по закрытию устройства. Это произойдет, когда будут закрыты все
дескрипторы устройства. Функция close не имеет возвращаемого
значения.

hardorsoft 26.10.10 в 06:21

0

18.2.2. Поток данных

hardorsoft 26.10.10 в 06:22

0

Функция write вызывается когда пользователь посылает данные в
устройство. С начала вызывается ядро tty-устройств, затем оно передает
управление функции tty-драйвера write, сообщая размер посылаемых
данных.

hardorsoft 26.10.10 в 06:22

0

Иногда, из-за ограничения скорости передачи и ёмкости буфера
устройства, не все данные могут быть посланы сразу при вызове функции
write. Функция write возвращает количество символов отосланных
устройством (или поставленных в очередь отправки), таким образом,
программа пользователя может проверить все, ли данные были
действительно посланы. Такой подход делает проверку отправки в
пространстве пользователя менее надежной, чем в драйвере ядра,
который может дождаться отправки всех данных. Если, при вызове write,
возникает ошибка, функция должна вернуть отрицательное значение,
вместо количества переданных символов.

hardorsoft 26.10.10 в 06:22

0

Функция write может вызываться как из контекста прерывания, так
и из контекста пользователя. Это важно учитывать при вызове других
функций, которые могут уснуть в контексте прерывания. Некоторые
функции, такие как copy_from_user, kmalloc и printk, могут вызывать
schedule. Если вам нужно уйти в сон, то для начала сделайте проверку, не
находитесь ли вы в контексте прерывания. Для этой проверки
используется функция in_interrupt.

hardorsoft 26.10.10 в 06:22

0

Наш пример tty-драйвера не работает с реальным устройством,
поэтому функция записи просто пишет в отладочный log ядра, что данные
успешно переданы. (1)

hardorsoft 26.10.10 в 06:23

0

static int tiny_write(struct tty_struct *tty,
const unsigned char *buffer, int count)
{
struct tiny_serial *tiny = tty->driver_data;
int i;
int retval = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count)
/* порт не открыт */
goto exit;
/* сделаем вид что записали данные в порт устройства,
* о чем запишем в отладочный журнал.
*/
printk(KERN_DEBUG "%s - ", __FUNCTION__);
for (i = 0; i < count; ++i)
printk("%02x ", buffer[i]);
printk("\n");
exit:
up(&tiny->sem);
return retval;
}

hardorsoft 26.10.10 в 06:24

0

Функция write может быть вызвана, когда самой подсистеме tty
требуется послать данные устройству. Это может произойти, если tty-
драйвер не реализует функцию put_char в структуре tty_struct. В этом
случае ядро tty-устройств использует метод write для записи данных
размера 1 символ. Обычно это происходит, когда ядру tty-устройств
требуется конвертировать символ новой строки в символы протяжки
бумаги плюс символ новой строки. Если в этом месте функция драйвера
write вернет «0», могут возникнуть серьезные проблемы. В данном
случае драйвер обязательно должен записать байт данных в устройство,
т.к. ядро tty-устройств передает ему не просто обычный буфер данных,
который можно послать и позже. Функция write не может определить
используется ли она в качестве put_char, поэтому постарайтесь
реализовать функцию так, что если посылается только один байт, то
возврат происходит только после того, как этот байт отправлен.
Некоторые современные tty-драйверы преобразователя USB-
последовательный порт не следуют этому правилу, по этой причине
некоторые терминалы не работают должным образом.

hardorsoft 26.10.10 в 06:26

0

Функция write_room вызывается, когда ядру tty-устройств требуется
узнать, сколько пространства доступно в буфере записи устройства. С
течением времени это значение изменяется, т.к. символы из буфера
отсылаются, и, при вызове write, дописываются

hardorsoft 26.10.10 в 06:26

0

static int tiny_write_room(struct tty_struct *tty)
{
struct tiny_serial *tiny = tty->driver_data;
int room = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count) {
/* порт не открыт */
goto exit;
}
/* вычисляем, как много места осталось */
room = 255;
exit:
up(&tiny->sem);
return room;
}

hardorsoft 26.10.10 в 06:27

0

18.2.3. Другие функции работы с буфером

hardorsoft 26.10.10 в 06:27

0

Функция chars_in_buffer не обязательно должна определятся в
структуре tty_driver, но желательна. Эта функция вызывается, когда
ядру tty-устройств требуется узнать, сколько еще символов из буфера
осталось послать. Если драйвер может сохранять символы в буфере перед
их аппаратной передачей, ему следует реализовать эту функцию, чтобы
ядро могло определить, факт отправки всех данных.

hardorsoft 26.10.10 в 06:27

0

Еще три функции в структуре tty_driver, могут использоваться для
очистки данных находящихся в буфере драйвера. Их реализация не
обязательна, но рекомендуются в том случае, если tty-драйвер может
буферизировать посылаемые данные. Первые две функции flush_chars и
wait_until_sent вызываются, когда ядру требуется послать некоторое
количество символов функцией put_char. Функция flush_chars
заканчивает работу перед тем как все данные будут переданы. Функция
wait_until_sent работает аналогично, но возвращается после передачи
всех данных, или после истечения интервала времени timeout, который
передается в эту функцию. Tty-дравер, ожидая завершения, может спать в
этих функциях. Если передаваемое значение timeout равно «0», то
функция ждет завершения операций.

hardorsoft 26.10.10 в 06:28

0

Третья функция flush_buffer. Она вызывается ядром tty-устройств,
когда нужно очистить буфер записи в драйвере. Данные, находящиеся в
буфере – теряются и не посылаются в устройство.

hardorsoft 26.10.10 в 06:28

0

18.2.4. Нет функции read?

hardorsoft 26.10.10 в 06:28

0

Это все функции, которые могут быть зарегистрированы: открытие
node устройства, запись данных в устройство, закрытие node устройства,
отмена регистрации драйвера, и выгрузка из ядра. Но ядро tty-устройств
и структура tty_driver не предоставляют функции чтения. Иными
словами не существует функции чтения из устройства.

hardorsoft 26.10.10 в 06:28

0

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

hardorsoft 26.10.10 в 06:29

0

Буфер принимаемых данных называется struct tty_flip_buffer.
Переключающийся (flip) буфер является структурой содержащий два
главных массива. Принятые данные хранятся в первом массиве, когда
массив заполняется, он становится доступным для пользователя,
ожидающего данных. Пока пользователь читает данные из первого
массива, входящие данные пишутся во второй массив. Когда работа с
этим массивом закончена, данные снова передаются пользователю, и
драйвер вновь работает с первым массивом.

hardorsoft 26.10.10 в 06:29

0

По существу происходит переключение с одного буфера на другой, в
надежде что не произойдет переполнение сразу двух буферов. Чтобы
попытаться предотвратить потерю данных, tty-драйвер может
отслеживать размер входящего массива данных, и если он заполняет
буфер, драйвер говорит о необходимости переключить буфер, вместо
ожидания его освобождения.

hardorsoft 26.10.10 в 06:29

0

for (i = 0; i < data_size; ++i) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty);

hardorsoft 26.10.10 в 06:29

0

Для добавления принятых символов используется функция
tty_insert_flip_char. Сохранение данных происходит в первый параметр
этой функции – struct tty_struct. Второй параметр – сохраняемый
символ, и последний параметр – флаг этого символа. Если принят
обычный символ, то устанавливается флаг TTY_NORMAL. Если при приеме
данных возникла ошибка, то, в зависимости от ее типа, устанавливается
один из флагов TTY_BREAK, TTY_FRAME, TTY_PARITY, или TTY_OVERRUN.

hardorsoft 26.10.10 в 06:29

0

При передаче данных пользователю используется функция
tty_flip_buffer_push. Так же эта функция используется, если
передаваемый буфер может переполниться, как показано в примере.
Всякий раз, когда данные добавляются в передаваемый буфер, или когда
он заполнен, tty-драйвер должен вызвать tty_flip_buffer_push. Если
tty-драйвер может принимать данные с очень высокой скоростью, должен
выставляться флаг tty->low_latency, благодаря которому вся работа
tty_flip_buffer_push выполняется сразу же. В противном случае в
функции tty_flip_buffer_push происходит вызов планировщика, и
данные перемещаются в выходной буфер несколько позже.

hardorsoft 26.10.10 в 06:30

0

1 Прим. переводчика: файл <linux></linux>tty_driver.h&gt; ядра 2.6.5 декларирует функцию write
несколько иначе:
int (*write)(struct tty_struct * tty, int from_user, const unsigned char *buf, int count);

hardorsoft 26.10.10 в 06:25

0
Минутку...