power: Add support for power models
[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
38 #if defined(__FreeBSD__)
39 #include <termios.h>
40
41 #else
42 #include <sys/termios.h>
43
44 #endif
45 #include "dev/terminal.hh"
46
47 #include <poll.h>
48 #include <unistd.h>
49
50 #include <cctype>
51 #include <cerrno>
52 #include <fstream>
53 #include <iostream>
54 #include <sstream>
55 #include <string>
56
57 #include "base/atomicio.hh"
58 #include "base/misc.hh"
59 #include "base/output.hh"
60 #include "base/socket.hh"
61 #include "base/trace.hh"
62 #include "debug/Terminal.hh"
63 #include "debug/TerminalVerbose.hh"
64 #include "dev/platform.hh"
65 #include "dev/uart.hh"
66
67 using namespace std;
68
69
70 /*
71 * Poll event for the listen socket
72 */
73 Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
74 : PollEvent(fd, e), term(t)
75 {
76 }
77
78 void
79 Terminal::ListenEvent::process(int revent)
80 {
81 term->accept();
82 }
83
84 /*
85 * Poll event for the data socket
86 */
87 Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
88 : PollEvent(fd, e), term(t)
89 {
90 }
91
92 void
93 Terminal::DataEvent::process(int revent)
94 {
95 // As a consequence of being called from the PollQueue, we might
96 // have been called from a different thread. Migrate to "our"
97 // thread.
98 EventQueue::ScopedMigration migrate(term->eventQueue());
99
100 if (revent & POLLIN)
101 term->data();
102 else if (revent & POLLNVAL)
103 term->detach();
104 }
105
106 /*
107 * Terminal code
108 */
109 Terminal::Terminal(const Params *p)
110 : SimObject(p), termDataAvail(NULL), listenEvent(NULL), dataEvent(NULL),
111 number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384),
112 outfile(p->output ? simout.findOrCreate(p->name) : NULL)
113 #if TRACING_ON == 1
114 , linebuf(16384)
115 #endif
116 {
117 if (outfile)
118 outfile->stream()->setf(ios::unitbuf);
119
120 if (p->port)
121 listen(p->port);
122 }
123
124 Terminal::~Terminal()
125 {
126 if (data_fd != -1)
127 ::close(data_fd);
128
129 if (listenEvent)
130 delete listenEvent;
131
132 if (dataEvent)
133 delete dataEvent;
134 }
135
136 void
137 Terminal::regDataAvailCallback(Callback *c)
138 {
139 // This can happen if the user has connected multiple UARTs to the
140 // same terminal. In that case, each of them tries to register
141 // callbacks.
142 if (termDataAvail)
143 fatal("Terminal already has already been associated with a UART.\n");
144 termDataAvail = c;
145 }
146
147 ///////////////////////////////////////////////////////////////////////
148 // socket creation and terminal attach
149 //
150
151 void
152 Terminal::listen(int port)
153 {
154 if (ListenSocket::allDisabled()) {
155 warn_once("Sockets disabled, not accepting terminal connections");
156 return;
157 }
158
159 while (!listener.listen(port, true)) {
160 DPRINTF(Terminal,
161 ": can't bind address terminal port %d inuse PID %d\n",
162 port, getpid());
163 port++;
164 }
165
166 int p1, p2;
167 p2 = name().rfind('.') - 1;
168 p1 = name().rfind('.', p2);
169 ccprintf(cerr, "Listening for %s connection on port %d\n",
170 name().substr(p1+1,p2-p1), port);
171
172 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
173 pollQueue.schedule(listenEvent);
174 }
175
176 void
177 Terminal::accept()
178 {
179 if (!listener.islistening())
180 panic("%s: cannot accept a connection if not listening!", name());
181
182 int fd = listener.accept(true);
183 if (data_fd != -1) {
184 char message[] = "terminal already attached!\n";
185 atomic_write(fd, message, sizeof(message));
186 ::close(fd);
187 return;
188 }
189
190 data_fd = fd;
191 dataEvent = new DataEvent(this, data_fd, POLLIN);
192 pollQueue.schedule(dataEvent);
193
194 stringstream stream;
195 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
196
197 // we need an actual carriage return followed by a newline for the
198 // terminal
199 stream << "\r\n";
200
201 write((const uint8_t *)stream.str().c_str(), stream.str().size());
202
203 DPRINTFN("attach terminal %d\n", number);
204 char buf[1024];
205 for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
206 const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
207 txbuf.peek(buf, chunk_len);
208 write((const uint8_t *)buf, chunk_len);
209 }
210 }
211
212 void
213 Terminal::detach()
214 {
215 if (data_fd != -1) {
216 ::close(data_fd);
217 data_fd = -1;
218 }
219
220 pollQueue.remove(dataEvent);
221 delete dataEvent;
222 dataEvent = NULL;
223
224 DPRINTFN("detach terminal %d\n", number);
225 }
226
227 void
228 Terminal::data()
229 {
230 uint8_t buf[1024];
231 int len;
232
233 len = read(buf, sizeof(buf));
234 if (len) {
235 rxbuf.write((char *)buf, len);
236 // Inform the UART there is data available
237 assert(termDataAvail);
238 termDataAvail->process();
239 }
240 }
241
242 size_t
243 Terminal::read(uint8_t *buf, size_t len)
244 {
245 if (data_fd < 0)
246 panic("Terminal not properly attached.\n");
247
248 ssize_t ret;
249 do {
250 ret = ::read(data_fd, buf, len);
251 } while (ret == -1 && errno == EINTR);
252
253
254 if (ret < 0)
255 DPRINTFN("Read failed.\n");
256
257 if (ret <= 0) {
258 detach();
259 return 0;
260 }
261
262 return ret;
263 }
264
265 // Terminal output.
266 size_t
267 Terminal::write(const uint8_t *buf, size_t len)
268 {
269 if (data_fd < 0)
270 panic("Terminal not properly attached.\n");
271
272 ssize_t ret = atomic_write(data_fd, buf, len);
273 if (ret < len)
274 detach();
275
276 return ret;
277 }
278
279 #define MORE_PENDING (ULL(1) << 61)
280 #define RECEIVE_SUCCESS (ULL(0) << 62)
281 #define RECEIVE_NONE (ULL(2) << 62)
282 #define RECEIVE_ERROR (ULL(3) << 62)
283
284 uint8_t
285 Terminal::in()
286 {
287 uint8_t c;
288
289 assert(!rxbuf.empty());
290 rxbuf.read((char *)&c, 1);
291
292 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
293 isprint(c) ? c : ' ', c, !rxbuf.empty());
294
295 return c;
296 }
297
298 uint64_t
299 Terminal::console_in()
300 {
301 uint64_t value;
302
303 if (dataAvailable()) {
304 value = RECEIVE_SUCCESS | in();
305 if (!rxbuf.empty())
306 value |= MORE_PENDING;
307 } else {
308 value = RECEIVE_NONE;
309 }
310
311 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
312
313 return value;
314 }
315
316 void
317 Terminal::out(char c)
318 {
319 #if TRACING_ON == 1
320 if (DTRACE(Terminal)) {
321 static char last = '\0';
322
323 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) {
324 if (c == '\n' || c == '\r') {
325 int size = linebuf.size();
326 char *buffer = new char[size + 1];
327 linebuf.read(buffer, size);
328 buffer[size] = '\0';
329 DPRINTF(Terminal, "%s\n", buffer);
330 delete [] buffer;
331 } else {
332 linebuf.write(&c, 1);
333 }
334 }
335
336 last = c;
337 }
338 #endif
339
340 txbuf.write(&c, 1);
341
342 if (data_fd >= 0)
343 write(c);
344
345 if (outfile)
346 outfile->stream()->write(&c, 1);
347
348 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
349 isprint(c) ? c : ' ', (int)c);
350
351 }
352
353 Terminal *
354 TerminalParams::create()
355 {
356 return new Terminal(this);
357 }