|
|
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")。
1.1.1.3 ! root 76: HostCOMDevice::HostCOMDevice(Device *parent_, const std::string& objname_)
! 77: : inherited(parent_, OBJ_NONE)
1.1 root 78: {
1.1.1.2 root 79: SetName(objname_);
80:
1.1 root 81: // 親が hostcom* か debugger かによっていろいろ動作が違う。
82: if (GetName() == "Debugger") {
83: // デバッガコンソールならログレベルは親に準じるため、こっちは不要。
84: ClearAlias();
85:
86: // 送受信バイト数がメインのモニタはこっちにはなくていいだろう。
87: } else {
88: // hostcom* なら親(mpscc などのシリアルデバイス)とこっち(HostCOM*)
89: // のログレベルは独立。
90:
91: // モニタは hostcom* のみ必要。
92: monitor.func = ToMonitorCallback(&HostCOMDevice::MonitorUpdate);
1.1.1.3 ! root 93: monitor.SetSize(30, 17);
1.1 root 94: monitor.Regist(ID_MONITOR_HOSTCOM);
95: }
96: }
97:
98: // デストラクタ
99: HostCOMDevice::~HostCOMDevice()
100: {
101: }
102:
103: // ログレベル設定
104: void
105: HostCOMDevice::SetLogLevel(int loglevel_)
106: {
107: inherited::SetLogLevel(loglevel_);
108:
109: if ((bool)driver) {
110: driver->SetLogLevel(loglevel_);
111: }
112: }
113:
114: // 初期化
115: bool
116: HostCOMDevice::Init()
117: {
118: if (inherited::Init() == false) {
119: return false;
120: }
121:
122: if (SelectDriver() == false) {
123: return false;
124: }
125:
126: return true;
127: }
128:
129: // ドライバ(再)選択
130: bool
131: HostCOMDevice::SelectDriver()
132: {
133: // 設定のキー名は親デバイスによって異なる
134: std::string key = GetConfigKey();
135: const ConfigItem& item = gConfig->Find(key + "-driver");
136: const std::string& type = item.AsString();
137:
138: driver.reset();
139:
140: if (type != "none") {
141: if (type == "stdio") {
142: // debugger-driver=stdio (または -D オプション) と
143: // hostcom*-driver=stdio とは共存できない。
144: // 歴史的経緯でとりあえず debugger-driver=stdio 側を優先とする。
145: // XXX これも後指定優先にするかどうか
146: // XXX hostcom が増えたらたぶんこうじゃなくなる
147: if (key != "debugger") {
148: const ConfigItem& dditem = gConfig->Find("debugger-driver");
149: const std::string& ddtype = dditem.AsString();
150: if (ddtype == "stdio") {
1.1.1.2 root 151: auto where = dditem.GetWhere();
152: if (where == "-V") {
153: where += " debugger-driver=stdio";
1.1 root 154: }
1.1.1.2 root 155: item.Err("stdio driver conflicts with " + where);
1.1 root 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) {
1.1.1.3 ! root 234: if (accept_func) {
! 235: (parent->*accept_func)();
1.1 root 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:
1.1.1.3 ! root 342: screen.Print(0, 0, "Parent Device : %s", parent->GetName().c_str());
! 343: screen.Print(0, 1, "HostCOM Driver: %s", driver->GetDriverName());
1.1 root 344: // 次の1行はドライバ依存情報
1.1.1.3 ! root 345: driver->MonitorUpdateMD(screen, 2);
1.1 root 346:
1.1.1.3 ! root 347: int y = 4;
1.1 root 348: screen.Print(0, y++, "%-17s%13s", "<Tx>", "Bytes");
349: screen.Print(0, y++, "%-17s%13s", "VM sends",
350: format_number(stat.tx_bytes).c_str());
351: screen.Print(0, y++, "%-17s%13s", "Write to host",
352: format_number(stat.write_bytes).c_str());
353: screen.Print(0, y++, "%-17s%13s", "Drop:TxQ Full",
354: format_number(stat.txqfull_bytes).c_str());
355: y++;
356:
357: screen.Print(0, y++, "%-17s%13s", "<Rx>", "Bytes");
358: screen.Print(0, y++, "%-17s%13s", "Read from host",
359: format_number(stat.read_bytes).c_str());
360: screen.Print(0, y++, "%-17s%13s", "VM receives",
361: format_number(stat.rx_bytes).c_str());
362: screen.Print(0, y++, "%-17s%13s", "Drop:RxQ Full",
363: format_number(stat.rxqfull_bytes).c_str());
364: y++;
365:
366: screen.Print(5, y++, "Capacity Peak");
367: screen.Print(0, y++, "TxQ %4d/%4d %4d",
368: (int)txq.Length(), (int)txq.Capacity(), stat.txq_peak);
369: screen.Print(0, y++, "RxQ %4d/%4d %4d",
370: (int)rxq.Length(), (int)rxq.Capacity(), stat.rxq_peak);
371: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.