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