--- nono/vm/scsidev.cpp 2026/04/29 17:05:29 1.1.1.18 +++ nono/vm/scsidev.cpp 2026/04/29 17:05:46 1.1.1.21 @@ -18,6 +18,7 @@ #include "scsicmd.h" #include "scsidev.h" +#include "scsidomain.h" #include "config.h" #include "mainapp.h" #include "scheduler.h" @@ -39,12 +40,11 @@ SCSIDevice::~SCSIDevice() { } -// このデバイスがバスに接続された (SCSIBus::AttachDevice() から呼ばれる) +// バスに接続する。 void -SCSIDevice::OnAttached(SCSIBus *bus_, uint myid_) +SCSIDevice::Attach(SCSIBus *bus_) { bus = bus_; - myid = myid_; } @@ -53,17 +53,15 @@ SCSIDevice::OnAttached(SCSIBus *bus_, ui // // コンストラクタ -SCSIHostDevice::SCSIHostDevice(IODevice *parent_, const char *config_name_) - : inherited(OBJ_SCSI) +SCSIHostDevice::SCSIHostDevice(SCSIDomain *domain_, IODevice *parent_) + : inherited(OBJ_NONE) { + domain = domain_; parent = parent_; // イニシエータの ID は実行時にコントローラによって決まる。 devtype = SCSI::DevType::Initiator; - // バスデバイスを作成する。 - // ただし自身がバスに参加するのは継承側で行う - // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。 - scsibus.reset(new SCSIBus(this, config_name_)); + SetLogLevel(parent->loglevel); } // デストラクタ @@ -71,30 +69,17 @@ SCSIHostDevice::~SCSIHostDevice() { } -// 初期化。 -bool -SCSIHostDevice::Init() +// SCSI ID を設定する。 +// (SCSIDomain からのみ呼ぶこと) +void +SCSIHostDevice::SetID(uint id_) { - if (inherited::Init() == false) { - return false; - } - - // 初期化ではないけど、ログが使えるようになったここでデバッグ表示。 - if (loglevel >= 1) { - const char *name = scsibus->GetConfigName(); - const auto& conn = scsibus->GetConnectedDevices(); - for (const auto dev : conn) { - putmsgn("%s target #%u %s", - name, dev->GetMyID(), SCSI::GetDevTypeName(dev->GetDevType())); - } - } - - return true; + myid = id_; } -// コールバック関数を設定する。 +// SCSIBus からのコールバック関数を設定する。 void -SCSIHostDevice::SetCallback( +SCSIHostDevice::SetBusCallback( BusFreeCallback_t busfree_, SCSIHostCallback_t selack_, SCSIHostCallback_t xferreq_) @@ -134,6 +119,11 @@ SCSIHostDevice::OnBusFree(uint id) // 親のバスフリーコールバックを呼ぶ (parent->*(BusFreeCallback))(id); + + // RST が見えてる状態で親に通知はしたので、ここで下ろす。 + if (GetRST()) { + NegateRST(); + } } // ターゲットがセレクションに応答した (SCSIBus から呼ばれる) @@ -163,10 +153,10 @@ SCSIHostDevice::OnTransferReq() // // コンストラクタ。 -SCSITarget::SCSITarget(SCSIBus *bus_, uint id) +SCSITarget::SCSITarget(SCSIDomain *domain_, uint id) : inherited(OBJ_NONE) // オブジェクト ID で検索することはない { - bus = bus_; + domain = domain_; myid = id; } @@ -320,9 +310,20 @@ SCSITarget::OnTransfer() } putlogn("%s", cmdbuf.c_str()); } - // 最終バイトを受信したらコマンドを選択 - DispatchCmd(); + // 最終バイトを受信したらコマンドを選択。 lastbyte = true; + SelectCommand(); + if ((bool)cmd == false) { + std::string namebuf; + const char *name = SCSI::GetCommandName(cmdseq[0]); + if (name) { + namebuf = std::string(name); + } else { + namebuf = string_format("$%02x", cmdseq[0]); + } + putlog(0, "Failed to initialize command %s", namebuf.c_str()); + AssertRST(); + } } } else if (GetIO() == false) { // OUT 方向 (ターゲットが受信側) @@ -358,7 +359,7 @@ SCSITarget::OnTransferAck() // フェーズ完了 switch (xfer) { case SCSI::XferPhase::Command: - next = cmd->Command(cmdseq); + next = cmd->ExecCommand(cmdseq); break; case SCSI::XferPhase::DataOut: next = cmd->DoneDataOut(); @@ -389,6 +390,7 @@ SCSITarget::OnTransferAck() if (next != xfer) { // End ならバスフリーにして終了。 if (next == SCSI::XferPhase::End) { + cmd.reset(); NegateBSY(); return; } @@ -443,7 +445,8 @@ SCSITarget::OnTransferAck() AssertREQ(); } -// SCSI コマンドのディスパッチャ +// SCSI コマンドのディスパッチャ。 +// C++ の例外は呼び出し側で捕捉している。 void SCSITarget::DispatchCmd() { @@ -489,24 +492,38 @@ SCSITarget::DispatchCmd() } } -// コマンド列を指定して対応するコマンドを選択する。なければ NULL を返す。 -// ROM30 エミュレーション用。 +// コマンド列を指定して対応するコマンドを選択する。 +// 知らないコマンドなら SCSICmdNotSupportedCommand という仮想のコマンドを返す。 +// コマンドインスタンスが生成できなければ NULL を返す。 +// 外部向け。 SCSICmd * SCSITarget::SelectCommand(const std::vector& cmdseq_) { cmdseq = cmdseq_; - DispatchCmd(); + SelectCommand(); return cmd.get(); } +// 内部版。 +// 入力コマンド列はメンバ変数 cmdseq だし、生成した cmd もメンバ変数。 +void +SCSITarget::SelectCommand() +{ + cmd.reset(); + // DispatchCmd() の中は new が多いので個別に捕捉せずこっちでやる。 + try { + DispatchCmd(); + } catch (...) { } +} + // // SCSI Disk // // コンストラクタ -SCSIDisk::SCSIDisk(SCSIBus *bus_, uint id, SCSI::DevType devtype_) - : inherited(bus_, id) +SCSIDisk::SCSIDisk(SCSIDomain *domain_, uint id, SCSI::DevType devtype_) + : inherited(domain_, id) { devtype = devtype_; @@ -553,15 +570,10 @@ SCSIDisk::~SCSIDisk() bool SCSIDisk::Init() { - if (inherited::Init() == false) { - return false; - } - const std::string keybody = string_format("%s-id%u-", - bus->GetConfigName(), myid); + domain->GetConfigName(), myid); const std::string imgkey = keybody + "image"; const std::string wikey = keybody + "writeignore"; - const std::string wpkey = keybody + "writeprotect"; const std::string stkey = keybody + "seektime"; // デバイス種別(v[0])とパス(v[1])に分解。 @@ -591,32 +603,10 @@ SCSIDisk::Init() // オープン前に writeignore をチェック。 // この後パス名を処理するとすぐに UI に通知を出すため、それより前で // 行わないといけない。うーん。 - // - // 変数名は元々 -writeprotect だったが -writeignore に変更した。 - // 単にリネームするだけだと、開発中に古いバージョンを二分探索とかする - // 必要が出来た場合に面倒なので、当面は古いほうも面倒を見る。 - // 十分いらなくなった頃に削除する。 - // - // wi指定 wp指定 - // なし なし wi を採用。 - // なし あり wp を採用。ただし警告 - // あり なし wi を採用。 - // あり あり wi を採用。ここは警告不要でいいだろう - // - const ConfigItem& wi = gConfig->Find(wikey); - const ConfigItem& wp = gConfig->Find(wpkey); - if (wi.GetFrom() == ConfigItem::FromInitial && - wp.GetFrom() != ConfigItem::FromInitial ) - { - // -writeignore (新)がなく -writeprotect (旧) だけが指定されると警告。 - // エラー停止まではしなくていいか。 - warnx("Warning: '%s' is obsolete. Use new '%s'", - wpkey.c_str(), wikey.c_str()); + write_ignore = gConfig->Find(wikey).AsInt(); - write_ignore = wp.AsInt(); - } else { - write_ignore = wi.AsInt(); - } + // writeignore が決まったので状態を初期化。 + Clear(); // 設定時点でファイルが指定されていればオープン if (filename.empty() == false) { @@ -630,9 +620,12 @@ SCSIDisk::Init() MediaChanged(); } - // 設定は [msec]、変数は [nsec]。 - seektime = gConfig->Find(stkey).AsInt(); - seektime *= 1_msec; + // 平均シークタイム。設定は [msec]、変数は [nsec]。 + // virtio-scsi にはない。 + if (strncmp(stkey.c_str(), "virtio", 6) != 0) { + seektime = gConfig->Find(stkey).AsInt(); + seektime *= 1_msec; + } return true; } @@ -824,7 +817,16 @@ SCSIDisk::Clear() medium_loaded = false; image.Close(); pathname.clear(); - write_mode = RW::Writeable; + + if (IsWriteableDevice()) { + if (write_ignore) { + write_mode = RW::WriteIgnore; + } else { + write_mode = RW::Writeable; + } + } else { + write_mode = RW::ReadOnly; + } } // メディア状態変更を UI に通知する @@ -869,7 +871,8 @@ SCSIDisk::UnloadMessage(MessageID msgid, UnloadDisk(force); } -// SCSI コマンドのディスパッチャ +// SCSI コマンドのディスパッチャ。 +// C++ の例外は呼び出し側で捕捉している。 void SCSIDisk::DispatchCmd() {