--- nono/host/hostcom.cpp 2026/04/29 17:05:43 1.1.1.7 +++ nono/host/hostcom.cpp 2026/04/29 17:06:02 1.1.1.11 @@ -73,30 +73,36 @@ #include "config.h" #include "mainapp.h" #include "monitor.h" +#include "uimessage.h" // コンストラクタ // -// objname_ は "HostCOM" とか (スレッド名も同じになる)。 -// 設定ファイルキーワードプレフィックスはこれを小文字にしたもの("hostcom")。 -HostCOMDevice::HostCOMDevice(Device *parent_, const std::string& objname_) - : inherited(parent_, OBJ_NONE) +// n は 0 以上なら本体 COM のいずれか。-1 ならデバッガ。 +HostCOMDevice::HostCOMDevice(Device *parent_, int n, + const std::string& portname_) + : inherited(parent_, (n >= 0 ? OBJ_HOSTCOM(n) : OBJ_HOSTCOM_DBG), portname_) { - SetName(objname_); + // SetName() で設定したオブジェクト名はスレッド名でも使う。 + // また設定ファイルキーワードプレフィックスはこれを小文字にしたもの。 // 親が hostcom* か debugger かによっていろいろ動作が違う。 - if (GetName() == "Debugger") { + if (n < 0) { + SetName("Debugger"); + // デバッガコンソールならログレベルは親に準じるため、こっちは不要。 ClearAlias(); // 送受信バイト数がメインのモニタはこっちにはなくていいだろう。 } else { + SetName(string_format("HostCOM%u", n)); + // hostcom* なら親(mpscc などのシリアルデバイス)とこっち(HostCOM*) // のログレベルは独立。 // モニタは hostcom* のみ必要。 - monitor = gMonitorManager->Regist(ID_MONITOR_HOSTCOM, this); - monitor->func = ToMonitorCallback(&HostCOMDevice::MonitorUpdate); - monitor->SetSize(30, 17); + monitor = gMonitorManager->Regist(ID_MONITOR_HOSTCOM(n), this); + monitor->SetCallback(&HostCOMDevice::MonitorScreen); + monitor->SetSize(33, 17); } } @@ -111,6 +117,7 @@ HostCOMDevice::SetLogLevel(int loglevel_ { inherited::SetLogLevel(loglevel_); + RWLockGuard_Read guard(driverlock); if ((bool)driver) { driver->SetLogLevel(loglevel_); } @@ -124,141 +131,182 @@ HostCOMDevice::Create2() return false; } - if (SelectDriver() == false) { + if (SelectDriver(true) == false) { return false; } return true; } -// ドライバ(再)選択 +// ドライバ(再)選択。 +// エラーが起きた場合、 +// 起動時なら、warn() 等で表示して false を返す(終了する)。 +// 実行中なら、warn() 等で表示してもいいが、必ず None にフォールバックして +// true を返すこと。 bool -HostCOMDevice::SelectDriver() +HostCOMDevice::SelectDriver(bool startup) { + RWLockGuard_Write guard(driverlock); + + driver.reset(); + errmsg.clear(); + // 設定のキー名は親デバイスによって異なる std::string key = GetConfigKey(); const ConfigItem& item = gConfig->Find(key + "-driver"); const std::string& type = item.AsString(); - driver.reset(); - - if (type != "none") { - if (type == "stdio") { - // debugger-driver=stdio (または -D オプション) と - // hostcom*-driver=stdio とは共存できない。 - // 歴史的経緯でとりあえず debugger-driver=stdio 側を優先とする。 - // XXX これも後指定優先にするかどうか - // XXX hostcom が増えたらたぶんこうじゃなくなる - if (key != "debugger") { - const ConfigItem& dditem = gConfig->Find("debugger-driver"); - const std::string& ddtype = dditem.AsString(); - if (ddtype == "stdio") { - auto where = dditem.GetWhere(); - if (where == "-V") { - where += " debugger-driver=stdio"; - } - item.Err("stdio driver conflicts with " + where); - return false; - } - } + enum { + ERR_CONFIG, // 設定でのエラー + ERR_INVALID, // 知らないドライバ + } reason = ERR_CONFIG; + + if (type == "none") { + CreateNone(); + } else if (type == "stdio") { + CreateStdio(item, key); + } else if (type == "tcp") { + CreateTCP(); + } else if (type == "console" || type == "cons") { + if (key == "debugger") { + reason = ERR_INVALID; + } else { + CreateConsole(item); + } + } else { + // 知らないドライバ種別 + reason = ERR_INVALID; + } - try { - driver.reset(new COMDriverStdio(this)); - } catch (...) { } - if ((bool)driver && driver->InitDriver() == false) { - driver.reset(); + if ((bool)driver == false) { + // 指定のドライバが使えなかった場合、hostcom* でも debugger でも + // o 起動時ならエラー終了する。 + // o 実行中なら none にフォールバックする。 + if (startup) { + switch (reason) { + case ERR_INVALID: + item.Err("Invalid driver name"); + break; + case ERR_CONFIG: + item.Err("Could not configure the driver"); + warnx("(See details with option -C -L%s=1)", key.c_str()); + break; + default: + assert(false); } + return false; + } else { + // UI に通知してフォールバック。 + int n = (key == "debugger") ? -1 : GetId() - OBJ_HOSTCOM0; + gMainApp.GetUIMessage()->Post(UIMessage::HOSTCOM_FAILED, n); - } else if (type == "tcp") { - try { - driver.reset(new COMDriverTCP(this)); - } catch (...) { } - if ((bool)driver && driver->InitDriver() == false) { - driver.reset(); - } + CreateNone(); + } + } + assert((bool)driver); - } else if (type == "cons") { - // console デバイスのいない機種では指定出来ない。 - if (gMainApp.FindObject(OBJ_CONSOLE) == NULL) { - item.Err("cannot be specified on vmtype=%s", - gMainApp.GetVMTypeStr().c_str()); - return false; - } + // ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える… + putmsg(1, "selected host driver: %s", + string_tolower(driver->GetDriverName()).c_str()); - try { - driver.reset(new COMDriverCons(this)); - } catch (...) { } - if ((bool)driver && driver->InitDriver() == false) { - driver.reset(); - } + return true; +} - } else { - // 知らないドライバ種別 - item.Err(); - return false; +// None ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void +HostCOMDevice::CreateNone() +{ + try { + driver.reset(new COMDriverNone(this)); + } catch (...) { } + if ((bool)driver) { + if (driver->InitDriver()) { + // 成功。 + return; } + errmsg = driver->errmsg; + driver.reset(); + } +} - if ((bool)driver == false) { - bool fallback = false; - - // 指定のドライバが使えなかった場合、 - // hostcom*-driver なら fallback するかどうかは設定による。 - // debugger-driver なら常に fallback せず終了する。 - // - // 元々 hostnet が tap, bpf のように順に試してという流れだったので - // fallback の選択肢が用意されていたが、hostcom は今の所どれか1つ - // (stdio か tcp) か、そうでなければ none しかないので、fallback - // するかどうかを選ばせる意味があまりような気もするけど、 - // とりあえず形式だけ真似してみる…。 - // 一方、debugger-driver なら常に fallback せず、設定自体も用意 - // しない。デバッガを使いたいと言ってる時点でデバッガがなくても - // とりあえず起動してほしいとはならないだろうと思うので。 - if (key != "debugger") { - fallback = gConfig->Find(key + "-fallback").AsInt(); - putmsg(1, "%s-fallback=%d", key.c_str(), fallback ? 1 : 0); - } +// stdio ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void +HostCOMDevice::CreateStdio(const ConfigItem& item, const std::string& key) +{ + // セマフォの処理は InitDriver() 内で行ってある。 - if (fallback == false) { - // フォールバックしないならエラー終了 - std::string errmsg; - errmsg = string_format("No %s driver found", key.c_str()); - putmsg(1, "%s", errmsg.c_str()); - warnx("%s (See details with option -C -L%s=1)", - errmsg.c_str(), key.c_str()); - return false; - } + try { + driver.reset(new COMDriverStdio(this)); + } catch (...) { } + if ((bool)driver) { + if (driver->InitDriver()) { + // 成功。 + return; } + errmsg = driver->errmsg; + driver.reset(); } +} - if ((bool)driver == false) { - try { - driver.reset(new COMDriverNone(this)); - } catch (...) { } - if ((bool)driver && driver->InitDriver() == false) { - // 失敗したら出来ることはあまりなさげ - assert(false); +// TCP ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void +HostCOMDevice::CreateTCP() +{ + try { + driver.reset(new COMDriverTCP(this)); + } catch (...) { } + if ((bool)driver) { + if (driver->InitDriver()) { + // 成功。 + return; } + errmsg = driver->errmsg; + driver.reset(); } - assert((bool)driver); +} - // ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える… - putmsg(1, "selected host driver: %s", - string_tolower(driver->GetDriverName()).c_str()); +// Console ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void +HostCOMDevice::CreateConsole(const ConfigItem& item) +{ + // console デバイスのいない機種では指定出来ない。 + if (gMainApp.FindObject(OBJ_CONSOLE) == NULL) { + item.Err("cannot be specified on vmtype=%s", + gMainApp.GetVMTypeStr().c_str()); + return; + } - return true; + try { + driver.reset(new COMDriverConsole(this)); + } catch (...) { } + if ((bool)driver) { + if (driver->InitDriver()) { + // 成功。 + return; + } + errmsg = driver->errmsg; + driver.reset(); + } } void HostCOMDevice::Dispatch(int udata) { // driver の Dispatch は処理し終わったら DONE を返す - udata = driver->Dispatch(udata); + { + RWLockGuard_Read guard(driverlock); + udata = driver->Dispatch(udata); + } // ...のだが、Dispatch(LISTEN_SOCKET) は着信を受け付けた時には // LISTEN_SOCKET を返し、それを受けてここで通知処理を行う。 if (udata == LISTEN_SOCKET) { if (accept_func) { - (parent->*accept_func)(); + (parent->*accept_func)(0); } udata = DONE; } @@ -291,8 +339,8 @@ HostCOMDevice::Tx(uint32 data) // ざっくりピーク値 stat.txq_peak = std::max((uint)txq.Length(), stat.txq_peak); - // パイプに通知 (値はダミー) - return WritePipe(0); + // パイプで通知。 + return WritePipe(PIPE_TX); } // キューから取り出す (VM スレッドで呼ばれる) @@ -325,6 +373,7 @@ HostCOMDevice::Rx() int HostCOMDevice::Read() { + RWLockGuard_Read guard(driverlock); assert((bool)driver); int data = driver->Read(); @@ -346,10 +395,11 @@ HostCOMDevice::Read() // 外部への書き出し (ホストスレッドで呼ばれる) void -HostCOMDevice::Write(uint32 dummy) +HostCOMDevice::Write() { uint8 data; + RWLockGuard_Read guard(driverlock); assert(driver); // 送信キューを全部吐き出す @@ -359,33 +409,46 @@ HostCOMDevice::Write(uint32 dummy) } } +// ドライバ名を返す +const std::string& +HostCOMDevice::GetDriverName() +{ + RWLockGuard_Read guard(driverlock); + assert((bool)driver); + return driver->GetDriverName(); +} + // モニタ void -HostCOMDevice::MonitorUpdate(Monitor *, TextScreen& screen) +HostCOMDevice::MonitorScreen(Monitor *, TextScreen& screen) { screen.Clear(); - screen.Print(0, 0, "Parent Device : %s", parent->GetName().c_str()); - screen.Print(0, 1, "HostCOM Driver: %s", driver->GetDriverName()); - // 次の1行はドライバ依存情報 - driver->MonitorUpdateMD(screen, 2); + screen.Print(0, 0, "Device(Port) : %s", GetPortName().c_str()); + { + RWLockGuard_Read gurad(driverlock); + screen.Print(0, 1, "HostCOM Driver: %s", + driver->GetDriverName().c_str()); + // 次の1行はドライバ依存情報 + driver->MonitorScreenMD(screen, 2); + } int y = 4; - screen.Print(0, y++, "%-17s%13s", "", "Bytes"); - screen.Print(0, y++, "%-17s%13s", "VM sends", + screen.Print(0, y++, "%-20s%13s", "", "Bytes"); + screen.Print(0, y++, "%-20s%13s", "VM sends", format_number(stat.tx_bytes).c_str()); - screen.Print(0, y++, "%-17s%13s", "Write to host", + screen.Print(0, y++, "%-20s%13s", "Write to host", format_number(stat.write_bytes).c_str()); - screen.Print(0, y++, "%-17s%13s", "Drop:TxQ Full", + screen.Print(0, y++, "%-20s%13s", "Drop:TxQ Full", format_number(stat.txqfull_bytes).c_str()); y++; - screen.Print(0, y++, "%-17s%13s", "", "Bytes"); - screen.Print(0, y++, "%-17s%13s", "Read from host", + screen.Print(0, y++, "%-20s%13s", "", "Bytes"); + screen.Print(0, y++, "%-20s%13s", "Read from host", format_number(stat.read_bytes).c_str()); - screen.Print(0, y++, "%-17s%13s", "VM receives", + screen.Print(0, y++, "%-20s%13s", "VM receives", format_number(stat.rx_bytes).c_str()); - screen.Print(0, y++, "%-17s%13s", "Drop:RxQ Full", + screen.Print(0, y++, "%-20s%13s", "Drop:RxQ Full", format_number(stat.rxqfull_bytes).c_str()); y++;