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