1433d6423SLionel Sambuc /* TTY part of PTY. */
2433d6423SLionel Sambuc #include <assert.h>
3433d6423SLionel Sambuc #include <minix/drivers.h>
4433d6423SLionel Sambuc #include <minix/driver.h>
5da21d850SDavid van Moolenbroek #include <minix/optset.h>
6433d6423SLionel Sambuc #include <termios.h>
7433d6423SLionel Sambuc #include <sys/ttycom.h>
8433d6423SLionel Sambuc #include <sys/ttydefaults.h>
9433d6423SLionel Sambuc #include <sys/fcntl.h>
10433d6423SLionel Sambuc #include <signal.h>
11433d6423SLionel Sambuc #include "tty.h"
12433d6423SLionel Sambuc
13433d6423SLionel Sambuc #include <sys/time.h>
14433d6423SLionel Sambuc #include <sys/select.h>
15433d6423SLionel Sambuc
16433d6423SLionel Sambuc /* Address of a tty structure. */
17433d6423SLionel Sambuc #define tty_addr(line) (&tty_table[line])
18433d6423SLionel Sambuc
19433d6423SLionel Sambuc /* Macros for magic tty structure pointers. */
20433d6423SLionel Sambuc #define FIRST_TTY tty_addr(0)
21433d6423SLionel Sambuc #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0]))
22433d6423SLionel Sambuc
23433d6423SLionel Sambuc /* A device exists if at least its 'devread' function is defined. */
24433d6423SLionel Sambuc #define tty_active(tp) ((tp)->tty_devread != NULL)
25433d6423SLionel Sambuc
26*cfd712b4SDavid van Moolenbroek static void tty_timed_out(int arg);
27433d6423SLionel Sambuc static void settimer(tty_t *tty_ptr, int enable);
28433d6423SLionel Sambuc static void in_transfer(tty_t *tp);
29433d6423SLionel Sambuc static int tty_echo(tty_t *tp, int ch);
30433d6423SLionel Sambuc static void rawecho(tty_t *tp, int ch);
31433d6423SLionel Sambuc static int back_over(tty_t *tp);
32433d6423SLionel Sambuc static void reprint(tty_t *tp);
33433d6423SLionel Sambuc static void dev_ioctl(tty_t *tp);
34433d6423SLionel Sambuc static void setattr(tty_t *tp);
35433d6423SLionel Sambuc static void tty_icancel(tty_t *tp);
36433d6423SLionel Sambuc
37433d6423SLionel Sambuc static int do_open(devminor_t minor, int access, endpoint_t user_endpt);
38433d6423SLionel Sambuc static int do_close(devminor_t minor);
39433d6423SLionel Sambuc static ssize_t do_read(devminor_t minor, u64_t position, endpoint_t endpt,
40433d6423SLionel Sambuc cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
41433d6423SLionel Sambuc static ssize_t do_write(devminor_t minor, u64_t position, endpoint_t endpt,
42433d6423SLionel Sambuc cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
43433d6423SLionel Sambuc static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
44433d6423SLionel Sambuc static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt);
45433d6423SLionel Sambuc
46433d6423SLionel Sambuc static struct chardriver tty_tab = {
47433d6423SLionel Sambuc .cdr_open = do_open,
48433d6423SLionel Sambuc .cdr_close = do_close,
49433d6423SLionel Sambuc .cdr_read = do_read,
50433d6423SLionel Sambuc .cdr_write = do_write,
51da21d850SDavid van Moolenbroek .cdr_ioctl = tty_ioctl,
52433d6423SLionel Sambuc .cdr_cancel = do_cancel,
53433d6423SLionel Sambuc .cdr_select = do_select
54433d6423SLionel Sambuc };
55433d6423SLionel Sambuc
56433d6423SLionel Sambuc /* Default attributes. */
57433d6423SLionel Sambuc static struct termios termios_defaults = {
58433d6423SLionel Sambuc .c_iflag = TTYDEF_IFLAG,
59433d6423SLionel Sambuc .c_oflag = TTYDEF_OFLAG,
60433d6423SLionel Sambuc .c_cflag = TTYDEF_CFLAG,
61433d6423SLionel Sambuc .c_lflag = TTYDEF_LFLAG,
62433d6423SLionel Sambuc .c_ispeed = TTYDEF_SPEED,
63433d6423SLionel Sambuc .c_ospeed = TTYDEF_SPEED,
64433d6423SLionel Sambuc .c_cc = {
65433d6423SLionel Sambuc [VEOF] = CEOF,
66433d6423SLionel Sambuc [VEOL] = CEOL,
67433d6423SLionel Sambuc [VERASE] = CERASE,
68433d6423SLionel Sambuc [VINTR] = CINTR,
69433d6423SLionel Sambuc [VKILL] = CKILL,
70433d6423SLionel Sambuc [VMIN] = CMIN,
71433d6423SLionel Sambuc [VQUIT] = CQUIT,
72433d6423SLionel Sambuc [VTIME] = CTIME,
73433d6423SLionel Sambuc [VSUSP] = CSUSP,
74433d6423SLionel Sambuc [VSTART] = CSTART,
75433d6423SLionel Sambuc [VSTOP] = CSTOP,
76433d6423SLionel Sambuc [VREPRINT] = CREPRINT,
77433d6423SLionel Sambuc [VLNEXT] = CLNEXT,
78433d6423SLionel Sambuc [VDISCARD] = CDISCARD,
79433d6423SLionel Sambuc [VSTATUS] = CSTATUS
80433d6423SLionel Sambuc }
81433d6423SLionel Sambuc };
82433d6423SLionel Sambuc static struct winsize winsize_defaults; /* = all zeroes */
83433d6423SLionel Sambuc
84391516ddSDavid van Moolenbroek static const char lined[TTLINEDNAMELEN] = "termios"; /* line discipline */
85391516ddSDavid van Moolenbroek
86433d6423SLionel Sambuc /* Global variables for the TTY task (declared extern in tty.h). */
87433d6423SLionel Sambuc tty_t tty_table[NR_PTYS];
88433d6423SLionel Sambuc u32_t system_hz;
89da21d850SDavid van Moolenbroek int tty_gid;
90da21d850SDavid van Moolenbroek
91da21d850SDavid van Moolenbroek static struct optset optset_table[] = {
92da21d850SDavid van Moolenbroek { "gid", OPT_INT, &tty_gid, 10 },
93da21d850SDavid van Moolenbroek { NULL, 0, NULL, 0 }
94da21d850SDavid van Moolenbroek };
95433d6423SLionel Sambuc
96433d6423SLionel Sambuc static void tty_startup(void);
97433d6423SLionel Sambuc static int tty_init(int, sef_init_info_t *);
98433d6423SLionel Sambuc
99433d6423SLionel Sambuc /*===========================================================================*
100da21d850SDavid van Moolenbroek * is_pty *
101da21d850SDavid van Moolenbroek *===========================================================================*/
is_pty(devminor_t line)102da21d850SDavid van Moolenbroek static int is_pty(devminor_t line)
103da21d850SDavid van Moolenbroek {
104da21d850SDavid van Moolenbroek /* Return TRUE if the given minor device number refers to a pty (the master
105da21d850SDavid van Moolenbroek * side of a pty/tty pair), and FALSE otherwise.
106da21d850SDavid van Moolenbroek */
107da21d850SDavid van Moolenbroek
108da21d850SDavid van Moolenbroek if (line == PTMX_MINOR)
109da21d850SDavid van Moolenbroek return TRUE;
110da21d850SDavid van Moolenbroek if (line >= PTYPX_MINOR && line < PTYPX_MINOR + NR_PTYS)
111da21d850SDavid van Moolenbroek return TRUE;
112da21d850SDavid van Moolenbroek if (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2 && !(line & 1))
113da21d850SDavid van Moolenbroek return TRUE;
114da21d850SDavid van Moolenbroek
115da21d850SDavid van Moolenbroek return FALSE;
116da21d850SDavid van Moolenbroek }
117da21d850SDavid van Moolenbroek
118da21d850SDavid van Moolenbroek /*===========================================================================*
119433d6423SLionel Sambuc * tty_task *
120433d6423SLionel Sambuc *===========================================================================*/
main(int argc,char ** argv)121da21d850SDavid van Moolenbroek int main(int argc, char **argv)
122433d6423SLionel Sambuc {
123433d6423SLionel Sambuc /* Main routine of the terminal task. */
124433d6423SLionel Sambuc
125433d6423SLionel Sambuc message tty_mess; /* buffer for all incoming messages */
126433d6423SLionel Sambuc int ipc_status;
127433d6423SLionel Sambuc int line;
128433d6423SLionel Sambuc int r;
129433d6423SLionel Sambuc register tty_t *tp;
130433d6423SLionel Sambuc
131da21d850SDavid van Moolenbroek env_setargs(argc, argv);
132da21d850SDavid van Moolenbroek
133433d6423SLionel Sambuc tty_startup();
134433d6423SLionel Sambuc
135433d6423SLionel Sambuc while (TRUE) {
136433d6423SLionel Sambuc /* Check for and handle any events on any of the ttys. */
137433d6423SLionel Sambuc for (tp = FIRST_TTY; tp < END_TTY; tp++) {
138433d6423SLionel Sambuc if (tp->tty_events) handle_events(tp);
139433d6423SLionel Sambuc }
140433d6423SLionel Sambuc
141433d6423SLionel Sambuc /* Get a request message. */
142433d6423SLionel Sambuc r= driver_receive(ANY, &tty_mess, &ipc_status);
143433d6423SLionel Sambuc if (r != 0)
144433d6423SLionel Sambuc panic("driver_receive failed with: %d", r);
145433d6423SLionel Sambuc
146433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
147433d6423SLionel Sambuc switch (_ENDPOINT_P(tty_mess.m_source)) {
148433d6423SLionel Sambuc case CLOCK:
149433d6423SLionel Sambuc /* run watchdogs of expired timers */
150433d6423SLionel Sambuc expire_timers(tty_mess.m_notify.timestamp);
151433d6423SLionel Sambuc break;
152433d6423SLionel Sambuc default:
153433d6423SLionel Sambuc /* do nothing */
154433d6423SLionel Sambuc break;
155433d6423SLionel Sambuc }
156433d6423SLionel Sambuc
157433d6423SLionel Sambuc /* done, get new message */
158433d6423SLionel Sambuc continue;
159433d6423SLionel Sambuc }
160433d6423SLionel Sambuc
161433d6423SLionel Sambuc if (!IS_CDEV_RQ(tty_mess.m_type)) {
162433d6423SLionel Sambuc chardriver_process(&tty_tab, &tty_mess, ipc_status);
163433d6423SLionel Sambuc continue;
164433d6423SLionel Sambuc }
165433d6423SLionel Sambuc
166433d6423SLionel Sambuc /* Only device requests should get to this point.
167433d6423SLionel Sambuc * All requests have a minor device number.
168433d6423SLionel Sambuc */
169433d6423SLionel Sambuc if (OK != chardriver_get_minor(&tty_mess, &line))
170433d6423SLionel Sambuc continue;
171433d6423SLionel Sambuc
172da21d850SDavid van Moolenbroek if (is_pty(line)) {
173433d6423SLionel Sambuc /* Terminals and pseudo terminals belong together. We can only
174da21d850SDavid van Moolenbroek * make a distinction between the two based on minor number and
175da21d850SDavid van Moolenbroek * not on position in the tty_table. Hence this special case.
176433d6423SLionel Sambuc */
177433d6423SLionel Sambuc do_pty(&tty_mess, ipc_status);
178433d6423SLionel Sambuc continue;
179433d6423SLionel Sambuc }
180433d6423SLionel Sambuc
181433d6423SLionel Sambuc /* Execute the requested device driver function. */
182433d6423SLionel Sambuc chardriver_process(&tty_tab, &tty_mess, ipc_status);
183433d6423SLionel Sambuc }
184433d6423SLionel Sambuc
185433d6423SLionel Sambuc return 0;
186433d6423SLionel Sambuc }
187433d6423SLionel Sambuc
188433d6423SLionel Sambuc tty_t *
line2tty(devminor_t line)189433d6423SLionel Sambuc line2tty(devminor_t line)
190433d6423SLionel Sambuc {
191433d6423SLionel Sambuc /* Convert a terminal line to tty_table pointer */
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc tty_t* tp;
194433d6423SLionel Sambuc
195433d6423SLionel Sambuc if ((line - TTYPX_MINOR) < NR_PTYS) {
196433d6423SLionel Sambuc tp = tty_addr(line - TTYPX_MINOR);
197433d6423SLionel Sambuc } else if ((line - PTYPX_MINOR) < NR_PTYS) {
198433d6423SLionel Sambuc tp = tty_addr(line - PTYPX_MINOR);
199da21d850SDavid van Moolenbroek } else if ((line - UNIX98_MINOR) < NR_PTYS * 2) {
200da21d850SDavid van Moolenbroek tp = tty_addr((line - UNIX98_MINOR) >> 1);
201433d6423SLionel Sambuc } else {
202433d6423SLionel Sambuc tp = NULL;
203433d6423SLionel Sambuc }
204433d6423SLionel Sambuc
205433d6423SLionel Sambuc return(tp);
206433d6423SLionel Sambuc }
207433d6423SLionel Sambuc
208433d6423SLionel Sambuc /*===========================================================================*
209433d6423SLionel Sambuc * tty_startup *
210433d6423SLionel Sambuc *===========================================================================*/
tty_startup(void)211433d6423SLionel Sambuc static void tty_startup(void)
212433d6423SLionel Sambuc {
213433d6423SLionel Sambuc /* Register init callbacks. */
214433d6423SLionel Sambuc sef_setcb_init_fresh(tty_init);
215433d6423SLionel Sambuc sef_setcb_init_restart(tty_init);
216433d6423SLionel Sambuc
217433d6423SLionel Sambuc /* No signal support for now. */
218433d6423SLionel Sambuc
219433d6423SLionel Sambuc /* Let SEF perform startup. */
220433d6423SLionel Sambuc sef_startup();
221433d6423SLionel Sambuc }
222433d6423SLionel Sambuc
223433d6423SLionel Sambuc /*===========================================================================*
224433d6423SLionel Sambuc * do_read *
225433d6423SLionel Sambuc *===========================================================================*/
do_read(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)226433d6423SLionel Sambuc static ssize_t do_read(devminor_t minor, u64_t UNUSED(position),
227433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
228433d6423SLionel Sambuc cdev_id_t id)
229433d6423SLionel Sambuc {
230433d6423SLionel Sambuc /* A process wants to read from a terminal. */
231433d6423SLionel Sambuc tty_t *tp;
232433d6423SLionel Sambuc int r;
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
235433d6423SLionel Sambuc return ENXIO;
236433d6423SLionel Sambuc
237433d6423SLionel Sambuc /* Check if there is already a process hanging in a read, check if the
238433d6423SLionel Sambuc * parameters are correct, do I/O.
239433d6423SLionel Sambuc */
240433d6423SLionel Sambuc if (tp->tty_incaller != NONE || tp->tty_inleft > 0)
241433d6423SLionel Sambuc return EIO;
242433d6423SLionel Sambuc if (size <= 0)
243433d6423SLionel Sambuc return EINVAL;
244433d6423SLionel Sambuc
245433d6423SLionel Sambuc /* Copy information from the message to the tty struct. */
246433d6423SLionel Sambuc tp->tty_incaller = endpt;
247433d6423SLionel Sambuc tp->tty_inid = id;
248433d6423SLionel Sambuc tp->tty_ingrant = grant;
249433d6423SLionel Sambuc assert(tp->tty_incum == 0);
250433d6423SLionel Sambuc tp->tty_inleft = size;
251433d6423SLionel Sambuc
252433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ICANON) && tp->tty_termios.c_cc[VTIME] > 0) {
253433d6423SLionel Sambuc if (tp->tty_termios.c_cc[VMIN] == 0) {
254433d6423SLionel Sambuc /* MIN & TIME specify a read timer that finishes the
255433d6423SLionel Sambuc * read in TIME/10 seconds if no bytes are available.
256433d6423SLionel Sambuc */
257433d6423SLionel Sambuc settimer(tp, TRUE);
258433d6423SLionel Sambuc tp->tty_min = 1;
259433d6423SLionel Sambuc } else {
260433d6423SLionel Sambuc /* MIN & TIME specify an inter-byte timer that may
261433d6423SLionel Sambuc * have to be cancelled if there are no bytes yet.
262433d6423SLionel Sambuc */
263433d6423SLionel Sambuc if (tp->tty_eotct == 0) {
264433d6423SLionel Sambuc settimer(tp, FALSE);
265433d6423SLionel Sambuc tp->tty_min = tp->tty_termios.c_cc[VMIN];
266433d6423SLionel Sambuc }
267433d6423SLionel Sambuc }
268433d6423SLionel Sambuc }
269433d6423SLionel Sambuc
270433d6423SLionel Sambuc /* Anything waiting in the input buffer? Clear it out... */
271433d6423SLionel Sambuc in_transfer(tp);
272433d6423SLionel Sambuc /* ...then go back for more. */
273433d6423SLionel Sambuc handle_events(tp);
274433d6423SLionel Sambuc if (tp->tty_inleft == 0)
275433d6423SLionel Sambuc return EDONTREPLY; /* already done */
276433d6423SLionel Sambuc
277433d6423SLionel Sambuc /* There were no bytes in the input queue available. */
278433d6423SLionel Sambuc if (flags & CDEV_NONBLOCK) {
279433d6423SLionel Sambuc tty_icancel(tp);
280433d6423SLionel Sambuc r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
281433d6423SLionel Sambuc tp->tty_inleft = tp->tty_incum = 0;
282433d6423SLionel Sambuc tp->tty_incaller = NONE;
283433d6423SLionel Sambuc return r;
284433d6423SLionel Sambuc }
285433d6423SLionel Sambuc
286433d6423SLionel Sambuc if (tp->tty_select_ops)
287433d6423SLionel Sambuc select_retry(tp);
288433d6423SLionel Sambuc
289433d6423SLionel Sambuc return EDONTREPLY; /* suspend the caller */
290433d6423SLionel Sambuc }
291433d6423SLionel Sambuc
292433d6423SLionel Sambuc /*===========================================================================*
293433d6423SLionel Sambuc * do_write *
294433d6423SLionel Sambuc *===========================================================================*/
do_write(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)295433d6423SLionel Sambuc static ssize_t do_write(devminor_t minor, u64_t UNUSED(position),
296433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
297433d6423SLionel Sambuc cdev_id_t id)
298433d6423SLionel Sambuc {
299433d6423SLionel Sambuc /* A process wants to write on a terminal. */
300433d6423SLionel Sambuc tty_t *tp;
301433d6423SLionel Sambuc int r;
302433d6423SLionel Sambuc
303433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
304433d6423SLionel Sambuc return ENXIO;
305433d6423SLionel Sambuc
306433d6423SLionel Sambuc /* Check if there is already a process hanging in a write, check if the
307433d6423SLionel Sambuc * parameters are correct, do I/O.
308433d6423SLionel Sambuc */
309433d6423SLionel Sambuc if (tp->tty_outcaller != NONE || tp->tty_outleft > 0)
310433d6423SLionel Sambuc return EIO;
311433d6423SLionel Sambuc if (size <= 0)
312433d6423SLionel Sambuc return EINVAL;
313433d6423SLionel Sambuc
314433d6423SLionel Sambuc /* Copy message parameters to the tty structure. */
315433d6423SLionel Sambuc tp->tty_outcaller = endpt;
316433d6423SLionel Sambuc tp->tty_outid = id;
317433d6423SLionel Sambuc tp->tty_outgrant = grant;
318433d6423SLionel Sambuc assert(tp->tty_outcum == 0);
319433d6423SLionel Sambuc tp->tty_outleft = size;
320433d6423SLionel Sambuc
321433d6423SLionel Sambuc /* Try to write. */
322433d6423SLionel Sambuc handle_events(tp);
323433d6423SLionel Sambuc if (tp->tty_outleft == 0)
324433d6423SLionel Sambuc return EDONTREPLY; /* already done */
325433d6423SLionel Sambuc
326433d6423SLionel Sambuc /* None or not all the bytes could be written. */
327433d6423SLionel Sambuc if (flags & CDEV_NONBLOCK) {
328433d6423SLionel Sambuc r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
329433d6423SLionel Sambuc tp->tty_outleft = tp->tty_outcum = 0;
330433d6423SLionel Sambuc tp->tty_outcaller = NONE;
331433d6423SLionel Sambuc return r;
332433d6423SLionel Sambuc }
333433d6423SLionel Sambuc
334433d6423SLionel Sambuc if (tp->tty_select_ops)
335433d6423SLionel Sambuc select_retry(tp);
336433d6423SLionel Sambuc
337433d6423SLionel Sambuc return EDONTREPLY; /* suspend the caller */
338433d6423SLionel Sambuc }
339433d6423SLionel Sambuc
340433d6423SLionel Sambuc /*===========================================================================*
341da21d850SDavid van Moolenbroek * tty_ioctl *
342433d6423SLionel Sambuc *===========================================================================*/
tty_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,int flags,endpoint_t user_endpt,cdev_id_t id)343da21d850SDavid van Moolenbroek int tty_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
344433d6423SLionel Sambuc cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id)
345433d6423SLionel Sambuc {
346433d6423SLionel Sambuc /* Perform an IOCTL on this terminal. POSIX termios calls are handled
347433d6423SLionel Sambuc * by the IOCTL system call.
348433d6423SLionel Sambuc */
349433d6423SLionel Sambuc tty_t *tp;
350433d6423SLionel Sambuc int i, r;
351433d6423SLionel Sambuc
352433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
353433d6423SLionel Sambuc return ENXIO;
354433d6423SLionel Sambuc
355433d6423SLionel Sambuc r = OK;
356433d6423SLionel Sambuc switch (request) {
357433d6423SLionel Sambuc case TIOCGETA:
358433d6423SLionel Sambuc /* Get the termios attributes. */
359433d6423SLionel Sambuc r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios,
360433d6423SLionel Sambuc sizeof(struct termios));
361433d6423SLionel Sambuc break;
362433d6423SLionel Sambuc
363433d6423SLionel Sambuc case TIOCSETAW:
364433d6423SLionel Sambuc case TIOCSETAF:
365433d6423SLionel Sambuc case TIOCDRAIN:
366433d6423SLionel Sambuc if (tp->tty_outleft > 0) {
367433d6423SLionel Sambuc if (flags & CDEV_NONBLOCK)
368433d6423SLionel Sambuc return EAGAIN;
369433d6423SLionel Sambuc /* Wait for all ongoing output processing to finish. */
370433d6423SLionel Sambuc tp->tty_iocaller = endpt;
371433d6423SLionel Sambuc tp->tty_ioid = id;
372433d6423SLionel Sambuc tp->tty_ioreq = request;
373433d6423SLionel Sambuc tp->tty_iogrant = grant;
374433d6423SLionel Sambuc return EDONTREPLY; /* suspend the caller */
375433d6423SLionel Sambuc }
376433d6423SLionel Sambuc if (request == TIOCDRAIN) break;
377433d6423SLionel Sambuc if (request == TIOCSETAF) tty_icancel(tp);
378433d6423SLionel Sambuc /*FALL THROUGH*/
379433d6423SLionel Sambuc case TIOCSETA:
380433d6423SLionel Sambuc /* Set the termios attributes. */
381433d6423SLionel Sambuc r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios,
382433d6423SLionel Sambuc sizeof(struct termios));
383433d6423SLionel Sambuc if (r != OK) break;
384433d6423SLionel Sambuc setattr(tp);
385433d6423SLionel Sambuc break;
386433d6423SLionel Sambuc
387433d6423SLionel Sambuc case TIOCFLUSH:
388433d6423SLionel Sambuc r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, sizeof(i));
389433d6423SLionel Sambuc if (r != OK) break;
390433d6423SLionel Sambuc if(i & FREAD) { tty_icancel(tp); }
391433d6423SLionel Sambuc if(i & FWRITE) { (*tp->tty_ocancel)(tp, 0); }
392433d6423SLionel Sambuc break;
393433d6423SLionel Sambuc case TIOCSTART:
394433d6423SLionel Sambuc tp->tty_inhibited = 0;
395433d6423SLionel Sambuc tp->tty_events = 1;
396433d6423SLionel Sambuc break;
397433d6423SLionel Sambuc case TIOCSTOP:
398433d6423SLionel Sambuc tp->tty_inhibited = 1;
399433d6423SLionel Sambuc tp->tty_events = 1;
400433d6423SLionel Sambuc break;
401433d6423SLionel Sambuc case TIOCSBRK: /* tcsendbreak - turn break on */
402433d6423SLionel Sambuc if (tp->tty_break_on != NULL) (*tp->tty_break_on)(tp,0);
403433d6423SLionel Sambuc break;
404433d6423SLionel Sambuc case TIOCCBRK: /* tcsendbreak - turn break off */
405433d6423SLionel Sambuc if (tp->tty_break_off != NULL) (*tp->tty_break_off)(tp,0);
406433d6423SLionel Sambuc break;
407433d6423SLionel Sambuc
408433d6423SLionel Sambuc case TIOCGWINSZ:
409433d6423SLionel Sambuc r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize,
410433d6423SLionel Sambuc sizeof(struct winsize));
411433d6423SLionel Sambuc break;
412433d6423SLionel Sambuc
413433d6423SLionel Sambuc case TIOCSWINSZ:
414433d6423SLionel Sambuc r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize,
415433d6423SLionel Sambuc sizeof(struct winsize));
416433d6423SLionel Sambuc sigchar(tp, SIGWINCH, 0);
417433d6423SLionel Sambuc break;
418433d6423SLionel Sambuc case TIOCGETD: /* get line discipline */
419391516ddSDavid van Moolenbroek i = TTYDISC;
420391516ddSDavid van Moolenbroek r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i));
421433d6423SLionel Sambuc break;
422433d6423SLionel Sambuc case TIOCSETD: /* set line discipline */
423433d6423SLionel Sambuc printf("TTY: TIOCSETD: can't set any other line discipline.\n");
424433d6423SLionel Sambuc r = ENOTTY;
425433d6423SLionel Sambuc break;
426391516ddSDavid van Moolenbroek case TIOCGLINED: /* get line discipline as string */
427391516ddSDavid van Moolenbroek r = sys_safecopyto(endpt, grant, 0, (vir_bytes) lined, sizeof(lined));
428391516ddSDavid van Moolenbroek break;
429391516ddSDavid van Moolenbroek case TIOCGQSIZE: /* get input/output queue sizes */
430391516ddSDavid van Moolenbroek /* Not sure what to report here. We are using input and output queues
431391516ddSDavid van Moolenbroek * of different sizes, and the IOCTL allows for one single value for
432391516ddSDavid van Moolenbroek * both. So far this seems to be just for stty(1) so let's just report
433391516ddSDavid van Moolenbroek * the larger of the two. TODO: revisit queue sizes altogether.
434391516ddSDavid van Moolenbroek */
435391516ddSDavid van Moolenbroek i = MAX(TTY_IN_BYTES, TTY_OUT_BYTES);
436391516ddSDavid van Moolenbroek r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i));
437391516ddSDavid van Moolenbroek break;
438433d6423SLionel Sambuc case TIOCSCTTY:
439433d6423SLionel Sambuc /* Process sets this tty as its controlling tty */
440433d6423SLionel Sambuc tp->tty_pgrp = user_endpt;
441433d6423SLionel Sambuc break;
442433d6423SLionel Sambuc
443433d6423SLionel Sambuc /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
444433d6423SLionel Sambuc * not defined.
445433d6423SLionel Sambuc */
446433d6423SLionel Sambuc case TIOCGPGRP:
447433d6423SLionel Sambuc case TIOCSPGRP:
448433d6423SLionel Sambuc default:
449433d6423SLionel Sambuc r = ENOTTY;
450433d6423SLionel Sambuc }
451433d6423SLionel Sambuc
452433d6423SLionel Sambuc return r;
453433d6423SLionel Sambuc }
454433d6423SLionel Sambuc
455433d6423SLionel Sambuc /*===========================================================================*
456433d6423SLionel Sambuc * do_open *
457433d6423SLionel Sambuc *===========================================================================*/
do_open(devminor_t minor,int access,endpoint_t user_endpt)458433d6423SLionel Sambuc static int do_open(devminor_t minor, int access, endpoint_t user_endpt)
459433d6423SLionel Sambuc {
460433d6423SLionel Sambuc /* A tty line has been opened. Make it the callers controlling tty if
461433d6423SLionel Sambuc * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned
462433d6423SLionel Sambuc * if the tty is made the controlling tty, otherwise OK or an error code.
463433d6423SLionel Sambuc */
464433d6423SLionel Sambuc tty_t *tp;
465433d6423SLionel Sambuc int r = OK;
466433d6423SLionel Sambuc
467433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
468433d6423SLionel Sambuc return ENXIO;
469433d6423SLionel Sambuc
470da21d850SDavid van Moolenbroek /* Make sure the user is not mixing Unix98 and old-style ends. */
471da21d850SDavid van Moolenbroek if ((*tp->tty_mayopen)(tp, minor) == FALSE)
472da21d850SDavid van Moolenbroek return EIO;
473da21d850SDavid van Moolenbroek
474433d6423SLionel Sambuc if (!(access & CDEV_NOCTTY)) {
475433d6423SLionel Sambuc tp->tty_pgrp = user_endpt;
476433d6423SLionel Sambuc r = CDEV_CTTY;
477433d6423SLionel Sambuc }
478433d6423SLionel Sambuc tp->tty_openct++;
479433d6423SLionel Sambuc if (tp->tty_openct == 1) {
480433d6423SLionel Sambuc /* Tell the device that the tty is opened */
481433d6423SLionel Sambuc (*tp->tty_open)(tp, 0);
482433d6423SLionel Sambuc }
483433d6423SLionel Sambuc
484433d6423SLionel Sambuc return r;
485433d6423SLionel Sambuc }
486433d6423SLionel Sambuc
487433d6423SLionel Sambuc /*===========================================================================*
488433d6423SLionel Sambuc * do_close *
489433d6423SLionel Sambuc *===========================================================================*/
do_close(devminor_t minor)490433d6423SLionel Sambuc static int do_close(devminor_t minor)
491433d6423SLionel Sambuc {
492433d6423SLionel Sambuc /* A tty line has been closed. Clean up the line if it is the last close. */
493433d6423SLionel Sambuc tty_t *tp;
494433d6423SLionel Sambuc
495433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
496433d6423SLionel Sambuc return ENXIO;
497433d6423SLionel Sambuc
498433d6423SLionel Sambuc if (--tp->tty_openct == 0) {
499433d6423SLionel Sambuc tp->tty_pgrp = 0;
500433d6423SLionel Sambuc tty_icancel(tp);
501433d6423SLionel Sambuc (*tp->tty_ocancel)(tp, 0);
502433d6423SLionel Sambuc (*tp->tty_close)(tp, 0);
503433d6423SLionel Sambuc tp->tty_termios = termios_defaults;
504433d6423SLionel Sambuc tp->tty_winsize = winsize_defaults;
505433d6423SLionel Sambuc setattr(tp);
506433d6423SLionel Sambuc }
507433d6423SLionel Sambuc
508433d6423SLionel Sambuc return OK;
509433d6423SLionel Sambuc }
510433d6423SLionel Sambuc
511433d6423SLionel Sambuc /*===========================================================================*
512433d6423SLionel Sambuc * do_cancel *
513433d6423SLionel Sambuc *===========================================================================*/
do_cancel(devminor_t minor,endpoint_t endpt,cdev_id_t id)514433d6423SLionel Sambuc static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
515433d6423SLionel Sambuc {
516433d6423SLionel Sambuc /* A signal has been sent to a process that is hanging trying to read or write.
517433d6423SLionel Sambuc * The pending read or write must be finished off immediately.
518433d6423SLionel Sambuc */
519433d6423SLionel Sambuc tty_t *tp;
520433d6423SLionel Sambuc int r;
521433d6423SLionel Sambuc
522433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
523433d6423SLionel Sambuc return ENXIO;
524433d6423SLionel Sambuc
525433d6423SLionel Sambuc /* Check the parameters carefully, to avoid cancelling twice. */
526433d6423SLionel Sambuc r = EDONTREPLY;
527433d6423SLionel Sambuc if (tp->tty_inleft != 0 && endpt == tp->tty_incaller && id == tp->tty_inid) {
528433d6423SLionel Sambuc /* Process was reading when killed. Clean up input. */
529433d6423SLionel Sambuc tty_icancel(tp);
530433d6423SLionel Sambuc r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
531433d6423SLionel Sambuc tp->tty_inleft = tp->tty_incum = 0;
532433d6423SLionel Sambuc tp->tty_incaller = NONE;
533433d6423SLionel Sambuc } else if (tp->tty_outleft != 0 && endpt == tp->tty_outcaller &&
534433d6423SLionel Sambuc id == tp->tty_outid) {
535433d6423SLionel Sambuc /* Process was writing when killed. Clean up output. */
536433d6423SLionel Sambuc r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
537433d6423SLionel Sambuc tp->tty_outleft = tp->tty_outcum = 0;
538433d6423SLionel Sambuc tp->tty_outcaller = NONE;
539433d6423SLionel Sambuc } else if (tp->tty_ioreq != 0 && endpt == tp->tty_iocaller &&
540433d6423SLionel Sambuc id == tp->tty_ioid) {
541433d6423SLionel Sambuc /* Process was waiting for output to drain. */
542433d6423SLionel Sambuc r = EINTR;
543433d6423SLionel Sambuc tp->tty_ioreq = 0;
544433d6423SLionel Sambuc tp->tty_iocaller = NONE;
545433d6423SLionel Sambuc }
546433d6423SLionel Sambuc if (r != EDONTREPLY)
547433d6423SLionel Sambuc tp->tty_events = 1;
548433d6423SLionel Sambuc /* Only reply if we found a matching request. */
549433d6423SLionel Sambuc return r;
550433d6423SLionel Sambuc }
551433d6423SLionel Sambuc
select_try(struct tty * tp,int ops)552433d6423SLionel Sambuc int select_try(struct tty *tp, int ops)
553433d6423SLionel Sambuc {
554433d6423SLionel Sambuc int ready_ops = 0;
555433d6423SLionel Sambuc
556433d6423SLionel Sambuc /* Special case. If line is hung up, no operations will block.
557433d6423SLionel Sambuc * (and it can be seen as an exceptional condition.)
558433d6423SLionel Sambuc */
559433d6423SLionel Sambuc if (tp->tty_termios.c_ospeed == B0) {
560433d6423SLionel Sambuc ready_ops |= ops;
561433d6423SLionel Sambuc }
562433d6423SLionel Sambuc
563433d6423SLionel Sambuc if (ops & CDEV_OP_RD) {
564433d6423SLionel Sambuc /* will i/o not block on read? */
565433d6423SLionel Sambuc if (tp->tty_inleft > 0) {
566433d6423SLionel Sambuc ready_ops |= CDEV_OP_RD; /* EIO - no blocking */
567433d6423SLionel Sambuc } else if (tp->tty_incount > 0) {
568433d6423SLionel Sambuc /* Is a regular read possible? tty_incount
569433d6423SLionel Sambuc * says there is data. But a read will only succeed
570433d6423SLionel Sambuc * in canonical mode if a newline has been seen.
571433d6423SLionel Sambuc */
572433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ICANON) ||
573433d6423SLionel Sambuc tp->tty_eotct > 0) {
574433d6423SLionel Sambuc ready_ops |= CDEV_OP_RD;
575433d6423SLionel Sambuc }
576433d6423SLionel Sambuc }
577433d6423SLionel Sambuc }
578433d6423SLionel Sambuc
579433d6423SLionel Sambuc if (ops & CDEV_OP_WR) {
580433d6423SLionel Sambuc if (tp->tty_outleft > 0) ready_ops |= CDEV_OP_WR;
581433d6423SLionel Sambuc else if ((*tp->tty_devwrite)(tp, 1)) ready_ops |= CDEV_OP_WR;
582433d6423SLionel Sambuc }
583433d6423SLionel Sambuc return ready_ops;
584433d6423SLionel Sambuc }
585433d6423SLionel Sambuc
select_retry(struct tty * tp)586433d6423SLionel Sambuc int select_retry(struct tty *tp)
587433d6423SLionel Sambuc {
588433d6423SLionel Sambuc int ops;
589433d6423SLionel Sambuc
590433d6423SLionel Sambuc if (tp->tty_select_ops && (ops = select_try(tp, tp->tty_select_ops))) {
591433d6423SLionel Sambuc chardriver_reply_select(tp->tty_select_proc,
592433d6423SLionel Sambuc tp->tty_select_minor, ops);
593433d6423SLionel Sambuc tp->tty_select_ops &= ~ops;
594433d6423SLionel Sambuc }
595433d6423SLionel Sambuc return OK;
596433d6423SLionel Sambuc }
597433d6423SLionel Sambuc
598433d6423SLionel Sambuc /*===========================================================================*
599433d6423SLionel Sambuc * do_select *
600433d6423SLionel Sambuc *===========================================================================*/
do_select(devminor_t minor,unsigned int ops,endpoint_t endpt)601433d6423SLionel Sambuc static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
602433d6423SLionel Sambuc {
603433d6423SLionel Sambuc tty_t *tp;
604433d6423SLionel Sambuc int ready_ops, watch;
605433d6423SLionel Sambuc
606433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL)
607433d6423SLionel Sambuc return ENXIO;
608433d6423SLionel Sambuc
609433d6423SLionel Sambuc watch = (ops & CDEV_NOTIFY);
610433d6423SLionel Sambuc ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
611433d6423SLionel Sambuc
612433d6423SLionel Sambuc ready_ops = select_try(tp, ops);
613433d6423SLionel Sambuc
614433d6423SLionel Sambuc ops &= ~ready_ops;
615433d6423SLionel Sambuc if (ops && watch) {
616433d6423SLionel Sambuc /* Translated minor numbers are a problem with late select replies. We
617433d6423SLionel Sambuc * have to save the minor number used to do the select, since otherwise
618433d6423SLionel Sambuc * VFS won't be able to make sense of those late replies. We do not
619433d6423SLionel Sambuc * support selecting on two different minors for the same object.
620433d6423SLionel Sambuc */
621433d6423SLionel Sambuc if (tp->tty_select_ops != 0 && tp->tty_select_minor != minor) {
622433d6423SLionel Sambuc printf("TTY: select on one object with two minors (%d, %d)\n",
623433d6423SLionel Sambuc tp->tty_select_minor, minor);
624433d6423SLionel Sambuc return EBADF;
625433d6423SLionel Sambuc }
626433d6423SLionel Sambuc tp->tty_select_ops |= ops;
627433d6423SLionel Sambuc tp->tty_select_proc = endpt;
628433d6423SLionel Sambuc tp->tty_select_minor = minor;
629433d6423SLionel Sambuc }
630433d6423SLionel Sambuc
631433d6423SLionel Sambuc return ready_ops;
632433d6423SLionel Sambuc }
633433d6423SLionel Sambuc
634433d6423SLionel Sambuc /*===========================================================================*
635433d6423SLionel Sambuc * handle_events *
636433d6423SLionel Sambuc *===========================================================================*/
handle_events(tp)637433d6423SLionel Sambuc void handle_events(tp)
638433d6423SLionel Sambuc tty_t *tp; /* TTY to check for events. */
639433d6423SLionel Sambuc {
640433d6423SLionel Sambuc /* Handle any events pending on a TTY. */
641433d6423SLionel Sambuc
642433d6423SLionel Sambuc do {
643433d6423SLionel Sambuc tp->tty_events = 0;
644433d6423SLionel Sambuc
645433d6423SLionel Sambuc /* Read input and perform input processing. */
646433d6423SLionel Sambuc (*tp->tty_devread)(tp, 0);
647433d6423SLionel Sambuc
648433d6423SLionel Sambuc /* Perform output processing and write output. */
649433d6423SLionel Sambuc (*tp->tty_devwrite)(tp, 0);
650433d6423SLionel Sambuc
651433d6423SLionel Sambuc /* Ioctl waiting for some event? */
652433d6423SLionel Sambuc if (tp->tty_ioreq != 0) dev_ioctl(tp);
653433d6423SLionel Sambuc } while (tp->tty_events);
654433d6423SLionel Sambuc
655433d6423SLionel Sambuc /* Transfer characters from the input queue to a waiting process. */
656433d6423SLionel Sambuc in_transfer(tp);
657433d6423SLionel Sambuc
658433d6423SLionel Sambuc /* Reply if enough bytes are available. */
659433d6423SLionel Sambuc if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) {
660433d6423SLionel Sambuc chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum);
661433d6423SLionel Sambuc tp->tty_inleft = tp->tty_incum = 0;
662433d6423SLionel Sambuc tp->tty_incaller = NONE;
663433d6423SLionel Sambuc }
664433d6423SLionel Sambuc if (tp->tty_select_ops)
665433d6423SLionel Sambuc {
666433d6423SLionel Sambuc select_retry(tp);
667433d6423SLionel Sambuc }
668433d6423SLionel Sambuc select_retry_pty(tp);
669433d6423SLionel Sambuc }
670433d6423SLionel Sambuc
671433d6423SLionel Sambuc /*===========================================================================*
672433d6423SLionel Sambuc * in_transfer *
673433d6423SLionel Sambuc *===========================================================================*/
in_transfer(tp)674433d6423SLionel Sambuc static void in_transfer(tp)
675433d6423SLionel Sambuc register tty_t *tp; /* pointer to terminal to read from */
676433d6423SLionel Sambuc {
677433d6423SLionel Sambuc /* Transfer bytes from the input queue to a process reading from a terminal. */
678433d6423SLionel Sambuc
679433d6423SLionel Sambuc int ch;
680433d6423SLionel Sambuc int count;
681433d6423SLionel Sambuc char buf[64], *bp;
682433d6423SLionel Sambuc
683433d6423SLionel Sambuc /* Force read to succeed if the line is hung up, looks like EOF to reader. */
684433d6423SLionel Sambuc if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0;
685433d6423SLionel Sambuc
686433d6423SLionel Sambuc /* Anything to do? */
687433d6423SLionel Sambuc if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return;
688433d6423SLionel Sambuc
689433d6423SLionel Sambuc bp = buf;
690433d6423SLionel Sambuc while (tp->tty_inleft > 0 && tp->tty_eotct > 0) {
691433d6423SLionel Sambuc ch = *tp->tty_intail;
692433d6423SLionel Sambuc
693433d6423SLionel Sambuc if (!(ch & IN_EOF)) {
694433d6423SLionel Sambuc /* One character to be delivered to the user. */
695433d6423SLionel Sambuc *bp = ch & IN_CHAR;
696433d6423SLionel Sambuc tp->tty_inleft--;
697433d6423SLionel Sambuc if (++bp == bufend(buf)) {
698433d6423SLionel Sambuc /* Temp buffer full, copy to user space. */
699433d6423SLionel Sambuc sys_safecopyto(tp->tty_incaller,
700433d6423SLionel Sambuc tp->tty_ingrant, tp->tty_incum,
701433d6423SLionel Sambuc (vir_bytes) buf, (vir_bytes) buflen(buf));
702433d6423SLionel Sambuc tp->tty_incum += buflen(buf);
703433d6423SLionel Sambuc bp = buf;
704433d6423SLionel Sambuc }
705433d6423SLionel Sambuc }
706433d6423SLionel Sambuc
707433d6423SLionel Sambuc /* Remove the character from the input queue. */
708433d6423SLionel Sambuc if (++tp->tty_intail == bufend(tp->tty_inbuf))
709433d6423SLionel Sambuc tp->tty_intail = tp->tty_inbuf;
710433d6423SLionel Sambuc tp->tty_incount--;
711433d6423SLionel Sambuc if (ch & IN_EOT) {
712433d6423SLionel Sambuc tp->tty_eotct--;
713433d6423SLionel Sambuc /* Don't read past a line break in canonical mode. */
714433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0;
715433d6423SLionel Sambuc }
716433d6423SLionel Sambuc }
717433d6423SLionel Sambuc
718433d6423SLionel Sambuc if (bp > buf) {
719433d6423SLionel Sambuc /* Leftover characters in the buffer. */
720433d6423SLionel Sambuc count = bp - buf;
721433d6423SLionel Sambuc sys_safecopyto(tp->tty_incaller, tp->tty_ingrant, tp->tty_incum,
722433d6423SLionel Sambuc (vir_bytes) buf, (vir_bytes) count);
723433d6423SLionel Sambuc tp->tty_incum += count;
724433d6423SLionel Sambuc }
725433d6423SLionel Sambuc
726433d6423SLionel Sambuc /* Usually reply to the reader, possibly even if incum == 0 (EOF). */
727433d6423SLionel Sambuc if (tp->tty_inleft == 0) {
728433d6423SLionel Sambuc chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum);
729433d6423SLionel Sambuc tp->tty_inleft = tp->tty_incum = 0;
730433d6423SLionel Sambuc tp->tty_incaller = NONE;
731433d6423SLionel Sambuc }
732433d6423SLionel Sambuc }
733433d6423SLionel Sambuc
734433d6423SLionel Sambuc /*===========================================================================*
735433d6423SLionel Sambuc * in_process *
736433d6423SLionel Sambuc *===========================================================================*/
in_process(tp,buf,count)737433d6423SLionel Sambuc int in_process(tp, buf, count)
738433d6423SLionel Sambuc register tty_t *tp; /* terminal on which character has arrived */
739433d6423SLionel Sambuc char *buf; /* buffer with input characters */
740433d6423SLionel Sambuc int count; /* number of input characters */
741433d6423SLionel Sambuc {
742433d6423SLionel Sambuc /* Characters have just been typed in. Process, save, and echo them. Return
743433d6423SLionel Sambuc * the number of characters processed.
744433d6423SLionel Sambuc */
745433d6423SLionel Sambuc
746da21d850SDavid van Moolenbroek int ch, ct;
747433d6423SLionel Sambuc int timeset = FALSE;
748433d6423SLionel Sambuc
749433d6423SLionel Sambuc for (ct = 0; ct < count; ct++) {
750433d6423SLionel Sambuc /* Take one character. */
751433d6423SLionel Sambuc ch = *buf++ & BYTE;
752433d6423SLionel Sambuc
753433d6423SLionel Sambuc /* Strip to seven bits? */
754433d6423SLionel Sambuc if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F;
755433d6423SLionel Sambuc
756433d6423SLionel Sambuc /* Input extensions? */
757433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & IEXTEN) {
758433d6423SLionel Sambuc
759433d6423SLionel Sambuc /* Previous character was a character escape? */
760433d6423SLionel Sambuc if (tp->tty_escaped) {
761433d6423SLionel Sambuc tp->tty_escaped = NOT_ESCAPED;
762433d6423SLionel Sambuc ch |= IN_ESC; /* protect character */
763433d6423SLionel Sambuc }
764433d6423SLionel Sambuc
765433d6423SLionel Sambuc /* LNEXT (^V) to escape the next character? */
766433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VLNEXT]) {
767433d6423SLionel Sambuc tp->tty_escaped = ESCAPED;
768433d6423SLionel Sambuc rawecho(tp, '^');
769433d6423SLionel Sambuc rawecho(tp, '\b');
770433d6423SLionel Sambuc continue; /* do not store the escape */
771433d6423SLionel Sambuc }
772433d6423SLionel Sambuc
773433d6423SLionel Sambuc /* REPRINT (^R) to reprint echoed characters? */
774433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VREPRINT]) {
775433d6423SLionel Sambuc reprint(tp);
776433d6423SLionel Sambuc continue;
777433d6423SLionel Sambuc }
778433d6423SLionel Sambuc }
779433d6423SLionel Sambuc
780433d6423SLionel Sambuc /* _POSIX_VDISABLE is a normal character value, so better escape it. */
781433d6423SLionel Sambuc if (ch == _POSIX_VDISABLE) ch |= IN_ESC;
782433d6423SLionel Sambuc
783433d6423SLionel Sambuc /* Map CR to LF, ignore CR, or map LF to CR. */
784433d6423SLionel Sambuc if (ch == '\r') {
785433d6423SLionel Sambuc if (tp->tty_termios.c_iflag & IGNCR) continue;
786433d6423SLionel Sambuc if (tp->tty_termios.c_iflag & ICRNL) ch = '\n';
787433d6423SLionel Sambuc } else
788433d6423SLionel Sambuc if (ch == '\n') {
789433d6423SLionel Sambuc if (tp->tty_termios.c_iflag & INLCR) ch = '\r';
790433d6423SLionel Sambuc }
791433d6423SLionel Sambuc
792433d6423SLionel Sambuc /* Canonical mode? */
793433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ICANON) {
794433d6423SLionel Sambuc
795433d6423SLionel Sambuc /* Erase processing (rub out of last character). */
796433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VERASE]) {
797433d6423SLionel Sambuc (void) back_over(tp);
798433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ECHOE)) {
799433d6423SLionel Sambuc (void) tty_echo(tp, ch);
800433d6423SLionel Sambuc }
801433d6423SLionel Sambuc continue;
802433d6423SLionel Sambuc }
803433d6423SLionel Sambuc
804433d6423SLionel Sambuc /* Kill processing (remove current line). */
805433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VKILL]) {
806433d6423SLionel Sambuc while (back_over(tp)) {}
807433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ECHOE)) {
808433d6423SLionel Sambuc (void) tty_echo(tp, ch);
809433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ECHOK)
810433d6423SLionel Sambuc rawecho(tp, '\n');
811433d6423SLionel Sambuc }
812433d6423SLionel Sambuc continue;
813433d6423SLionel Sambuc }
814433d6423SLionel Sambuc
815433d6423SLionel Sambuc /* EOF (^D) means end-of-file, an invisible "line break". */
816433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF;
817433d6423SLionel Sambuc
818433d6423SLionel Sambuc /* The line may be returned to the user after an LF. */
819433d6423SLionel Sambuc if (ch == '\n') ch |= IN_EOT;
820433d6423SLionel Sambuc
821433d6423SLionel Sambuc /* Same thing with EOL, whatever it may be. */
822433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT;
823433d6423SLionel Sambuc }
824433d6423SLionel Sambuc
825433d6423SLionel Sambuc /* Start/stop input control? */
826433d6423SLionel Sambuc if (tp->tty_termios.c_iflag & IXON) {
827433d6423SLionel Sambuc
828433d6423SLionel Sambuc /* Output stops on STOP (^S). */
829433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VSTOP]) {
830433d6423SLionel Sambuc tp->tty_inhibited = STOPPED;
831433d6423SLionel Sambuc tp->tty_events = 1;
832433d6423SLionel Sambuc continue;
833433d6423SLionel Sambuc }
834433d6423SLionel Sambuc
835433d6423SLionel Sambuc /* Output restarts on START (^Q) or any character if IXANY. */
836433d6423SLionel Sambuc if (tp->tty_inhibited) {
837433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VSTART]
838433d6423SLionel Sambuc || (tp->tty_termios.c_iflag & IXANY)) {
839433d6423SLionel Sambuc tp->tty_inhibited = RUNNING;
840433d6423SLionel Sambuc tp->tty_events = 1;
841433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VSTART])
842433d6423SLionel Sambuc continue;
843433d6423SLionel Sambuc }
844433d6423SLionel Sambuc }
845433d6423SLionel Sambuc }
846433d6423SLionel Sambuc
847433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ISIG) {
848433d6423SLionel Sambuc /* Check for INTR, QUIT and STATUS characters. */
849433d6423SLionel Sambuc int sig = -1;
850433d6423SLionel Sambuc if (ch == tp->tty_termios.c_cc[VINTR])
851433d6423SLionel Sambuc sig = SIGINT;
852433d6423SLionel Sambuc else if(ch == tp->tty_termios.c_cc[VQUIT])
853433d6423SLionel Sambuc sig = SIGQUIT;
854433d6423SLionel Sambuc else if(ch == tp->tty_termios.c_cc[VSTATUS])
855433d6423SLionel Sambuc sig = SIGINFO;
856433d6423SLionel Sambuc
857433d6423SLionel Sambuc if(sig >= 0) {
858433d6423SLionel Sambuc sigchar(tp, sig, 1);
859433d6423SLionel Sambuc (void) tty_echo(tp, ch);
860433d6423SLionel Sambuc continue;
861433d6423SLionel Sambuc }
862433d6423SLionel Sambuc }
863433d6423SLionel Sambuc
864433d6423SLionel Sambuc /* Is there space in the input buffer? */
865433d6423SLionel Sambuc if (tp->tty_incount == buflen(tp->tty_inbuf)) {
866433d6423SLionel Sambuc /* No space; discard in canonical mode, keep in raw mode. */
867433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ICANON) continue;
868433d6423SLionel Sambuc break;
869433d6423SLionel Sambuc }
870433d6423SLionel Sambuc
871433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ICANON)) {
872433d6423SLionel Sambuc /* In raw mode all characters are "line breaks". */
873433d6423SLionel Sambuc ch |= IN_EOT;
874433d6423SLionel Sambuc
875433d6423SLionel Sambuc /* Start an inter-byte timer? */
876433d6423SLionel Sambuc if (!timeset && tp->tty_termios.c_cc[VMIN] > 0
877433d6423SLionel Sambuc && tp->tty_termios.c_cc[VTIME] > 0) {
878433d6423SLionel Sambuc settimer(tp, TRUE);
879433d6423SLionel Sambuc timeset = TRUE;
880433d6423SLionel Sambuc }
881433d6423SLionel Sambuc }
882433d6423SLionel Sambuc
883433d6423SLionel Sambuc /* Perform the intricate function of echoing. */
884433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = tty_echo(tp, ch);
885433d6423SLionel Sambuc
886433d6423SLionel Sambuc /* Save the character in the input queue. */
887433d6423SLionel Sambuc *tp->tty_inhead++ = ch;
888433d6423SLionel Sambuc if (tp->tty_inhead == bufend(tp->tty_inbuf))
889433d6423SLionel Sambuc tp->tty_inhead = tp->tty_inbuf;
890433d6423SLionel Sambuc tp->tty_incount++;
891433d6423SLionel Sambuc if (ch & IN_EOT) tp->tty_eotct++;
892433d6423SLionel Sambuc
893433d6423SLionel Sambuc /* Try to finish input if the queue threatens to overflow. */
894433d6423SLionel Sambuc if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp);
895433d6423SLionel Sambuc }
896433d6423SLionel Sambuc return ct;
897433d6423SLionel Sambuc }
898433d6423SLionel Sambuc
899433d6423SLionel Sambuc /*===========================================================================*
900433d6423SLionel Sambuc * echo *
901433d6423SLionel Sambuc *===========================================================================*/
tty_echo(tp,ch)902433d6423SLionel Sambuc static int tty_echo(tp, ch)
903433d6423SLionel Sambuc register tty_t *tp; /* terminal on which to echo */
904433d6423SLionel Sambuc register int ch; /* pointer to character to echo */
905433d6423SLionel Sambuc {
906433d6423SLionel Sambuc /* Echo the character if echoing is on. Some control characters are echoed
907433d6423SLionel Sambuc * with their normal effect, other control characters are echoed as "^X",
908433d6423SLionel Sambuc * normal characters are echoed normally. EOF (^D) is echoed, but immediately
909433d6423SLionel Sambuc * backspaced over. Return the character with the echoed length added to its
910433d6423SLionel Sambuc * attributes.
911433d6423SLionel Sambuc */
912433d6423SLionel Sambuc int len, rp;
913433d6423SLionel Sambuc
914433d6423SLionel Sambuc ch &= ~IN_LEN;
915433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ECHO)) {
916433d6423SLionel Sambuc if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag
917433d6423SLionel Sambuc & (ICANON|ECHONL)) == (ICANON|ECHONL))
918433d6423SLionel Sambuc (*tp->tty_echo)(tp, '\n');
919433d6423SLionel Sambuc return(ch);
920433d6423SLionel Sambuc }
921433d6423SLionel Sambuc
922433d6423SLionel Sambuc /* "Reprint" tells if the echo output has been messed up by other output. */
923433d6423SLionel Sambuc rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint;
924433d6423SLionel Sambuc
925433d6423SLionel Sambuc if ((ch & IN_CHAR) < ' ') {
926433d6423SLionel Sambuc switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) {
927433d6423SLionel Sambuc case '\t':
928433d6423SLionel Sambuc len = 0;
929433d6423SLionel Sambuc do {
930433d6423SLionel Sambuc (*tp->tty_echo)(tp, ' ');
931433d6423SLionel Sambuc len++;
932433d6423SLionel Sambuc } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0);
933433d6423SLionel Sambuc break;
934433d6423SLionel Sambuc case '\r' | IN_EOT:
935433d6423SLionel Sambuc case '\n' | IN_EOT:
936433d6423SLionel Sambuc (*tp->tty_echo)(tp, ch & IN_CHAR);
937433d6423SLionel Sambuc len = 0;
938433d6423SLionel Sambuc break;
939433d6423SLionel Sambuc default:
940433d6423SLionel Sambuc (*tp->tty_echo)(tp, '^');
941433d6423SLionel Sambuc (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR));
942433d6423SLionel Sambuc len = 2;
943433d6423SLionel Sambuc }
944433d6423SLionel Sambuc } else
945433d6423SLionel Sambuc if ((ch & IN_CHAR) == '\177') {
946433d6423SLionel Sambuc /* A DEL prints as "^?". */
947433d6423SLionel Sambuc (*tp->tty_echo)(tp, '^');
948433d6423SLionel Sambuc (*tp->tty_echo)(tp, '?');
949433d6423SLionel Sambuc len = 2;
950433d6423SLionel Sambuc } else {
951433d6423SLionel Sambuc (*tp->tty_echo)(tp, ch & IN_CHAR);
952433d6423SLionel Sambuc len = 1;
953433d6423SLionel Sambuc }
954433d6423SLionel Sambuc if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; }
955433d6423SLionel Sambuc
956433d6423SLionel Sambuc tp->tty_reprint = rp;
957433d6423SLionel Sambuc return(ch | (len << IN_LSHIFT));
958433d6423SLionel Sambuc }
959433d6423SLionel Sambuc
960433d6423SLionel Sambuc /*===========================================================================*
961433d6423SLionel Sambuc * rawecho *
962433d6423SLionel Sambuc *===========================================================================*/
rawecho(tp,ch)963433d6423SLionel Sambuc static void rawecho(tp, ch)
964433d6423SLionel Sambuc register tty_t *tp;
965433d6423SLionel Sambuc int ch;
966433d6423SLionel Sambuc {
967433d6423SLionel Sambuc /* Echo without interpretation if ECHO is set. */
968433d6423SLionel Sambuc int rp = tp->tty_reprint;
969433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch);
970433d6423SLionel Sambuc tp->tty_reprint = rp;
971433d6423SLionel Sambuc }
972433d6423SLionel Sambuc
973433d6423SLionel Sambuc /*===========================================================================*
974433d6423SLionel Sambuc * back_over *
975433d6423SLionel Sambuc *===========================================================================*/
back_over(tp)976433d6423SLionel Sambuc static int back_over(tp)
977433d6423SLionel Sambuc register tty_t *tp;
978433d6423SLionel Sambuc {
979433d6423SLionel Sambuc /* Backspace to previous character on screen and erase it. */
980433d6423SLionel Sambuc u16_t *head;
981433d6423SLionel Sambuc int len;
982433d6423SLionel Sambuc
983433d6423SLionel Sambuc if (tp->tty_incount == 0) return(0); /* queue empty */
984433d6423SLionel Sambuc head = tp->tty_inhead;
985433d6423SLionel Sambuc if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf);
986433d6423SLionel Sambuc if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */
987433d6423SLionel Sambuc if (tp->tty_reprint) reprint(tp); /* reprint if messed up */
988433d6423SLionel Sambuc tp->tty_inhead = head;
989433d6423SLionel Sambuc tp->tty_incount--;
990433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ECHOE) {
991433d6423SLionel Sambuc len = (*head & IN_LEN) >> IN_LSHIFT;
992433d6423SLionel Sambuc while (len > 0) {
993433d6423SLionel Sambuc rawecho(tp, '\b');
994433d6423SLionel Sambuc rawecho(tp, ' ');
995433d6423SLionel Sambuc rawecho(tp, '\b');
996433d6423SLionel Sambuc len--;
997433d6423SLionel Sambuc }
998433d6423SLionel Sambuc }
999433d6423SLionel Sambuc return(1); /* one character erased */
1000433d6423SLionel Sambuc }
1001433d6423SLionel Sambuc
1002433d6423SLionel Sambuc /*===========================================================================*
1003433d6423SLionel Sambuc * reprint *
1004433d6423SLionel Sambuc *===========================================================================*/
reprint(tp)1005433d6423SLionel Sambuc static void reprint(tp)
1006433d6423SLionel Sambuc register tty_t *tp; /* pointer to tty struct */
1007433d6423SLionel Sambuc {
1008433d6423SLionel Sambuc /* Restore what has been echoed to screen before if the user input has been
1009433d6423SLionel Sambuc * messed up by output, or if REPRINT (^R) is typed.
1010433d6423SLionel Sambuc */
1011433d6423SLionel Sambuc int count;
1012433d6423SLionel Sambuc u16_t *head;
1013433d6423SLionel Sambuc
1014433d6423SLionel Sambuc tp->tty_reprint = FALSE;
1015433d6423SLionel Sambuc
1016433d6423SLionel Sambuc /* Find the last line break in the input. */
1017433d6423SLionel Sambuc head = tp->tty_inhead;
1018433d6423SLionel Sambuc count = tp->tty_incount;
1019433d6423SLionel Sambuc while (count > 0) {
1020433d6423SLionel Sambuc if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf);
1021433d6423SLionel Sambuc if (head[-1] & IN_EOT) break;
1022433d6423SLionel Sambuc head--;
1023433d6423SLionel Sambuc count--;
1024433d6423SLionel Sambuc }
1025433d6423SLionel Sambuc if (count == tp->tty_incount) return; /* no reason to reprint */
1026433d6423SLionel Sambuc
1027433d6423SLionel Sambuc /* Show REPRINT (^R) and move to a new line. */
1028433d6423SLionel Sambuc (void) tty_echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC);
1029433d6423SLionel Sambuc rawecho(tp, '\r');
1030433d6423SLionel Sambuc rawecho(tp, '\n');
1031433d6423SLionel Sambuc
1032433d6423SLionel Sambuc /* Reprint from the last break onwards. */
1033433d6423SLionel Sambuc do {
1034433d6423SLionel Sambuc if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf;
1035433d6423SLionel Sambuc *head = tty_echo(tp, *head);
1036433d6423SLionel Sambuc head++;
1037433d6423SLionel Sambuc count++;
1038433d6423SLionel Sambuc } while (count < tp->tty_incount);
1039433d6423SLionel Sambuc }
1040433d6423SLionel Sambuc
1041433d6423SLionel Sambuc /*===========================================================================*
1042433d6423SLionel Sambuc * out_process *
1043433d6423SLionel Sambuc *===========================================================================*/
out_process(tp,bstart,bpos,bend,icount,ocount)1044433d6423SLionel Sambuc void out_process(tp, bstart, bpos, bend, icount, ocount)
1045433d6423SLionel Sambuc tty_t *tp;
1046433d6423SLionel Sambuc char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */
1047433d6423SLionel Sambuc int *icount; /* # input chars / input chars used */
1048433d6423SLionel Sambuc int *ocount; /* max output chars / output chars used */
1049433d6423SLionel Sambuc {
1050433d6423SLionel Sambuc /* Perform output processing on a circular buffer. *icount is the number of
1051433d6423SLionel Sambuc * bytes to process, and the number of bytes actually processed on return.
1052433d6423SLionel Sambuc * *ocount is the space available on input and the space used on output.
1053433d6423SLionel Sambuc * (Naturally *icount < *ocount.) The column position is updated modulo
1054433d6423SLionel Sambuc * the TAB size, because we really only need it for tabs.
1055433d6423SLionel Sambuc */
1056433d6423SLionel Sambuc
1057433d6423SLionel Sambuc int tablen;
1058433d6423SLionel Sambuc int ict = *icount;
1059433d6423SLionel Sambuc int oct = *ocount;
1060433d6423SLionel Sambuc int pos = tp->tty_position;
1061433d6423SLionel Sambuc
1062433d6423SLionel Sambuc while (ict > 0) {
1063433d6423SLionel Sambuc switch (*bpos) {
1064433d6423SLionel Sambuc case '\7':
1065433d6423SLionel Sambuc break;
1066433d6423SLionel Sambuc case '\b':
1067433d6423SLionel Sambuc pos--;
1068433d6423SLionel Sambuc break;
1069433d6423SLionel Sambuc case '\r':
1070433d6423SLionel Sambuc pos = 0;
1071433d6423SLionel Sambuc break;
1072433d6423SLionel Sambuc case '\n':
1073433d6423SLionel Sambuc if ((tp->tty_termios.c_oflag & (OPOST|ONLCR))
1074433d6423SLionel Sambuc == (OPOST|ONLCR)) {
1075433d6423SLionel Sambuc /* Map LF to CR+LF if there is space. Note that the
1076433d6423SLionel Sambuc * next character in the buffer is overwritten, so
1077433d6423SLionel Sambuc * we stop at this point.
1078433d6423SLionel Sambuc */
1079433d6423SLionel Sambuc if (oct >= 2) {
1080433d6423SLionel Sambuc *bpos = '\r';
1081433d6423SLionel Sambuc if (++bpos == bend) bpos = bstart;
1082433d6423SLionel Sambuc *bpos = '\n';
1083433d6423SLionel Sambuc pos = 0;
1084433d6423SLionel Sambuc ict--;
1085433d6423SLionel Sambuc oct -= 2;
1086433d6423SLionel Sambuc }
1087433d6423SLionel Sambuc goto out_done; /* no space or buffer got changed */
1088433d6423SLionel Sambuc }
1089433d6423SLionel Sambuc break;
1090433d6423SLionel Sambuc case '\t':
1091433d6423SLionel Sambuc /* Best guess for the tab length. */
1092433d6423SLionel Sambuc tablen = TAB_SIZE - (pos & TAB_MASK);
1093433d6423SLionel Sambuc
1094433d6423SLionel Sambuc if ((tp->tty_termios.c_oflag & (OPOST|OXTABS))
1095433d6423SLionel Sambuc == (OPOST|OXTABS)) {
1096433d6423SLionel Sambuc /* Tabs must be expanded. */
1097433d6423SLionel Sambuc if (oct >= tablen) {
1098433d6423SLionel Sambuc pos += tablen;
1099433d6423SLionel Sambuc ict--;
1100433d6423SLionel Sambuc oct -= tablen;
1101433d6423SLionel Sambuc do {
1102433d6423SLionel Sambuc *bpos = ' ';
1103433d6423SLionel Sambuc if (++bpos == bend) bpos = bstart;
1104433d6423SLionel Sambuc } while (--tablen != 0);
1105433d6423SLionel Sambuc }
1106433d6423SLionel Sambuc goto out_done;
1107433d6423SLionel Sambuc }
1108433d6423SLionel Sambuc /* Tabs are output directly. */
1109433d6423SLionel Sambuc pos += tablen;
1110433d6423SLionel Sambuc break;
1111433d6423SLionel Sambuc default:
1112433d6423SLionel Sambuc /* Assume any other character prints as one character. */
1113433d6423SLionel Sambuc pos++;
1114433d6423SLionel Sambuc }
1115433d6423SLionel Sambuc if (++bpos == bend) bpos = bstart;
1116433d6423SLionel Sambuc ict--;
1117433d6423SLionel Sambuc oct--;
1118433d6423SLionel Sambuc }
1119433d6423SLionel Sambuc out_done:
1120433d6423SLionel Sambuc tp->tty_position = pos & TAB_MASK;
1121433d6423SLionel Sambuc
1122433d6423SLionel Sambuc *icount -= ict; /* [io]ct are the number of chars not used */
1123433d6423SLionel Sambuc *ocount -= oct; /* *[io]count are the number of chars that are used */
1124433d6423SLionel Sambuc }
1125433d6423SLionel Sambuc
1126433d6423SLionel Sambuc /*===========================================================================*
1127433d6423SLionel Sambuc * dev_ioctl *
1128433d6423SLionel Sambuc *===========================================================================*/
dev_ioctl(tp)1129433d6423SLionel Sambuc static void dev_ioctl(tp)
1130433d6423SLionel Sambuc tty_t *tp;
1131433d6423SLionel Sambuc {
1132433d6423SLionel Sambuc /* The ioctl's TCSETSW, TCSETSF and TIOCDRAIN wait for output to finish to make
1133433d6423SLionel Sambuc * sure that an attribute change doesn't affect the processing of current
1134433d6423SLionel Sambuc * output. Once output finishes the ioctl is executed as in do_ioctl().
1135433d6423SLionel Sambuc */
1136433d6423SLionel Sambuc int result = EINVAL;
1137433d6423SLionel Sambuc
1138433d6423SLionel Sambuc if (tp->tty_outleft > 0) return; /* output not finished */
1139433d6423SLionel Sambuc
1140433d6423SLionel Sambuc if (tp->tty_ioreq != TIOCDRAIN) {
1141433d6423SLionel Sambuc if (tp->tty_ioreq == TIOCSETAF) tty_icancel(tp);
1142433d6423SLionel Sambuc result = sys_safecopyfrom(tp->tty_iocaller, tp->tty_iogrant, 0,
1143433d6423SLionel Sambuc (vir_bytes) &tp->tty_termios,
1144433d6423SLionel Sambuc (vir_bytes) sizeof(tp->tty_termios));
1145433d6423SLionel Sambuc if (result == OK) setattr(tp);
1146433d6423SLionel Sambuc }
1147433d6423SLionel Sambuc tp->tty_ioreq = 0;
1148433d6423SLionel Sambuc chardriver_reply_task(tp->tty_iocaller, tp->tty_ioid, result);
1149433d6423SLionel Sambuc tp->tty_iocaller = NONE;
1150433d6423SLionel Sambuc }
1151433d6423SLionel Sambuc
1152433d6423SLionel Sambuc /*===========================================================================*
1153433d6423SLionel Sambuc * setattr *
1154433d6423SLionel Sambuc *===========================================================================*/
setattr(tp)1155433d6423SLionel Sambuc static void setattr(tp)
1156433d6423SLionel Sambuc tty_t *tp;
1157433d6423SLionel Sambuc {
1158433d6423SLionel Sambuc /* Apply the new line attributes (raw/canonical, line speed, etc.) */
1159433d6423SLionel Sambuc u16_t *inp;
1160433d6423SLionel Sambuc int count;
1161433d6423SLionel Sambuc
1162433d6423SLionel Sambuc if (!(tp->tty_termios.c_lflag & ICANON)) {
1163433d6423SLionel Sambuc /* Raw mode; put a "line break" on all characters in the input queue.
1164433d6423SLionel Sambuc * It is undefined what happens to the input queue when ICANON is
1165433d6423SLionel Sambuc * switched off, a process should use TCSAFLUSH to flush the queue.
1166433d6423SLionel Sambuc * Keeping the queue to preserve typeahead is the Right Thing, however
1167433d6423SLionel Sambuc * when a process does use TCSANOW to switch to raw mode.
1168433d6423SLionel Sambuc */
1169433d6423SLionel Sambuc count = tp->tty_eotct = tp->tty_incount;
1170433d6423SLionel Sambuc inp = tp->tty_intail;
1171433d6423SLionel Sambuc while (count > 0) {
1172433d6423SLionel Sambuc *inp |= IN_EOT;
1173433d6423SLionel Sambuc if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf;
1174433d6423SLionel Sambuc --count;
1175433d6423SLionel Sambuc }
1176433d6423SLionel Sambuc }
1177433d6423SLionel Sambuc
1178433d6423SLionel Sambuc /* Inspect MIN and TIME. */
1179433d6423SLionel Sambuc settimer(tp, FALSE);
1180433d6423SLionel Sambuc if (tp->tty_termios.c_lflag & ICANON) {
1181433d6423SLionel Sambuc /* No MIN & TIME in canonical mode. */
1182433d6423SLionel Sambuc tp->tty_min = 1;
1183433d6423SLionel Sambuc } else {
1184433d6423SLionel Sambuc /* In raw mode MIN is the number of chars wanted, and TIME how long
1185433d6423SLionel Sambuc * to wait for them. With interesting exceptions if either is zero.
1186433d6423SLionel Sambuc */
1187433d6423SLionel Sambuc tp->tty_min = tp->tty_termios.c_cc[VMIN];
1188433d6423SLionel Sambuc if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0)
1189433d6423SLionel Sambuc tp->tty_min = 1;
1190433d6423SLionel Sambuc }
1191433d6423SLionel Sambuc
1192433d6423SLionel Sambuc if (!(tp->tty_termios.c_iflag & IXON)) {
1193433d6423SLionel Sambuc /* No start/stop output control, so don't leave output inhibited. */
1194433d6423SLionel Sambuc tp->tty_inhibited = RUNNING;
1195433d6423SLionel Sambuc tp->tty_events = 1;
1196433d6423SLionel Sambuc }
1197433d6423SLionel Sambuc
1198433d6423SLionel Sambuc /* Setting the output speed to zero hangs up the phone. */
1199433d6423SLionel Sambuc if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1);
1200433d6423SLionel Sambuc }
1201433d6423SLionel Sambuc
1202433d6423SLionel Sambuc /*===========================================================================*
1203433d6423SLionel Sambuc * sigchar *
1204433d6423SLionel Sambuc *===========================================================================*/
sigchar(tp,sig,mayflush)1205433d6423SLionel Sambuc void sigchar(tp, sig, mayflush)
1206433d6423SLionel Sambuc register tty_t *tp;
1207433d6423SLionel Sambuc int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */
1208433d6423SLionel Sambuc int mayflush;
1209433d6423SLionel Sambuc {
1210433d6423SLionel Sambuc /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from
1211433d6423SLionel Sambuc * a tty close, "stty 0", or a real RS-232 hangup. PM will send the signal to
1212433d6423SLionel Sambuc * the process group (INT, QUIT), all processes (KILL), or the session leader
1213433d6423SLionel Sambuc * (HUP).
1214433d6423SLionel Sambuc */
1215433d6423SLionel Sambuc int status;
1216433d6423SLionel Sambuc
1217433d6423SLionel Sambuc if (tp->tty_pgrp != 0) {
1218433d6423SLionel Sambuc if (OK != (status = sys_kill(tp->tty_pgrp, sig))) {
1219433d6423SLionel Sambuc panic("Error; call to sys_kill failed: %d", status);
1220433d6423SLionel Sambuc }
1221433d6423SLionel Sambuc }
1222433d6423SLionel Sambuc
1223433d6423SLionel Sambuc if (mayflush && !(tp->tty_termios.c_lflag & NOFLSH)) {
1224433d6423SLionel Sambuc tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */
1225433d6423SLionel Sambuc tp->tty_intail = tp->tty_inhead;
1226433d6423SLionel Sambuc (*tp->tty_ocancel)(tp, 0); /* kill all output */
1227433d6423SLionel Sambuc tp->tty_inhibited = RUNNING;
1228433d6423SLionel Sambuc tp->tty_events = 1;
1229433d6423SLionel Sambuc }
1230433d6423SLionel Sambuc }
1231433d6423SLionel Sambuc
1232433d6423SLionel Sambuc /*===========================================================================*
1233433d6423SLionel Sambuc * tty_icancel *
1234433d6423SLionel Sambuc *===========================================================================*/
tty_icancel(tp)1235433d6423SLionel Sambuc static void tty_icancel(tp)
1236433d6423SLionel Sambuc register tty_t *tp;
1237433d6423SLionel Sambuc {
1238433d6423SLionel Sambuc /* Discard all pending input, tty buffer or device. */
1239433d6423SLionel Sambuc
1240433d6423SLionel Sambuc tp->tty_incount = tp->tty_eotct = 0;
1241433d6423SLionel Sambuc tp->tty_intail = tp->tty_inhead;
1242433d6423SLionel Sambuc (*tp->tty_icancel)(tp, 0);
1243433d6423SLionel Sambuc }
1244433d6423SLionel Sambuc
1245433d6423SLionel Sambuc /*===========================================================================*
1246433d6423SLionel Sambuc * tty_devnop *
1247433d6423SLionel Sambuc *===========================================================================*/
tty_devnop(tty_t * UNUSED (tp),int UNUSED (try))1248433d6423SLionel Sambuc static int tty_devnop(tty_t *UNUSED(tp), int UNUSED(try))
1249433d6423SLionel Sambuc {
1250433d6423SLionel Sambuc /* Some functions need not be implemented at the device level. */
1251433d6423SLionel Sambuc return 0;
1252433d6423SLionel Sambuc }
1253433d6423SLionel Sambuc
1254433d6423SLionel Sambuc /*===========================================================================*
1255433d6423SLionel Sambuc * tty_init *
1256433d6423SLionel Sambuc *===========================================================================*/
tty_init(int UNUSED (type),sef_init_info_t * UNUSED (info))1257433d6423SLionel Sambuc static int tty_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
1258433d6423SLionel Sambuc {
1259433d6423SLionel Sambuc /* Initialize tty structure and call device initialization routines. */
1260433d6423SLionel Sambuc
1261433d6423SLionel Sambuc register tty_t *tp;
1262433d6423SLionel Sambuc int s;
1263433d6423SLionel Sambuc
1264433d6423SLionel Sambuc system_hz = sys_hz();
1265433d6423SLionel Sambuc
1266da21d850SDavid van Moolenbroek tty_gid = -1;
1267da21d850SDavid van Moolenbroek if (env_argc > 1)
1268da21d850SDavid van Moolenbroek optset_parse(optset_table, env_argv[1]);
1269da21d850SDavid van Moolenbroek
1270433d6423SLionel Sambuc /* Initialize the terminal lines. */
1271433d6423SLionel Sambuc memset(tty_table, '\0' , sizeof(tty_table));
1272433d6423SLionel Sambuc
1273433d6423SLionel Sambuc for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) {
1274433d6423SLionel Sambuc
1275433d6423SLionel Sambuc tp->tty_index = s;
1276433d6423SLionel Sambuc init_timer(&tp->tty_tmr);
1277433d6423SLionel Sambuc
1278433d6423SLionel Sambuc tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
1279433d6423SLionel Sambuc tp->tty_min = 1;
1280433d6423SLionel Sambuc tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE;
1281433d6423SLionel Sambuc tp->tty_termios = termios_defaults;
1282da21d850SDavid van Moolenbroek tp->tty_icancel = tp->tty_ocancel = tp->tty_close =
1283433d6423SLionel Sambuc tp->tty_open = tty_devnop;
1284433d6423SLionel Sambuc pty_init(tp);
1285433d6423SLionel Sambuc }
1286433d6423SLionel Sambuc
1287433d6423SLionel Sambuc return OK;
1288433d6423SLionel Sambuc }
1289433d6423SLionel Sambuc
1290433d6423SLionel Sambuc /*===========================================================================*
1291433d6423SLionel Sambuc * tty_timed_out *
1292433d6423SLionel Sambuc *===========================================================================*/
tty_timed_out(int arg)1293*cfd712b4SDavid van Moolenbroek static void tty_timed_out(int arg)
1294433d6423SLionel Sambuc {
1295433d6423SLionel Sambuc /* This timer has expired. Set the events flag, to force processing. */
1296433d6423SLionel Sambuc tty_t *tty_ptr;
1297*cfd712b4SDavid van Moolenbroek tty_ptr = &tty_table[arg];
1298433d6423SLionel Sambuc tty_ptr->tty_min = 0; /* force read to succeed */
1299433d6423SLionel Sambuc tty_ptr->tty_events = 1;
1300433d6423SLionel Sambuc }
1301433d6423SLionel Sambuc
1302433d6423SLionel Sambuc /*===========================================================================*
1303433d6423SLionel Sambuc * settimer *
1304433d6423SLionel Sambuc *===========================================================================*/
settimer(tty_ptr,enable)1305433d6423SLionel Sambuc static void settimer(tty_ptr, enable)
1306433d6423SLionel Sambuc tty_t *tty_ptr; /* line to set or unset a timer on */
1307433d6423SLionel Sambuc int enable; /* set timer if true, otherwise unset */
1308433d6423SLionel Sambuc {
1309433d6423SLionel Sambuc clock_t ticks;
1310433d6423SLionel Sambuc
1311433d6423SLionel Sambuc if (enable) {
1312433d6423SLionel Sambuc ticks = tty_ptr->tty_termios.c_cc[VTIME] * (system_hz/10);
1313433d6423SLionel Sambuc
1314433d6423SLionel Sambuc /* Set a new timer for enabling the TTY events flags. */
1315433d6423SLionel Sambuc set_timer(&tty_ptr->tty_tmr, ticks, tty_timed_out, tty_ptr->tty_index);
1316433d6423SLionel Sambuc } else {
1317433d6423SLionel Sambuc /* Remove the timer from the active and expired lists. */
1318433d6423SLionel Sambuc cancel_timer(&tty_ptr->tty_tmr);
1319433d6423SLionel Sambuc }
1320433d6423SLionel Sambuc }
1321