Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
18.2. Указатели на функции tty-драйвера
hardorsoft
26.10.10 в 06:18
|
|
В конце, драйвер tiny_tty декларирует четыре указателя на функции. 18.2.1. Функции оpen и close
hardorsoft
26.10.10 в 06:18
|
|
Функция open вызывается, когда происходит открытие устройства пользователем. Ядро tty-устройств вызывает метод open, передавая ему указатель на структуру tty_struct, назначенную данному устройству, и file-указатель. Драйвер должен корректно инициализировать поле open, иначе при открытии пользователь получит ошибку -ENODEV.
hardorsoft
26.10.10 в 06:19
|
|
При вызывове функции open ожидается, что tty-драйвер сохранит в переменную tty_struct, или в статичесий массив, информацию о младшем номере. Это необходимо, чтобы драйвер знал на какие устройства можно ссылаться при вызове функций закрытия, чтения, записи и других.
hardorsoft
26.10.10 в 06:19
|
|
В приведенном коде, драйвер tiny_tty сохраняет указатель в структуре tty:
hardorsoft
26.10.10 в 06:19
|
|
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
|
|
Структура tiny_serial определена так:
hardorsoft
26.10.10 в 06:20
|
|
struct tiny_serial { struct tty_struct *tty; /* указатель на tty данного устройства */ int open_count; /* сколько раз порт открывался */ struct semaphore sem; /* для блокировки этой структуры */ struct timer_list *timer; };
hardorsoft
26.10.10 в 06:20
|
|
Как мы видим, при первом вызове open переменная open_count инициализируется нулем. Это типичный счетчик ссылок, который необходим т.к. open и close могут неоднократно вызываться для одного и того же устройства в условиях многозадачности при чтении и записи данных. Для корректной работы необходимо вести подсчет, сколько раз порт открывался и закрывался. Драйвер инкрементирует и декрементирует счетчик в процессе использования порта. При первом открытии порта инициализируется аппаратное обеспечение, выделяется память. При последнем закрытии происходит выключение аппаратного обеспечения и освобождение памяти.
hardorsoft
26.10.10 в 06:20
|
|
Оставшаяся часть функции tiny_open учитывает, сколько раз устройство было открыто:
hardorsoft
26.10.10 в 06:20
|
|
++tiny->open_count; if (tiny->open_count = = 1) { /* это первое открытие устройства*/ /* выполняется инициализация аппаратного обеспечения */
hardorsoft
26.10.10 в 06:21
|
|
Функция open должна возвращать отрицательное число при возникновении ошибки, или ноль в случае успешного завершения.
hardorsoft
26.10.10 в 06:21
|
|
Функция close вызывается ядром tty-устройств, когда пользователь вызывает метод close для дескриптора открытого устройства. Поскольку устройство может быть открыто несколько раз, то и функция close так же вызывается несколько раз. Функция ведет учет того, сколько раз производилось закрытие, и при необходимости выключает аппаратное обеспечение. Вот производящий это код:
hardorsoft
26.10.10 в 06:21
|
|
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
|
|
Функция tiny_close вызывает do_close, которая и выполняет всю работу по закрытию устройства. Это произойдет, когда будут закрыты все дескрипторы устройства. Функция close не имеет возвращаемого значения.
hardorsoft
26.10.10 в 06:21
|
|
|
|
|
Функция write вызывается когда пользователь посылает данные в устройство. С начала вызывается ядро tty-устройств, затем оно передает управление функции tty-драйвера write, сообщая размер посылаемых данных.
hardorsoft
26.10.10 в 06:22
|
|
Иногда, из-за ограничения скорости передачи и ёмкости буфера устройства, не все данные могут быть посланы сразу при вызове функции write. Функция write возвращает количество символов отосланных устройством (или поставленных в очередь отправки), таким образом, программа пользователя может проверить все, ли данные были действительно посланы. Такой подход делает проверку отправки в пространстве пользователя менее надежной, чем в драйвере ядра, который может дождаться отправки всех данных. Если, при вызове write, возникает ошибка, функция должна вернуть отрицательное значение, вместо количества переданных символов.
hardorsoft
26.10.10 в 06:22
|
|
Функция write может вызываться как из контекста прерывания, так и из контекста пользователя. Это важно учитывать при вызове других функций, которые могут уснуть в контексте прерывания. Некоторые функции, такие как copy_from_user, kmalloc и printk, могут вызывать schedule. Если вам нужно уйти в сон, то для начала сделайте проверку, не находитесь ли вы в контексте прерывания. Для этой проверки используется функция in_interrupt.
hardorsoft
26.10.10 в 06:22
|
|
Наш пример tty-драйвера не работает с реальным устройством, поэтому функция записи просто пишет в отладочный log ядра, что данные успешно переданы. (1)
hardorsoft
26.10.10 в 06:23
|
|
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
|
|
Функция 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
|
|
Функция write_room вызывается, когда ядру tty-устройств требуется узнать, сколько пространства доступно в буфере записи устройства. С течением времени это значение изменяется, т.к. символы из буфера отсылаются, и, при вызове write, дописываются
hardorsoft
26.10.10 в 06:26
|
|
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
|
|
18.2.3. Другие функции работы с буфером
hardorsoft
26.10.10 в 06:27
|
|
Функция chars_in_buffer не обязательно должна определятся в структуре tty_driver, но желательна. Эта функция вызывается, когда ядру tty-устройств требуется узнать, сколько еще символов из буфера осталось послать. Если драйвер может сохранять символы в буфере перед их аппаратной передачей, ему следует реализовать эту функцию, чтобы ядро могло определить, факт отправки всех данных.
hardorsoft
26.10.10 в 06:27
|
|
Еще три функции в структуре 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
|
|
Третья функция flush_buffer. Она вызывается ядром tty-устройств, когда нужно очистить буфер записи в драйвере. Данные, находящиеся в буфере – теряются и не посылаются в устройство.
hardorsoft
26.10.10 в 06:28
|
|
18.2.4. Нет функции read?
hardorsoft
26.10.10 в 06:28
|
|
Это все функции, которые могут быть зарегистрированы: открытие node устройства, запись данных в устройство, закрытие node устройства, отмена регистрации драйвера, и выгрузка из ядра. Но ядро tty-устройств и структура tty_driver не предоставляют функции чтения. Иными словами не существует функции чтения из устройства.
hardorsoft
26.10.10 в 06:28
|
|
Вместо привычной функции чтения, tty-драйвер посылает ядру данные, принятые от устройства. Ядро tty-устройств накапливает данные в буфере, пока их не запросит пользователь. Поскольку ядро tty-устройств предоставляет логику работы с буфером, каждому драйверу ненужно ее реализовывать. Ядро извещает драйвер, когда пользователь хочет остановить и запустить передачу данных, но если внутренний буфер заполнен, извещения не происходит.
hardorsoft
26.10.10 в 06:29
|
|
Буфер принимаемых данных называется struct tty_flip_buffer. Переключающийся (flip) буфер является структурой содержащий два главных массива. Принятые данные хранятся в первом массиве, когда массив заполняется, он становится доступным для пользователя, ожидающего данных. Пока пользователь читает данные из первого массива, входящие данные пишутся во второй массив. Когда работа с этим массивом закончена, данные снова передаются пользователю, и драйвер вновь работает с первым массивом.
hardorsoft
26.10.10 в 06:29
|
|
По существу происходит переключение с одного буфера на другой, в надежде что не произойдет переполнение сразу двух буферов. Чтобы попытаться предотвратить потерю данных, tty-драйвер может отслеживать размер входящего массива данных, и если он заполняет буфер, драйвер говорит о необходимости переключить буфер, вместо ожидания его освобождения.
hardorsoft
26.10.10 в 06:29
|
|
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
|
|
Для добавления принятых символов используется функция tty_insert_flip_char. Сохранение данных происходит в первый параметр этой функции – struct tty_struct. Второй параметр – сохраняемый символ, и последний параметр – флаг этого символа. Если принят обычный символ, то устанавливается флаг TTY_NORMAL. Если при приеме данных возникла ошибка, то, в зависимости от ее типа, устанавливается один из флагов TTY_BREAK, TTY_FRAME, TTY_PARITY, или TTY_OVERRUN.
hardorsoft
26.10.10 в 06:29
|
|
При передаче данных пользователю используется функция 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
|
|
1 Прим. переводчика: файл <linux></linux>tty_driver.h> ядра 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
|
|