terminal.cc (12334:e0ab29a34764) terminal.cc (12693:4db8d6442b44)
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/serial/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/logging.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/serial/uart.hh"
66
67using namespace std;
68
69
70/*
71 * Poll event for the listen socket
72 */
73Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
74 : PollEvent(fd, e), term(t)
75{
76}
77
78void
79Terminal::ListenEvent::process(int revent)
80{
81 term->accept();
82}
83
84/*
85 * Poll event for the data socket
86 */
87Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
88 : PollEvent(fd, e), term(t)
89{
90}
91
92void
93Terminal::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 */
109Terminal::Terminal(const Params *p)
110 : SerialDevice(p), 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
124Terminal::~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
137///////////////////////////////////////////////////////////////////////
138// socket creation and terminal attach
139//
140
141void
142Terminal::listen(int port)
143{
144 if (ListenSocket::allDisabled()) {
145 warn_once("Sockets disabled, not accepting terminal connections");
146 return;
147 }
148
149 while (!listener.listen(port, true)) {
150 DPRINTF(Terminal,
151 ": can't bind address terminal port %d inuse PID %d\n",
152 port, getpid());
153 port++;
154 }
155
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/serial/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/logging.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/serial/uart.hh"
66
67using namespace std;
68
69
70/*
71 * Poll event for the listen socket
72 */
73Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
74 : PollEvent(fd, e), term(t)
75{
76}
77
78void
79Terminal::ListenEvent::process(int revent)
80{
81 term->accept();
82}
83
84/*
85 * Poll event for the data socket
86 */
87Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
88 : PollEvent(fd, e), term(t)
89{
90}
91
92void
93Terminal::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 */
109Terminal::Terminal(const Params *p)
110 : SerialDevice(p), 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
124Terminal::~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
137///////////////////////////////////////////////////////////////////////
138// socket creation and terminal attach
139//
140
141void
142Terminal::listen(int port)
143{
144 if (ListenSocket::allDisabled()) {
145 warn_once("Sockets disabled, not accepting terminal connections");
146 return;
147 }
148
149 while (!listener.listen(port, true)) {
150 DPRINTF(Terminal,
151 ": can't bind address terminal port %d inuse PID %d\n",
152 port, getpid());
153 port++;
154 }
155
156 int p1, p2;
157 p2 = name().rfind('.') - 1;
158 p1 = name().rfind('.', p2);
159 ccprintf(cerr, "Listening for %s connection on port %d\n",
160 name().substr(p1+1,p2-p1), port);
156 ccprintf(cerr, "%s: Listening for connections on port %d\n",
157 name(), port);
161
162 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
163 pollQueue.schedule(listenEvent);
164}
165
166void
167Terminal::accept()
168{
169 if (!listener.islistening())
170 panic("%s: cannot accept a connection if not listening!", name());
171
172 int fd = listener.accept(true);
173 if (data_fd != -1) {
174 char message[] = "terminal already attached!\n";
175 atomic_write(fd, message, sizeof(message));
176 ::close(fd);
177 return;
178 }
179
180 data_fd = fd;
181 dataEvent = new DataEvent(this, data_fd, POLLIN);
182 pollQueue.schedule(dataEvent);
183
184 stringstream stream;
185 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
186
187 // we need an actual carriage return followed by a newline for the
188 // terminal
189 stream << "\r\n";
190
191 write((const uint8_t *)stream.str().c_str(), stream.str().size());
192
193 DPRINTFN("attach terminal %d\n", number);
194 char buf[1024];
195 for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
196 const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
197 txbuf.peek(buf, i, chunk_len);
198 write((const uint8_t *)buf, chunk_len);
199 }
200}
201
202void
203Terminal::detach()
204{
205 if (data_fd != -1) {
206 ::close(data_fd);
207 data_fd = -1;
208 }
209
210 pollQueue.remove(dataEvent);
211 delete dataEvent;
212 dataEvent = NULL;
213
214 DPRINTFN("detach terminal %d\n", number);
215}
216
217void
218Terminal::data()
219{
220 uint8_t buf[1024];
221 int len;
222
223 len = read(buf, sizeof(buf));
224 if (len) {
225 rxbuf.write((char *)buf, len);
226 notifyInterface();
227 }
228}
229
230size_t
231Terminal::read(uint8_t *buf, size_t len)
232{
233 if (data_fd < 0)
234 panic("Terminal not properly attached.\n");
235
236 ssize_t ret;
237 do {
238 ret = ::read(data_fd, buf, len);
239 } while (ret == -1 && errno == EINTR);
240
241
242 if (ret < 0)
243 DPRINTFN("Read failed.\n");
244
245 if (ret <= 0) {
246 detach();
247 return 0;
248 }
249
250 return ret;
251}
252
253// Terminal output.
254size_t
255Terminal::write(const uint8_t *buf, size_t len)
256{
257 if (data_fd < 0)
258 panic("Terminal not properly attached.\n");
259
260 ssize_t ret = atomic_write(data_fd, buf, len);
261 if (ret < len)
262 detach();
263
264 return ret;
265}
266
267#define MORE_PENDING (ULL(1) << 61)
268#define RECEIVE_SUCCESS (ULL(0) << 62)
269#define RECEIVE_NONE (ULL(2) << 62)
270#define RECEIVE_ERROR (ULL(3) << 62)
271
272uint8_t
273Terminal::readData()
274{
275 uint8_t c;
276
277 assert(!rxbuf.empty());
278 rxbuf.read((char *)&c, 1);
279
280 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
281 isprint(c) ? c : ' ', c, !rxbuf.empty());
282
283 return c;
284}
285
286uint64_t
287Terminal::console_in()
288{
289 uint64_t value;
290
291 if (dataAvailable()) {
292 value = RECEIVE_SUCCESS | readData();
293 if (!rxbuf.empty())
294 value |= MORE_PENDING;
295 } else {
296 value = RECEIVE_NONE;
297 }
298
299 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
300
301 return value;
302}
303
304void
305Terminal::writeData(uint8_t c)
306{
307#if TRACING_ON == 1
308 if (DTRACE(Terminal)) {
309 static char last = '\0';
310
311 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) {
312 if (c == '\n' || c == '\r') {
313 int size = linebuf.size();
314 char *buffer = new char[size + 1];
315 linebuf.read(buffer, size);
316 buffer[size] = '\0';
317 DPRINTF(Terminal, "%s\n", buffer);
318 delete [] buffer;
319 } else {
320 linebuf.write(&c, 1);
321 }
322 }
323
324 last = c;
325 }
326#endif
327
328 txbuf.write(&c, 1);
329
330 if (data_fd >= 0)
331 write(c);
332
333 if (outfile)
334 outfile->stream()->put((char)c);
335
336 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
337 isprint(c) ? c : ' ', (int)c);
338
339}
340
341Terminal *
342TerminalParams::create()
343{
344 return new Terminal(this);
345}
158
159 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
160 pollQueue.schedule(listenEvent);
161}
162
163void
164Terminal::accept()
165{
166 if (!listener.islistening())
167 panic("%s: cannot accept a connection if not listening!", name());
168
169 int fd = listener.accept(true);
170 if (data_fd != -1) {
171 char message[] = "terminal already attached!\n";
172 atomic_write(fd, message, sizeof(message));
173 ::close(fd);
174 return;
175 }
176
177 data_fd = fd;
178 dataEvent = new DataEvent(this, data_fd, POLLIN);
179 pollQueue.schedule(dataEvent);
180
181 stringstream stream;
182 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
183
184 // we need an actual carriage return followed by a newline for the
185 // terminal
186 stream << "\r\n";
187
188 write((const uint8_t *)stream.str().c_str(), stream.str().size());
189
190 DPRINTFN("attach terminal %d\n", number);
191 char buf[1024];
192 for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
193 const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
194 txbuf.peek(buf, i, chunk_len);
195 write((const uint8_t *)buf, chunk_len);
196 }
197}
198
199void
200Terminal::detach()
201{
202 if (data_fd != -1) {
203 ::close(data_fd);
204 data_fd = -1;
205 }
206
207 pollQueue.remove(dataEvent);
208 delete dataEvent;
209 dataEvent = NULL;
210
211 DPRINTFN("detach terminal %d\n", number);
212}
213
214void
215Terminal::data()
216{
217 uint8_t buf[1024];
218 int len;
219
220 len = read(buf, sizeof(buf));
221 if (len) {
222 rxbuf.write((char *)buf, len);
223 notifyInterface();
224 }
225}
226
227size_t
228Terminal::read(uint8_t *buf, size_t len)
229{
230 if (data_fd < 0)
231 panic("Terminal not properly attached.\n");
232
233 ssize_t ret;
234 do {
235 ret = ::read(data_fd, buf, len);
236 } while (ret == -1 && errno == EINTR);
237
238
239 if (ret < 0)
240 DPRINTFN("Read failed.\n");
241
242 if (ret <= 0) {
243 detach();
244 return 0;
245 }
246
247 return ret;
248}
249
250// Terminal output.
251size_t
252Terminal::write(const uint8_t *buf, size_t len)
253{
254 if (data_fd < 0)
255 panic("Terminal not properly attached.\n");
256
257 ssize_t ret = atomic_write(data_fd, buf, len);
258 if (ret < len)
259 detach();
260
261 return ret;
262}
263
264#define MORE_PENDING (ULL(1) << 61)
265#define RECEIVE_SUCCESS (ULL(0) << 62)
266#define RECEIVE_NONE (ULL(2) << 62)
267#define RECEIVE_ERROR (ULL(3) << 62)
268
269uint8_t
270Terminal::readData()
271{
272 uint8_t c;
273
274 assert(!rxbuf.empty());
275 rxbuf.read((char *)&c, 1);
276
277 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
278 isprint(c) ? c : ' ', c, !rxbuf.empty());
279
280 return c;
281}
282
283uint64_t
284Terminal::console_in()
285{
286 uint64_t value;
287
288 if (dataAvailable()) {
289 value = RECEIVE_SUCCESS | readData();
290 if (!rxbuf.empty())
291 value |= MORE_PENDING;
292 } else {
293 value = RECEIVE_NONE;
294 }
295
296 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
297
298 return value;
299}
300
301void
302Terminal::writeData(uint8_t c)
303{
304#if TRACING_ON == 1
305 if (DTRACE(Terminal)) {
306 static char last = '\0';
307
308 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) {
309 if (c == '\n' || c == '\r') {
310 int size = linebuf.size();
311 char *buffer = new char[size + 1];
312 linebuf.read(buffer, size);
313 buffer[size] = '\0';
314 DPRINTF(Terminal, "%s\n", buffer);
315 delete [] buffer;
316 } else {
317 linebuf.write(&c, 1);
318 }
319 }
320
321 last = c;
322 }
323#endif
324
325 txbuf.write(&c, 1);
326
327 if (data_fd >= 0)
328 write(c);
329
330 if (outfile)
331 outfile->stream()->put((char)c);
332
333 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
334 isprint(c) ? c : ' ', (int)c);
335
336}
337
338Terminal *
339TerminalParams::create()
340{
341 return new Terminal(this);
342}