Annotation of nono/vm/scsidev.cpp, revision 1.1.1.13

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
1.1.1.11  root        7: // ログレベルは
                      8: // 1: SCSI コマンドの概要。
                      9: //    デバイスの動作のうち大きな変更のもの
                     10: // 2: SCSI コマンドの詳細、ダンプ。
                     11: //    デバイスの動作のうち些細なもの、あるいは頻繁なもの
                     12: // 3: フェーズ遷移
                     13: // 4: データも含める
1.1       root       14: 
1.1.1.11  root       15: #include "scsicmd.h"
1.1       root       16: #include "scsidev.h"
1.1.1.2   root       17: #include "config.h"
                     18: #include "mainapp.h"
1.1.1.11  root       19: #include "uimessage.h"
1.1       root       20: 
                     21: //
1.1.1.11  root       22: // SCSI デバイス共通部分
1.1       root       23: //
                     24: 
                     25: // コンストラクタ
1.1.1.10  root       26: SCSIDevice::SCSIDevice(const std::string& objname_)
                     27:        : inherited(objname_)
1.1       root       28: {
                     29: }
                     30: 
                     31: // デストラクタ
                     32: SCSIDevice::~SCSIDevice()
                     33: {
                     34: }
                     35: 
                     36: //
                     37: // SCSI ホストデバイス
                     38: //
                     39: 
                     40: // コンストラクタ
1.1.1.10  root       41: SCSIHostDevice::SCSIHostDevice(const std::string& objname_)
                     42:        : inherited(objname_)
1.1       root       43: {
1.1.1.5   root       44:        devtype = SCSI::DevType::Initiator;
                     45: 
1.1.1.11  root       46:        monitor_devs.func = (MonitorCallback_t)&SCSIHostDevice::MonitorUpdateDevs;
                     47:        // サイズは Create で決まる
                     48:        monitor_devs.Regist(ID_MONITOR_SCSIDEVS);
                     49: 
1.1       root       50:        // バスデバイスを作成する。
                     51:        // ただし自身がバスに参加するのは継承側で行う
                     52:        // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。
                     53:        scsibus.reset(new SCSIBus());
                     54: }
                     55: 
                     56: // デストラクタ
                     57: SCSIHostDevice::~SCSIHostDevice()
                     58: {
                     59: }
                     60: 
1.1.1.2   root       61: // 設定ファイルに使われるこのホストデバイスを示す接頭語を設定する
                     62: // (継承クラス側のコンストラクタで config_name を設定後に呼ぶこと)
                     63: void
                     64: SCSIHostDevice::SetConfigName(const char *name_)
                     65: {
                     66:        config_name = name_;
                     67: }
                     68: 
1.1       root       69: bool
                     70: SCSIHostDevice::Create()
                     71: {
                     72:        // 設定を読み込む。
                     73:        for (int id = 0; id < 8; id++) {
1.1.1.2   root       74:                const std::string key = string_format("%s-id%d-image",
1.1       root       75:                        GetConfigName(), id);
1.1.1.3   root       76:                const ConfigItem& item = gConfig->Find(key);
1.1.1.2   root       77:                const std::string& image = item.AsString();
                     78: 
                     79:                // 空ならデバイスなし
                     80:                if (image.length() == 0) {
1.1.1.5   root       81:                        // 未接続部分のパラメータは減らしておくか。
                     82:                        // --show-config するとさすがに無駄に多くて邪魔なので。
1.1.1.11  root       83:                        const std::string wikey = string_format("%s-id%d-writeignore",
                     84:                                GetConfigName(), id);
1.1.1.5   root       85:                        const std::string wpkey = string_format("%s-id%d-writeprotect",
                     86:                                GetConfigName(), id);
                     87:                        const std::string stkey = string_format("%s-id%d-seektime",
                     88:                                GetConfigName(), id);
1.1.1.11  root       89:                        gConfig->Delete(wikey);
1.1.1.5   root       90:                        gConfig->Delete(wpkey);
                     91:                        gConfig->Delete(stkey);
1.1.1.2   root       92:                        continue;
                     93:                }
                     94: 
                     95:                // ',' より前がデバイス種別 (ここではパスは不要)
1.1.1.11  root       96:                auto v = string_split(image, ',');
                     97:                // 比較のため前後の空白を取り除いて小文字にする
                     98:                std::string type = string_trim(v[0]);
                     99:                type = string_tolower(type);
1.1.1.2   root      100: 
1.1.1.11  root      101:                SCSI::DevType target_devtype;
1.1.1.2   root      102:                if (type == "hd") {
1.1.1.11  root      103:                        target_devtype = SCSI::DevType::HD;
                    104:                } else if (type == "cd") {
                    105:                        target_devtype = SCSI::DevType::CD;
                    106:                } else if (type == "mo") {
                    107:                        target_devtype = SCSI::DevType::MO;
1.1.1.2   root      108:                } else {
                    109:                        item.Err("invaild device type '%s'", type.c_str());
1.1.1.11  root      110:                        return false;
1.1       root      111:                }
1.1.1.11  root      112:                target[id].reset(new SCSIDisk(this, id, target_devtype));
                    113:                scsibus->Attach(target[id].get(), id);
1.1       root      114:        }
                    115: 
                    116:        return true;
                    117: }
                    118: 
1.1.1.11  root      119: // 初期化。
                    120: // 継承クラスが connected_devices をセットした後で呼ぶこと。
1.1       root      121: bool
                    122: SCSIHostDevice::Init()
                    123: {
                    124:        // 初期化ではないけどログレベルがセットできたここでデバッグ表示。
                    125:        // -L オプションの処理は Create() の後なので。
                    126:        if (loglevel >= 1) {
                    127:                for (int id = 0; id < 8; id++) {
                    128:                        std::string typestr;
                    129:                        if (target[id]) {
1.1.1.11  root      130:                                typestr = SCSI::GetDevTypeName(target[id]->GetDevType());
1.1.1.8   root      131:                                putmsgn("%s target #%d %s", GetConfigName(), id,
1.1       root      132:                                        typestr.c_str());
                    133:                        }
                    134:                }
                    135:        }
                    136: 
1.1.1.11  root      137:        // connected_devices によってモニタの大きさを決定。
                    138:        monitor_devs.SetSize(80, (connected_devices.size() - 1) * 4 + 1);
                    139: 
1.1       root      140:        return true;
                    141: }
                    142: 
                    143: // バスフリーコールバック (SCSIBus から呼ばれる)
                    144: void
                    145: SCSIHostDevice::BusFreeCB(int id)
                    146: {
                    147:        // 実際にはバスフリーを検出したデバイスは各自自身が送出している信号を
                    148:        // 下げるのだが、色々面倒なので、ここではホストのバスフリーコールバックが
                    149:        // 一括して全部の線を下げる。
                    150:        // ただしさすがにログがうるさいので、上がってる線だけ下げる。
                    151: 
                    152:        uint32 data;
                    153:        while ((data = GetBSY()) != 0) {
                    154:                bus->NegateBSY(DecodeID(data));
                    155:        }
                    156:        while ((data = GetSEL()) != 0) {
                    157:                bus->NegateSEL(DecodeID(data));
                    158:        }
                    159:        if (GetREQ()) {
                    160:                NegateREQ();
                    161:        }
                    162:        if (GetACK()) {
                    163:                NegateACK();
                    164:        }
                    165:        if (GetATN()) {
                    166:                NegateATN();
                    167:        }
                    168:        SetXfer(0);
                    169:        SetData(0);
                    170: }
                    171: 
1.1.1.11  root      172: // SCSI デバイスモニタ
                    173: // (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので
                    174: // とりあえずここで)
                    175: void
                    176: SCSIHostDevice::MonitorUpdateDevs(Monitor *, TextScreen& screen)
1.1       root      177: {
1.1.1.11  root      178:        int x;
                    179:        int y;
1.1       root      180: 
1.1.1.11  root      181:        screen.Clear();
1.1.1.4   root      182: 
1.1.1.11  root      183:        y = 0;
                    184:        for (const auto dev : connected_devices) {
                    185:                std::string desc = SCSI::GetDevTypeName(dev->GetDevType());
                    186:                std::string pathname;
                    187: 
                    188:                SCSIDisk *disk = dynamic_cast<SCSIDisk*>(dev);
                    189:                if (disk && disk->IsMediumLoaded()) {
                    190:                        desc += string_format("(%dMB)",
                    191:                                (int)(disk->GetSize() / 1024 / 1024));
                    192:                        pathname = disk->GetPathName();
                    193:                }
                    194:                screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str());
                    195: 
                    196: #if 1
                    197:                (void)pathname;
                    198: #else
                    199:                // パス名の非ASCIIどうするか
                    200:                if (!pathname.empty()) {
                    201:                        if (pathname.size() > screen.GetCol() - 16 - 3) {
                    202:                                pathname = pathname.substr(0, screen.GetCol() - 16 - 3);
                    203:                                pathname += "...";
                    204:                        }
                    205:                        screen.Puts(16, y, pathname.c_str());
1.1.1.4   root      206:                }
1.1.1.11  root      207: #endif
                    208:                y++;
1.1       root      209: 
1.1.1.11  root      210:                // イニシエータならここまで。(今の所イニシエータとディスクしかない)
                    211:                if (disk == NULL) {
                    212:                        continue;
1.1.1.7   root      213:                }
                    214: 
1.1.1.11  root      215:                // 1列目
                    216:                x = 5;
                    217:                screen.Print(x, y,
                    218:                        (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
                    219:                        "MediumLoaded   %s",
                    220:                        disk->IsMediumLoaded() ? "On" : "Off");
                    221:                screen.Print(x, y + 1,
                    222:                        (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
                    223:                        "RemovalPrevent %s",
                    224:                        disk->IsMediumRemovalPrevented() ? "On" : "Off");
                    225: 
                    226:                // 2列目
                    227:                x = 29;
                    228:                const char *wstr;
                    229:                switch (disk->GetWriteMode()) {
                    230:                 case SCSIDisk::RW::ReadOnly:           wstr = "ReadOnly";                      break;
                    231:                 case SCSIDisk::RW::WriteProtect:       wstr = "WriteProtected";        break;
                    232:                 case SCSIDisk::RW::Writeable:          wstr = "Writeable";                     break;
                    233:                 case SCSIDisk::RW::WriteIgnore:        wstr = "WriteIgnored";          break;
                    234:                 default: __unreachable();
                    235:                }
                    236:                screen.Puts(x, y,
                    237:                        (disk->IsMediumLoaded() ? TA::Normal : TA::Disable), wstr);
                    238: 
                    239:                // 3列目
                    240:                x = 50;
                    241:                uint64 v = disk->GetSize();
                    242:                std::string bytes;
                    243:                if (v < 1000) {
                    244:                        bytes = string_format("%u", (uint32)v);
                    245:                } else if (v < 1000 * 1000) {
                    246:                        bytes = string_format("%u,%03u",
                    247:                                (uint)(v / 1000),
                    248:                                (uint)(v % 1000));
                    249:                } else if (v < 1000 * 1000 * 1000) {
                    250:                        bytes = string_format("%u,%03u,%03u",
                    251:                                (uint)(v / 1000 / 1000),
                    252:                                (uint)((v / 1000) % 1000),
                    253:                                (uint)(v % 1000));
                    254:                } else if (v < 1000ULL * 1000 * 1000 * 1000) {
                    255:                        bytes = string_format("%u,%03u,%03u,%03u",
                    256:                                (uint)(v / 1000 / 1000 / 1000),
                    257:                                (uint)((v / 1000 / 1000) % 1000),
                    258:                                (uint)((v / 1000) % 1000),
                    259:                                (uint)(v % 1000));
                    260:                } else {
                    261:                        bytes = string_format("%ju", (uintmax_t)v);
1.1.1.7   root      262:                }
1.1.1.11  root      263:                screen.Print(x, y,     "TotalBytes   %s", bytes.c_str());
                    264:                screen.Print(x, y + 1, "LogicalBlock %d", disk->GetBlocksize());
1.1.1.7   root      265: 
1.1.1.11  root      266:                y += 3;
1.1       root      267:        }
1.1.1.11  root      268: }
1.1       root      269: 
                    270: 
                    271: //
                    272: // SCSI ターゲットデバイスの共通部分
                    273: //
                    274: 
                    275: // コンストラクタ
1.1.1.11  root      276: SCSITarget::SCSITarget(const std::string& objname_, SCSIHostDevice *h, int id)
1.1.1.10  root      277:        : inherited(objname_)
1.1       root      278: {
                    279:        // ホストへのリンク (所有関係のないポインタ)
                    280:        host = h;
                    281:        myid = id;
                    282: }
                    283: 
                    284: // デストラクタ
                    285: SCSITarget::~SCSITarget()
                    286: {
                    287: }
                    288: 
1.1.1.9   root      289: // 電源オン
                    290: bool
                    291: SCSITarget::PowerOn()
                    292: {
                    293:        ClearSense();
                    294:        cmdseq.clear();
                    295:        cmdlen = 0;
                    296:        cmd.reset();
                    297:        bytecount = 0;
                    298:        lastbyte = false;
                    299: 
                    300:        return true;
                    301: }
                    302: 
1.1.1.4   root      303: // バスリセットされた (SCSIBus から呼ばれる)
                    304: void
                    305: SCSITarget::BusResetCB()
                    306: {
1.1.1.11  root      307:        // センスキーをクリア
                    308:        ClearSense();
                    309: 
1.1.1.5   root      310:        // リセットされたので持ってる信号を全部解放。
                    311:        // 一応自分が変えていい信号線だけを落とす。
                    312:        switch (GetPhase()) {
                    313:         case SCSI::Phase::BusFree:
                    314:                break;
                    315:         case SCSI::Phase::Arbitration:
                    316:         case SCSI::Phase::Selection:
                    317:         case SCSI::Phase::Reselection:
                    318:                NegateSEL();
                    319:                NegateBSY();
                    320:                break;
                    321:         case SCSI::Phase::Transfer:
                    322:                NegateBSY();
                    323:                break;
                    324:        }
1.1.1.4   root      325: }
                    326: 
1.1       root      327: // セレクションで選択された (SCSIBus から呼ばれる)
                    328: void
                    329: SCSITarget::SelectionCB()
                    330: {
                    331:        // ターゲットが、自身が選択されていることを認識してから BSY を
                    332:        // 立てるまでの時間はもうバス側でカウントしてあるので、ここでは
                    333:        // ただちに BSY を上げてよい。
                    334:        putlog(4, "Selection Callback");
                    335:        AssertBSY();
                    336: }
                    337: 
                    338: // 情報転送フェーズを開始する (SCSIBus から呼ばれる)
                    339: void
                    340: SCSITarget::StartTransferCB()
                    341: {
                    342:        // 最初はコマンドフェーズ。(ATN はとりあえず放置)
                    343:        putlog(4, "Start Command Phase");
                    344:        cmdseq.clear();
                    345:        cmdlen = 0;
                    346:        SetXfer(SCSI::XferPhase::Command);
                    347:        AssertREQ();
                    348: }
                    349: 
                    350: // 情報転送フェーズ間の遷移について
                    351: //
                    352: // 情報転送フェーズは TransferCB() で1バイトずつ転送を行い、所定のバイト数
                    353: // の転送が完了すればフェーズを遷移する。遷移方法は入出力方向別で6通り。
                    354: //  o IN  -> IN
                    355: //  o IN  -> OUT
                    356: //  o IN  -> END
                    357: //  o OUT -> OUT
                    358: //  o OUT -> IN
                    359: //  o OUT -> END
                    360: //
                    361: // IN -> IN の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを
                    362: // 変更して REQ を立てるところまで。
                    363: //
                    364: // IN -> OUT の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを
                    365: // 変更して (おそらく {Phase}Begin() を発行してから)、データをセットし REQ
                    366: // を立てる?
                    367: //
                    368: // IN -> END の遷移。TransferCB() で前フェーズの最終バイト受信後、BSY を
                    369: // 下げて終了。
                    370: //
                    371: // OUT -> OUT の遷移。前フェーズの最終バイトをイニシエータが受信後に
                    372: // TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
                    373: // を発行してから)、データをセットし REQ を立てる?
                    374: //
                    375: // OUT -> IN の遷移。前フェーズの最終バイトをイニシエータが受信後に
                    376: // TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
                    377: // を発行してから)、REQ を立てる?
                    378: //
                    379: // OUT -> END の遷移。前フェーズの最終バイトをイニシエータが受信後に
                    380: // TransferCB() が呼ばれるので、BSY を下げる?
                    381: 
                    382: // 情報転送フェーズでイニシエータが ACK を上げた。(SCSIBus から呼ばれる)
                    383: // OUT (イニシエータ → ターゲット方向) なら、ターゲットが上げた REQ に応答
                    384: // してイニシエータがデータをデータバスに乗せてからこれをコールしてくる。
                    385: // IN (ターゲット → イニシエータ方向) なら、ターゲットがデータをデータバスに
                    386: // 乗せて REQ を上げたのをイニシエータが受信した後でこれをコールしてくる。
                    387: void
                    388: SCSITarget::TransferCB()
                    389: {
                    390:        uint8 data;
                    391:        SCSI::XferPhase xfer;
                    392: 
                    393:        // このフェーズの最終バイトか
                    394:        lastbyte = false;
                    395: 
                    396:        // このフェーズの1バイト処理
                    397:        xfer = GetXfer();
                    398:        if (xfer == SCSI::XferPhase::Command) {
                    399:                // コマンドフェーズだけいろいろ特殊なので別処理。
                    400:                // 受信バッファが異なる、1バイト目で受信長が決まるなど。
                    401:                data = GetData();
                    402:                putlog(4, "TransferCB: cmdseq[%02d] = $%02x",
                    403:                        (int)cmdseq.size(), data);
                    404:                cmdseq.push_back(data);
                    405:                if (cmdseq.size() == 1) {
                    406:                        // 1バイト目ならこのコマンドが何バイトか調べる
                    407:                        switch (cmdseq[0] >> 5) {
                    408:                         case 0:        cmdlen =  6;    break;
                    409:                         case 1:        cmdlen = 10;    break;
                    410:                         case 2:        cmdlen = 10;    break;
                    411:                         case 5:        cmdlen = 12;    break;
                    412:                         default:
                    413:                                // 3,4 はリサーブ、6,7 はベンダー定義。XXX 来たらどうする?
                    414:                                cmdlen = 6;
                    415:                                break;
                    416:                        }
                    417:                } else if (cmdseq.size() >= cmdlen) {
1.1.1.11  root      418:                        if (loglevel >= 2
                    419: #if defined(NO_READWRITE_LOG)
                    420:                                && (cmdseq[0] != SCSI::Command::Read6 &&
                    421:                                    cmdseq[0] != SCSI::Command::Read10 &&
                    422:                                    cmdseq[0] != SCSI::Command::Write6 &&
                    423:                                    cmdseq[0] != SCSI::Command::Write10)
                    424: #endif
                    425:                        ) {
1.1       root      426:                                std::string cmdbuf;
                    427:                                const char *name = SCSI::GetCommandName(cmdseq[0]);
                    428:                                cmdbuf += string_format("Command \"%s\"", name ?: "?");
                    429:                                for (const auto& v : cmdseq) {
1.1.1.11  root      430:                                        cmdbuf += string_format(" %02x", v);
1.1       root      431:                                }
1.1.1.8   root      432:                                putlogn("%s", cmdbuf.c_str());
1.1       root      433:                        }
                    434:                        // 最終バイトを受信したらコマンドを選択
                    435:                        DispatchCmd();
                    436:                        lastbyte = true;
                    437:                }
                    438:        } else if (GetIO() == false) {
                    439:                // OUT 方向 (ターゲットが受信側)
                    440:                // bytecount は減算方向。
                    441:                cmd->buf.push_back(GetData());
                    442:                bytecount--;
                    443:                if (bytecount == 0) {
                    444:                        lastbyte = true;
                    445:                }
                    446:        } else {
                    447:                // IN 方向 (ターゲットが送信側)
                    448:                // bytecount は加算方向。
                    449:                // データは前のループの終わり (TransferAckCB()の下のほう) で
                    450:                // セットしているのでここでは引き取られた分をカウントする。
                    451:                bytecount++;
                    452:                if (bytecount >= cmd->buf.size()) {
                    453:                        lastbyte = true;
                    454:                }
                    455:        }
                    456: 
                    457:        NegateREQ();
                    458: }
                    459: 
                    460: // 情報転送フェーズでイニシエータが ACK を下げた。(SCSIBus から呼ばれる)
                    461: void
                    462: SCSITarget::TransferAckCB()
                    463: {
                    464:        SCSI::XferPhase xfer = GetXfer();
                    465: 
                    466:        // これが最終バイトだったら、フェーズ完了 → フェーズ切り替え。
                    467:        if (lastbyte) {
                    468:                SCSI::XferPhase next;
                    469:                // フェーズ完了
                    470:                switch (xfer) {
                    471:                 case SCSI::XferPhase::Command:
                    472:                        next = cmd->Command(cmdseq);
                    473:                        break;
                    474:                 case SCSI::XferPhase::DataOut:
                    475:                        next = cmd->DoneDataOut();
                    476:                        break;
                    477:                 case SCSI::XferPhase::DataIn:
                    478:                        next = cmd->DoneDataIn();
                    479:                        break;
                    480:                 case SCSI::XferPhase::MsgOut:
                    481:                        next = cmd->DoneMsgOut();
                    482:                        break;
                    483:                 case SCSI::XferPhase::MsgIn:
                    484:                        next = cmd->DoneMsgIn();
                    485:                        break;
                    486:                 case SCSI::XferPhase::Status:
                    487:                        next = cmd->DoneStatus();
                    488:                        break;
                    489:                 default:
                    490:                        PANIC("TransferCB unknown xfer done %d", (int)xfer);
                    491:                }
1.1.1.11  root      492:                putlog(3, "%s done", SCSI::GetXferPhaseName(xfer));
1.1       root      493: 
1.1.1.5   root      494:                // 簡略化のため、フェーズ完了時だけ REQ を引き伸ばすことができる
                    495:                if (cmd->optime != 0) {
                    496:                        bus->SetOneshotReqWait(cmd->optime);
                    497:                        cmd->optime = 0;
                    498:                }
                    499: 
1.1       root      500:                if (next != xfer) {
                    501:                        // End ならバスフリーにして終了。
                    502:                        if (next == SCSI::XferPhase::End) {
                    503:                                NegateBSY();
                    504:                                return;
                    505:                        }
                    506: 
                    507:                        // (必要なら)フェーズを更新
                    508:                        SetXfer(next);
                    509:                        xfer = next;
                    510: 
                    511:                        // 新しいフェーズの開始処理
                    512:                        // XXX ここで bytecount とかをリセットできればいいのだが
                    513:                        // Command フェーズで Data フェーズの準備をすることが多いので
                    514:                        // 悩ましい。
                    515:                        switch (xfer) {
                    516:                         case SCSI::XferPhase::DataOut:
                    517:                                cmd->buf.clear();
                    518:                                bytecount = cmd->recvbytes;
                    519:                                putlog(3, "DataOut begin (%d bytes)", bytecount);
                    520:                                break;
                    521:                         case SCSI::XferPhase::DataIn:
                    522:                                bytecount = 0;
                    523:                                putlog(3, "DataIn begin (%d bytes)", (int)cmd->buf.size());
                    524:                                break;
                    525:                         case SCSI::XferPhase::MsgOut:
                    526:                                cmd->BeginMsgOut();
                    527:                                cmd->buf.clear();
                    528:                                bytecount = cmd->recvbytes;
                    529:                                putlog(3, "MsgOut begin (%d bytes)", bytecount);
                    530:                                break;
                    531:                         case SCSI::XferPhase::MsgIn:
                    532:                                cmd->BeginMsgIn();
                    533:                                bytecount = 0;
                    534:                                putlog(3, "MsgIn begin (%d bytes)", (int)cmd->buf.size());
                    535:                                break;
                    536:                         case SCSI::XferPhase::Status:
                    537:                                cmd->BeginStatus();
                    538:                                bytecount = 0;
                    539:                                putlog(3, "Status begin (%d bytes)", (int)cmd->buf.size());
                    540:                                break;
                    541:                         default:
                    542:                                PANIC("TransferCB unknown xfer begin %d", (int)xfer);
                    543:                        }
                    544:                }
                    545:        }
                    546: 
                    547:        // IN 方向 (ターゲットが送信側) ならここでデータをバスにセット。
                    548:        if (GetIO()) {
                    549:                SetData(cmd->buf[bytecount]);
                    550:                putlog(4, "SetData $%02x", GetData());
                    551:        }
                    552: 
                    553:        // 次のバイト転送を行うために REQ を上げる。
                    554:        AssertREQ();
                    555: }
                    556: 
                    557: // SCSI コマンドのディスパッチャ
                    558: void
                    559: SCSITarget::DispatchCmd()
                    560: {
                    561:        switch (cmdseq[0]) {
                    562:         case SCSI::Command::TestUnitReady:     // 0x00
1.1.1.11  root      563:                cmd.reset(new SCSICmd(this));
1.1       root      564:                break;
                    565: 
1.1.1.2   root      566:         case SCSI::Command::RezeroUnit:        // 0x01
1.1.1.11  root      567:                cmd.reset(new SCSICmd(this));
1.1.1.2   root      568:                break;
                    569: 
1.1       root      570:         case SCSI::Command::RequestSense:      // 0x03
                    571:                cmd.reset(new SCSICmdRequestSense(this));
                    572:                break;
                    573: 
                    574:         case SCSI::Command::Inquiry:           // 0x12
                    575:                cmd.reset(new SCSICmdInquiry(this));
                    576:                break;
                    577: 
                    578:         case SCSI::Command::ModeSelect6:       // 0x15
1.1.1.11  root      579:         case SCSI::Command::ModeSelect10:      // 0x55
                    580:                cmd.reset(new SCSICmdModeSelect(this));
1.1       root      581:                break;
                    582: 
1.1.1.2   root      583:         case SCSI::Command::ModeSense6:        // 0x1a
1.1.1.11  root      584:         case SCSI::Command::ModeSense10:       // 0x5a
                    585:                cmd.reset(new SCSICmdModeSense(this));
1.1       root      586:                break;
                    587: 
                    588:         default:
                    589:                // サポートしていないコマンド
                    590:                const char *cname = SCSI::GetCommandName(cmdseq[0]);
                    591:                std::string name;
                    592:                if (cname) {
                    593:                        name = string_format(" %s", cname);
                    594:                }
                    595:                putlog(0, "未実装 SCSI コマンド $%02x%s len=%d",
                    596:                        cmdseq[0], name.c_str(), (int)cmdseq.size());
                    597: 
                    598:                cmd.reset(new SCSICmdNotSupportedCommand(this));
                    599:                break;
                    600:        }
                    601: }
                    602: 
1.1.1.11  root      603: 
1.1       root      604: //
1.1.1.11  root      605: // SCSI Disk
1.1       root      606: //
                    607: 
                    608: // コンストラクタ
1.1.1.11  root      609: SCSIDisk::SCSIDisk(SCSIHostDevice *h, int id, SCSI::DevType devtype_)
                    610:        : inherited("", h, id)
1.1       root      611: {
1.1.1.11  root      612:        devtype = devtype_;
                    613: 
                    614:        const char *devtypename = SCSI::GetDevTypeName(devtype);
                    615: 
                    616:        // コンストラクタの初期化子では devtypename を組み込みにくいので、
                    617:        // コンストラクタ本文でログ名などを設定する。その時の作法に
                    618:        // 従っているのですこし面倒な手法が必要。
                    619:        // lib/object.h 参照。
                    620:        SetName(string_format("SCSI%s%d", devtypename, id));
                    621:        ClearAlias();
1.1.1.12  root      622:        AddAlias(string_format("SCSI%d", id));
1.1.1.11  root      623:        AddAlias(string_format("%s%d", devtypename, id));
                    624: 
                    625:        switch (devtype) {
                    626:         case SCSI::DevType::HD:
                    627:                removable_device = false;
                    628:                writeable_device = true;
                    629:                blocksize = 512;
                    630:                break;
                    631:         case SCSI::DevType::CD:
                    632:                removable_device = true;
                    633:                writeable_device = false;
                    634:                blocksize = 2048;
                    635:                break;
                    636:         case SCSI::DevType::MO:
                    637:                removable_device = true;
                    638:                writeable_device = true;
                    639:                blocksize = 512;
                    640:                break;
                    641:         default:
                    642:                __unreachable();
                    643:        }
1.1.1.10  root      644: 
1.1.1.11  root      645:        // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意
                    646:        if (IsRemovableDevice()) {
                    647:                event = new Event(this);
                    648:                event->SetName(GetName() + " MediaChange");
                    649:        }
1.1       root      650: }
                    651: 
                    652: // デストラクタ
1.1.1.11  root      653: SCSIDisk::~SCSIDisk()
1.1       root      654: {
1.1.1.13! root      655:        if (event) {
        !           656:                delete event;
        !           657:        }
1.1       root      658: }
                    659: 
                    660: // 初期化
                    661: bool
1.1.1.11  root      662: SCSIDisk::Init()
1.1       root      663: {
1.1.1.2   root      664:        const std::string keybody = string_format("%s-id%d-",
1.1       root      665:                host->GetConfigName(), myid);
                    666:        const std::string imgkey = keybody + "image";
1.1.1.11  root      667:        const std::string wikey  = keybody + "writeignore";
1.1       root      668:        const std::string wpkey  = keybody + "writeprotect";
1.1.1.5   root      669:        const std::string stkey  = keybody + "seektime";
1.1       root      670: 
1.1.1.11  root      671:        // デバイス種別(v[0])とパス(v[1])に分解。
                    672:        // デバイス種別はすでにチェックしてあるのでここでは不要。
1.1.1.3   root      673:        const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.11  root      674:        const std::string& imageval = itemimg.AsString();
                    675:        auto v = string_split(imageval, ',', 2);
                    676:        std::string filename;
                    677:        if (v.size() > 1) {
                    678:                filename = string_trim(v[1]);
                    679:        }
                    680: 
                    681:        // 固定デバイス(HD)ならファイル名は省略不可
                    682:        if (!IsRemovableDevice() && filename.empty()) {
1.1.1.2   root      683:                itemimg.Err("imagefile not specified");
1.1       root      684:                return false;
                    685:        }
                    686: 
1.1.1.11  root      687:        // オープン前に writeignore をチェック。
                    688:        // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
                    689:        // 行わないといけない。うーん。
                    690:        //
                    691:        // 変数名は元々 -writeprotect だったが -writeignore に変更した。
                    692:        // 単にリネームするだけだと、開発中に古いバージョンを二分探索とかする
                    693:        // 必要が出来た場合に面倒なので、当面は古いほうも面倒を見る。
                    694:        // 十分いらなくなった頃に削除する。
                    695:        //
                    696:        // wi指定     wp指定
                    697:        // なし               なし  wi を採用。
                    698:        // なし               あり  wp を採用。ただし警告
                    699:        // あり               なし  wi を採用。
                    700:        // あり               あり  wi を採用。ここは警告不要でいいだろう
                    701:        //
                    702:        const ConfigItem& wi = gConfig->Find(wikey);
                    703:        const ConfigItem& wp = gConfig->Find(wpkey);
                    704:        if (wi.GetFrom() == ConfigItem::FromInitial &&
                    705:            wp.GetFrom() != ConfigItem::FromInitial   )
                    706:        {
                    707:                // -writeignore (新)がなく -writeprotect (旧) だけが指定されると警告。
                    708:                // エラー停止まではしなくていいか。
                    709:                warnx("Warning: '%s' is obsolete. Use new '%s'",
                    710:                        wpkey.c_str(), wikey.c_str());
                    711: 
                    712:                write_ignore = wp.AsInt();
                    713:        } else {
                    714:                write_ignore = wi.AsInt();
1.1       root      715:        }
                    716: 
1.1.1.11  root      717:        // 設定時点でファイルが指定されていればオープン
                    718:        if (filename.empty() == false) {
                    719:                const std::string path = gMainApp.GetVMDir() + filename;
                    720:                if (LoadDisk(path) == false) {
                    721:                        return false;
                    722:                }
                    723:        } else {
                    724:                // メディアがロードされないままでも起動時は必ず通知を投げる。
                    725:                // ステータスパネルはこの UIMessage でのみ状態を更新するため。
                    726:                MediaChanged();
1.1       root      727:        }
                    728: 
1.1.1.5   root      729:        // 設定は [msec]、変数は [nsec]。
                    730:        seektime = gConfig->Find(stkey).AsInt();
                    731:        seektime *= 1_msec;
                    732: 
1.1       root      733:        return true;
                    734: }
                    735: 
1.1.1.11  root      736: // 電源オン
                    737: bool
                    738: SCSIDisk::PowerOn()
                    739: {
                    740:        if (inherited::PowerOn() == false) {
                    741:                return false;
                    742:        }
                    743: 
                    744:        // メディアの取り出し禁止を解除
                    745:        PreventMediumRemoval(false);
                    746: 
                    747:        return true;
                    748: }
                    749: 
                    750: // バスリセット
                    751: void
                    752: SCSIDisk::BusResetCB()
                    753: {
                    754:        // メディアの取り出し禁止を解除
                    755:        PreventMediumRemoval(false);
                    756: 
                    757:        inherited::BusResetCB();
                    758: }
                    759: 
                    760: // バックエンドのディスクイメージを開く。
                    761: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
                    762: // のではないが、medium_loaded が同じ値になるので使いまわしている)
                    763: bool
                    764: SCSIDisk::LoadDisk(const std::string& pathname_)
                    765: {
                    766:        bool changed = false;
                    767:        off_t size;
                    768:        bool w_ok;
                    769:        bool read_only;
                    770: 
                    771:        // すでにあればクローズ (ここでは通知は行わない)
                    772:        changed = UnloadDisk_internal();
                    773: 
                    774:        // 新しいイメージをオープン(する準備)
                    775:        pathname = pathname_;
1.1.1.12  root      776:        if (image.CreateHandler(pathname) == false) {
1.1.1.11  root      777:                goto done;
                    778:        }
                    779: 
1.1.1.12  root      780:        if (image.GetInfo(&size, &w_ok) == false) {
1.1.1.11  root      781:                goto done;
                    782:        }
                    783: 
                    784:        // セクタ単位になっていること
                    785:        if (size % blocksize != 0) {
                    786:                warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%d)",
                    787:                        pathname.c_str(), (uintmax_t)size, blocksize);
                    788:                goto done;
                    789:        }
                    790:        // メディアごとの最大サイズを越えていないこと
                    791:        switch (GetDevType()) {
                    792:         case SCSI::DevType::HD:
                    793:                // LBA (32bit) * 512 byte/sector
                    794:                if (size >= 0x200'0000'0000ULL) {
                    795:                        warnx("%s: Bad image size(%ju): Too large",
                    796:                                pathname.c_str(), (uintmax_t)size);
                    797:                        goto done;
                    798:                }
                    799:                break;
                    800:         case SCSI::DevType::CD:
                    801:                // https://cdrfaq.org/faq07.html#S7-6
                    802:                // 63min = 283,500 [sect] = 553.7MB CD-ROM = 635.9MB CD-DA
                    803:                // 74min = 333,000 [sect] = 650.3MB CD-ROM = 746.9MB CD-DA
                    804:                // 80min = 360,000 [sect] = 703.1MB CD-ROM = 807.4MB CD-DA
                    805:                // 90min = 405,000 [sect] = 791.0MB CD-ROM = 908.4MB CD-DA
                    806:                // 99min = 445,500 [sect] = 870.1MB CD-ROM = 999.3MB CD-DA
                    807:                if (size >= 445500 * 2048) {
                    808:                        warnx("%s: Bad image size(%ju): Too Large",
                    809:                                pathname.c_str(), (uintmax_t)size);
                    810:                        goto done;
                    811:                }
                    812:                break;
                    813:         case SCSI::DevType::MO:
                    814:                // 128M は 25 [sect] * 10,000 [sect/track] * 512 [byte/sect]
                    815:                // 230M は 25 [sect] * 17,853 [sect/track] * 512 [byte/sect]
                    816:                // 540M は 25 [sect] * 41,660 [sect/track] * 512 [byte/sect]
                    817:                // 640M は 17 [sect] * 18,256 [sect/track] * 2048 [byte/sect]
                    818:                // 1.3G は 17 [sect] * 35,638 [sect/track] * 2048 [byte/sect]
                    819:                // 2.3G は 17 [sect] * 62,538 [sect/track] * 2048 [byte/sect]
                    820:                //
                    821:                // 今の所 512 byte/sector フォーマットのみ対応しておく。
                    822:                if (size >= 25 * 41660 * 512) {
                    823:                        warnx("%s: Bad image size(%ju): 640M or larger MO not supported",
                    824:                                pathname.c_str(), (uintmax_t)size);
                    825:                        goto done;
                    826:                }
                    827:                break;
                    828:         default:
                    829:                __unreachable();
                    830:        }
                    831: 
                    832:        // 書き込みモードをここで確定
                    833:        if (IsWriteableDevice()) {
                    834:                // 書き込み可能デバイスの場合
                    835:                if (w_ok) {
                    836:                        if (write_ignore) {
                    837:                                write_mode = RW::WriteIgnore;
                    838:                        } else {
                    839:                                write_mode = RW::Writeable;
                    840:                        }
                    841:                } else {
                    842:                        write_mode = RW::WriteProtect;
                    843:                }
                    844:        } else {
                    845:                // 読み込み専用デバイスの場合
                    846:                write_mode = RW::ReadOnly;
                    847:        }
                    848: 
                    849:        // ここでオープン
                    850:        read_only = (GetWriteMode() != RW::Writeable);
1.1.1.12  root      851:        if (image.Open(read_only, write_ignore) == false) {
1.1.1.11  root      852:                goto done;
                    853:        }
                    854: 
                    855:        // 書き込み無視なら一応ログ出力
                    856:        if (GetWriteMode() == RW::WriteIgnore) {
                    857:                putmsg(0, "write is ignored");
                    858:        }
                    859: 
                    860:        medium_loaded = true;
                    861:        changed = true;
                    862: done:
                    863:        if (changed) {
                    864:                // HD 初期化時はログに出さない
                    865:                if (IsRemovableDevice()) {
                    866:                        putlog(1, "Medium loaded");
                    867:                }
                    868: 
                    869:                // 変化があれば通知
                    870:                MediaChanged();
                    871:        }
                    872:        if (medium_loaded == false) {
                    873:                // ここで状態をクリアする
                    874:                Clear();
                    875:        }
                    876:        return medium_loaded;
                    877: }
                    878: 
                    879: // バックエンドのディスクイメージを閉じる。
                    880: // オープンされてなければ何もしない。
                    881: void
                    882: SCSIDisk::UnloadDisk(bool force)
                    883: {
                    884:        if (IsMediumRemovalAllowed() || force) {
                    885:                if (UnloadDisk_internal()) {
                    886:                        // 実際に閉じたら通知
                    887:                        MediaChanged();
                    888:                }
                    889:        }
                    890: }
                    891: 
                    892: // バックエンドのディスクイメージを閉じる (内部用)。
                    893: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
                    894: // ここでは通知は行わない。
                    895: bool
                    896: SCSIDisk::UnloadDisk_internal()
                    897: {
                    898:        if ((bool)image == false) {
                    899:                return false;
                    900:        }
                    901: 
                    902:        Clear();
                    903: 
                    904:        // 取り出し禁止はされてても解除する
                    905:        if (IsMediumRemovalPrevented()) {
                    906:                // XXX PreventMediumRemoval() を呼ぶかどうするか
                    907:                medium_removal_prevented = false;
                    908:        }
                    909: 
                    910:        putlog(1, "Medium unloaded");
                    911:        return true;
                    912: }
                    913: 
                    914: // ディスク状態をクリアする
                    915: // (メディアを取り外した時など)
                    916: void
                    917: SCSIDisk::Clear()
                    918: {
                    919:        medium_loaded = false;
1.1.1.12  root      920:        image.Close();
1.1.1.11  root      921:        pathname.clear();
                    922:        write_mode = RW::Writeable;
                    923: }
                    924: 
                    925: // メディア状態変更を UI に通知する
                    926: void
                    927: SCSIDisk::MediaChanged() const
                    928: {
                    929:        UIMessage::Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID());
                    930: }
                    931: 
                    932: 
                    933: // メディアを挿入する。
                    934: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    935: void
                    936: SCSIDisk::LoadDiskUI(const std::string& pathname_)
                    937: {
                    938:        new_pathname = pathname_;
                    939:        event->func = (DeviceCallback_t)&SCSIDisk::LoadCallback;
                    940:        event->Start();
                    941: }
                    942: 
                    943: // メディアを排出する。
                    944: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    945: void
                    946: SCSIDisk::UnloadDiskUI(bool force)
                    947: {
                    948:        event->func = (DeviceCallback_t)&SCSIDisk::UnloadCallback;
                    949:        event->code = force;
                    950:        event->Start();
                    951: }
                    952: 
                    953: // メディア挿入コールバック
                    954: void
                    955: SCSIDisk::LoadCallback(Event& ev)
                    956: {
                    957:        if (LoadDisk(new_pathname) == false) {
                    958:                // 失敗したら UI に通知
                    959:                UIMessage::Post(UIMessage::SCSI_MEDIA_FAILED);
                    960:        }
                    961: }
                    962: 
                    963: // メディア排出コールバック
                    964: void
                    965: SCSIDisk::UnloadCallback(Event& ev)
                    966: {
                    967:        bool force = ev.code;
                    968:        UnloadDisk(force);
                    969: }
                    970: 
1.1       root      971: // SCSI コマンドのディスパッチャ
                    972: void
1.1.1.11  root      973: SCSIDisk::DispatchCmd()
1.1       root      974: {
                    975:        switch (cmdseq[0]) {
                    976:         case SCSI::Command::Read6:                     // 0x08
1.1.1.11  root      977:         case SCSI::Command::Read10:            // 0x28
                    978:                cmd.reset(new SCSICmdRead(this));
1.1       root      979:                break;
                    980: 
                    981:         case SCSI::Command::Write6:            // 0x0a
1.1.1.11  root      982:         case SCSI::Command::Write10:           // 0x2a
                    983:                if (IsWriteableDevice()) {
                    984:                        cmd.reset(new SCSICmdWrite(this));
                    985:                } else {
                    986:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    987:                }
1.1       root      988:                break;
                    989: 
1.1.1.4   root      990:         case SCSI::Command::StartStopUnit:     // 0x1b
                    991:                cmd.reset(new SCSICmdStartStopUnit(this));
                    992:                break;
                    993: 
1.1.1.11  root      994:         case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
                    995:                if (IsRemovableDevice() == false) {
                    996:                        // HD は取り出せないので、とりあえず。
                    997:                        // XXX 要調査
                    998:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    999:                        return;
                   1000:                }
                   1001:                cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
                   1002:                break;
                   1003: 
1.1       root     1004:         case SCSI::Command::ReadCapacity:      // 0x25
1.1.1.11  root     1005:                // DA デバイスでは Read Capacity (0x25)、
                   1006:                // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
                   1007:                // 今の所同じ動作なので区別せず処理する。
1.1       root     1008:                cmd.reset(new SCSICmdReadCapacity(this));
                   1009:                break;
                   1010: 
1.1.1.11  root     1011:         case SCSI::Command::SynchronizeCache:  // 0x35
                   1012:                // このクラスは、物理ドライブにあるようなディスクとの間の
                   1013:                // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
                   1014:                // しろという指示に対しては、黙って成功するだけでいい気がする。
                   1015:                // このコマンドはオプションだが NetBSD カーネルが使う。
                   1016:                //
                   1017:                // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
                   1018:                // とりあえず黙って成功しておく。
                   1019:                cmd.reset(new SCSICmd(this));
1.1       root     1020:                break;
                   1021: 
1.1.1.11  root     1022:         case SCSI::Command::ReadTOC:           // 0x43
                   1023:                if (devtype == SCSI::DevType::CD) {
                   1024:                        cmd.reset(new SCSICmdReadTOC(this));
                   1025:                } else {
                   1026:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                   1027:                }
                   1028:                break;
                   1029: 
                   1030:         case SCSI::Command::ReadFormatCapacities:      // 0x23
                   1031:                // SCSI-2 規格でないので詳細不明。
                   1032:                // ディスク用か共通かも分からないけど、とりあえず
                   1033:         case SCSI::Command::ReadDiscInformation:       // 0x51
                   1034:                // CD-ROM デバイスのオプションコマンドで
                   1035:                // CD-R ライター用(?)らしいので無視する。
                   1036:                cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7   root     1037:                break;
                   1038: 
1.1       root     1039:         default:
                   1040:                // 見付からなければ基本クラス側に任せる
                   1041:                inherited::DispatchCmd();
                   1042:                break;
                   1043:        }
                   1044: }
                   1045: 
1.1.1.11  root     1046: // Inquiry データを返す
                   1047: bool
                   1048: SCSIDisk::Inquiry(std::vector<uint8>& buf, int lun)
                   1049: {
                   1050:        uint8 inqtype;
                   1051:        const char *prodname;
                   1052: 
                   1053:        switch (devtype) {
                   1054:         case SCSI::DevType::HD:
                   1055:                inqtype = SCSI::InquiryDeviceType::DirectAccess;
                   1056:                prodname = "SCSIHD";
                   1057:                break;
                   1058:         case SCSI::DevType::CD:
                   1059:                inqtype = SCSI::InquiryDeviceType::CDROM;
                   1060:                prodname = "SCSICD";
                   1061:                break;
                   1062:         case SCSI::DevType::MO:
                   1063:                inqtype = SCSI::InquiryDeviceType::MO;
                   1064:                prodname = "SCSIMO";
                   1065:                break;
                   1066:         default:
                   1067:                assert(false);
                   1068:        }
                   1069: 
                   1070:        buf[0] = inqtype;
                   1071:        if (lun != 0) {
                   1072:                buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
                   1073:        }
                   1074:        buf[1] = 0;
                   1075:        if (IsRemovableDevice()) {
                   1076:                buf[1] |= 0x80;
                   1077:        }
                   1078:        buf[2] = 2;             // SCSI-2
                   1079:        buf[3] = 2;             // Response Data Format = SCSI-2
                   1080:        buf[4] = buf.size() - 4;        // 追加データ長
                   1081: 
                   1082:        // 文字列はゼロ終端ではなく、余りは空白で埋める
                   1083:        memset(&buf[8], ' ', 36 - 8);
                   1084:        memcpy(&buf[8],  "NONO", 4);
                   1085:        memcpy(&buf[16], prodname, strlen(prodname));
                   1086:        memcpy(&buf[32], "0", 1);
                   1087: 
                   1088:        return true;
                   1089: }
                   1090: 
                   1091: // ModeSense コマンドのデバイス固有パラメータを返す
                   1092: uint8
                   1093: SCSIDisk::GetDeviceSpecificParam() const
                   1094: {
                   1095:        uint8 param = 0;
                   1096: 
                   1097:        // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
                   1098:        // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
                   1099:        //
                   1100:        //      7   6   5     4    3   2   1   0
                   1101:        //    +---+---+---+------+---+---+---+---+
                   1102:        // DA |WP |   0   |DPOFUA|       0       | WP: Write Protect
                   1103:        //    +---+---+---+------+---+---+---+---+
                   1104:        //
                   1105:        //    +---+---+---+------+---+---+---+---+
                   1106:        // MO |WP |   0   |DPOFUA|     0     |EBC| EBC: Enable Blank Check
                   1107:        //    +---+---+---+------+---+---+---+---+
                   1108:        //
                   1109:        //    +---+---+---+------+---+---+---+---+
                   1110:        // CD |     0     |DPOFUA|       0       |
                   1111:        //    +---+---+---+------+---+---+---+---+
                   1112: 
                   1113:        if (GetDevType() == SCSI::DevType::CD) {
                   1114:                // CD には WP ビットの定義がない
                   1115:        } else {
                   1116:                // DA, MO は WP ビットの定義がある。
                   1117:                if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
                   1118:                        param |= 0x80;
                   1119:                }
                   1120:        }
                   1121: 
                   1122:        return param;
                   1123: }
                   1124: 
                   1125: bool
                   1126: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
                   1127: {
                   1128:        if (__predict_false((bool)image == false)) {
                   1129:                return false;
                   1130:        }
1.1.1.12  root     1131:        return image.Read(buf.data(), start, buf.size());
1.1.1.11  root     1132: }
                   1133: 
                   1134: bool
                   1135: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
                   1136: {
                   1137:        if (__predict_false((bool)image == false)) {
                   1138:                return false;
                   1139:        }
                   1140: 
1.1.1.12  root     1141:        if (GetWriteMode() >= RW::WriteProtect) {
1.1.1.11  root     1142:                // 書き込み不可
                   1143:                return false;
                   1144:        }
1.1.1.12  root     1145:        return image.Write(buf.data(), start, buf.size());
1.1.1.11  root     1146: }
                   1147: 
                   1148: off_t
                   1149: SCSIDisk::GetSize() const
                   1150: {
                   1151:        if (__predict_false((bool)image == false)) {
                   1152:                return 0;
                   1153:        }
1.1.1.12  root     1154:        return image.GetSize();
1.1.1.11  root     1155: }
                   1156: 
1.1.1.2   root     1157: // 指定の場所を自由に読み出す
                   1158: // (エミュレータ特権アクセス)
                   1159: bool
1.1.1.11  root     1160: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2   root     1161: {
1.1.1.11  root     1162:        if (__predict_false((bool)image == false)) {
1.1.1.2   root     1163:                return false;
                   1164:        }
1.1.1.12  root     1165:        return image.Read(buf, start, len);
1.1.1.2   root     1166: }
                   1167: 
1.1.1.11  root     1168: // メディアの取り出し可否(取り出し禁止)を設定する
                   1169: void
                   1170: SCSIDisk::PreventMediumRemoval(bool val)
                   1171: {
                   1172:        // 状態が変わったところでログを表示
                   1173:        if (medium_removal_prevented == false && val == true) {
                   1174:                putlog(1, "Medium removal prevented");
                   1175:        } else if (medium_removal_prevented == true && val == false) {
                   1176:                putlog(1, "Medium removal allowed");
                   1177:        }
                   1178:        medium_removal_prevented = val;
                   1179: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.