Функция symbolic_speed()
в строке 19 преобразует целочисленную скорость в символическую, которую поддерживает termios
. К сожалению, termios не предназначен для работы с произвольными скоростями, так что каждая скорость, которую вы хотите использовать, должна быть частью интерфейса пользователь — ядро [110] На man-странице setserial описан способ обхода этого ограничения, специфический для Linux.
.
Обратите внимание, что предусмотрены и довольно высокие скорости. Не все последовательные порты поддерживают скорости 230 400 или 460 800 бит/с; в стандарте POSIX определены скорости лишь до 38 400 бит/с. Чтобы сделать эту программу переносимой, каждую строку над строкой, в которой устанавливается скорость 38 400 бит/с, потребуется расширить до трех строк, как показано ниже:
#ifdef В460800
if (speednum >= 460800) return B46000;
#end if
Это позволит пользователям устанавливать скорости выше тех, которые последовательные порты способны поддерживать, а исходный код теперь будет компилироваться в любой системе с POSIX termios
. (Как упоминалось ранее в этой главе, любой последовательный порт может отказаться принять на обработку любую установку termios
, которую он не способен поддержать, включая также установки скорости. Поэтому установка B460800
не означает, что можно установить скорость порта равной 460 800 бит/с.)
В строках 44—55 определяются глобальные переменные для передачи некоторых переменных обработчику сигналов и сам обработчик сигналов. Обработчик сигналов предназначен для восстановления настроек termios на обоих интерфейсах tty при доставке сигнала, поэтому ему необходимо получить доступ к структурам, содержащим старые настройки termios
. Ему также необходимо знать файловый дескриптор или последовательный порт (файловый дескриптор для стандартных входных данных не меняется, поэтому компилируется в бинарный). Этот код идентичен коду в случае нормального пути завершения, который рассматривается позже. Обработчик сигналов затем прикрепляется к сигналам, которые завершат процесс, если они были проигнорированы.
Функции send_escape()
и cook_buf()
будут рассматриваться позже. Они используются как часть обработки входных данных в цикле ввода-вывода в конце функции main()
.
Условно скомпилированный sleep(600)
в начале функции main()
предназначен для отладки. С целью отладки программ, модифицирующих настройки termios
для стандартных входных или выходных данных, лучше всего присоединить процесс в другой окне или терминальном сеансе. Однако это означает, что нельзя установить точку прерывания на основную функцию и проходить по одной инструкции за раз. Необходимо запустить программу, найти идентификатор ее процесса и присоединиться к ней из отладчика. Более подробно этот процесс описан далее в настоящей главе.
Поэтому если необходимо отладить код, запускаемый до того, как программа дождется входных данных, нужно перевести программу в режим ожидания, чтобы оставить время для присоединения. После прикрепления режим ожидания прерывается, поэтому длительный режим ожидания безопасен. Чтобы активизировать это свойство, скомпилируйте robin.с
с опцией -DDSLEEP
.
Игнорируя отладку, мы в первую очередь анализируем опции, используя библиотеку popt
, описанную в главе 26, а затем открываем последовательный порт, с которым будем взаимодействовать.
Затем мы вызываем функцию tcgetattr()
, чтобы получить существующую конфигурацию termios
последовательного порта, а затем сохраняем копию в pots
, чтобы восстановить ее по окончании.
Начиная со строки 183, мы модифицируем установки последовательного порта:
183: pts.c_lflag &= ~ICANON;
Эта строка отключает приведение к каноническому виду в драйвере последовательного порта — то есть переводит его в неформатируемый режим. В этом режиме нет специальных символов — ни символов новой строки, ни управляющих символов:
184: pts.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
Это отключает локальный эхо-контроль в последовательном порте:
185: pts.c_cflag |= HUPCL;
Если подключен модем, HUPCL
сообщает ему об отбое при закрытии устройства конечной программой:
186: pts.с_сс[VMIN] = 1;
187: pts.с_сс[VTIME] = 0;
Когда tty находится в неформатируемом режиме, эти две установки определяют поведение системного вызова read()
. Эта особая установка сообщает, что при вызове read()
мы хотим, чтобы он подождал с возвратом, пока не считаются один или несколько байтов. Мы никогда не вызовем read()
, пока не будем знать, что остался хотя бы один байт для чтения, потому это функциональный эквивалент неблокирующего read()
. Определение VMIN
и VTIME
сложное, как показано далее в настоящей главе.
Читать дальше