includes: sort all includes
[gem5.git] / src / dev / terminal.cc
1 /*
2 * Copyright (c) 2001-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Nathan Binkert
29 * Ali Saidi
30 */
31
32 /* @file
33 * Implements the user interface to a serial terminal
34 */
35
36 #include <sys/ioctl.h>
37 #include <sys/termios.h>
38 #include <poll.h>
39 #include <unistd.h>
40
41 #include <cctype>
42 #include <cerrno>
43 #include <fstream>
44 #include <iostream>
45 #include <sstream>
46 #include <string>
47
48 #include "base/atomicio.hh"
49 #include "base/misc.hh"
50 #include "base/output.hh"
51 #include "base/socket.hh"
52 #include "base/trace.hh"
53 #include "dev/platform.hh"
54 #include "dev/terminal.hh"
55 #include "dev/uart.hh"
56
57 using namespace std;
58
59
60 /*
61 * Poll event for the listen socket
62 */
63 Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
64 : PollEvent(fd, e), term(t)
65 {
66 }
67
68 void
69 Terminal::ListenEvent::process(int revent)
70 {
71 term->accept();
72 }
73
74 /*
75 * Poll event for the data socket
76 */
77 Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
78 : PollEvent(fd, e), term(t)
79 {
80 }
81
82 void
83 Terminal::DataEvent::process(int revent)
84 {
85 if (revent & POLLIN)
86 term->data();
87 else if (revent & POLLNVAL)
88 term->detach();
89 }
90
91 /*
92 * Terminal code
93 */
94 Terminal::Terminal(const Params *p)
95 : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number),
96 data_fd(-1), txbuf(16384), rxbuf(16384), outfile(NULL)
97 #if TRACING_ON == 1
98 , linebuf(16384)
99 #endif
100 {
101 if (p->output) {
102 outfile = simout.find(p->name);
103 outfile->setf(ios::unitbuf);
104 }
105
106 if (p->port)
107 listen(p->port);
108 }
109
110 Terminal::~Terminal()
111 {
112 if (data_fd != -1)
113 ::close(data_fd);
114
115 if (listenEvent)
116 delete listenEvent;
117
118 if (dataEvent)
119 delete dataEvent;
120 }
121
122 ///////////////////////////////////////////////////////////////////////
123 // socket creation and terminal attach
124 //
125
126 void
127 Terminal::listen(int port)
128 {
129 if (ListenSocket::allDisabled()) {
130 warn_once("Sockets disabled, not accepting terminal connections");
131 return;
132 }
133
134 while (!listener.listen(port, true)) {
135 DPRINTF(Terminal,
136 ": can't bind address terminal port %d inuse PID %d\n",
137 port, getpid());
138 port++;
139 }
140
141 int p1, p2;
142 p2 = name().rfind('.') - 1;
143 p1 = name().rfind('.', p2);
144 ccprintf(cerr, "Listening for %s connection on port %d\n",
145 name().substr(p1+1,p2-p1), port);
146
147 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
148 pollQueue.schedule(listenEvent);
149 }
150
151 void
152 Terminal::accept()
153 {
154 if (!listener.islistening())
155 panic("%s: cannot accept a connection if not listening!", name());
156
157 int fd = listener.accept(true);
158 if (data_fd != -1) {
159 char message[] = "terminal already attached!\n";
160 atomic_write(fd, message, sizeof(message));
161 ::close(fd);
162 return;
163 }
164
165 data_fd = fd;
166 dataEvent = new DataEvent(this, data_fd, POLLIN);
167 pollQueue.schedule(dataEvent);
168
169 stringstream stream;
170 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
171
172 // we need an actual carriage return followed by a newline for the
173 // terminal
174 stream << "\r\n";
175
176 write((const uint8_t *)stream.str().c_str(), stream.str().size());
177
178 DPRINTFN("attach terminal %d\n", number);
179
180 txbuf.readall(data_fd);
181 }
182
183 void
184 Terminal::detach()
185 {
186 if (data_fd != -1) {
187 ::close(data_fd);
188 data_fd = -1;
189 }
190
191 pollQueue.remove(dataEvent);
192 delete dataEvent;
193 dataEvent = NULL;
194
195 DPRINTFN("detach terminal %d\n", number);
196 }
197
198 void
199 Terminal::data()
200 {
201 uint8_t buf[1024];
202 int len;
203
204 len = read(buf, sizeof(buf));
205 if (len) {
206 rxbuf.write((char *)buf, len);
207 // Inform the UART there is data available
208 uart->dataAvailable();
209 }
210 }
211
212 size_t
213 Terminal::read(uint8_t *buf, size_t len)
214 {
215 if (data_fd < 0)
216 panic("Terminal not properly attached.\n");
217
218 size_t ret;
219 do {
220 ret = ::read(data_fd, buf, len);
221 } while (ret == -1 && errno == EINTR);
222
223
224 if (ret < 0)
225 DPRINTFN("Read failed.\n");
226
227 if (ret <= 0) {
228 detach();
229 return 0;
230 }
231
232 return ret;
233 }
234
235 // Terminal output.
236 size_t
237 Terminal::write(const uint8_t *buf, size_t len)
238 {
239 if (data_fd < 0)
240 panic("Terminal not properly attached.\n");
241
242 ssize_t ret = atomic_write(data_fd, buf, len);
243 if (ret < len)
244 detach();
245
246 return ret;
247 }
248
249 #define MORE_PENDING (ULL(1) << 61)
250 #define RECEIVE_SUCCESS (ULL(0) << 62)
251 #define RECEIVE_NONE (ULL(2) << 62)
252 #define RECEIVE_ERROR (ULL(3) << 62)
253
254 uint8_t
255 Terminal::in()
256 {
257 bool empty;
258 uint8_t c;
259
260 empty = rxbuf.empty();
261 assert(!empty);
262 rxbuf.read((char *)&c, 1);
263 empty = rxbuf.empty();
264
265
266 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
267 isprint(c) ? c : ' ', c, !empty);
268
269 return c;
270 }
271
272 uint64_t
273 Terminal::console_in()
274 {
275 uint64_t value;
276
277 if (dataAvailable()) {
278 value = RECEIVE_SUCCESS | in();
279 if (!rxbuf.empty())
280 value |= MORE_PENDING;
281 } else {
282 value = RECEIVE_NONE;
283 }
284
285 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
286
287 return value;
288 }
289
290 void
291 Terminal::out(char c)
292 {
293 #if TRACING_ON == 1
294 if (DTRACE(Terminal)) {
295 static char last = '\0';
296
297 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) {
298 if (c == '\n' || c == '\r') {
299 int size = linebuf.size();
300 char *buffer = new char[size + 1];
301 linebuf.read(buffer, size);
302 buffer[size] = '\0';
303 DPRINTF(Terminal, "%s\n", buffer);
304 delete [] buffer;
305 } else {
306 linebuf.write(c);
307 }
308 }
309
310 last = c;
311 }
312 #endif
313
314 txbuf.write(c);
315
316 if (data_fd >= 0)
317 write(c);
318
319 if (outfile)
320 outfile->write(&c, 1);
321
322 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
323 isprint(c) ? c : ' ', (int)c);
324
325 }
326
327 Terminal *
328 TerminalParams::create()
329 {
330 return new Terminal(this);
331 }