The Pedigree Project 0.1
ConsolePhysical.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/process/Thread.h"
21#include "pedigree/kernel/processor/Processor.h"
22#include "pedigree/kernel/processor/ProcessorInformation.h"
23#include "pedigree/kernel/processor/types.h"
24#include "pedigree/kernel/syscallError.h"
25#include "pedigree/kernel/utilities/Buffer.h"
26#include "pedigree/kernel/utilities/String.h"
27#include "pedigree/kernel/utilities/utility.h"
28
29#include "Console.h"
30#include "ConsoleDefines.h"
31#include "modules/system/vfs/File.h"
32
33class Filesystem;
34
35namespace {
36class PhysicalTerminalReadinessObserver final : public ReadinessObserver {
37 public:
38 explicit PhysicalTerminalReadinessObserver(ConsolePhysicalFile* owner) : m_Owner(owner) {}
39
40 void readinessChanged(ReadyMask mask) override {
41 m_Owner->terminalReadinessChanged(mask);
42 }
43
44 private:
45 ConsolePhysicalFile* m_Owner;
46};
47} // namespace
48
49ConsolePhysicalFile::ConsolePhysicalFile(size_t nth, File* pTerminal, String consoleName,
50 Filesystem* pFs)
51 : ConsoleFile(~0U, consoleName, pFs),
52 m_pTerminal(pTerminal),
53 m_TerminalNumber(nth),
54 m_TerminalReadinessObserver(new PhysicalTerminalReadinessObserver(this)) {
55 if (m_pTerminal) {
56 if (!m_pTerminal->subscribeReadiness(ReadyAll, m_TerminalReadinessObserver,
57 m_TerminalReadinessSubscription)) {
58 ERROR(
59 "ConsolePhysicalFile failed to subscribe to readiness in its constructor - this is a "
60 "silent failure (console="
61 << consoleName << ")");
62 }
63 }
64}
65
66void ConsolePhysicalFile::terminalReadinessChanged(ReadyMask mask) {
67 notifyReadiness(mask);
68}
69
70namespace {
71// Physical sources without notifications retain their existing polling policy,
72// but sleep between nonblocking attempts so revocation can cancel every wait.
73bool waitPhysical(ConsoleIoState& state) {
74 if (state.revoked())
75 return false;
76 Semaphore::SemaphoreError error = Semaphore::NoError;
77 if (!state.physicalWake.acquireWithError(1, 0, 10000, error) && error != Semaphore::TimedOut) {
78 SYSCALL_ERROR(Interrupted);
79 return false;
80 }
81 return !state.revoked();
82}
83} // namespace
84
85void ConsolePhysicalFile::collectInput(ConsoleIoState& state, bool canBlock) {
86 char input[512];
87 size_t amount = m_pTerminal->read(0, sizeof(input), reinterpret_cast<uintptr_t>(input), false);
88 if (amount) {
89 inputLineDiscipline(state, input, amount, canBlock, m_Flags, m_ControlChars);
90 }
91 char echo[512];
92 while (!state.revoked() && state.output.canRead(false)) {
93 size_t echoed = state.output.read(echo, sizeof(echo), false);
94 if (!echoed || writeIo(state, echoed, reinterpret_cast<uintptr_t>(echo), canBlock) != echoed) {
95 break;
96 }
97 }
98}
99
100size_t ConsolePhysicalFile::readableBytes(ConsoleIoState& state) {
101 // Physical input is collected on demand. FIONREAD must make it visible
102 // without consuming the bytes that the application's next read needs.
103 if (!state.revoked() && !state.input.canRead(false)) {
104 collectInput(state, false);
105 }
106 return state.input.getDataSize();
107}
108
109uint64_t ConsolePhysicalFile::readIo(ConsoleIoState& state, uint64_t size, uintptr_t buffer,
110 bool canBlock) {
111 while (!state.revoked()) {
112 if (state.input.canRead(false)) {
113 return state.input.read(reinterpret_cast<char*>(buffer), size, false);
114 }
115 collectInput(state, canBlock);
116 if (state.input.canRead(false))
117 continue;
118 if (!canBlock || !waitPhysical(state))
119 break;
120 }
121 return 0;
122}
123
124uint64_t ConsolePhysicalFile::writeIo(ConsoleIoState& state, uint64_t size, uintptr_t buffer,
125 bool canBlock) {
126 size_t total = 0;
127 while (total < size && !state.revoked()) {
128 char output[512];
129 size_t chunk = size - total;
130 if (chunk > sizeof(output) / 2)
131 chunk = sizeof(output) / 2;
132 MemoryCopy(output, reinterpret_cast<const void*>(buffer + total), chunk);
133 size_t length = outputLineDiscipline(output, chunk, sizeof(output), m_Flags);
134 size_t sent = 0;
135 while (sent < length && !state.revoked()) {
136 size_t amount =
137 m_pTerminal->write(0, length - sent, reinterpret_cast<uintptr_t>(output + sent), false);
138 sent += amount;
139 if (!amount && (!canBlock || !waitPhysical(state)))
140 return total;
141 }
142 if (sent != length)
143 break;
144 total += chunk;
145 }
146 return total;
147}
148
149uint64_t ConsolePhysicalFile::readBytewise(uint64_t, uint64_t size, uintptr_t buffer,
150 bool canBlock) {
151 return readEpoch(captureOpenEpoch(), size, buffer, canBlock);
152}
153
154uint64_t ConsolePhysicalFile::writeBytewise(uint64_t, uint64_t size, uintptr_t buffer,
155 bool canBlock) {
156 return writeEpoch(captureOpenEpoch(), size, buffer, canBlock);
157}
158
159int ConsolePhysicalFile::select(bool writing, int timeout) {
160 if (writing)
161 return m_pTerminal->select(true, timeout);
162 auto state = captureOpenEpoch(false);
163 if (state && state->input.canRead(false))
164 return 1;
165 return m_pTerminal->select(false, timeout);
166}
size_t getDataSize()
Definition Buffer.cc:470
bool canRead(bool block)
Definition Buffer.cc:525
size_t read(T *buffer, size_t count, bool block=true)
Definition Buffer.cc:296
static size_t outputLineDiscipline(char *buf, size_t len, size_t maxSz, size_t flags=0)
Output line discipline.
size_t inputLineDiscipline(ConsoleIoState &state, char *buf, size_t len, bool canBlock, size_t flags=~0U, const char *controlChars=nullptr)
Input line discipline.
virtual int select(bool bWriting, int timeout)
select - check and optionally for a particular state.
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.h:75
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:239
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:342
virtual int select(bool bWriting=false, int timeout=0)
Definition File.cc:1050
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
MUST_USE_RESULT bool acquireWithError(size_t n, size_t timeoutSecs, size_t timeoutUsecs, SemaphoreError &error)
Definition Semaphore.cc:360