|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // シリアルポートのホストデバイス
9: //
10:
11: // 送信のフロー
12: //
13: // VM thread : Host thread
14: //
15: // 各シリアルデバイス::Tx() :
16: // |
17: // SerialDevice::Tx() :
18: // |
19: // HostCOMDevice::Tx() : HostDevice::ThreadRun
20: // |
21: // +-------------->| queue |----+
22: // +-------------->| pipe |----+
23: // | |
24: // <-+ : v
25: // --- kevent
26: // : |
27: // HostCOMDevice::Write()
28: // : |
29: // COMDriver*::Write()
30: // : |
31: // +-----> Send to the real world
32:
33: // 受信のフロー
34: //
35: // VM thread : Host thread
36: //
37: // : HostDevice::ThreadRun
38: //
39: // : +-----< Recv from the real world
40: // |
41: // : v
42: // --- kevent
43: // : |
44: // HostCOMDevice::Read()
45: // |
46: // +---------------| queue |<---+
47: // +---------------| event |<---+
48: // v
49: // SerialDevice::Callback()
50: // |
51: // 各シリアルデバイス::Rx()
52:
53: // ログ名
54: // MPSCC HostDevice COMDriver
55: // | | |
56: // v v v
57: // SIODevice -> HostCOMDevice -> COMDriver***
58: // 表示名 (sio) (HostCOMx) (HostCOMx.***)
59: // 識別名 "sio" "hostcomx" "hostcomx"
60: //
61: // HostCOM は 1VM あたり 1個とは限らないので、親デバイスがそれぞれ対応する
62: // HostCOM に名前を付ける。
63:
64:
65: #include "hostcom.h"
66: #include "comdriver_none.h"
67: #include "comdriver_stdio.h"
68: #include "comdriver_tcp.h"
69: #include "config.h"
70: #include "mainapp.h"
71:
72: // コンストラクタ
73: //
74: // objname_ は "HostCOM" とか (スレッド名も同じになる)。
75: // 設定ファイルキーワードプレフィックスはこれを小文字にしたもの("hostcom")。
76: HostCOMDevice::HostCOMDevice(const std::string& objname_)
77: : inherited(objname_)
78: {
79: // 親が hostcom* か debugger かによっていろいろ動作が違う。
80: if (GetName() == "Debugger") {
81: // デバッガコンソールならログレベルは親に準じるため、こっちは不要。
82: ClearAlias();
83:
84: // 送受信バイト数がメインのモニタはこっちにはなくていいだろう。
85: } else {
86: // hostcom* なら親(mpscc などのシリアルデバイス)とこっち(HostCOM*)
87: // のログレベルは独立。
88:
89: // モニタは hostcom* のみ必要。
90: monitor.func = ToMonitorCallback(&HostCOMDevice::MonitorUpdate);
91: monitor.SetSize(30, 16);
92: monitor.Regist(ID_MONITOR_HOSTCOM);
93: }
94: }
95:
96: // デストラクタ
97: HostCOMDevice::~HostCOMDevice()
98: {
99: }
100:
101: // ログレベル設定
102: void
103: HostCOMDevice::SetLogLevel(int loglevel_)
104: {
105: inherited::SetLogLevel(loglevel_);
106:
107: if ((bool)driver) {
108: driver->SetLogLevel(loglevel_);
109: }
110: }
111:
112: // 初期化
113: bool
114: HostCOMDevice::Init()
115: {
116: if (inherited::Init() == false) {
117: return false;
118: }
119:
120: if (SelectDriver() == false) {
121: return false;
122: }
123:
124: return true;
125: }
126:
127: // ドライバ(再)選択
128: bool
129: HostCOMDevice::SelectDriver()
130: {
131: // 設定のキー名は親デバイスによって異なる
132: std::string key = GetConfigKey();
133: const ConfigItem& item = gConfig->Find(key + "-driver");
134: const std::string& type = item.AsString();
135:
136: driver.reset();
137:
138: if (type != "none") {
139: if (type == "stdio") {
140: // debugger-driver=stdio (または -D オプション) と
141: // hostcom*-driver=stdio とは共存できない。
142: // 歴史的経緯でとりあえず debugger-driver=stdio 側を優先とする。
143: // XXX これも後指定優先にするかどうか
144: // XXX hostcom が増えたらたぶんこうじゃなくなる
145: if (key != "debugger") {
146: const ConfigItem& dditem = gConfig->Find("debugger-driver");
147: const std::string& ddtype = dditem.AsString();
148: if (ddtype == "stdio") {
149: const char *opt;
150: if (gMainApp.opt_D) {
151: opt = "-D";
152: } else {
153: opt = "'-V debugger-driver=stdio'";
154: }
155: item.Err("cannot be used with %s option", opt);
156: return false;
157: }
158: }
159:
160: driver.reset(new COMDriverStdio(this));
161: if ((bool)driver && driver->InitDriver() == false) {
162: driver.reset();
163: }
164:
165: } else if (type == "tcp") {
166: driver.reset(new COMDriverTCP(this));
167: if ((bool)driver && driver->InitDriver() == false) {
168: driver.reset();
169: }
170:
171: } else {
172: // 知らないドライバ種別
173: item.Err();
174: return false;
175: }
176:
177: if ((bool)driver == false) {
178: bool fallback = false;
179:
180: // 指定のドライバが使えなかった場合、
181: // hostcom*-driver なら fallback するかどうかは設定による。
182: // debugger-driver なら常に fallback せず終了する。
183: //
184: // 元々 hostnet が tap, bpf のように順に試してという流れだったので
185: // fallback の選択肢が用意されていたが、hostcom は今の所どれか1つ
186: // (stdio か tcp) か、そうでなければ none しかないので、fallback
187: // するかどうかを選ばせる意味があまりような気もするけど、
188: // とりあえず形式だけ真似してみる…。
189: // 一方、debugger-driver なら常に fallback せず、設定自体も用意
190: // しない。デバッガを使いたいと言ってる時点でデバッガがなくても
191: // とりあえず起動してほしいとはならないだろうと思うので。
192: if (key != "debugger") {
193: fallback = gConfig->Find(key + "-fallback").AsInt();
194: putmsg(1, "%s-fallback=%d", key.c_str(), fallback ? 1 : 0);
195: }
196:
197: if (fallback == false) {
198: // フォールバックしないならエラー終了
199: std::string errmsg;
200: errmsg = string_format("No %s driver found", key.c_str());
201: putmsg(1, "%s", errmsg.c_str());
202: warnx("%s (See details with option -C -L%s=1)",
203: errmsg.c_str(), key.c_str());
204: return false;
205: }
206: }
207: }
208:
209: if ((bool)driver == false) {
210: driver.reset(new COMDriverNone(this));
211: if ((bool)driver && driver->InitDriver() == false) {
212: // 失敗したら出来ることはあまりなさげ
213: assert(false);
214: }
215: }
216: assert((bool)driver);
217:
218: // ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える…
219: putmsg(1, "selected host driver: %s",
220: string_tolower(driver->GetDriverName()).c_str());
221:
222: return true;
223: }
224:
225: void
226: HostCOMDevice::Dispatch(int udata)
227: {
228: // driver の Dispatch は処理し終わったら DONE を返す
229: udata = driver->Dispatch(udata);
230:
231: // ...のだが、Dispatch(LISTEN_SOCKET) は着信を受け付けた時には
232: // LISTEN_SOCKET を返し、それを受けてここで通知処理を行う。
233: if (udata == LISTEN_SOCKET) {
234: if (dev && accept_func) {
235: (dev->*accept_func)();
236: }
237: udata = DONE;
238: }
239:
240: inherited::Dispatch(udata);
241: }
242:
243: // VM からの送信 (VM スレッドで呼ばれる)
244: bool
245: HostCOMDevice::Tx(uint32 data)
246: {
247: if (loglevel >= 2) {
248: char buf[8];
249:
250: buf[0] = '\0';
251: if (0x20 <= data && data < 0x7f) {
252: snprintf(buf, sizeof(buf), " '%c'", data);
253: }
254: putlog(2, "Send $%02x%s", data, buf);
255: }
256:
257: // 送信キューに入れて..
258: if (txq.Enqueue(data) == false) {
259: putlog(2, "txq exhausted");
260: stat.txqfull_bytes++;
261: return false;
262: }
263: stat.tx_bytes++;
264:
265: // ざっくりピーク値
266: stat.txq_peak = std::max((int)txq.Length(), stat.txq_peak);
267:
268: // パイプに通知 (値はダミー)
269: return WritePipe(0);
270: }
271:
272: // キューから取り出す (VM スレッドで呼ばれる)
273: uint32
274: HostCOMDevice::Rx()
275: {
276: uint8 data;
277:
278: if (rxq.Dequeue(&data) == false) {
279: // キューが空
280: return -1;
281: }
282: stat.rx_bytes++;
283:
284: if (loglevel >= 2) {
285: char buf[8];
286:
287: buf[0] = '\0';
288: if (0x20 <= data && data < 0x7f) {
289: snprintf(buf, sizeof(buf), " '%c'", data);
290: }
291: putlog(2, "Recv $%02x%s", data, buf);
292: }
293:
294: return data;
295: }
296:
297: // ドライバから読み込む。
298: // 戻り値はキューに投入したデータ数。
299: int
300: HostCOMDevice::Read()
301: {
302: assert((bool)driver);
303:
304: int data = driver->Read();
305: if (data < 0) {
306: return 0;
307: }
308: stat.read_bytes++;
309:
310: if (rxq.Enqueue(data) == false) {
311: stat.rxqfull_bytes++;
312: return 0;
313: }
314:
315: // ざっくりピーク値
316: stat.rxq_peak = std::max((int)rxq.Length(), stat.rxq_peak);
317:
318: return 1;
319: }
320:
321: // 外部への書き出し (ホストスレッドで呼ばれる)
322: void
323: HostCOMDevice::Write(uint32 dummy)
324: {
325: uint8 data;
326:
327: assert(driver);
328:
329: // 送信キューを全部吐き出す
330: while (txq.Dequeue(&data)) {
331: driver->Write(data);
332: stat.write_bytes++;
333: }
334: }
335:
336: // モニタ
337: void
338: HostCOMDevice::MonitorUpdate(Monitor *, TextScreen& screen)
339: {
340: screen.Clear();
341:
342: screen.Print(0, 0, "HostCOM Driver: %s", driver->GetDriverName());
343: // 次の1行はドライバ依存情報
344: driver->MonitorUpdateMD(screen);
345:
346: int y = 3;
347: screen.Print(0, y++, "%-17s%13s", "<Tx>", "Bytes");
348: screen.Print(0, y++, "%-17s%13s", "VM sends",
349: format_number(stat.tx_bytes).c_str());
350: screen.Print(0, y++, "%-17s%13s", "Write to host",
351: format_number(stat.write_bytes).c_str());
352: screen.Print(0, y++, "%-17s%13s", "Drop:TxQ Full",
353: format_number(stat.txqfull_bytes).c_str());
354: y++;
355:
356: screen.Print(0, y++, "%-17s%13s", "<Rx>", "Bytes");
357: screen.Print(0, y++, "%-17s%13s", "Read from host",
358: format_number(stat.read_bytes).c_str());
359: screen.Print(0, y++, "%-17s%13s", "VM receives",
360: format_number(stat.rx_bytes).c_str());
361: screen.Print(0, y++, "%-17s%13s", "Drop:RxQ Full",
362: format_number(stat.rxqfull_bytes).c_str());
363: y++;
364:
365: screen.Print(5, y++, "Capacity Peak");
366: screen.Print(0, y++, "TxQ %4d/%4d %4d",
367: (int)txq.Length(), (int)txq.Capacity(), stat.txq_peak);
368: screen.Print(0, y++, "RxQ %4d/%4d %4d",
369: (int)rxq.Length(), (int)rxq.Capacity(), stat.rxq_peak);
370: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.