n_tty.c
/* w tym pliku znajduja sie funkcje najważniejszej dyscypliny * linii, dyscypliny N_TTY. Ta dyscyplina jest najważniejsza ponieważ * zawsze, w przypadku niemożliwości otwarcia żądanej dyscypliny jest * otwierana N_TTY Ten kod zwykł był być w pliku tty_io.c, ale * już nie jest. Pozostałością tego faktu jest zapewne to, że niektóre * pola w tty_struct są prywatnymi polami tej dyscypliny linii. */
/* * n_tty.c --- implements the N_TTY line discipline. * * This code used to be in tty_io.c, but things are getting hairy * enough that it made sense to split things off. (The N_TTY * processing has changed so much that it's hardly recognizable, * anyway...) * * Note that the open routine for N_TTY is guaranteed never to return * an error. This is because Linux will fall back to setting a line * to N_TTY if it can not switch to any other line discipline. * * Written by Theodore Ts'o, Copyright 1994. * * This file also contains code originally written by Linus Torvalds, * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. * * This file may be redistributed under the terms of the GNU Public * License. */ #include <linux/types.h> #include <linux/major.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/fcntl.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/timer.h> #include <linux/ctype.h> #include <linux/kd.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/malloc.h> #include <asm/segment.h> #include <asm/system.h> #include <asm/bitops.h> #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif /* number of characters left in xmit buffer before select has we have room */ #define WAKEUP_CHARS 256 /* * This defines the low- and high-watermarks for throttling and * unthrottling the TTY driver. These watermarks are used for * controlling the space in the read buffer. */ #define TTY_THRESHOLD_THROTTLE (N_TTY_BUF_SIZE - 128) #define TTY_THRESHOLD_UNTHROTTLE 128 /* Funkcja pomocnicza - umieszczenie pojedynczego znaku w buforze * dyscypliny linii */ static inline void put_tty_queue(unsigned char c, struct tty_struct *tty) { if (tty->read_cnt < N_TTY_BUF_SIZE) { tty->read_buf[tty->read_head] = c; tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1); tty->read_cnt++; } } /* * Flush the input buffer */
/* Opróżnianie bufora dyscypliny linii i przekazanie znaków do procesu * o ile jakiś na nie czeka - w interfejsie funkcja flush_buffer */ void n_tty_flush_buffer(struct tty_struct * tty) {
/* Ustawienie wszystkich parametrow w tty_struct tak, by bufor * wyglądał na pusty */ tty->read_head = tty->read_tail = tty->read_cnt = 0; tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); if (!tty->link) return; /* if (tty->link) , czyli dany terminal jest jednym (częścią nadrzędną * w domyśle) z pary pseudoterminalowej */ if (tty->driver.unthrottle) (tty->driver.unthrottle)(tty); /* wywołanie funkcji sterownika, która informuje sterownik, że bufor * dyscypliny linii nie jest bynajmniej przepełniony */ if (tty->link->packet) { /* jeśli urządzenie jest w odpowiednim trybie (packet mode), to zmiana * stanu kontrolnego i zwalniany proces czekający na czytanie z drugiej * (podrzędnej) części terminala; jeśli nie ma procesu w kolejce, to * to,co zostało przeczytane ginie bezpowrotnie */ tty->ctrl_status |= TIOCPKT_FLUSHREAD; wake_up_interruptible(&tty->link->read_wait); } }
/* w interfejsie - funkcja chars_in_buffer */ /* * Return number of characters buffered to be delivered to user */ int n_tty_chars_in_buffer(struct tty_struct *tty) { if (tty->icanon) { /* tryb kanoniczny */ if (!tty->canon_data) /* brak danych */ return 0; /* zwracamy liczbę znaków między "ogonem" a "głową" w buforze * cyklicznym */ return (tty->canon_head > tty->read_tail) ? tty->canon_head - tty->read_tail : tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); } return tty->read_cnt; } /* * Perform OPOST processing. Returns -1 when the output device is * full and the character must be retried. */ /* końcowe przetwarzanie wyjścia */ static int opost(unsigned char c, struct tty_struct *tty) { int space, spaces; space = tty->driver.write_room(tty); /* jeśli nie ma miejsca na urządzeniu to nic nie robi, tylko zwraca * -1 */ if (!space) return -1; /* jeżeli jest ustawiona flaga O_OPOST to przetwarza znak c */ if (O_OPOST(tty)) { switch (c) { case '\n': /* przetwarzanie znaku końca linii w zależności od ustawionej * flagi*/ if (O_ONLRET(tty)) /* sam powrót do pierwszej kolumny */ tty->column = 0; if (O_ONLCR(tty)) { /* powrót do pierwszej kolumny i wysłanie znaku powrotu na urządzenie * o ile jest na nim miejsce na ten znak i następny (czyli '\n')*/ if (space < 2) return -1; tty->driver.put_char(tty, '\r'); tty->column = 0; } /* ustawienie kolumny trybu kanonicznego na przeczytaną kolumnę */ tty->canon_column = tty->column; break; case '\r': /* przetwarzanie znaku powrotu karetki w zależności od ustawionej flagi*/ if (O_ONOCR(tty) && tty->column == 0) /* jesteśmy na początku linii; nic nie robimy */ return 0; if (O_OCRNL(tty)) { /* zamiast znaku '\r' wysyłamy na sterownik znak '\n' */ c = '\n'; if (O_ONLRET(tty)) tty->canon_column = tty->column = 0; break; } tty->canon_column = tty->column = 0; break; case '\t':
/* spaces -dopełnienie kolumny do wielokrotności 8 */ spaces = 8 - (tty->column & 7); if (O_TABDLY(tty) == XTABS) { /* jeśli mniej miejsca na sterowniku niż wynosi spaces to powrót * w przeciwnym razie wypisujemy na sterownik tyle spacji ile wynosi * spaces i wychodzimy*/ if (space < spaces) return -1; tty->column += spaces; tty->driver.write(tty, 0, " ", spaces); return 0; } /* przesuwamy aktualną kolumnę o spaces */ tty->column += spaces; break; case '\b': /* przetwarzanie znaku kasowania w tył (backspace) */ if (tty->column > 0) /* przesunięcie kolumny o jeden w tył */ tty->column--; break; default: /* przetwarzanie innych znaków niż powyższe */ if (O_OLCUC(tty)) /* wszystkie znaki DUŻE */ c = toupper(c); if (!iscntrl(c)) /* jeżeli c nie jest znakiem kontrolnym, to przesuwa kolumnę */ tty->column++; break; } } /* wysłanie znaku c na sterownik */ tty->driver.put_char(tty, c); return 0; }
/* funkcja inline powodująca wypisanie zanku c na sterownik */ static inline void put_char(unsigned char c, struct tty_struct *tty) { tty->driver.put_char(tty, c); } /* Must be called only when L_ECHO(tty) is true. */ /* wypisanie echa znaku na sterownik */ static void echo_char(unsigned char c, struct tty_struct *tty) { if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') { /* wypisanie Ctrl jako ^ */ put_char('^', tty); put_char(c ^ 0100, tty); tty->column += 2; } else /* przetworzenie "końcowe" innych znaków niż kontrolne */ opost(c, tty); } /* koniec przetwarzania znaków czyszczących */ static inline void finish_erasing(struct tty_struct *tty) { if (tty->erasing) { put_char('/', tty); tty->column += 2; tty->erasing = 0; } } /* przetwarzanie znaków czyszczących */ static void eraser(unsigned char c, struct tty_struct *tty) { enum { ERASE, WERASE, KILL } kill_type; int head, seen_alnums; /* nie ma potrzeby żadnego przetwarzania; autor kodu zastanawia się, * czy w takim przypadku wydać beep */ if (tty->read_head == tty->canon_head) { /* opost('\a', tty); */ /* what do you think? */ return; } /* ustawienie typu znaku */ if (c == ERASE_CHAR(tty)) kill_type = ERASE; else if (c == WERASE_CHAR(tty)) kill_type = WERASE; else { /* wyłączone flagi echa, tylko przesuwanie kolumny */ if (!L_ECHO(tty)) { tty->read_cnt -= ((tty->read_head - tty->canon_head) & (N_TTY_BUF_SIZE - 1)); tty->read_head = tty->canon_head; return; } /* zmiana ilości znaków w buforze, ewentualne dodanie nowej linii */ if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { tty->read_cnt -= ((tty->read_head - tty->canon_head) & (N_TTY_BUF_SIZE - 1)); tty->read_head = tty->canon_head; finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty)) opost('\n', tty); return; } kill_type = KILL; } seen_alnums = 0; /* przejście wszystkich znaków w buforze aż do wyrównania miejsca * znaków przeczytanych do miejsca końca danych kanonicznych */ while (tty->read_head != tty->canon_head) { /* cofnięcie się o jeden znak */ head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1); c = tty->read_buf[head]; if (kill_type == WERASE) { /* usuwanie tylko znaków alfanumerycznych i znaku '_' */ /* Equivalent to BSD's ALTWERASE. */ if (isalnum(c) || c == '_') seen_alnums++; else if (seen_alnums) break; } /* ustawienie początku przeczytanych znaków i ilości przeczytanych * znaków */ tty->read_head = head; tty->read_cnt--; if (L_ECHO(tty)) { /* zależnie od flagi dalsze przetwarzanie ...*/ if (L_ECHOPRT(tty)) { if (!tty->erasing) { put_char('\\', tty); tty->column++; tty->erasing = 1; } echo_char(c, tty); } else if (kill_type == ERASE && !L_ECHOE(tty)) { /* L_ECHOE nie włączone, wypisujemy echo standartowego znaku * kasującego ze struktury termios */ echo_char(ERASE_CHAR(tty), tty); } else if (c == '\t') { /* przetwarzanie znaku tabulacji */ unsigned int col = tty->canon_column; unsigned long tail = tty->canon_head; /* Find the column of the last char. */ /* szukanie kolumny ostatniego znaku */ while (tail != tty->read_head) { c = tty->read_buf[tail]; if (c == '\t') col = (col | 7) + 1; else if (iscntrl(c)) { if (L_ECHOCTL(tty)) col += 2; } else col++; tail = (tail+1) & (N_TTY_BUF_SIZE-1); } /* should never happen */ if (tty->column > 0x80000000) tty->column = 0; /* dochodzi do znalezionej kolumny znakami typu backspace */ /* Now backup to that column. */ while (tty->column > col) { /* bez przetwarzania końcowego */ /* Can't use opost here. */ put_char('\b', tty); if (tty->column > 0) tty->column--; } } else { /* kasowanie znaku kontrolnego */ if (iscntrl(c) && L_ECHOCTL(tty)) { put_char('\b', tty); put_char(' ', tty); put_char('\b', tty); if (tty->column > 0) tty->column--; } if (!iscntrl(c) || L_ECHOCTL(tty)) { put_char('\b', tty); put_char(' ', tty); put_char('\b', tty); if (tty->column > 0) tty->column--; } } } if (kill_type == ERASE) break; } /* zostało zakończone ścieranie */ if (tty->read_head == tty->canon_head) finish_erasing(tty); } /* porządki związane z przyjściem sygnału */ static inline void isig(int sig, struct tty_struct *tty, int flush) { if (tty->pgrp > 0) /* wysłanie sygnału sig do grupy procesów, dla której ten terminal jest * terminalem sterującym */ kill_pg(tty->pgrp, sig, 1); if (flush || !L_NOFLSH(tty)) { /* opróżnienie buforów dyscypliny linii i sterownika */ n_tty_flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); } } /* funkcja inline - otrzymanie przerwy- pustych znaków przez pewien czas */ static inline void n_tty_receive_break(struct tty_struct *tty) { if (I_IGNBRK(tty)) /* ignorowanie przerwy */ return; if (I_BRKINT(tty)) { /* wysłanie sygnału SIGINT do wszystkich */ isig(SIGINT, tty, 1); return; } if (I_PARMRK(tty)) { put_tty_queue('\377', tty); put_tty_queue('\0', tty); } /* umieszczenie w buforze dyscypliny linii znaku pustego * i obudzenie procesu czekającego na czytanie z terminala */ put_tty_queue('\0', tty); wake_up_interruptible(&tty->read_wait); } /* otrzymanie znaku z przepełnieniem */ static inline void n_tty_receive_overrun(struct tty_struct *tty) { char buf[64]; /* zmiana licznika przepełnień i czasu przepełnienia */ tty->num_overrun++; if (tty->overrun_time < (jiffies - HZ)) { printk("%s: %d input overrun(s)\n", _tty_name(tty, buf), tty->num_overrun); tty->overrun_time = jiffies; tty->num_overrun = 0; } } /* otrzymanie błędu parzystości */ static inline void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { if (I_IGNPAR(tty)) { /* ignoruje błąd */ return; } if (I_PARMRK(tty)) { /* zamiast znaków kontroli parzystości wstawia 0 */ put_tty_queue('\377', tty); put_tty_queue('\0', tty); put_tty_queue(c, tty); } else if (I_INPCK(tty)) put_tty_queue('\0', tty); else put_tty_queue(c, tty); /* obudzenie procesu czekającego na czytanie */ wake_up_interruptible(&tty->read_wait); } /* odebranie pojedynczego znaku */ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { if (tty->raw) { /* w trybie surowym znak jest po prostu umieszczany w buforze */ put_tty_queue(c, tty); return; } /* jeżeli terminal jest zatrzymany to go startujemy */ if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) { start_tty(tty); return; } /* w zależności od ustawienia flag wejściowych przetwarzany jest znak c */ if (I_ISTRIP(tty)) c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty)) c=tolower(c); /* jeżeli ustawione tty->closing, to odpowiednio zatrzymuje albo * startuje tty */ if (tty->closing) { if (I_IXON(tty)) { if (c == START_CHAR(tty)) start_tty(tty); else if (c == STOP_CHAR(tty)) stop_tty(tty); } return; } /* * If the previous character was LNEXT, or we know that this * character is not one of the characters that we'll have to * handle specially, do shortcut processing to speed things * up. */ if (!test_bit(c, &tty->process_char_map) || tty->lnext) { /* wiemy, że ostatni znak był znakiem nowej linii, a obecny nie jest * żadnym znakiem specjalnym to zaznaczamy, że przeszliśmy do nowej linii*/ finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); /* beep if no space */ return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; /* wypisujemy echo znaku c */ echo_char(c, tty); } if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); /* wkładamy c do bufora */ put_tty_queue(c, tty); return; } if (c == '\r') { /* przetwarzanie znaków powrotu karetki i końca linii*/ if (I_IGNCR(tty)) return; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) c = '\r'; /* zatrzymanie lub startowanie tty, w zależności od znaku c*/ if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); return; } if (c == STOP_CHAR(tty)) { stop_tty(tty); return; } } /* wysłanie odpowiednich sygnałów, o ile znak c jest adekwatnym znakiem * sterującym */ if (L_ISIG(tty)) { int signal; signal = SIGINT; if (c == INTR_CHAR(tty)) goto send_signal; signal = SIGQUIT; if (c == QUIT_CHAR(tty)) goto send_signal; signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: isig(signal, tty, 0); return; } } if (L_ICANON(tty)) { /* jesteśmy w trybie kanonicznym */ /* przetwarzanie znaków czyszczących */ if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); return; } /* przetwarzanie znaków nowej linii */ if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { tty->lnext = 1; /* zaznaczamy, że ostatni jest znakiem nowej linii */ if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { put_char('^', tty); put_char('\b', tty); } } return; } /* przetwarzanie znaku REPRINT_CHAR */ if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { unsigned long tail = tty->canon_head; finish_erasing(tty); /* echo znaku c i końcowe przetworzenie znaku końca linii */ echo_char(c, tty); opost('\n', tty); while (tail != tty->read_head) { /* wypisanie wszystkich znaków w trybie kanonicznym */ echo_char(tty->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } return; } /* przetworzenie znaku nowej linii */ if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { /* próba wpisania więcej niż rozmiar bufora - beep */ put_char('\a', tty); return; } /* końcowe przetworzenie */ opost('\n', tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { /* znak końca pliku */ if (tty->canon_head != tty->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } /* znaki końca linii ustawione w termios */ if ((c == EOL_CHAR(tty)) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? r*/ if (L_ECHO(tty)) { /* nie mieści się w buforze */ if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } /* * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR? */ if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); /* przetwarzanie nowej linii */ handle_newline: set_bit(tty->read_head, &tty->read_flags); put_tty_queue(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; if (tty->fasync) kill_fasync(tty->fasync, SIGIO); if (tty->read_wait) wake_up_interruptible(&tty->read_wait); return; } } finish_erasing(tty); if (L_ECHO(tty)) { /* nie ma miejsca */ if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); /* beep if no space */ return; } /* przetwaranie końcowe znaku nowej linii */ if (c == '\n') opost('\n', tty); else { /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } } /* ustawiona flaga w termios - zmiana znaku parzystości */ if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); put_tty_queue(c, tty); } /* ta funkcja odbiera count znaków z bufora cp i obrabia je * zwykle jest wołana przez procedury "dolnej połowy" po przyjściu * przerwania; w interfejsie receive_buf */ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { const unsigned char *p; char *f, flags = 0; int i; if (!tty->read_buf) return; if (tty->real_raw) { /* tryb surowy */ /* kopiowanie do bufora zawartości cp; kopiowanie odbywa się w dwóch * fazach, ponieważ jest to bufor cykliczny */ i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head)); memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; cp += i; count -= i; i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head)); memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; } else { /* tryb kanoniczny, każdy znak przetwarzany oddzielnie w zależności od * parametru f, czyli flag */ for (i=count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; switch (flags) { case TTY_NORMAL: n_tty_receive_char(tty, *p); break; case TTY_BREAK: n_tty_receive_break(tty); break; case TTY_PARITY: case TTY_FRAME: n_tty_receive_parity_error(tty, *p); break; case TTY_OVERRUN: n_tty_receive_overrun(tty); break; default: printk("%s: unknown flag %d\n", tty_name(tty), flags); break; } } /* wywołanie funkcji flush_chars ze sterownika- wypisanie znaków z bufora*/ if (tty->driver.flush_chars) tty->driver.flush_chars(tty); } /* jeżeli w buforze jest wystarczająco wiele znaków do przeczytania i * nie działamy w trybie kanonicznym ... */ if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) { /* to wysyłamy do wszystkich grup procesów do których należą właściciele * plików z listy tty->fasync sygnał SIGIO ... */ if (tty->fasync) kill_fasync(tty->fasync, SIGIO); /* i budzimy proces z kolejki read_wait */ if (tty->read_wait) wake_up_interruptible(&tty->read_wait); } /* jeżeli jest już dużo znaków, to dyscyplina linii "przyhamowuje" * sterownik, wywołując funkcję throttle */ if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) && tty->driver.throttle && !set_bit(TTY_THROTTLED, &tty->flags)) tty->driver.throttle(tty); }
/* funkcja zwracająca liczbę znaków, które może jeszcze przyjąć od * sterownika; w interfejsie funkcja receive_room */ static int n_tty_receive_room(struct tty_struct *tty) { /* left- ilość wolnego miejsca w buforze */ int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; /* * If we are doing input canonicalization, and there are no * pending newlines, let characters through without limit, so * that erase characters will be handled. Other excess * characters will be beeped. */ /* w trybie kanonicznym nie ma żadnego ograniczenia, bo dodatkowe, nie * mieszczące się w buforze znaki będą inaczej obrobione; jedynym * ograniczeniem jest fizyczna wielkość bufora */ if (tty->icanon && !tty->canon_data) return N_TTY_BUF_SIZE; /* w pozostałych przypadkach zwraca dodatnią wielkość albo zero */ if (left > 0) return left; return 0; } /* sprawdza, czy sygnał sig jest ignorowany */ int is_ignored(int sig) { return ((current->blocked & (1<<(sig-1))) || (current->sig->action[sig-1].sa_handler == SIG_IGN)); } /* funkcja ustawiająca parametry dla dyscypliny linii w zależności od * ustawionych flag w strukturze termios opisującej terminal; * wszystkie makra I_COŚ , L_COŚ itp. zwracają odpowiednie flagi ze * struktury termios, która jest polem we właściwej tty_struct; * parametr old nie jest w tej funkcji używany; znajduje się tu ze * względu na zgodność z interfejsem, a w interfejsie to jest funkcja * set_termios*/ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) { if (!tty) return; /* ustawienie trybu kanonicznego */ tty->icanon = (L_ICANON(tty) != 0); /* ustawienie "normalnych" znaków sterujących dla dyscypliny linii */ if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { /* wyłączenie przerwań; te operacje mają być niepodzielne */ cli(); memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) set_bit('\r', &tty->process_char_map); if (I_INLCR(tty)) set_bit('\n', &tty->process_char_map); if (L_ICANON(tty)) { set_bit(ERASE_CHAR(tty), &tty->process_char_map); set_bit(KILL_CHAR(tty), &tty->process_char_map); set_bit(EOF_CHAR(tty), &tty->process_char_map); set_bit('\n', &tty->process_char_map); set_bit(EOL_CHAR(tty), &tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), &tty->process_char_map); set_bit(LNEXT_CHAR(tty), &tty->process_char_map); set_bit(EOL2_CHAR(tty), &tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), &tty->process_char_map); } } if (I_IXON(tty)) { set_bit(START_CHAR(tty), &tty->process_char_map); set_bit(STOP_CHAR(tty), &tty->process_char_map); } if (L_ISIG(tty)) { set_bit(INTR_CHAR(tty), &tty->process_char_map); set_bit(QUIT_CHAR(tty), &tty->process_char_map); set_bit(SUSP_CHAR(tty), &tty->process_char_map); } clear_bit(__DISABLED_CHAR, &tty->process_char_map); sti(); /* włączenie przerwań */ tty->raw = 0; tty->real_raw = 0; } else { /* ustawianie trybu surowego */ tty->raw = 1; if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && (I_IGNPAR(tty) || !I_INPCK(tty)) && (tty->driver.flags & TTY_DRIVER_REAL_RAW)) tty->real_raw = 1; else tty->real_raw = 0; } } /* zamknięcie dyscypliny linii, w interfejsie funkcja close */ static void n_tty_close(struct tty_struct *tty) { /* opróżnienie bufora dyscypliny linii */ n_tty_flush_buffer(tty); if (tty->read_buf) { /* zwolnienie pamięci (strony) zajmowanej przez ten bufor */ free_page((unsigned long) tty->read_buf); tty->read_buf = 0; } } /* otwarcie dyscypliny linii, w interfejsie funkcja open */ static int n_tty_open(struct tty_struct *tty) { if (!tty) return -EINVAL; if (!tty->read_buf) { /* zaalokowanie bufora dyscypliny linii, który ze względów określanych * przez autorów jako historyczne jest umieszczony w strukturze * tty_struct; jeżeli się to nie uda, to zwraca bład braku pamięci */ tty->read_buf = (unsigned char *) get_free_page(intr_count ? GFP_ATOMIC : GFP_KERNEL); if (!tty->read_buf) return -ENOMEM; } /* określenie wielkości bufora i ustawienie parametrów potrzebnych do * obsługi tego bufora */ memset(tty->read_buf, 0, N_TTY_BUF_SIZE); tty->read_head = tty->read_tail = tty->read_cnt = 0; tty->canon_head = tty->canon_data = tty->erasing = 0; tty->column = 0; /* zaalokowanie miejsca na flagi */ memset(tty->read_flags, 0, sizeof(tty->read_flags)); /* ustawienie parametrów dla dyscypliny linii */ n_tty_set_termios(tty, 0); tty->minimum_to_wake = 1; tty->closing = 0; return 0; } /* sprawdza, czy jest conajmniej amt znaków do przeczytania lub czy jest * chociaż jeden jeżeli amt jest równe zero */ static inline int input_available_p(struct tty_struct *tty, int amt) { if (L_ICANON(tty)) { if (tty->canon_data) return 1; } else if (tty->read_cnt >= (amt ? amt : 1)) return 1; return 0; } /* * Helper function to speed up read_chan. It is only called when * ICANON is off; it copies characters straight from the tty queue to * user space directly. It can be profitably called twice; once to * drain the space from the tail pointer to the (physical) end of the * buffer, and once to drain the space from the (physical) beginning of * the buffer to head pointer. */ /* funkcja pomocnicza kopiująca *nr znaków do miejsca wskazywanego przez * *b z bufora dyscypliny linii */ static inline void copy_from_read_buf(struct tty_struct *tty, unsigned char **b, unsigned int *nr) { int n; n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail)); if (!n) return; memcpy_tofs(*b, &tty->read_buf[tty->read_tail], n); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; *b += n; *nr -= n; } /* czytanie w dyscyplinie linii - w interfejsie funkcja open */ static int read_chan(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr) { struct wait_queue wait = { current, NULL }; int c; unsigned char *b = buf; int minimum, time; int retval = 0; int size; do_it_again: if (!tty->read_buf) { /* wołane przy pustym buforze */ printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); return -EIO; } /* Job control check -- must be done at start and after every sleep (POSIX.1 7.1.1.4). */ /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ /* sprawdzenie stanu pliku, procesu i terminala */ if (file->f_inode->i_rdev != CONSOLE_DEV && current->tty == tty) { if (tty->pgrp <= 0) printk("read_chan: tty->pgrp <= 0!\n"); else if (current->pgrp != tty->pgrp) { if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp)) return -EIO; kill_pg(current->pgrp, SIGTTIN, 1); return -ERESTARTSYS; } } if (L_ICANON(tty)) { /* tryb kanoniczy, minimum i czas na 0 */ minimum = time = 0; current->timeout = (unsigned long) -1; } else { /* w trybie innym niż kanoniczny - surowy lub znakowy * ustawienie parametru minimum i czasu */ time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { current->timeout = (unsigned long) -1; if (time) tty->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || (tty->minimum_to_wake > minimum)) tty->minimum_to_wake = minimum; } else { if (time) { current->timeout = time + jiffies; time = 0; } else current->timeout = 0; tty->minimum_to_wake = minimum = 1; } } /* wkładamy siebie do kolejki czekających na czytanie */ add_wait_queue(&tty->read_wait, &wait); while (1) { /* First test for status change. */ /* sprawdzenie dla pseudoterminali trybu pakietowego */ if (tty->packet && tty->link->ctrl_status) { if (b != buf) break; put_user(tty->link->ctrl_status, b++); tty->link->ctrl_status = 0; break; } /* This statement must be first before checking for input so that any interrupt will set the state back to TASK_RUNNING. */ /* zmiana stanu procesu bieżącego */ current->state = TASK_INTERRUPTIBLE; /* ustawienie mimimum_to_wake */ if (((minimum - (b - buf)) < tty->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) tty->minimum_to_wake = (minimum - (b - buf)); /* jest cokolwiek w buforze do przeczytania */ if (!input_available_p(tty, 0)) { /* sprawdzenie stanu */ if (tty->flags & (1 << TTY_OTHER_CLOSED)) { retval = -EIO; break; } if (tty_hung_up_p(file)) break; if (!current->timeout) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; break; } /* wybranie nowego procesu do wykonywania */ schedule(); continue; } current->state = TASK_RUNNING; /* zmiana stanu na TASK_RUNNING */ /* tryb pakietowy */ /* Deal with packet mode. */ if (tty->packet && b == buf) { put_user(TIOCPKT_DATA, b++); nr--; } if (L_ICANON(tty)) { /* ustawiony tryb kanoniczny */ while (1) { int eol; /* wyłączenie zadań dolnej połowy */ disable_bh(TQUEUE_BH); if (!tty->read_cnt) { /* nie ma danych */ enable_bh(TQUEUE_BH); break; } eol = clear_bit(tty->read_tail, &tty->read_flags); /* przeczytanie jednego znaku z bufora */ c = tty->read_buf[tty->read_tail]; tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); tty->read_cnt--; /* włączenie zadań dolnej połowy */ enable_bh(TQUEUE_BH); if (!eol) { put_user(c, b++); if (--nr) continue; break; } if (--tty->canon_data < 0) { tty->canon_data = 0; } if (c != __DISABLED_CHAR) { /* wstawienie znaku do bufora */ put_user(c, b++); nr--; } break; } } else { /* w trybie surowym i znakowym to samo co w kanonicznym, tyle, że * przyspieszone kopiowanie od razu całego bufora */ disable_bh(TQUEUE_BH); copy_from_read_buf(tty, &b, &nr); copy_from_read_buf(tty, &b, &nr); enable_bh(TQUEUE_BH); } /* If there is enough space in the read buffer now, let the low-level driver know. */ /* daje znać sterownikowi, że zrobiło się miejsce w buforze */ if (tty->driver.unthrottle && (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE) && clear_bit(TTY_THROTTLED, &tty->flags)) tty->driver.unthrottle(tty); if (b - buf >= minimum || !nr) break; if (time) current->timeout = time + jiffies; } /* usuwamy się z kolejki czekających na czytanie */ remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) /* przywrócenie minimum_to_wake */ tty->minimum_to_wake = minimum; /* zmiana stanu na TASK_RUNNING */ current->state = TASK_RUNNING; current->timeout = 0; /* sprawdzenie ilości przeczytanych, zmiana bitu TTY_PUSH */ size = b - buf; if (size && nr) clear_bit(TTY_PUSH, &tty->flags); if (!size && clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; if (!size && !retval) clear_bit(TTY_PUSH, &tty->flags); /* zwraca ilość przeczytanych znaków */ return (size ? size : retval); } /* czytanie w dyscyplinie linii - w interfejsie funkcja write */ static int write_chan(struct tty_struct * tty, struct file * file, const unsigned char * buf, unsigned int nr) { struct wait_queue wait = { current, NULL }; int c; const unsigned char *b = buf; int retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ /* sprawdzenie stanu pliku i terminala */ if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) { retval = tty_check_change(tty); if (retval) return retval; } /* wkładamy siebie do kolejki czytających na czytanie */ add_wait_queue(&tty->write_wait, &wait); while (1) { /* zmiana stanu zadania */ current->state = TASK_INTERRUPTIBLE; if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; break; } if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) { /* zawieszony terminal lub nie otwarta para pseudoterminalowa */ retval = -EIO; break; } if (O_OPOST(tty)) { /* ustawiona flaga OPOST - wpisuje znaki do bufora b*/ while (nr > 0) { c = get_user(b); if (opost(c, tty) < 0) break; b++; nr--; } /* opróżnienie bufora sterownika */ if (tty->driver.flush_chars) tty->driver.flush_chars(tty); } else { /* wywołanie funkcji write ze sterownika */ c = tty->driver.write(tty, 1, b, nr); b += c; nr -= c; } if (!nr) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } /* wybranie nowego procesu */ schedule(); } /* zmiana stanu zadania i usunięcie siebie z kolejki */ current->state = TASK_RUNNING; remove_wait_queue(&tty->write_wait, &wait); /* zwraca ilość zapisanych znaków, albo kod błędu */ return (b - buf) ? b - buf : retval; } /* funkcja wywoływana przy zastosowaniu funkcji select do pliku specjalnego * terminala */ static int normal_select(struct tty_struct * tty, struct inode * inode, struct file * file, int sel_type, select_table *wait) { switch (sel_type) { case SEL_IN: /* sprawdzenie, czy dostępne dane wejściowe */ if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty))) return 1; /* fall through */ case SEL_EX: /* tryb pakietowy, zamknięty, drugi z pary lub zawieszony to zwraca 1 */ if (tty->packet && tty->link->ctrl_status) return 1; if (tty->flags & (1 << TTY_OTHER_CLOSED)) return 1; if (tty_hung_up_p(file)) return 1; if (!waitqueue_active(&tty->read_wait)) { /* aktywna kolejka czekających na czytanie */ if (MIN_CHAR(tty) && !TIME_CHAR(tty)) tty->minimum_to_wake = MIN_CHAR(tty); else tty->minimum_to_wake = 1; } /* wybierany czekający */ select_wait(&tty->read_wait, wait); return 0; case SEL_OUT: /* zwraca 1, jeżeli znaków w buforze sterownika jest mniej niż * WAKEUP_CHARS, w przeciwnym wypadku wybór zadania do pisania */ if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS) return 1; select_wait(&tty->write_wait, wait); return 0; } return 0; } /* interfejs dyscypliny linii N_TTY- struktura tty_ldisc jest zdefiniowan * w pliku tty_ldisc.h */ struct tty_ldisc tty_ldisc_N_TTY = { TTY_LDISC_MAGIC, /* magic */ 0, /* num */ 0, /* flags */ n_tty_open, /* open */ n_tty_close, /* close */ n_tty_flush_buffer, /* flush_buffer */ n_tty_chars_in_buffer, /* chars_in_buffer */ read_chan, /* read */ write_chan, /* write */ n_tty_ioctl, /* ioctl */ /* zdefiniowana w pliku tty_ioctl.c */ n_tty_set_termios, /* set_termios */ normal_select, /* select */ n_tty_receive_buf, /* receive_buf */ n_tty_receive_room, /* receive_room */ 0 /* write_wakeup */ };
Autorka komentarzy: Maja Królikowska