|
|
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:
165: driver.reset(new COMDriverStdio(this));
166: if ((bool)driver && driver->InitDriver() == false) {
167: driver.reset();
168: }
169:
170: } else if (type == "tcp") {
171: driver.reset(new COMDriverTCP(this));
172: if ((bool)driver && driver->InitDriver() == false) {
173: driver.reset();
174: }
175:
1.1.1.5 root 176: } else if (type == "cons") {
177: // console デバイスのいない機種では指定出来ない。
178: if (gMainApp.FindObject(OBJ_CONSOLE) == NULL) {
179: item.Err("cannot be specified on vmtype=%s",
180: gMainApp.GetVMTypeStr().c_str());
181: return false;
182: }
183:
184: driver.reset(new COMDriverCons(this));
185: if ((bool)driver && driver->InitDriver() == false) {
186: driver.reset();
187: }
188:
1.1 root 189: } else {
190: // 知らないドライバ種別
191: item.Err();
192: return false;
193: }
194:
195: if ((bool)driver == false) {
196: bool fallback = false;
197:
198: // 指定のドライバが使えなかった場合、
199: // hostcom*-driver なら fallback するかどうかは設定による。
200: // debugger-driver なら常に fallback せず終了する。
201: //
202: // 元々 hostnet が tap, bpf のように順に試してという流れだったので
203: // fallback の選択肢が用意されていたが、hostcom は今の所どれか1つ
204: // (stdio か tcp) か、そうでなければ none しかないので、fallback
205: // するかどうかを選ばせる意味があまりような気もするけど、
206: // とりあえず形式だけ真似してみる…。
207: // 一方、debugger-driver なら常に fallback せず、設定自体も用意
208: // しない。デバッガを使いたいと言ってる時点でデバッガがなくても
209: // とりあえず起動してほしいとはならないだろうと思うので。
210: if (key != "debugger") {
211: fallback = gConfig->Find(key + "-fallback").AsInt();
212: putmsg(1, "%s-fallback=%d", key.c_str(), fallback ? 1 : 0);
213: }
214:
215: if (fallback == false) {
216: // フォールバックしないならエラー終了
217: std::string errmsg;
218: errmsg = string_format("No %s driver found", key.c_str());
219: putmsg(1, "%s", errmsg.c_str());
220: warnx("%s (See details with option -C -L%s=1)",
221: errmsg.c_str(), key.c_str());
222: return false;
223: }
224: }
225: }
226:
227: if ((bool)driver == false) {
228: driver.reset(new COMDriverNone(this));
229: if ((bool)driver && driver->InitDriver() == false) {
230: // 失敗したら出来ることはあまりなさげ
231: assert(false);
232: }
233: }
234: assert((bool)driver);
235:
236: // ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える…
237: putmsg(1, "selected host driver: %s",
238: string_tolower(driver->GetDriverName()).c_str());
239:
240: return true;
241: }
242:
243: void
244: HostCOMDevice::Dispatch(int udata)
245: {
246: // driver の Dispatch は処理し終わったら DONE を返す
247: udata = driver->Dispatch(udata);
248:
249: // ...のだが、Dispatch(LISTEN_SOCKET) は着信を受け付けた時には
250: // LISTEN_SOCKET を返し、それを受けてここで通知処理を行う。
251: if (udata == LISTEN_SOCKET) {
1.1.1.3 root 252: if (accept_func) {
253: (parent->*accept_func)();
1.1 root 254: }
255: udata = DONE;
256: }
257:
258: inherited::Dispatch(udata);
259: }
260:
261: // VM からの送信 (VM スレッドで呼ばれる)
262: bool
263: HostCOMDevice::Tx(uint32 data)
264: {
265: if (loglevel >= 2) {
266: char buf[8];
267:
268: buf[0] = '\0';
269: if (0x20 <= data && data < 0x7f) {
270: snprintf(buf, sizeof(buf), " '%c'", data);
271: }
272: putlog(2, "Send $%02x%s", data, buf);
273: }
274:
275: // 送信キューに入れて..
276: if (txq.Enqueue(data) == false) {
277: putlog(2, "txq exhausted");
278: stat.txqfull_bytes++;
279: return false;
280: }
281: stat.tx_bytes++;
282:
283: // ざっくりピーク値
1.1.1.4 root 284: stat.txq_peak = std::max((uint)txq.Length(), stat.txq_peak);
1.1 root 285:
286: // パイプに通知 (値はダミー)
287: return WritePipe(0);
288: }
289:
290: // キューから取り出す (VM スレッドで呼ばれる)
291: uint32
292: HostCOMDevice::Rx()
293: {
294: uint8 data;
295:
296: if (rxq.Dequeue(&data) == false) {
297: // キューが空
298: return -1;
299: }
300: stat.rx_bytes++;
301:
302: if (loglevel >= 2) {
303: char buf[8];
304:
305: buf[0] = '\0';
306: if (0x20 <= data && data < 0x7f) {
307: snprintf(buf, sizeof(buf), " '%c'", data);
308: }
309: putlog(2, "Recv $%02x%s", data, buf);
310: }
311:
312: return data;
313: }
314:
315: // ドライバから読み込む。
316: // 戻り値はキューに投入したデータ数。
317: int
318: HostCOMDevice::Read()
319: {
320: assert((bool)driver);
321:
322: int data = driver->Read();
323: if (data < 0) {
324: return 0;
325: }
326: stat.read_bytes++;
327:
328: if (rxq.Enqueue(data) == false) {
329: stat.rxqfull_bytes++;
330: return 0;
331: }
332:
333: // ざっくりピーク値
1.1.1.4 root 334: stat.rxq_peak = std::max((uint)rxq.Length(), stat.rxq_peak);
1.1 root 335:
336: return 1;
337: }
338:
339: // 外部への書き出し (ホストスレッドで呼ばれる)
340: void
341: HostCOMDevice::Write(uint32 dummy)
342: {
343: uint8 data;
344:
345: assert(driver);
346:
347: // 送信キューを全部吐き出す
348: while (txq.Dequeue(&data)) {
349: driver->Write(data);
350: stat.write_bytes++;
351: }
352: }
353:
354: // モニタ
355: void
356: HostCOMDevice::MonitorUpdate(Monitor *, TextScreen& screen)
357: {
358: screen.Clear();
359:
1.1.1.3 root 360: screen.Print(0, 0, "Parent Device : %s", parent->GetName().c_str());
361: screen.Print(0, 1, "HostCOM Driver: %s", driver->GetDriverName());
1.1 root 362: // 次の1行はドライバ依存情報
1.1.1.3 root 363: driver->MonitorUpdateMD(screen, 2);
1.1 root 364:
1.1.1.3 root 365: int y = 4;
1.1 root 366: screen.Print(0, y++, "%-17s%13s", "<Tx>", "Bytes");
367: screen.Print(0, y++, "%-17s%13s", "VM sends",
368: format_number(stat.tx_bytes).c_str());
369: screen.Print(0, y++, "%-17s%13s", "Write to host",
370: format_number(stat.write_bytes).c_str());
371: screen.Print(0, y++, "%-17s%13s", "Drop:TxQ Full",
372: format_number(stat.txqfull_bytes).c_str());
373: y++;
374:
375: screen.Print(0, y++, "%-17s%13s", "<Rx>", "Bytes");
376: screen.Print(0, y++, "%-17s%13s", "Read from host",
377: format_number(stat.read_bytes).c_str());
378: screen.Print(0, y++, "%-17s%13s", "VM receives",
379: format_number(stat.rx_bytes).c_str());
380: screen.Print(0, y++, "%-17s%13s", "Drop:RxQ Full",
381: format_number(stat.rxqfull_bytes).c_str());
382: y++;
383:
384: screen.Print(5, y++, "Capacity Peak");
1.1.1.4 root 385: screen.Print(0, y++, "TxQ %4u/%4u %4u",
386: (uint)txq.Length(), (uint)txq.Capacity(), stat.txq_peak);
387: screen.Print(0, y++, "RxQ %4u/%4u %4u",
388: (uint)rxq.Length(), (uint)rxq.Capacity(), stat.rxq_peak);
1.1 root 389: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.