OXYGENHUB
◆ main openmsx / src / serial / RS232Tester.cc
4.2 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "RS232Tester.hh"
2
3
#include "RS232Connector.hh"
4
5
#include "EventDistributor.hh"
6
#include "FileOperations.hh"
7
#include "PlugException.hh"
8
#include "Scheduler.hh"
9
#include "serialize.hh"
10
11
#include "checked_cast.hh"
12
#include "narrow.hh"
13
14
#include <array>
15
16
namespace openmsx {
17
18
RS232Tester::RS232Tester(EventDistributor& eventDistributor_,
19
                         Scheduler& scheduler_,
20
                         CommandController& commandController)
21
	: eventDistributor(eventDistributor_), scheduler(scheduler_)
22
	, rs232InputFilenameSetting(
23
	        commandController, "rs232-inputfilename",
24
	        "filename of the file where the RS232 input is read from",
25
	        "rs232-input")
26
	, rs232OutputFilenameSetting(
27
	        commandController, "rs232-outputfilename",
28
	        "filename of the file where the RS232 output is written to",
29
	        "rs232-output")
30
{
31
	eventDistributor.registerEventListener(EventType::RS232_TESTER, *this);
32
}
33
34
RS232Tester::~RS232Tester()
35
{
36
	eventDistributor.unregisterEventListener(EventType::RS232_TESTER, *this);
37
}
38
39
// Pluggable
40
void RS232Tester::plugHelper(Connector& connector_, EmuTime /*time*/)
41
{
42
	// output
43
	auto outName = rs232OutputFilenameSetting.getString();
44
	FileOperations::openOfStream(outFile, outName);
45
	if (outFile.fail()) {
46
		outFile.clear();
47
		throw PlugException("Error opening output file: ", outName);
48
	}
49
50
	// input
51
	auto inName = rs232InputFilenameSetting.getString();
52
	inFile = FileOperations::openFile(inName, "rb");
53
	if (!inFile) {
54
		outFile.close();
55
		throw PlugException("Error opening input file: ", inName);
56
	}
57
58
	auto& rs232Connector = checked_cast<RS232Connector&>(connector_);
59
	rs232Connector.setDataBits(SerialDataInterface::DataBits::D8);	// 8 data bits
60
	rs232Connector.setStopBits(SerialDataInterface::StopBits::S1);	// 1 stop bit
61
	rs232Connector.setParityBit(false, SerialDataInterface::Parity::EVEN); // no parity
62
63
	setConnector(&connector_); // base class will do this in a moment,
64
	                           // but thread already needs it
65
	poller.emplace();
66
	thread = std::thread([this]() { run(); });
67
}
68
69
void RS232Tester::unplugHelper(EmuTime /*time*/)
70
{
71
	// output
72
	outFile.close();
73
74
	// input
75
	poller->abort();
76
	thread.join();
77
	poller.reset();
78
	inFile.reset();
79
}
80
81
zstring_view RS232Tester::getName() const
82
{
83
	return "rs232-tester";
84
}
85
86
zstring_view RS232Tester::getDescription() const
87
{
88
	return	"RS232 tester pluggable. Reads all data from file specified "
89
		"with the 'rs-232-inputfilename' setting. Writes all data "
90
		"to the file specified with the 'rs232-outputfilename' "
91
		"setting.";
92
}
93
94
void RS232Tester::run()
95
{
96
	if (!inFile) return;
97
	while (!feof(inFile.get())) {
98
#ifndef _WIN32
99
		if (poller->poll(fileno(inFile.get()))) {
100
			break;
101
		}
102
#endif
103
		std::array<uint8_t, 1> buf;
104
		size_t num = fread(buf.data(), sizeof(uint8_t), buf.size(), inFile.get());
105
		if (poller->aborted()) {
106
			break;
107
		}
108
		if (num != 1) {
109
			continue;
110
		}
111
		assert(isPluggedIn());
112
		std::scoped_lock lock(mutex);
113
		queue.push_back(buf[0]);
114
		eventDistributor.distributeEvent(Rs232TesterEvent());
115
	}
116
}
117
118
// Control lines
119
// Needed to set these lines in the correct state for a plugged device
120
121
std::optional<bool> RS232Tester::getDSR(EmuTime /*time*/) const
122
{
123
	return true;
124
}
125
126
std::optional<bool> RS232Tester::getCTS(EmuTime /*time*/) const
127
{
128
	return true;
129
}
130
131
// input
132
void RS232Tester::signal(EmuTime time)
133
{
134
	auto* conn = checked_cast<RS232Connector*>(getConnector());
135
	if (!conn->acceptsData()) {
136
		std::scoped_lock lock(mutex);
137
		queue.clear();
138
		return;
139
	}
140
	if (!conn->ready()) return;
141
142
	std::scoped_lock lock(mutex);
143
	if (queue.empty()) return;
144
	conn->recvByte(queue.pop_front(), time);
145
}
146
147
// EventListener
148
bool RS232Tester::signalEvent(const Event& /*event*/)
149
{
150
	if (isPluggedIn()) {
151
		signal(scheduler.getCurrentTime());
152
	} else {
153
		std::scoped_lock lock(mutex);
154
		queue.clear();
155
	}
156
	return false;
157
}
158
159
160
// output
161
void RS232Tester::recvByte(uint8_t value, EmuTime /*time*/)
162
{
163
	if (outFile.is_open()) {
164
		outFile.put(narrow_cast<char>(value));
165
		outFile.flush();
166
	}
167
}
168
169
170
template<typename Archive>
171
void RS232Tester::serialize(Archive& /*ar*/, unsigned /*version*/)
172
{
173
	// don't try to resume a previous logfile (see PrinterPortLogger)
174
}
175
INSTANTIATE_SERIALIZE_METHODS(RS232Tester);
176
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, RS232Tester, "RS232Tester");
177
178
} // namespace openmsx