112239Sandreas.sandberg@arm.com/*
214249Sgiacomo.travaglini@arm.com * Copyright (c) 2019 ARM Limited
314249Sgiacomo.travaglini@arm.com * All rights reserved
414249Sgiacomo.travaglini@arm.com *
514249Sgiacomo.travaglini@arm.com * The license below extends only to copyright in the software and shall
614249Sgiacomo.travaglini@arm.com * not be construed as granting a license to any other intellectual
714249Sgiacomo.travaglini@arm.com * property including but not limited to intellectual property relating
814249Sgiacomo.travaglini@arm.com * to a hardware implementation of the functionality of the software
914249Sgiacomo.travaglini@arm.com * licensed hereunder.  You may use the software subject to the license
1014249Sgiacomo.travaglini@arm.com * terms below provided that you ensure that this notice is replicated
1114249Sgiacomo.travaglini@arm.com * unmodified and in its entirety in all distributions of the software,
1214249Sgiacomo.travaglini@arm.com * modified or unmodified, in source code or in binary form.
1314249Sgiacomo.travaglini@arm.com *
1412239Sandreas.sandberg@arm.com * Copyright (c) 2001-2005 The Regents of The University of Michigan
1512239Sandreas.sandberg@arm.com * All rights reserved.
1612239Sandreas.sandberg@arm.com *
1712239Sandreas.sandberg@arm.com * Redistribution and use in source and binary forms, with or without
1812239Sandreas.sandberg@arm.com * modification, are permitted provided that the following conditions are
1912239Sandreas.sandberg@arm.com * met: redistributions of source code must retain the above copyright
2012239Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer;
2112239Sandreas.sandberg@arm.com * redistributions in binary form must reproduce the above copyright
2212239Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer in the
2312239Sandreas.sandberg@arm.com * documentation and/or other materials provided with the distribution;
2412239Sandreas.sandberg@arm.com * neither the name of the copyright holders nor the names of its
2512239Sandreas.sandberg@arm.com * contributors may be used to endorse or promote products derived from
2612239Sandreas.sandberg@arm.com * this software without specific prior written permission.
2712239Sandreas.sandberg@arm.com *
2812239Sandreas.sandberg@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2912239Sandreas.sandberg@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3012239Sandreas.sandberg@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3112239Sandreas.sandberg@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3212239Sandreas.sandberg@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3312239Sandreas.sandberg@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3412239Sandreas.sandberg@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3512239Sandreas.sandberg@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3612239Sandreas.sandberg@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3712239Sandreas.sandberg@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3812239Sandreas.sandberg@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3912239Sandreas.sandberg@arm.com *
4012239Sandreas.sandberg@arm.com * Authors: Nathan Binkert
4112239Sandreas.sandberg@arm.com *          Ali Saidi
4212239Sandreas.sandberg@arm.com */
4312239Sandreas.sandberg@arm.com
4412239Sandreas.sandberg@arm.com/* @file
4512239Sandreas.sandberg@arm.com * Implements the user interface to a serial terminal
4612239Sandreas.sandberg@arm.com */
4712239Sandreas.sandberg@arm.com
4812239Sandreas.sandberg@arm.com#include <sys/ioctl.h>
4912239Sandreas.sandberg@arm.com
5012239Sandreas.sandberg@arm.com#if defined(__FreeBSD__)
5112239Sandreas.sandberg@arm.com#include <termios.h>
5212239Sandreas.sandberg@arm.com
5312239Sandreas.sandberg@arm.com#else
5412239Sandreas.sandberg@arm.com#include <sys/termios.h>
5512239Sandreas.sandberg@arm.com
5612239Sandreas.sandberg@arm.com#endif
5712239Sandreas.sandberg@arm.com#include "dev/serial/terminal.hh"
5812239Sandreas.sandberg@arm.com
5912239Sandreas.sandberg@arm.com#include <poll.h>
6012239Sandreas.sandberg@arm.com#include <unistd.h>
6112239Sandreas.sandberg@arm.com
6212239Sandreas.sandberg@arm.com#include <cctype>
6312239Sandreas.sandberg@arm.com#include <cerrno>
6412239Sandreas.sandberg@arm.com#include <fstream>
6512239Sandreas.sandberg@arm.com#include <iostream>
6612239Sandreas.sandberg@arm.com#include <sstream>
6712239Sandreas.sandberg@arm.com#include <string>
6812239Sandreas.sandberg@arm.com
6912239Sandreas.sandberg@arm.com#include "base/atomicio.hh"
7012334Sgabeblack@google.com#include "base/logging.hh"
7112239Sandreas.sandberg@arm.com#include "base/output.hh"
7212239Sandreas.sandberg@arm.com#include "base/socket.hh"
7312239Sandreas.sandberg@arm.com#include "base/trace.hh"
7412239Sandreas.sandberg@arm.com#include "debug/Terminal.hh"
7512239Sandreas.sandberg@arm.com#include "debug/TerminalVerbose.hh"
7612239Sandreas.sandberg@arm.com#include "dev/platform.hh"
7712239Sandreas.sandberg@arm.com#include "dev/serial/uart.hh"
7812239Sandreas.sandberg@arm.com
7912239Sandreas.sandberg@arm.comusing namespace std;
8012239Sandreas.sandberg@arm.com
8112239Sandreas.sandberg@arm.com
8212239Sandreas.sandberg@arm.com/*
8312239Sandreas.sandberg@arm.com * Poll event for the listen socket
8412239Sandreas.sandberg@arm.com */
8512239Sandreas.sandberg@arm.comTerminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
8612239Sandreas.sandberg@arm.com    : PollEvent(fd, e), term(t)
8712239Sandreas.sandberg@arm.com{
8812239Sandreas.sandberg@arm.com}
8912239Sandreas.sandberg@arm.com
9012239Sandreas.sandberg@arm.comvoid
9112239Sandreas.sandberg@arm.comTerminal::ListenEvent::process(int revent)
9212239Sandreas.sandberg@arm.com{
9312239Sandreas.sandberg@arm.com    term->accept();
9412239Sandreas.sandberg@arm.com}
9512239Sandreas.sandberg@arm.com
9612239Sandreas.sandberg@arm.com/*
9712239Sandreas.sandberg@arm.com * Poll event for the data socket
9812239Sandreas.sandberg@arm.com */
9912239Sandreas.sandberg@arm.comTerminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
10012239Sandreas.sandberg@arm.com    : PollEvent(fd, e), term(t)
10112239Sandreas.sandberg@arm.com{
10212239Sandreas.sandberg@arm.com}
10312239Sandreas.sandberg@arm.com
10412239Sandreas.sandberg@arm.comvoid
10512239Sandreas.sandberg@arm.comTerminal::DataEvent::process(int revent)
10612239Sandreas.sandberg@arm.com{
10712239Sandreas.sandberg@arm.com    // As a consequence of being called from the PollQueue, we might
10812239Sandreas.sandberg@arm.com    // have been called from a different thread. Migrate to "our"
10912239Sandreas.sandberg@arm.com    // thread.
11012239Sandreas.sandberg@arm.com    EventQueue::ScopedMigration migrate(term->eventQueue());
11112239Sandreas.sandberg@arm.com
11212239Sandreas.sandberg@arm.com    if (revent & POLLIN)
11312239Sandreas.sandberg@arm.com        term->data();
11412239Sandreas.sandberg@arm.com    else if (revent & POLLNVAL)
11512239Sandreas.sandberg@arm.com        term->detach();
11612239Sandreas.sandberg@arm.com}
11712239Sandreas.sandberg@arm.com
11812239Sandreas.sandberg@arm.com/*
11912239Sandreas.sandberg@arm.com * Terminal code
12012239Sandreas.sandberg@arm.com */
12112239Sandreas.sandberg@arm.comTerminal::Terminal(const Params *p)
12212239Sandreas.sandberg@arm.com    : SerialDevice(p), listenEvent(NULL), dataEvent(NULL),
12312239Sandreas.sandberg@arm.com      number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384),
12414249Sgiacomo.travaglini@arm.com      outfile(terminalDump(p))
12512239Sandreas.sandberg@arm.com#if TRACING_ON == 1
12612239Sandreas.sandberg@arm.com      , linebuf(16384)
12712239Sandreas.sandberg@arm.com#endif
12812239Sandreas.sandberg@arm.com{
12912239Sandreas.sandberg@arm.com    if (outfile)
13012239Sandreas.sandberg@arm.com        outfile->stream()->setf(ios::unitbuf);
13112239Sandreas.sandberg@arm.com
13212239Sandreas.sandberg@arm.com    if (p->port)
13312239Sandreas.sandberg@arm.com        listen(p->port);
13412239Sandreas.sandberg@arm.com}
13512239Sandreas.sandberg@arm.com
13612239Sandreas.sandberg@arm.comTerminal::~Terminal()
13712239Sandreas.sandberg@arm.com{
13812239Sandreas.sandberg@arm.com    if (data_fd != -1)
13912239Sandreas.sandberg@arm.com        ::close(data_fd);
14012239Sandreas.sandberg@arm.com
14112239Sandreas.sandberg@arm.com    if (listenEvent)
14212239Sandreas.sandberg@arm.com        delete listenEvent;
14312239Sandreas.sandberg@arm.com
14412239Sandreas.sandberg@arm.com    if (dataEvent)
14512239Sandreas.sandberg@arm.com        delete dataEvent;
14612239Sandreas.sandberg@arm.com}
14712239Sandreas.sandberg@arm.com
14814249Sgiacomo.travaglini@arm.comOutputStream *
14914249Sgiacomo.travaglini@arm.comTerminal::terminalDump(const TerminalParams* p)
15014249Sgiacomo.travaglini@arm.com{
15114249Sgiacomo.travaglini@arm.com    switch (p->outfile) {
15214249Sgiacomo.travaglini@arm.com      case Enums::TerminalDump::none:
15314249Sgiacomo.travaglini@arm.com        return nullptr;
15414288SAndrea.Mondelli@ucf.edu      case Enums::TerminalDump::stdoutput:
15514249Sgiacomo.travaglini@arm.com        return simout.findOrCreate("stdout");
15614288SAndrea.Mondelli@ucf.edu      case Enums::TerminalDump::stderror:
15714249Sgiacomo.travaglini@arm.com        return simout.findOrCreate("stderr");
15814249Sgiacomo.travaglini@arm.com      case Enums::TerminalDump::file:
15914249Sgiacomo.travaglini@arm.com        return simout.findOrCreate(p->name);
16014249Sgiacomo.travaglini@arm.com      default:
16114249Sgiacomo.travaglini@arm.com        panic("Invalid option\n");
16214249Sgiacomo.travaglini@arm.com    }
16314249Sgiacomo.travaglini@arm.com}
16412239Sandreas.sandberg@arm.com
16512239Sandreas.sandberg@arm.com///////////////////////////////////////////////////////////////////////
16612239Sandreas.sandberg@arm.com// socket creation and terminal attach
16712239Sandreas.sandberg@arm.com//
16812239Sandreas.sandberg@arm.com
16912239Sandreas.sandberg@arm.comvoid
17012239Sandreas.sandberg@arm.comTerminal::listen(int port)
17112239Sandreas.sandberg@arm.com{
17212239Sandreas.sandberg@arm.com    if (ListenSocket::allDisabled()) {
17312239Sandreas.sandberg@arm.com        warn_once("Sockets disabled, not accepting terminal connections");
17412239Sandreas.sandberg@arm.com        return;
17512239Sandreas.sandberg@arm.com    }
17612239Sandreas.sandberg@arm.com
17712239Sandreas.sandberg@arm.com    while (!listener.listen(port, true)) {
17812239Sandreas.sandberg@arm.com        DPRINTF(Terminal,
17912239Sandreas.sandberg@arm.com                ": can't bind address terminal port %d inuse PID %d\n",
18012239Sandreas.sandberg@arm.com                port, getpid());
18112239Sandreas.sandberg@arm.com        port++;
18212239Sandreas.sandberg@arm.com    }
18312239Sandreas.sandberg@arm.com
18412693Sandreas.sandberg@arm.com    ccprintf(cerr, "%s: Listening for connections on port %d\n",
18512693Sandreas.sandberg@arm.com             name(), port);
18612239Sandreas.sandberg@arm.com
18712239Sandreas.sandberg@arm.com    listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
18812239Sandreas.sandberg@arm.com    pollQueue.schedule(listenEvent);
18912239Sandreas.sandberg@arm.com}
19012239Sandreas.sandberg@arm.com
19112239Sandreas.sandberg@arm.comvoid
19212239Sandreas.sandberg@arm.comTerminal::accept()
19312239Sandreas.sandberg@arm.com{
19412239Sandreas.sandberg@arm.com    if (!listener.islistening())
19512239Sandreas.sandberg@arm.com        panic("%s: cannot accept a connection if not listening!", name());
19612239Sandreas.sandberg@arm.com
19712239Sandreas.sandberg@arm.com    int fd = listener.accept(true);
19812239Sandreas.sandberg@arm.com    if (data_fd != -1) {
19912239Sandreas.sandberg@arm.com        char message[] = "terminal already attached!\n";
20012239Sandreas.sandberg@arm.com        atomic_write(fd, message, sizeof(message));
20112239Sandreas.sandberg@arm.com        ::close(fd);
20212239Sandreas.sandberg@arm.com        return;
20312239Sandreas.sandberg@arm.com    }
20412239Sandreas.sandberg@arm.com
20512239Sandreas.sandberg@arm.com    data_fd = fd;
20612239Sandreas.sandberg@arm.com    dataEvent = new DataEvent(this, data_fd, POLLIN);
20712239Sandreas.sandberg@arm.com    pollQueue.schedule(dataEvent);
20812239Sandreas.sandberg@arm.com
20912239Sandreas.sandberg@arm.com    stringstream stream;
21012239Sandreas.sandberg@arm.com    ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
21112239Sandreas.sandberg@arm.com
21212239Sandreas.sandberg@arm.com    // we need an actual carriage return followed by a newline for the
21312239Sandreas.sandberg@arm.com    // terminal
21412239Sandreas.sandberg@arm.com    stream << "\r\n";
21512239Sandreas.sandberg@arm.com
21612239Sandreas.sandberg@arm.com    write((const uint8_t *)stream.str().c_str(), stream.str().size());
21712239Sandreas.sandberg@arm.com
21812239Sandreas.sandberg@arm.com    DPRINTFN("attach terminal %d\n", number);
21912239Sandreas.sandberg@arm.com    char buf[1024];
22012239Sandreas.sandberg@arm.com    for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
22112239Sandreas.sandberg@arm.com        const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
22212239Sandreas.sandberg@arm.com        txbuf.peek(buf, i, chunk_len);
22312239Sandreas.sandberg@arm.com        write((const uint8_t *)buf, chunk_len);
22412239Sandreas.sandberg@arm.com    }
22512239Sandreas.sandberg@arm.com}
22612239Sandreas.sandberg@arm.com
22712239Sandreas.sandberg@arm.comvoid
22812239Sandreas.sandberg@arm.comTerminal::detach()
22912239Sandreas.sandberg@arm.com{
23012239Sandreas.sandberg@arm.com    if (data_fd != -1) {
23112239Sandreas.sandberg@arm.com        ::close(data_fd);
23212239Sandreas.sandberg@arm.com        data_fd = -1;
23312239Sandreas.sandberg@arm.com    }
23412239Sandreas.sandberg@arm.com
23512239Sandreas.sandberg@arm.com    pollQueue.remove(dataEvent);
23612239Sandreas.sandberg@arm.com    delete dataEvent;
23712239Sandreas.sandberg@arm.com    dataEvent = NULL;
23812239Sandreas.sandberg@arm.com
23912239Sandreas.sandberg@arm.com    DPRINTFN("detach terminal %d\n", number);
24012239Sandreas.sandberg@arm.com}
24112239Sandreas.sandberg@arm.com
24212239Sandreas.sandberg@arm.comvoid
24312239Sandreas.sandberg@arm.comTerminal::data()
24412239Sandreas.sandberg@arm.com{
24512239Sandreas.sandberg@arm.com    uint8_t buf[1024];
24612239Sandreas.sandberg@arm.com    int len;
24712239Sandreas.sandberg@arm.com
24812239Sandreas.sandberg@arm.com    len = read(buf, sizeof(buf));
24912239Sandreas.sandberg@arm.com    if (len) {
25012239Sandreas.sandberg@arm.com        rxbuf.write((char *)buf, len);
25112239Sandreas.sandberg@arm.com        notifyInterface();
25212239Sandreas.sandberg@arm.com    }
25312239Sandreas.sandberg@arm.com}
25412239Sandreas.sandberg@arm.com
25512239Sandreas.sandberg@arm.comsize_t
25612239Sandreas.sandberg@arm.comTerminal::read(uint8_t *buf, size_t len)
25712239Sandreas.sandberg@arm.com{
25812239Sandreas.sandberg@arm.com    if (data_fd < 0)
25912239Sandreas.sandberg@arm.com        panic("Terminal not properly attached.\n");
26012239Sandreas.sandberg@arm.com
26112239Sandreas.sandberg@arm.com    ssize_t ret;
26212239Sandreas.sandberg@arm.com    do {
26312239Sandreas.sandberg@arm.com      ret = ::read(data_fd, buf, len);
26412239Sandreas.sandberg@arm.com    } while (ret == -1 && errno == EINTR);
26512239Sandreas.sandberg@arm.com
26612239Sandreas.sandberg@arm.com
26712239Sandreas.sandberg@arm.com    if (ret < 0)
26812239Sandreas.sandberg@arm.com        DPRINTFN("Read failed.\n");
26912239Sandreas.sandberg@arm.com
27012239Sandreas.sandberg@arm.com    if (ret <= 0) {
27112239Sandreas.sandberg@arm.com        detach();
27212239Sandreas.sandberg@arm.com        return 0;
27312239Sandreas.sandberg@arm.com    }
27412239Sandreas.sandberg@arm.com
27512239Sandreas.sandberg@arm.com    return ret;
27612239Sandreas.sandberg@arm.com}
27712239Sandreas.sandberg@arm.com
27812239Sandreas.sandberg@arm.com// Terminal output.
27912239Sandreas.sandberg@arm.comsize_t
28012239Sandreas.sandberg@arm.comTerminal::write(const uint8_t *buf, size_t len)
28112239Sandreas.sandberg@arm.com{
28212239Sandreas.sandberg@arm.com    if (data_fd < 0)
28312239Sandreas.sandberg@arm.com        panic("Terminal not properly attached.\n");
28412239Sandreas.sandberg@arm.com
28512239Sandreas.sandberg@arm.com    ssize_t ret = atomic_write(data_fd, buf, len);
28612239Sandreas.sandberg@arm.com    if (ret < len)
28712239Sandreas.sandberg@arm.com        detach();
28812239Sandreas.sandberg@arm.com
28912239Sandreas.sandberg@arm.com    return ret;
29012239Sandreas.sandberg@arm.com}
29112239Sandreas.sandberg@arm.com
29212239Sandreas.sandberg@arm.com#define MORE_PENDING (ULL(1) << 61)
29312239Sandreas.sandberg@arm.com#define RECEIVE_SUCCESS (ULL(0) << 62)
29412239Sandreas.sandberg@arm.com#define RECEIVE_NONE (ULL(2) << 62)
29512239Sandreas.sandberg@arm.com#define RECEIVE_ERROR (ULL(3) << 62)
29612239Sandreas.sandberg@arm.com
29712239Sandreas.sandberg@arm.comuint8_t
29812239Sandreas.sandberg@arm.comTerminal::readData()
29912239Sandreas.sandberg@arm.com{
30012239Sandreas.sandberg@arm.com    uint8_t c;
30112239Sandreas.sandberg@arm.com
30212239Sandreas.sandberg@arm.com    assert(!rxbuf.empty());
30312239Sandreas.sandberg@arm.com    rxbuf.read((char *)&c, 1);
30412239Sandreas.sandberg@arm.com
30512239Sandreas.sandberg@arm.com    DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
30612239Sandreas.sandberg@arm.com            isprint(c) ? c : ' ', c, !rxbuf.empty());
30712239Sandreas.sandberg@arm.com
30812239Sandreas.sandberg@arm.com    return c;
30912239Sandreas.sandberg@arm.com}
31012239Sandreas.sandberg@arm.com
31112239Sandreas.sandberg@arm.comuint64_t
31212239Sandreas.sandberg@arm.comTerminal::console_in()
31312239Sandreas.sandberg@arm.com{
31412239Sandreas.sandberg@arm.com    uint64_t value;
31512239Sandreas.sandberg@arm.com
31612239Sandreas.sandberg@arm.com    if (dataAvailable()) {
31712239Sandreas.sandberg@arm.com        value = RECEIVE_SUCCESS | readData();
31812239Sandreas.sandberg@arm.com        if (!rxbuf.empty())
31912239Sandreas.sandberg@arm.com            value  |= MORE_PENDING;
32012239Sandreas.sandberg@arm.com    } else {
32112239Sandreas.sandberg@arm.com        value = RECEIVE_NONE;
32212239Sandreas.sandberg@arm.com    }
32312239Sandreas.sandberg@arm.com
32412239Sandreas.sandberg@arm.com    DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
32512239Sandreas.sandberg@arm.com
32612239Sandreas.sandberg@arm.com    return value;
32712239Sandreas.sandberg@arm.com}
32812239Sandreas.sandberg@arm.com
32912239Sandreas.sandberg@arm.comvoid
33012239Sandreas.sandberg@arm.comTerminal::writeData(uint8_t c)
33112239Sandreas.sandberg@arm.com{
33212239Sandreas.sandberg@arm.com#if TRACING_ON == 1
33312239Sandreas.sandberg@arm.com    if (DTRACE(Terminal)) {
33412239Sandreas.sandberg@arm.com        static char last = '\0';
33512239Sandreas.sandberg@arm.com
33612239Sandreas.sandberg@arm.com        if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) {
33712239Sandreas.sandberg@arm.com            if (c == '\n' || c == '\r') {
33812239Sandreas.sandberg@arm.com                int size = linebuf.size();
33912239Sandreas.sandberg@arm.com                char *buffer = new char[size + 1];
34012239Sandreas.sandberg@arm.com                linebuf.read(buffer, size);
34112239Sandreas.sandberg@arm.com                buffer[size] = '\0';
34212239Sandreas.sandberg@arm.com                DPRINTF(Terminal, "%s\n", buffer);
34312239Sandreas.sandberg@arm.com                delete [] buffer;
34412239Sandreas.sandberg@arm.com            } else {
34512239Sandreas.sandberg@arm.com                linebuf.write(&c, 1);
34612239Sandreas.sandberg@arm.com            }
34712239Sandreas.sandberg@arm.com        }
34812239Sandreas.sandberg@arm.com
34912239Sandreas.sandberg@arm.com        last = c;
35012239Sandreas.sandberg@arm.com    }
35112239Sandreas.sandberg@arm.com#endif
35212239Sandreas.sandberg@arm.com
35312239Sandreas.sandberg@arm.com    txbuf.write(&c, 1);
35412239Sandreas.sandberg@arm.com
35512239Sandreas.sandberg@arm.com    if (data_fd >= 0)
35612239Sandreas.sandberg@arm.com        write(c);
35712239Sandreas.sandberg@arm.com
35812239Sandreas.sandberg@arm.com    if (outfile)
35912239Sandreas.sandberg@arm.com        outfile->stream()->put((char)c);
36012239Sandreas.sandberg@arm.com
36112239Sandreas.sandberg@arm.com    DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
36212239Sandreas.sandberg@arm.com            isprint(c) ? c : ' ', (int)c);
36312239Sandreas.sandberg@arm.com
36412239Sandreas.sandberg@arm.com}
36512239Sandreas.sandberg@arm.com
36612239Sandreas.sandberg@arm.comTerminal *
36712239Sandreas.sandberg@arm.comTerminalParams::create()
36812239Sandreas.sandberg@arm.com{
36912239Sandreas.sandberg@arm.com    return new Terminal(this);
37012239Sandreas.sandberg@arm.com}
371