pty.c
/* w tym pliku znajduja sie funkcje dotyczące pseudoterminali */
/* linux/drivers/char/pty.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* pty.c
*
* This module exports the following pty function:
*
* int pty_open(struct tty_struct * tty, struct file * filp);
*
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
/* struktura do opisu pty_struct */
struct pty_struct {
int magic;
struct wait_queue * open_wait;
};
#define PTY_MAGIC 0x5001
#define PTY_BUF_SIZE PAGE_SIZE/2
/*
* tmp_buf is used as a temporary buffer by pty_write. We need to
* lock it in case the memcpy_fromfs blocks while swapping in a page,
* and some other program tries to do a pty write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the PTY's, since it significantly saves memory if
* large numbers of PTY's are open.
*/
/*
* tmp_buf jest używany jako bufor tymczasowy przez funkcję pty_write.
* Ten bufor jest wspólny dla obu procesów z pary, nie ma przesyłania
* danych.
*/
static unsigned char *tmp_buf;
/*
* semafor używany do synchronizacji pisania
*/
static struct semaphore tmp_buf_sem = MUTEX;
struct tty_driver pty_driver, pty_slave_driver;
struct tty_driver old_pty_driver, old_pty_slave_driver;
static int pty_refcount;
static struct tty_struct *pty_table[NR_PTYS];
static struct termios *pty_termios[NR_PTYS];
static struct termios *pty_termios_locked[NR_PTYS];
/* tablice dla części nadrzędnych pseudoterminali */
static struct tty_struct *ttyp_table[NR_PTYS];
static struct termios *ttyp_termios[NR_PTYS];
static struct termios *ttyp_termios_locked[NR_PTYS];
/* tablice dla części podrzędnych pseudoterminali */
static struct pty_struct pty_state[NR_PTYS];
/* tablica stanów pseudoterminali */
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* zamykanie pseudoterminala */
static void pty_close(struct tty_struct * tty, struct file * filp)
{
if (!tty)
return;
if (tty->driver.subtype == PTY_TYPE_MASTER) {
/* część nadrzędna pseudoterminala */
if (tty->count > 1)
/* jeszcze ktoś otwierał ten terminal ...*/
printk("master pty_close: count = %d!!\n", tty->count);
} else {
/* więcej niż dwa razy otwarto ten terminal - wyjście z funkcji*/
if (tty->count > 2)
return;
}
/* obudzenie procesów: czekającego na czytanie i pisanie */
wake_up_interruptible(&tty->read_wait);
wake_up_interruptible(&tty->write_wait);
if (!tty->link)
/* nie ma drugiego z pary */
return;
/* budzenie procesu piszącego i czytającego u drugiego członka pary */
wake_up_interruptible(&tty->link->read_wait);
wake_up_interruptible(&tty->link->write_wait);
/* ustawienie flagi */
set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
if (tty->driver.subtype == PTY_TYPE_MASTER) {
/* jeżeli to terminal nadrzędny, to podrzędnemu robi tty_hangup
* (wysyła SIGHUP do procesów dla których jest on sterującym)*/
tty_hangup(tty->link);
set_bit(TTY_OTHER_CLOSED, &tty->flags);
}
}
/*
* The unthrottle routine is called by the line discipline to signal
* that it can receive more characters. For PTY's, the TTY_THROTTLED
* flag is always set, to force the line discipline to always call the
* unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
* characters in the queue. This is necessary since each time this
* happens, we need to wake up any sleeping processes that could be
* (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
* for the pty buffer to be drained.
*/
/* ta funkcja jest wołana przez dyscyplinę linii dla zasygnalizowania, że
* może ona przyjąć więcej znaków. Dla pseudoterminali flaga TTY_THROTTLED
* jest zawsze ustawiona, żeby zmusić dyscyplinę linii do wołania procedury
* unthrottle jeśli tylko jest mniej niż TTY_THRESHOLD_UNTHROTTLE znaków
* w buforze */
static void pty_unthrottle(struct tty_struct * tty)
{
struct tty_struct *o_tty = tty->link;
if (!o_tty)
return;
if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
o_tty->ldisc.write_wakeup)
/* funkcja write_wakup wołana z dyscypliny linii terminala podrzędnego
* proces zleca sterownikowi pisanie, a sam zasypia, ale na przykład
* w dyscyplinie linii N_TTY ta funkcja nie jest zaimplementowana */
(o_tty->ldisc.write_wakeup)(o_tty);
/* budzony jest proces czekający na pisanie */
wake_up_interruptible(&o_tty->write_wait);
set_bit(TTY_THROTTLED, &tty->flags);
}
/* pisanie do pseudoterminala */
static int pty_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
struct tty_struct *to = tty->link;
int c=0, n, r;
char *temp_buffer;
/* nie ma drugiego z pary albo zatrzymany */
if (!to || tty->stopped)
return 0;
if (from_user) {
/* wzajemne wykluczanie pisania za pomocą semafora systemowego */
down(&tmp_buf_sem);
/* temp_buffer ustawia się na początku strony przydzielonej na tmp_buf
* w funkcji pty_open i dla części nadrzędnej i w połowie dla części
* podrzędnej */
temp_buffer = tmp_buf +
((tty->driver.subtype-1) * PTY_BUF_SIZE);
/* w pętli stara się zapisać wszystkie count znaków */
while (count > 0) {
/* kopiuje count albo tyle ile się zmieści znaków do temp_buffer */
n = MIN(count, PTY_BUF_SIZE);
memcpy_fromfs(temp_buffer, buf, n);
/* pyta dyscyplinę linii ile znaków ta może przyjąć */
r = to->ldisc.receive_room(to);
if (r <= 0)
break;
/* wysyła tyle znaków do bufora dyscypliny linii ile ta może pomieścić */
n = MIN(n, r);
to->ldisc.receive_buf(to, temp_buffer, 0, n);
buf += n; c+= n;
count -= n;
} /* while */
up(&tmp_buf_sem);
} else {
/* nie ustawione from_user - na dyscyplinę linii wpisuje tylko tyle
* znaków ile ona może zmieścić*/
c = MIN(count, to->ldisc.receive_room(to));
to->ldisc.receive_buf(to, buf, 0, c);
}
/* zwraca ilość zapisanych znaków */
return c;
}
/* zwraca ile znaków można zapisać */
static int pty_write_room(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
if (!to || tty->stopped)
return 0;
/* wywołuje funkcję receive_room z dyscypliny linii drugiego z pary
* pseudoterminalowej */
return to->ldisc.receive_room(to);
}
/*
* Modified for asymmetric master/slave behavior
* The chars_in_buffer() value is used by the ldisc select() function
* to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
* To allow typed-ahead commands to accumulate, the master side returns 0
* until the buffer is half full. The slave side returns the true count.
*/
/* funkcja używana przez select() z dyscypliny linii do wstrzymywania
* pisania, inaczej działa dla części podrzędnej, a inaczej do nadrzędnej
* pseudoterminala */
static int pty_chars_in_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
int count;
if (!to || !to->ldisc.chars_in_buffer)
return 0;
/* The ldisc must report 0 if no characters available to be read */
/* count - liczba znaków do przeczytania w dyscyplinie linii*/
count = to->ldisc.chars_in_buffer(to);
/* dla części podrzędnej zwraca count */
if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;
/*
* Master side driver ... return 0 if the other side's read buffer
* is less than half full. This allows room for typed-ahead commands
* with a reasonable margin to avoid overflow.
*/
/* dla części nadrzędnej zwraca 0 jeżeli bufor drugiego z pary jest mniej
* niż w połowie pełny. To zostawia miejsce dla pisania z wyprzedzeniem
* i zapobiega przepełnieniu */
return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
}
/* opróżnianie bufora */
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
if (!to)
return;
/* opróżnienie bufora dyscypliny linii */
if (to->ldisc.flush_buffer)
to->ldisc.flush_buffer(to);
/* w trybie pakietowym zmiana stanu kontrolnego i budzenie procesu
* czekającego na pisanie */
if (to->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&to->read_wait);
}
}
/* otwieranie pseudoterminala
* w tej funkcji nie widać bezpośrednio tego, że terminale podrzędny i
* nadrzędny są inaczej otwierane. We wcześniejszej wersji linux-a ta
* funkcja wyglądała inaczej, obecnie to rozróżnienie jest realizowane
* w funkcji init_dev z pliku tty_io.c*/
int pty_open(struct tty_struct *tty, struct file * filp)
{
int retval;
int line;
struct pty_struct *pty;
/* sprawdzenie stanu terminala, znalezienie jego numeru (numeru
* pseudoterminala) na podstawie numeru głównego i drugorzędnego */
retval = -ENODEV;
if (!tty || !tty->link)
goto out;
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PTYS))
goto out;
/* na pty przypisana struktura pty_struct właściwa dla terminala */
pty = pty_state + line;
tty->driver_data = pty;
if (!tmp_buf) {
/* nie jest zaalokowany bufor */
unsigned long page = __get_free_page(GFP_KERNEL);
if (!tmp_buf) {
retval = -ENOMEM;
if (!page)
goto out;
tmp_buf = (unsigned char *) page;
memset((void *) page, 0, PAGE_SIZE);
} else
free_page(page);
}
retval = -EIO;
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
/* jest ustawione TTY_OTHER_CLOSED */
goto out;
if (tty->link->count != 1)
/* na drugim z pary wykonano nie jedną operację open ( w domyśle zero)*/
goto out;
/* ustawiamy TTY_OTHER_CLOSED w drugim z pary */
clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
/* budzimy proces z kolejki open_wait
* to jest dziwne, bo nic nie jest do tej kolejki wkładane*/
wake_up_interruptible(&pty->open_wait);
/* ustawiamy TTY_THROTTLED */
set_bit(TTY_THROTTLED, &tty->flags);
retval = 0;
out:
return retval;
}
/* ustawienie flag w strukturze termios */
static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
tty->termios->c_cflag &= ~(CSIZE | PARENB);
tty->termios->c_cflag |= (CS8 | CREAD);
}
/* inicjalizacja pseudoterminali */
int pty_init(void)
{
/* uzupełnienie struktury tty_driver dla części nadrzędnej
* pseudoterminala*/
memset(&pty_state, 0, sizeof(pty_state));
memset(&pty_driver, 0, sizeof(struct tty_driver));
pty_driver.magic = TTY_DRIVER_MAGIC;
pty_driver.name = "pty";
pty_driver.major = PTY_MASTER_MAJOR;
pty_driver.minor_start = 0;
pty_driver.num = NR_PTYS;
pty_driver.type = TTY_DRIVER_TYPE_PTY;
pty_driver.subtype = PTY_TYPE_MASTER;
pty_driver.init_termios = tty_std_termios;
pty_driver.init_termios.c_iflag = 0;
pty_driver.init_termios.c_oflag = 0;
pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
pty_driver.init_termios.c_lflag = 0;
/* część nadrzędna działa w trybie surowym */
pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
pty_driver.refcount = &pty_refcount;
pty_driver.table = pty_table;
pty_driver.termios = pty_termios;
pty_driver.termios_locked = pty_termios_locked;
pty_driver.other = &pty_slave_driver;
/* uzupełnienie struktury tty_driver dla części podrzędnej */
pty_driver.open = pty_open;
pty_driver.close = pty_close;
pty_driver.write = pty_write;
pty_driver.write_room = pty_write_room;
pty_driver.flush_buffer = pty_flush_buffer;
pty_driver.chars_in_buffer = pty_chars_in_buffer;
pty_driver.unthrottle = pty_unthrottle;
pty_driver.set_termios = pty_set_termios;
pty_slave_driver = pty_driver;
pty_slave_driver.name = "ttyp";
pty_slave_driver.subtype = PTY_TYPE_SLAVE;
pty_slave_driver.major = PTY_SLAVE_MAJOR;
pty_slave_driver.minor_start = 0;
pty_slave_driver.init_termios = tty_std_termios;
pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
pty_slave_driver.table = ttyp_table;
pty_slave_driver.termios = ttyp_termios;
pty_slave_driver.termios_locked = ttyp_termios_locked;
pty_slave_driver.other = &pty_driver;
/* wypełnienie struktur old_pty_driver i old_pty_slave_driver ;
* mają one numer główny normalengo terminala, wskazują na siebie
* jako członków pary */
old_pty_driver = pty_driver;
old_pty_driver.major = TTY_MAJOR;
old_pty_driver.minor_start = 128;
old_pty_driver.num = (NR_PTYS >64) ? 64 : NR_PTYS;
old_pty_driver.other = &old_pty_slave_driver;
old_pty_slave_driver = pty_slave_driver;
old_pty_slave_driver.major = TTY_MAJOR;
old_pty_slave_driver.minor_start = 192;
old_pty_slave_driver.num = (NR_PTYS >64) ? 64 : NR_PTYS;
old_pty_slave_driver.other = &old_pty_driver;
tmp_buf = 0;
/* zarejestrowanie sterowników, "nowych" z numerami głównymi
* PTY_MASTER_MAJOR i PTY_SLAVE_MAJOR i starych z TTY_MAJOR
* trudno orzec po co to jest zrobione, wygląda tak, jakby każdy
* pseudoterminal był umieszczony jednocześnie w dwóch miejscach
* tablicy rozdzielczej */
if (tty_register_driver(&pty_driver))
panic("Couldn't register pty driver");
if (tty_register_driver(&pty_slave_driver))
panic("Couldn't register pty slave driver");
if (tty_register_driver(&old_pty_driver))
panic("Couldn't register compat pty driver");
if (tty_register_driver(&old_pty_slave_driver))
panic("Couldn't register compat pty slave driver");
return 0;
}
Autorka komentarzy : Maja Królikowska