1 /* Implementation of an abstract terminal controller.
2    Copyright 2002 Paul Twohey.
3 
4 This file is part of VMIPS.
5 
6 VMIPS is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10 
11 VMIPS is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with VMIPS; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19 
20 #ifndef _TERMINALCONTROLLER_H_
21 #define _TERMINALCONTROLLER_H_
22 
23 #include "devreg.h"
24 #include "task.h"
25 #include <new>
26 #include <sys/types.h>
27 #include <termios.h>
28 class Clock;
29 
30 // XXX Maximum number of terminals the controller supports.
31 #define	MAX_TERMINALS	16
32 
33 /* A keyboard can be in one of two states: READY or UNREADY. The state READY
34    corresponds to when the keyboard has new data the simulated program hasn't
35    yet read. In the READY state the keyboard will check for new data every
36    keyboard_repoll_ns nanoseconds and replace the old data with the new data.
37    The keyboard transitions to the UNREADY state when the simulated program
38    reads the keyboard data. In the UNREADY state the keyboard will poll for
39    new data every keyboard_poll_ns nanoseconds. When the keyboard detects
40    data it will transition to the READY state. The keyboard is initially in
41    the UNREADY state.
42 
43    A display can be in one of two states: READY or UNREADY. The state READY
44    corresponds to when the display is able to accept a new character to
45    write out. The display transitions to the UNREADY state when a character
46    is written to the display data register. In the UNREADY state the display
47    ignores rights. The display transitions to the READY state after
48    display_ready_delay_ns nanoseconds. The display is initially in the READY
49    state.
50 */
51 
52 class TerminalController
53 {
54 public:
55 	/* Create a new TerminalController which uses CLOCK as its time base.
56 	   KEYBOARD_POLL_NS is the positive time in nanoseconds that the
57 	   keyboards are polled for input. KEYBOARD_REPOLL_NS is the positive
58 	   time in nanoseconds that a READY keyboard will wait to repoll for
59 	   data. DISPLAY_READY_DELAY_NS is the positive time in nanoseconds
60 	   that a display will wait to transition from the UNREADY to the
61 	   READY state. */
62 	TerminalController( Clock *clock, long keyboard_poll_ns,
63 			    long keyboard_repoll_ns,
64 			    long display_ready_delay_ns );
65 
66 	/* Reset and close all the terminal file descriptors. */
67 	virtual ~TerminalController();
68 
69 	/* Connect the terminal with file descriptor TTY_FD to the simulated
70 	   terminal line LINE. Save the initial terminal state, then configure
71 	   the terminal for use as a simulated terminal. The controller now
72 	   owns the file descriptor and is responsible for restoring its
73 	   state and closing it. Returns true if the terminal was connected
74 	   sucessfully, otherwise closes FD and returns false. */
75 	virtual bool connect_terminal( int tty_fd, int line );
76 
77 	/* Remove the terminal on line LINE. Has no effect if there is no
78 	   terminal on line LINE. Restore the original terminal settings
79 	   for the line and then close its associated file descriptor. */
80 	virtual void remove_terminal( int line );
81 
82 	/* Reinitialize terminals to the state they were in when VMIPS started.
83 	   This is the opposite of reinitialize_terminals(). */
84 	virtual void suspend ();
85 
86 	/* Return true if line LINE is connected, false otherwise. */
line_connected(const int line)87 	bool line_connected (const int line) const {
88       return line >= 0 && line < MAX_TERMINALS && lines[line].tty_fd != -1;
89     }
90 
91 	/* Reinitialize terminals to a state suitable for use as part of a
92 	   vmips simulation. Useful for restoring tty settings when vmips
93 	   is moved to the forground after being backgrounded. */
94 	virtual void reinitialize_terminals();
95 
96 	/* Poll all the keyboards for new data to read. If data is available
97 	   read it in and adjust the keyboard state accordingly. For each
98 	   keyboard with data available, schedule a KeyboardWait object to
99 	   enforce the simulated delay between data checks. */
100 	virtual void poll_keyboards();
101 
102 	/* Helper routine to repoll the keyboard. */
103 	virtual void repoll_keyboard( int line );
104 
105 	/* Write characater DATA to the terminal on line LINE and transition
106 	   the display from the READY and UNREADY states to the UNREADY state.
107 	   This should only be called for connected lines. */
108 	virtual void unready_display( int line, char data );
109 
110 	/* Transition the display on line LINE from the UNREADY state into
111 	   the READY state. Should only be called for connected lines in the
112 	   UNREADY state. */
113 	virtual void ready_display( int line );
114 
115 	/* Transition the keyboard on line LINE from the READY state into
116 	   the UNREADY state. Should only be called for connected lines in
117 	   the the READY state. */
118 	virtual void unready_keyboard( int line );
119 
120 protected:
121 	/* Transition the keyboard from the READY or UNREADY states to the
122 	   READY state. Read data from the keyboard on line LINE. Should only
123 	   be called on connected lines. */
124 	virtual void ready_keyboard( int line );
125 
126 	/* Take connected (or partially connected) line LINE and prepare it
127 	   for use as part of a simulated console device. Returns true if the
128 	   preparation was sucessful, false otherwise. */
129 	virtual bool prepare_tty( int line );
130 
131 protected:
132 	class DisplayDelay : public CancelableTask
133 	{
134 	public:
135 		DisplayDelay(TerminalController *controller, int line);
136 		~DisplayDelay();
137 
138 	protected:
139 		/* Make READY display on line LINE on controller CONTROLLER. */
140 		virtual void real_task();
141 
142 	protected:
143 		TerminalController	*controller;
144 		int			line;
145 	};
146 
147 	class KeyboardRepoll : public CancelableTask
148 	{
149 	public:
150 		KeyboardRepoll(TerminalController *controller,int line);
151 		virtual ~KeyboardRepoll();
152 
153 	protected:
154 		/* Repoll keyboard on line LINE of controller CONTROLLER. */
155 		virtual void real_task();
156 
157 	protected:
158 		TerminalController	*controller;
159 		int			line;
160 	};
161 
162 	class KeyboardPoll : public CancelableTask
163 	{
164 	public:
165 		KeyboardPoll(TerminalController *controller);
166 		virtual ~KeyboardPoll();
167 
168 	protected:
169 		/* Poll all unready keyboards for new data. */
170 		virtual void real_task();
171 
172 	protected:
173 		TerminalController	*controller;
174 	};
175 
176 protected:
177 	const long	keyboard_poll_ns;	// UNREADY->READY keyboard poll
178 	const long	keyboard_repoll_ns;	// READY keyboard repoll
179 	const long	display_ready_delay_ns;	// display READY->UNREADY delay
180 
181 	KeyboardPoll	*keyboard_poll;		// current keyboard poll
182 	Clock		*clock;
183 	int		max_fd;			// cache of largest fd + 1
184 	fd_set		unready_keyboards;	// unready keyboard descriptors
185 
186 	/* Make a copy of unready_keyboards in SET. */
copy_unready_keyboards(fd_set * set)187 	void copy_unready_keyboards (fd_set *set) {
188 		*set = unready_keyboards;
189 	}
190 
191 	enum State {
192 		UNREADY	= 0,
193 		READY	= CTL_RDY,
194 	};
195 
196 	struct LineState {
197 		termios		tty_state;
198 		int		tty_fd;
199 		KeyboardRepoll	*keyboard_repoll;
200 		DisplayDelay	*display_delay;
201 		char		keyboard_char;
202 		State		keyboard_state;
203 		State		display_state;
204 	};
205 
206 	LineState	lines[ MAX_TERMINALS ];
207 };
208 
209 #endif /* _TERMINALCONTROLLER_H_ */
210