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

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

unix.superglobalmegacorp.com

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