Настройки TTY линии

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

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

set_termios

Большинство функций termios пользовательского пространства транслируются библиотекой в вызов ioctl узла драйвера. Большое количество различных вызовов ioctl для tty затем переводятся ядром tty в один вызов функции set_termios tty драйвера. Обратному вызову set_termios необходимо определить, какие настройки строки его просят изменить, и затем выполнить эти изменения в tty устройстве. Драйвер tty должен быть в состоянии декодировать все разнообразные настройки в структуре termios и отреагировать на любые необходимые изменения. Это сложная задача, так как все настройки линии упакованы в структуру termios большим числом способов.

 

Первое, что должна сделать функция set_termios, это определить надо ли что-то изменять на самом деле. Это можно сделать с помощью следующего кода:

 

unsigned int cflag;

 

cflag = tty->termios->c_cflag;

 

/* проверяем, что действительно хотят, чтобы мы изменили */

if (old_termios) {

    if ((cflag == old_termios->c_cflag) &&

        (RELEVANT_IFLAG(tty->termios->c_iflag) ==

        RELEVANT_IFLAG(old_termios->c_iflag))) {

        printk(KERN_DEBUG " - nothing to change...\n");

        return;

    }

}

 

Макрос RELEVANT_IFLAG определён как:

 

#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

 

и используется, чтобы маскировать важные биты в переменной cflags. Затем это сравнивается со старым значением и смотрится, есть ли изменения. Если нет, то ничего не должно быть изменено, поэтому мы возвращаемся. Обратите внимание, что сначала проверяется, что переменная old_termios указывает на действительную структуру, прежде чем к ней обратиться. Это необходимо, поскольку в некоторых случаях эта переменная имеет значение NULL. Попытка доступа к полю по указателю NULL приводит к панике ядра.

 

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

 

/* получаем размер в байтах */

switch (cflag & CSIZE) {

    case CS5:

        printk(KERN_DEBUG " - data bits = 5\n");

        break;

    case CS6:

        printk(KERN_DEBUG " - data bits = 6\n");

        break;

    case CS7:

        printk(KERN_DEBUG " - data bits = 7\n");

        break;

    default:

    case CS8:

        printk(KERN_DEBUG " - data bits = 8\n");

        break;

}

 

Чтобы определить запрошенное значение чётности, к переменной cflag может быть применена битовая маска PARENB, чтобы определить, должна ли быть чётность вообще установлена. Если это так, может быть использована битовая маска PARODD, чтобы определить какая чётность должна использоваться: чёт или нечет. Реализация этого:

 

/* определяем чётность */

if (cflag & PARENB)

    if (cflag & PARODD)

        printk(KERN_DEBUG " - parity = odd\n");

    else

        printk(KERN_DEBUG " - parity = even\n");

else

    printk(KERN_DEBUG " - parity = none\n");

 

Запрошенные стоп-биты также могут быть определены с помощью переменной cflag, используя маску CSTOPB. Реализация этого:

 

/* выясняем требуемые стоп-биты */

if (cflag & CSTOPB)

    printk(KERN_DEBUG " - stop bits = 2\n");

else

    printk(KERN_DEBUG " - stop bits = 1\n");

 

Существуют два основных типа управления потоком данных: аппаратный и программный. Чтобы определить, запрашивает ли пользователь аппаратное управление потоком, к переменной cflag может применяться битовая маска CRTSCTS. Пример этого:

 

/* выясняем настройки аппаратного контроля передачи */

if (cflag & CRTSCTS)

    printk(KERN_DEBUG " - RTS/CTS is enabled\n");

else

    printk(KERN_DEBUG " - RTS/CTS is disabled\n");

 

Определение различных видов программного управления потоком и различных стоповых и стартовых символов немного более сложное:

 

/* определяем программное управление потоком */

/* если мы реализуем XON/XOFF, устанавливаем стартовый и

 * стоповый символ в устройстве */

if (I_IXOFF(tty) || I_IXON(tty)) {

    unsigned char stop_char = STOP_CHAR(tty);

    unsigned char start_char = START_CHAR(tty);

 

    /* если мы реализуем ВХОДНОЙ XON/XOFF */

    if (I_IXOFF(tty))

        printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "

                "XON = %2x, XOFF = %2x", start_char, stop_char);

    else

        printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");

 

    /* если мы реализуем ВЫХОДНОЙ XON/XOFF */

    if (I_IXON(tty))

        printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "

                "XON = %2x, XOFF = %2x", start_char, stop_char);

    else

        printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");

}

 

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

 

/* получаем скорость передачи */

printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));

 

Теперь, когда tty драйвер определили все разнообразные параметры линии, можно на основе этих значений должным образом проинициализировать оборудование.

tiocmget и tiocmset

В версии 2.4 и более старых ядрах, для получения и установки различных параметров управления линией использовалось несколько вызовов tty ioctl. Они были обозначены константами TIOCMGET, TIOCMBIS, TIOCMBIC и TIOCMSET. TIOCMGET была использована для получения значений настройки строки в ядре, и начиная с версии 2.6, этот вызов ioctl был превращён в функцию обратного вызова tty драйвера, названную tiocmget. Другие три ioctl-ы были упрощены и теперь представлены одной функцией обратного вызова tty драйвера, названной tiocmset.

 

Функция tiocmget в tty драйвере вызывается ядром tty, когда ядро хочет узнать текущие физические значения линий управления определённого tty устройства. Это обычно делается для получения значений линий DTR и RTS последовательного порта. Если tty драйвер не может напрямую прочитать регистр MCR или MSR последовательного порта, потому что оборудование не позволяет этого, их копия должна сохраняться локально. Ряд драйверов USB-последовательный порт должны реализовать такого рода "теневую" переменную. Вот как может быть реализована эта функция, если сохраняются локальные копии этих переменных:

 

static int tiny_tiocmget(struct tty_struct *tty, struct file *file)

{

    struct tiny_serial *tiny = tty->driver_data;

    unsigned int result = 0;

    unsigned int msr = tiny->msr;

    unsigned int mcr = tiny->mcr;

    result = ((mcr & MCR_DTR) ? TIOCM_DTR  : 0) | /* DTR установлен */

            ((mcr & MCR_RTS)  ? TIOCM_RTS  : 0) | /* RTS установлен */

            ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP установлен */

            ((msr & MSR_CTS)  ? TIOCM_CTS  : 0) | /* CTS установлен */

            ((msr & MSR_CD)   ? TIOCM_CAR  : 0) | /* Установлено обнаружение несущей (Carrier detect) */

            ((msr & MSR_RI)   ? TIOCM_RI   : 0) | /* Установлен индикатор вызова (Ring) */

            ((msr & MSR_DSR)  ? TIOCM_DSR  : 0);  /* DSR установлен */

    return result;

}

 

Когда ядро хочет установить значения управляющих линий определённого tty устройства, ядро tty вызывает в tty драйвере функцию tiocmset. Ядро tty сообщает tty драйверу, какие значения установить и какие очистить, передавая их в двух переменных: set и clear. Эти переменные содержат битовую маску настроек линии, которые должны быть изменены. Вызов ioctl никогда не просит драйвер установить и сбросить один и тот же бит в одно время, поэтому не важно, какие операции происходят в первую очередь. Вот пример того, как эта функция может быть реализована tty драйвером:

 

static int tiny_tiocmset(struct tty_struct *tty, struct file *file,

                         unsigned int set, unsigned int clear)

{

    struct tiny_serial *tiny = tty->driver_data;

    unsigned int mcr = tiny->mcr;

    if (set & TIOCM_RTS)

        mcr |= MCR_RTS;

    if (set & TIOCM_DTR)

        mcr |= MCR_RTS;

 

    if (clear & TIOCM_RTS)

        mcr &= ~MCR_RTS;

    if (clear & TIOCM_DTR)

        mcr &= ~MCR_RTS;

 

    /* устанавливаем в устройстве новое значение для MCR */

    tiny->mcr = mcr;

    return 0;

}

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