--- nono/vm/scsidev.cpp 2026/04/29 17:04:59 1.1.1.11 +++ nono/vm/scsidev.cpp 2026/04/29 17:05:38 1.1.1.19 @@ -4,6 +4,10 @@ // Licensed under nono-license.txt // +// +// SCSI デバイス +// + // ログレベルは // 1: SCSI コマンドの概要。 // デバイスの動作のうち大きな変更のもの @@ -16,6 +20,7 @@ #include "scsidev.h" #include "config.h" #include "mainapp.h" +#include "scheduler.h" #include "uimessage.h" // @@ -23,9 +28,10 @@ // // コンストラクタ -SCSIDevice::SCSIDevice(const std::string& objname_) - : inherited(objname_) +SCSIDevice::SCSIDevice(uint objid_) + : inherited(objid_) { + myid = -1; } // デストラクタ @@ -33,24 +39,31 @@ SCSIDevice::~SCSIDevice() { } +// このデバイスがバスに接続された (SCSIBus::AttachDevice() から呼ばれる) +void +SCSIDevice::OnAttached(SCSIBus *bus_, uint myid_) +{ + bus = bus_; + myid = myid_; +} + + // -// SCSI ホストデバイス +// SCSI ホストデバイス (イニシエータ) // // コンストラクタ -SCSIHostDevice::SCSIHostDevice(const std::string& objname_) - : inherited(objname_) +SCSIHostDevice::SCSIHostDevice(IODevice *parent_, const char *config_name_) + : inherited(OBJ_SCSI) { + parent = parent_; + // イニシエータの ID は実行時にコントローラによって決まる。 devtype = SCSI::DevType::Initiator; - monitor_devs.func = (MonitorCallback_t)&SCSIHostDevice::MonitorUpdateDevs; - // サイズは Create で決まる - monitor_devs.Regist(ID_MONITOR_SCSIDEVS); - // バスデバイスを作成する。 // ただし自身がバスに参加するのは継承側で行う // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。 - scsibus.reset(new SCSIBus()); + scsibus.reset(new SCSIBus(this, config_name_)); } // デストラクタ @@ -58,91 +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() { - // 初期化ではないけどログレベルがセットできたここでデバッグ表示。 - // -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(80, (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) { // 実際にはバスフリーを検出したデバイスは各自自身が送出している信号を // 下げるのだが、色々面倒なので、ここではホストのバスフリーコールバックが @@ -151,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(); @@ -167,104 +127,30 @@ SCSIHostDevice::BusFreeCB(int id) } SetXfer(0); SetData(0); -} -// SCSI デバイスモニタ -// (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので -// とりあえずここで) -void -SCSIHostDevice::MonitorUpdateDevs(Monitor *, TextScreen& screen) -{ - int x; - int y; - - 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; - } + // 親のバスフリーコールバックを呼ぶ + (parent->*(BusFreeCallback))(id); +} - // 1列目 - x = 5; - screen.Print(x, y, - (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), - "MediumLoaded %s", - disk->IsMediumLoaded() ? "On" : "Off"); - screen.Print(x, y + 1, - (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), - "RemovalPrevent %s", - disk->IsMediumRemovalPrevented() ? "On" : "Off"); - - // 2列目 - x = 29; - const char *wstr; - switch (disk->GetWriteMode()) { - case SCSIDisk::RW::ReadOnly: wstr = "ReadOnly"; break; - case SCSIDisk::RW::WriteProtect: wstr = "WriteProtected"; break; - case SCSIDisk::RW::Writeable: wstr = "Writeable"; break; - case SCSIDisk::RW::WriteIgnore: wstr = "WriteIgnored"; break; - default: __unreachable(); - } - screen.Puts(x, y, - (disk->IsMediumLoaded() ? TA::Normal : TA::Disable), wstr); +// ターゲットがセレクションに応答した (SCSIBus から呼ばれる) +void +SCSIHostDevice::OnSelectionAck() +{ + // ターゲットが BSY を立てて応答したので、 + // こちらはデータバスをクリアして SEL を下げる。 + // これによりセレクションフェーズは成功で完了する。 + SetData(0); + NegateSEL(); - // 3列目 - x = 50; - uint64 v = disk->GetSize(); - std::string bytes; - if (v < 1000) { - bytes = string_format("%u", (uint32)v); - } else if (v < 1000 * 1000) { - bytes = string_format("%u,%03u", - (uint)(v / 1000), - (uint)(v % 1000)); - } else if (v < 1000 * 1000 * 1000) { - bytes = string_format("%u,%03u,%03u", - (uint)(v / 1000 / 1000), - (uint)((v / 1000) % 1000), - (uint)(v % 1000)); - } else if (v < 1000ULL * 1000 * 1000 * 1000) { - bytes = string_format("%u,%03u,%03u,%03u", - (uint)(v / 1000 / 1000 / 1000), - (uint)((v / 1000 / 1000) % 1000), - (uint)((v / 1000) % 1000), - (uint)(v % 1000)); - } else { - bytes = string_format("%ju", (uintmax_t)v); - } - screen.Print(x, y, "TotalBytes %s", bytes.c_str()); - screen.Print(x, y + 1, "LogicalBlock %d", disk->GetBlocksize()); + // 親のバスフリーコールバックを呼ぶ + (parent->*(SelectionAckCallback))(); +} - y += 3; - } +// ターゲットが REQ を立てた (SCSIBus から呼ばれる) +void +SCSIHostDevice::OnTransferReq() +{ + (parent->*(TransferReqCallback))(); } @@ -272,12 +158,11 @@ SCSIHostDevice::MonitorUpdateDevs(Monito // SCSI ターゲットデバイスの共通部分 // -// コンストラクタ -SCSITarget::SCSITarget(const std::string& objname_, SCSIHostDevice *h, int id) - : inherited(objname_) +// コンストラクタ。 +SCSITarget::SCSITarget(SCSIBus *bus_, uint id) + : inherited(OBJ_NONE) // オブジェクト ID で検索することはない { - // ホストへのリンク (所有関係のないポインタ) - host = h; + bus = bus_; myid = id; } @@ -287,22 +172,22 @@ SCSITarget::~SCSITarget() } // 電源オン -bool -SCSITarget::PowerOn() +void +SCSITarget::ResetHard(bool poweron) { - ClearSense(); - cmdseq.clear(); - cmdlen = 0; - cmd.reset(); - bytecount = 0; - lastbyte = false; - - return true; + if (poweron) { + ClearSense(); + cmdseq.clear(); + cmdlen = 0; + cmd.reset(); + bytecount = 0; + lastbyte = false; + } } // バスリセットされた (SCSIBus から呼ばれる) void -SCSITarget::BusResetCB() +SCSITarget::OnBusReset() { // センスキーをクリア ClearSense(); @@ -326,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"); @@ -349,7 +234,7 @@ SCSITarget::StartTransferCB() // 情報転送フェーズ間の遷移について // -// 情報転送フェーズは TransferCB() で1バイトずつ転送を行い、所定のバイト数 +// 情報転送フェーズは OnTransfer() で1バイトずつ転送を行い、所定のバイト数 // の転送が完了すればフェーズを遷移する。遷移方法は入出力方向別で6通り。 // o IN -> IN // o IN -> OUT @@ -358,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 に応答 @@ -385,7 +270,7 @@ SCSITarget::StartTransferCB() // IN (ターゲット → イニシエータ方向) なら、ターゲットがデータをデータバスに // 乗せて REQ を上げたのをイニシエータが受信した後でこれをコールしてくる。 void -SCSITarget::TransferCB() +SCSITarget::OnTransfer() { uint8 data; SCSI::XferPhase xfer; @@ -399,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バイト目ならこのコマンドが何バイトか調べる @@ -446,7 +331,7 @@ SCSITarget::TransferCB() } else { // IN 方向 (ターゲットが送信側) // bytecount は加算方向。 - // データは前のループの終わり (TransferAckCB()の下のほう) で + // データは前のループの終わり (OnTransferAck()の下のほう) で // セットしているのでここでは引き取られた分をカウントする。 bytecount++; if (bytecount >= cmd->buf.size()) { @@ -459,7 +344,7 @@ SCSITarget::TransferCB() // 情報転送フェーズでイニシエータが ACK を下げた。(SCSIBus から呼ばれる) void -SCSITarget::TransferAckCB() +SCSITarget::OnTransferAck() { SCSI::XferPhase xfer = GetXfer(); @@ -487,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)); @@ -516,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); } } } @@ -592,22 +477,32 @@ SCSITarget::DispatchCmd() if (cname) { name = string_format(" %s", cname); } - putlog(0, "未実装 SCSI コマンド $%02x%s len=%d", - 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; } } +// コマンド列を指定して対応するコマンドを選択する。なければ NULL を返す。 +// ROM30 エミュレーション用。 +SCSICmd * +SCSITarget::SelectCommand(const std::vector& cmdseq_) +{ + cmdseq = cmdseq_; + DispatchCmd(); + return cmd.get(); +} + // // SCSI Disk // // コンストラクタ -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_; @@ -617,10 +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(GetName()); - 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,14 +535,9 @@ SCSIDisk::SCSIDisk(SCSIHostDevice *h, in blocksize = 512; break; default: - __unreachable(); + PANIC("corrupted devtype=%u", (uint)devtype); } - // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意 - if (IsRemovableDevice()) { - event = new Event(this); - event->SetName(GetName() + " MediaChange"); - } } // デストラクタ @@ -658,8 +549,8 @@ SCSIDisk::~SCSIDisk() bool SCSIDisk::Init() { - 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"; @@ -681,6 +572,14 @@ SCSIDisk::Init() return false; } + // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意 + if (IsRemovableDevice()) { + scheduler->ConnectMessage(MessageID::SCSIDEV_LOAD(myid), this, + ToMessageCallback(&SCSIDisk::LoadMessage)); + scheduler->ConnectMessage(MessageID::SCSIDEV_UNLOAD(myid), this, + ToMessageCallback(&SCSIDisk::UnloadMessage)); + } + // オープン前に writeignore をチェック。 // この後パス名を処理するとすぐに UI に通知を出すため、それより前で // 行わないといけない。うーん。 @@ -713,7 +612,7 @@ SCSIDisk::Init() // 設定時点でファイルが指定されていればオープン if (filename.empty() == false) { - const std::string path = gMainApp.GetVMDir() + filename; + std::string path = gMainApp.NormalizePath(filename); if (LoadDisk(path) == false) { return false; } @@ -731,27 +630,25 @@ SCSIDisk::Init() } // 電源オン -bool -SCSIDisk::PowerOn() +void +SCSIDisk::ResetHard(bool poweron) { - if (inherited::PowerOn() == false) { - return false; - } - - // メディアの取り出し禁止を解除 - PreventMediumRemoval(false); + inherited::ResetHard(poweron); - return true; + if (poweron) { + // メディアの取り出し禁止を解除 + PreventMediumRemoval(false); + } } -// バスリセット +// バスリセットされた (SCSIBus から呼ばれる) void -SCSIDisk::BusResetCB() +SCSIDisk::OnBusReset() { // メディアの取り出し禁止を解除 PreventMediumRemoval(false); - inherited::BusResetCB(); + inherited::OnBusReset(); } // バックエンドのディスクイメージを開く。 @@ -760,29 +657,53 @@ SCSIDisk::BusResetCB() bool SCSIDisk::LoadDisk(const std::string& pathname_) { - bool changed = false; + bool unloaded; off_t size; - bool w_ok; + int w_ok; bool read_only; // すでにあればクローズ (ここでは通知は行わない) - changed = UnloadDisk_internal(); + unloaded = UnloadDisk_internal(); // 新しいイメージをオープン(する準備) pathname = pathname_; - try { - image.reset(DiskImage::Create(pathname)); - } catch (...) { + if (image.CreateHandler(pathname) == false) { goto done; } - if (image->GetInfo(&size, &w_ok) < 0) { + 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; } @@ -825,30 +746,7 @@ SCSIDisk::LoadDisk(const std::string& pa } break; default: - __unreachable(); - } - - // 書き込みモードをここで確定 - 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) == false) { - goto done; + PANIC("corrupted devtype=%u", (uint)devtype); } // 書き込み無視なら一応ログ出力 @@ -857,21 +755,21 @@ SCSIDisk::LoadDisk(const std::string& pa } medium_loaded = true; - changed = true; -done: - if (changed) { - // HD 初期化時はログに出さない - if (IsRemovableDevice()) { - putlog(1, "Medium loaded"); - } - - // 変化があれば通知 - MediaChanged(); + // リムーバブルデバイスでだけログを出す + if (IsRemovableDevice()) { + putlog(1, "Medium loaded"); } + + done: if (medium_loaded == false) { // ここで状態をクリアする Clear(); } + + // 変化があれば通知 + if (unloaded || medium_loaded) { + MediaChanged(); + } return medium_loaded; } @@ -916,7 +814,7 @@ void SCSIDisk::Clear() { medium_loaded = false; - image.reset(); + image.Close(); pathname.clear(); write_mode = RW::Writeable; } @@ -928,15 +826,13 @@ SCSIDisk::MediaChanged() const UIMessage::Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID()); } - // メディアを挿入する。 // UI スレッドで実行されるので、VM スレッドに通知するだけ。 void SCSIDisk::LoadDiskUI(const std::string& pathname_) { new_pathname = pathname_; - event->func = (DeviceCallback_t)&SCSIDisk::LoadCallback; - event->Start(); + scheduler->SendMessage(MessageID::SCSIDEV_LOAD(GetMyID())); } // メディアを排出する。 @@ -944,14 +840,12 @@ SCSIDisk::LoadDiskUI(const std::string& void SCSIDisk::UnloadDiskUI(bool force) { - event->func = (DeviceCallback_t)&SCSIDisk::UnloadCallback; - event->code = force; - event->Start(); + scheduler->SendMessage(MessageID::SCSIDEV_UNLOAD(GetMyID()), force); } -// メディア挿入コールバック +// メディア挿入メッセージコールバック void -SCSIDisk::LoadCallback(Event& ev) +SCSIDisk::LoadMessage(MessageID msgid, uint32 arg) { if (LoadDisk(new_pathname) == false) { // 失敗したら UI に通知 @@ -959,11 +853,11 @@ SCSIDisk::LoadCallback(Event& ev) } } -// メディア排出コールバック +// メディア排出メッセージコールバック void -SCSIDisk::UnloadCallback(Event& ev) +SCSIDisk::UnloadMessage(MessageID msgid, uint32 arg) { - bool force = ev.code; + bool force = arg; UnloadDisk(force); } @@ -1044,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; @@ -1063,7 +957,7 @@ SCSIDisk::Inquiry(std::vector& bu prodname = "SCSIMO"; break; default: - assert(false); + PANIC("Unexpected devtype=%u", (uint)devtype); } buf[0] = inqtype; @@ -1127,7 +1021,7 @@ SCSIDisk::Read(std::vector& buf, if (__predict_false((bool)image == false)) { return false; } - return image->Read(buf.data(), start, buf.size()); + return image.Read(buf.data(), start, buf.size()); } bool @@ -1137,15 +1031,11 @@ SCSIDisk::Write(const std::vector return false; } - if (GetWriteMode() == RW::Writeable) { - return image->Write(buf.data(), start, buf.size()); - } else if (GetWriteMode() == RW::WriteIgnore) { - // 書き込み無視 - return true; - } else { + if (GetWriteMode() >= RW::WriteProtect) { // 書き込み不可 return false; } + return image.Write(buf.data(), start, buf.size()); } off_t @@ -1154,7 +1044,7 @@ SCSIDisk::GetSize() const if (__predict_false((bool)image == false)) { return 0; } - return image->GetSize(); + return image.GetSize(); } // 指定の場所を自由に読み出す @@ -1165,11 +1055,7 @@ SCSIDisk::Peek(void *buf, uint64 start, if (__predict_false((bool)image == false)) { return false; } - - if (start + len > GetSize()) { - return false; - } - return image->Read(buf, start, len); + return image.Read(buf, start, len); } // メディアの取り出し可否(取り出し禁止)を設定する @@ -1184,3 +1070,17 @@ SCSIDisk::PreventMediumRemoval(bool val) } medium_removal_prevented = val; } + +// メディアの書き込みモードを文字列で返す +const char * +SCSIDisk::GetWriteModeStr() const +{ + switch (write_mode) { + case RW::ReadOnly: return "ReadOnly"; + case RW::WriteProtect: return "WriteProtected"; + case RW::Writeable: return "Writeable"; + case RW::WriteIgnore: return "WriteIgnore"; + default: + PANIC("corrupted write_mode=%d", (int)write_mode); + } +}