--- nono/vm/scsidev.cpp 2026/04/29 17:05:18 1.1.1.16 +++ nono/vm/scsidev.cpp 2026/04/29 17:05:38 1.1.1.19 @@ -28,9 +28,10 @@ // // コンストラクタ -SCSIDevice::SCSIDevice(int objid_) +SCSIDevice::SCSIDevice(uint objid_) : inherited(objid_) { + myid = -1; } // デストラクタ @@ -38,25 +39,31 @@ SCSIDevice::~SCSIDevice() { } +// このデバイスがバスに接続された (SCSIBus::AttachDevice() から呼ばれる) +void +SCSIDevice::OnAttached(SCSIBus *bus_, uint myid_) +{ + bus = bus_; + myid = myid_; +} + // -// SCSI ホストデバイス +// SCSI ホストデバイス (イニシエータ) // // コンストラクタ -SCSIHostDevice::SCSIHostDevice(int objid_) - : inherited(objid_) +SCSIHostDevice::SCSIHostDevice(IODevice *parent_, const char *config_name_) + : inherited(OBJ_SCSI) { + parent = parent_; + // イニシエータの ID は実行時にコントローラによって決まる。 devtype = SCSI::DevType::Initiator; - monitor_devs.func = ToMonitorCallback(&SCSIHostDevice::MonitorUpdateDevs); - // サイズは Create で決まる - monitor_devs.Regist(ID_MONITOR_SCSIDEVS); - // バスデバイスを作成する。 // ただし自身がバスに参加するのは継承側で行う // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。 - scsibus.reset(new SCSIBus()); + scsibus.reset(new SCSIBus(this, config_name_)); } // デストラクタ @@ -64,95 +71,38 @@ SCSIHostDevice::~SCSIHostDevice() { } -// 設定ファイルに使われるこのホストデバイスを示す接頭語を設定する -// (継承クラス側のコンストラクタで config_name を設定後に呼ぶこと) -void -SCSIHostDevice::SetConfigName(const char *name_) -{ - config_name = name_; -} - -bool -SCSIHostDevice::Create() -{ - // 設定を読み込む。 - for (int id = 0; id < 8; id++) { - const std::string key = string_format("%s-id%d-image", - GetConfigName(), id); - const ConfigItem& item = gConfig->Find(key); - const std::string& image = item.AsString(); - - // 空ならデバイスなし - if (image.length() == 0) { - // 未接続部分のパラメータは減らしておくか。 - // --show-config するとさすがに無駄に多くて邪魔なので。 - const std::string wikey = string_format("%s-id%d-writeignore", - GetConfigName(), id); - const std::string wpkey = string_format("%s-id%d-writeprotect", - GetConfigName(), id); - const std::string stkey = string_format("%s-id%d-seektime", - GetConfigName(), id); - gConfig->Delete(wikey); - gConfig->Delete(wpkey); - gConfig->Delete(stkey); - continue; - } - - // ',' より前がデバイス種別 (ここではパスは不要) - auto v = string_split(image, ','); - // 比較のため前後の空白を取り除いて小文字にする - std::string type = string_trim(v[0]); - type = string_tolower(type); - - SCSI::DevType target_devtype; - if (type == "hd") { - target_devtype = SCSI::DevType::HD; - } else if (type == "cd") { - target_devtype = SCSI::DevType::CD; - } else if (type == "mo") { - target_devtype = SCSI::DevType::MO; - } else { - item.Err("Invaild device type '%s'", type.c_str()); - return false; - } - target[id].reset(new SCSIDisk(this, id, target_devtype)); - scsibus->Attach(target[id].get(), id); - } - - return true; -} - // 初期化。 -// 継承クラスが connected_devices をセットした後で呼ぶこと。 bool SCSIHostDevice::Init() { - if (inherited::Init() == false) { - return false; - } - - // 初期化ではないけどログレベルがセットできたここでデバッグ表示。 - // -L オプションの処理は Create() の後なので。 + // 初期化ではないけど、ログが使えるようになったここでデバッグ表示。 if (loglevel >= 1) { - for (int id = 0; id < 8; id++) { - std::string typestr; - if (target[id]) { - typestr = SCSI::GetDevTypeName(target[id]->GetDevType()); - putmsgn("%s target #%d %s", GetConfigName(), id, - typestr.c_str()); - } + 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())); } } - // connected_devices によってモニタの大きさを決定。 - monitor_devs.SetSize(75, (connected_devices.size() - 1) * 4 + 1); - return true; } -// バスフリーコールバック (SCSIBus から呼ばれる) +// コールバック関数を設定する。 void -SCSIHostDevice::BusFreeCB(int id) +SCSIHostDevice::SetCallback( + BusFreeCallback_t busfree_, + SCSIHostCallback_t selack_, + SCSIHostCallback_t xferreq_) +{ + BusFreeCallback = busfree_; + SelectionAckCallback = selack_; + TransferReqCallback = xferreq_; +} + +// バスフリーになった (SCSIBus から呼ばれる) +void +SCSIHostDevice::OnBusFree(uint id) { // 実際にはバスフリーを検出したデバイスは各自自身が送出している信号を // 下げるのだが、色々面倒なので、ここではホストのバスフリーコールバックが @@ -161,10 +111,10 @@ SCSIHostDevice::BusFreeCB(int id) uint32 data; while ((data = GetBSY()) != 0) { - bus->NegateBSY(DecodeID(data)); + bus->NegateBSY(SCSIBus::DecodeID(data)); } while ((data = GetSEL()) != 0) { - bus->NegateSEL(DecodeID(data)); + bus->NegateSEL(SCSIBus::DecodeID(data)); } if (GetREQ()) { NegateREQ(); @@ -177,83 +127,30 @@ SCSIHostDevice::BusFreeCB(int id) } SetXfer(0); SetData(0); + + // 親のバスフリーコールバックを呼ぶ + (parent->*(BusFreeCallback))(id); } -// SCSI デバイスモニタ -// (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので -// とりあえずここで) +// ターゲットがセレクションに応答した (SCSIBus から呼ばれる) void -SCSIHostDevice::MonitorUpdateDevs(Monitor *, TextScreen& screen) +SCSIHostDevice::OnSelectionAck() { - int y; - -// 012345678901234567890123456789012345678901234567890123456789012345678901234 -// ID6: HD(2048MB) -// MediumLoaded Off LogicalBlock 2048 ImageSize 2,147,483,648 -// RemovalPrevent Off WriteProtected - - - screen.Clear(); - - y = 0; - for (const auto dev : connected_devices) { - std::string desc = SCSI::GetDevTypeName(dev->GetDevType()); - std::string pathname; - - SCSIDisk *disk = dynamic_cast(dev); - if (disk && disk->IsMediumLoaded()) { - desc += string_format("(%dMB)", - (int)(disk->GetSize() / 1024 / 1024)); - pathname = disk->GetPathName(); - } - screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str()); - -#if 1 - (void)pathname; -#else - // パス名の非ASCIIどうするか - if (!pathname.empty()) { - if (pathname.size() > screen.GetCol() - 16 - 3) { - pathname = pathname.substr(0, screen.GetCol() - 16 - 3); - pathname += "..."; - } - screen.Puts(16, y, pathname.c_str()); - } -#endif - y++; - - // イニシエータならここまで。(今の所イニシエータとディスクしかない) - if (disk == NULL) { - continue; - } + // ターゲットが BSY を立てて応答したので、 + // こちらはデータバスをクリアして SEL を下げる。 + // これによりセレクションフェーズは成功で完了する。 + SetData(0); + NegateSEL(); - const int x1 = 5; - const int x2 = 29; - const int x3 = 50; - - // 1行目 - screen.Print(x1, y, - (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), - "MediumLoaded %s", - disk->IsMediumLoaded() ? "On" : "Off"); - screen.Print(x2, y, "LogicalBlock %d", disk->GetBlocksize()); - std::string imagesize = format_number(disk->GetSize()); - screen.Print(x3, y, "ImageSize %13s", imagesize.c_str()); - y++; - - // 2行目 - screen.Print(x1, y, - (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), - "RemovalPrevent %s", - disk->IsMediumRemovalPrevented() ? "On" : "Off"); - - screen.Puts(x2, y, - (disk->IsMediumLoaded() ? TA::Normal : TA::Disable), - disk->GetWriteModeStr()); - y++; + // 親のバスフリーコールバックを呼ぶ + (parent->*(SelectionAckCallback))(); +} - y++; - } +// ターゲットが REQ を立てた (SCSIBus から呼ばれる) +void +SCSIHostDevice::OnTransferReq() +{ + (parent->*(TransferReqCallback))(); } @@ -262,11 +159,10 @@ SCSIHostDevice::MonitorUpdateDevs(Monito // // コンストラクタ。 -SCSITarget::SCSITarget(SCSIHostDevice *h, int id) +SCSITarget::SCSITarget(SCSIBus *bus_, uint id) : inherited(OBJ_NONE) // オブジェクト ID で検索することはない { - // ホストへのリンク (所有関係のないポインタ) - host = h; + bus = bus_; myid = id; } @@ -291,7 +187,7 @@ SCSITarget::ResetHard(bool poweron) // バスリセットされた (SCSIBus から呼ばれる) void -SCSITarget::BusResetCB() +SCSITarget::OnBusReset() { // センスキーをクリア ClearSense(); @@ -315,18 +211,18 @@ SCSITarget::BusResetCB() // セレクションで選択された (SCSIBus から呼ばれる) void -SCSITarget::SelectionCB() +SCSITarget::OnSelected() { // ターゲットが、自身が選択されていることを認識してから BSY を // 立てるまでの時間はもうバス側でカウントしてあるので、ここでは // ただちに BSY を上げてよい。 - putlog(4, "Selection Callback"); + putlog(4, "OnSelected"); AssertBSY(); } // 情報転送フェーズを開始する (SCSIBus から呼ばれる) void -SCSITarget::StartTransferCB() +SCSITarget::OnStartTransfer() { // 最初はコマンドフェーズ。(ATN はとりあえず放置) putlog(4, "Start Command Phase"); @@ -338,7 +234,7 @@ SCSITarget::StartTransferCB() // 情報転送フェーズ間の遷移について // -// 情報転送フェーズは TransferCB() で1バイトずつ転送を行い、所定のバイト数 +// 情報転送フェーズは OnTransfer() で1バイトずつ転送を行い、所定のバイト数 // の転送が完了すればフェーズを遷移する。遷移方法は入出力方向別で6通り。 // o IN -> IN // o IN -> OUT @@ -347,26 +243,26 @@ SCSITarget::StartTransferCB() // o OUT -> IN // o OUT -> END // -// IN -> IN の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを +// IN -> IN の遷移。OnTransfer() で前フェーズの最終バイト受信後、フェーズを // 変更して REQ を立てるところまで。 // -// IN -> OUT の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを +// IN -> OUT の遷移。OnTransfer() で前フェーズの最終バイト受信後、フェーズを // 変更して (おそらく {Phase}Begin() を発行してから)、データをセットし REQ // を立てる? // -// IN -> END の遷移。TransferCB() で前フェーズの最終バイト受信後、BSY を +// IN -> END の遷移。OnTransfer() で前フェーズの最終バイト受信後、BSY を // 下げて終了。 // // OUT -> OUT の遷移。前フェーズの最終バイトをイニシエータが受信後に -// TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin() +// OnTransfer() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin() // を発行してから)、データをセットし REQ を立てる? // // OUT -> IN の遷移。前フェーズの最終バイトをイニシエータが受信後に -// TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin() +// OnTransfer() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin() // を発行してから)、REQ を立てる? // // OUT -> END の遷移。前フェーズの最終バイトをイニシエータが受信後に -// TransferCB() が呼ばれるので、BSY を下げる? +// OnTransfer() が呼ばれるので、BSY を下げる? // 情報転送フェーズでイニシエータが ACK を上げた。(SCSIBus から呼ばれる) // OUT (イニシエータ → ターゲット方向) なら、ターゲットが上げた REQ に応答 @@ -374,7 +270,7 @@ SCSITarget::StartTransferCB() // IN (ターゲット → イニシエータ方向) なら、ターゲットがデータをデータバスに // 乗せて REQ を上げたのをイニシエータが受信した後でこれをコールしてくる。 void -SCSITarget::TransferCB() +SCSITarget::OnTransfer() { uint8 data; SCSI::XferPhase xfer; @@ -388,8 +284,8 @@ SCSITarget::TransferCB() // コマンドフェーズだけいろいろ特殊なので別処理。 // 受信バッファが異なる、1バイト目で受信長が決まるなど。 data = GetData(); - putlog(4, "TransferCB: cmdseq[%02d] = $%02x", - (int)cmdseq.size(), data); + putlog(4, "%s: cmdseq[%02u] = $%02x", __func__, + (uint)cmdseq.size(), data); cmdseq.push_back(data); if (cmdseq.size() == 1) { // 1バイト目ならこのコマンドが何バイトか調べる @@ -435,7 +331,7 @@ SCSITarget::TransferCB() } else { // IN 方向 (ターゲットが送信側) // bytecount は加算方向。 - // データは前のループの終わり (TransferAckCB()の下のほう) で + // データは前のループの終わり (OnTransferAck()の下のほう) で // セットしているのでここでは引き取られた分をカウントする。 bytecount++; if (bytecount >= cmd->buf.size()) { @@ -448,7 +344,7 @@ SCSITarget::TransferCB() // 情報転送フェーズでイニシエータが ACK を下げた。(SCSIBus から呼ばれる) void -SCSITarget::TransferAckCB() +SCSITarget::OnTransferAck() { SCSI::XferPhase xfer = GetXfer(); @@ -476,7 +372,7 @@ SCSITarget::TransferAckCB() next = cmd->DoneStatus(); break; default: - PANIC("TransferCB unknown xfer done %d", (int)xfer); + PANIC("%s unknown xfer done %d", __func__, (int)xfer); } putlog(3, "%s done", SCSI::GetXferPhaseName(xfer)); @@ -505,30 +401,30 @@ SCSITarget::TransferAckCB() case SCSI::XferPhase::DataOut: cmd->buf.clear(); bytecount = cmd->recvbytes; - putlog(3, "DataOut begin (%d bytes)", bytecount); + putlog(3, "DataOut begin (%u bytes)", bytecount); break; case SCSI::XferPhase::DataIn: bytecount = 0; - putlog(3, "DataIn begin (%d bytes)", (int)cmd->buf.size()); + putlog(3, "DataIn begin (%u bytes)", (uint)cmd->buf.size()); break; case SCSI::XferPhase::MsgOut: cmd->BeginMsgOut(); cmd->buf.clear(); bytecount = cmd->recvbytes; - putlog(3, "MsgOut begin (%d bytes)", bytecount); + putlog(3, "MsgOut begin (%u bytes)", bytecount); break; case SCSI::XferPhase::MsgIn: cmd->BeginMsgIn(); bytecount = 0; - putlog(3, "MsgIn begin (%d bytes)", (int)cmd->buf.size()); + putlog(3, "MsgIn begin (%u bytes)", (uint)cmd->buf.size()); break; case SCSI::XferPhase::Status: cmd->BeginStatus(); bytecount = 0; - putlog(3, "Status begin (%d bytes)", (int)cmd->buf.size()); + putlog(3, "Status begin (%u bytes)", (uint)cmd->buf.size()); break; default: - PANIC("TransferCB unknown xfer begin %d", (int)xfer); + PANIC("%s unknown xfer begin %d", __func__, (int)xfer); } } } @@ -581,8 +477,8 @@ SCSITarget::DispatchCmd() if (cname) { name = string_format(" %s", cname); } - putlog(0, "SCSI Command $%02x%s len=%d not supported", - cmdseq[0], name.c_str(), (int)cmdseq.size()); + putlog(0, "SCSI Command $%02x%s len=%u not supported", + cmdseq[0], name.c_str(), (uint)cmdseq.size()); cmd.reset(new SCSICmdNotSupportedCommand(this)); break; @@ -605,8 +501,8 @@ SCSITarget::SelectCommand(const std::vec // // コンストラクタ -SCSIDisk::SCSIDisk(SCSIHostDevice *h, int id, SCSI::DevType devtype_) - : inherited(h, id) +SCSIDisk::SCSIDisk(SCSIBus *bus_, uint id, SCSI::DevType devtype_) + : inherited(bus_, id) { devtype = devtype_; @@ -616,11 +512,11 @@ SCSIDisk::SCSIDisk(SCSIHostDevice *h, in // コンストラクタ本文でログ名などを設定する。その時の作法に // 従っているのですこし面倒な手法が必要。 // lib/object.h 参照。 - SetName(string_format("SCSI%s%d", devtypename, id)); + SetName(string_format("SCSI%s%u", devtypename, id)); ClearAlias(); - AddAlias(string_format("SCSI%s%d", devtypename, id)); - AddAlias(string_format("SCSI%d", id)); - AddAlias(string_format("%s%d", devtypename, id)); + AddAlias(string_format("SCSI%s%u", devtypename, id)); + AddAlias(string_format("SCSI%u", id)); + AddAlias(string_format("%s%u", devtypename, id)); switch (devtype) { case SCSI::DevType::HD: @@ -639,7 +535,7 @@ SCSIDisk::SCSIDisk(SCSIHostDevice *h, in blocksize = 512; break; default: - PANIC("corrupted devtype=%d", (int)devtype); + PANIC("corrupted devtype=%u", (uint)devtype); } } @@ -653,12 +549,8 @@ SCSIDisk::~SCSIDisk() bool SCSIDisk::Init() { - if (inherited::Init() == false) { - return false; - } - - const std::string keybody = string_format("%s-id%d-", - host->GetConfigName(), myid); + const std::string keybody = string_format("%s-id%u-", + bus->GetConfigName(), myid); const std::string imgkey = keybody + "image"; const std::string wikey = keybody + "writeignore"; const std::string wpkey = keybody + "writeprotect"; @@ -683,9 +575,9 @@ SCSIDisk::Init() // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意 if (IsRemovableDevice()) { scheduler->ConnectMessage(MessageID::SCSIDEV_LOAD(myid), this, - ToMessageCallback(&SCSIDisk::LoadCallback)); + ToMessageCallback(&SCSIDisk::LoadMessage)); scheduler->ConnectMessage(MessageID::SCSIDEV_UNLOAD(myid), this, - ToMessageCallback(&SCSIDisk::UnloadCallback)); + ToMessageCallback(&SCSIDisk::UnloadMessage)); } // オープン前に writeignore をチェック。 @@ -720,14 +612,7 @@ SCSIDisk::Init() // 設定時点でファイルが指定されていればオープン if (filename.empty() == false) { - std::string path; - if (filename[0] == '/') { - path = filename; - } else { - // 相対パスなら VM ディレクトリから - path = gMainApp.GetVMDir() + filename; - } - + std::string path = gMainApp.NormalizePath(filename); if (LoadDisk(path) == false) { return false; } @@ -756,14 +641,14 @@ SCSIDisk::ResetHard(bool poweron) } } -// バスリセット +// バスリセットされた (SCSIBus から呼ばれる) void -SCSIDisk::BusResetCB() +SCSIDisk::OnBusReset() { // メディアの取り出し禁止を解除 PreventMediumRemoval(false); - inherited::BusResetCB(); + inherited::OnBusReset(); } // バックエンドのディスクイメージを開く。 @@ -774,7 +659,7 @@ SCSIDisk::LoadDisk(const std::string& pa { bool unloaded; off_t size; - bool w_ok; + int w_ok; bool read_only; // すでにあればクローズ (ここでは通知は行わない) @@ -786,13 +671,39 @@ SCSIDisk::LoadDisk(const std::string& pa goto done; } - if (image.GetInfo(&size, &w_ok) == false) { + w_ok = image.IsWriteable(); + if (w_ok < 0) { goto done; } + // 書き込みモードをここで確定 + if (IsWriteableDevice()) { + // 書き込み可能デバイスの場合 + if (w_ok) { + if (write_ignore) { + write_mode = RW::WriteIgnore; + } else { + write_mode = RW::Writeable; + } + } else { + write_mode = RW::WriteProtect; + } + } else { + // 読み込み専用デバイスの場合 + write_mode = RW::ReadOnly; + } + + // ここでオープン + read_only = (GetWriteMode() != RW::Writeable); + if (image.Open(read_only, write_ignore) == false) { + goto done; + } + + size = image.GetSize(); + // セクタ単位になっていること if (size % blocksize != 0) { - warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%d)", + warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%u)", pathname.c_str(), (uintmax_t)size, blocksize); goto done; } @@ -835,30 +746,7 @@ SCSIDisk::LoadDisk(const std::string& pa } break; default: - PANIC("corrupted devtype=%d", (int)devtype); - } - - // 書き込みモードをここで確定 - if (IsWriteableDevice()) { - // 書き込み可能デバイスの場合 - if (w_ok) { - if (write_ignore) { - write_mode = RW::WriteIgnore; - } else { - write_mode = RW::Writeable; - } - } else { - write_mode = RW::WriteProtect; - } - } else { - // 読み込み専用デバイスの場合 - write_mode = RW::ReadOnly; - } - - // ここでオープン - read_only = (GetWriteMode() != RW::Writeable); - if (image.Open(read_only, write_ignore) == false) { - goto done; + PANIC("corrupted devtype=%u", (uint)devtype); } // 書き込み無視なら一応ログ出力 @@ -957,7 +845,7 @@ SCSIDisk::UnloadDiskUI(bool force) // メディア挿入メッセージコールバック void -SCSIDisk::LoadCallback(MessageID msgid, uint32 arg) +SCSIDisk::LoadMessage(MessageID msgid, uint32 arg) { if (LoadDisk(new_pathname) == false) { // 失敗したら UI に通知 @@ -967,7 +855,7 @@ SCSIDisk::LoadCallback(MessageID msgid, // メディア排出メッセージコールバック void -SCSIDisk::UnloadCallback(MessageID msgid, uint32 arg) +SCSIDisk::UnloadMessage(MessageID msgid, uint32 arg) { bool force = arg; UnloadDisk(force); @@ -1050,7 +938,7 @@ SCSIDisk::DispatchCmd() // Inquiry データを返す bool -SCSIDisk::Inquiry(std::vector& buf, int lun) +SCSIDisk::Inquiry(std::vector& buf, uint lun) { uint8 inqtype; const char *prodname; @@ -1069,7 +957,7 @@ SCSIDisk::Inquiry(std::vector& bu prodname = "SCSIMO"; break; default: - PANIC("Unexpected devtype=%d", (int)devtype); + PANIC("Unexpected devtype=%u", (uint)devtype); } buf[0] = inqtype;