|
|
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.