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

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:                }
1.1.1.15! root      580:                putlog(0, "SCSI Command $%02x%s len=%d not supported",
1.1       root      581:                        cmdseq[0], name.c_str(), (int)cmdseq.size());
                    582: 
                    583:                cmd.reset(new SCSICmdNotSupportedCommand(this));
                    584:                break;
                    585:        }
                    586: }
                    587: 
1.1.1.15! root      588: // コマンド列を指定して対応するコマンドを選択する。なければ NULL を返す。
        !           589: // ROM30 エミュレーション用。
        !           590: SCSICmd *
        !           591: SCSITarget::SelectCommand(const std::vector<uint8>& cmdseq_)
        !           592: {
        !           593:        cmdseq = cmdseq_;
        !           594:        DispatchCmd();
        !           595:        return cmd.get();
        !           596: }
        !           597: 
1.1.1.11  root      598: 
1.1       root      599: //
1.1.1.11  root      600: // SCSI Disk
1.1       root      601: //
                    602: 
                    603: // コンストラクタ
1.1.1.11  root      604: SCSIDisk::SCSIDisk(SCSIHostDevice *h, int id, SCSI::DevType devtype_)
                    605:        : inherited("", h, id)
1.1       root      606: {
1.1.1.11  root      607:        devtype = devtype_;
                    608: 
                    609:        const char *devtypename = SCSI::GetDevTypeName(devtype);
                    610: 
                    611:        // コンストラクタの初期化子では devtypename を組み込みにくいので、
                    612:        // コンストラクタ本文でログ名などを設定する。その時の作法に
                    613:        // 従っているのですこし面倒な手法が必要。
                    614:        // lib/object.h 参照。
                    615:        SetName(string_format("SCSI%s%d", devtypename, id));
                    616:        ClearAlias();
1.1.1.12  root      617:        AddAlias(string_format("SCSI%d", id));
1.1.1.11  root      618:        AddAlias(string_format("%s%d", devtypename, id));
                    619: 
                    620:        switch (devtype) {
                    621:         case SCSI::DevType::HD:
                    622:                removable_device = false;
                    623:                writeable_device = true;
                    624:                blocksize = 512;
                    625:                break;
                    626:         case SCSI::DevType::CD:
                    627:                removable_device = true;
                    628:                writeable_device = false;
                    629:                blocksize = 2048;
                    630:                break;
                    631:         case SCSI::DevType::MO:
                    632:                removable_device = true;
                    633:                writeable_device = true;
                    634:                blocksize = 512;
                    635:                break;
                    636:         default:
                    637:                __unreachable();
                    638:        }
1.1.1.10  root      639: 
1.1       root      640: }
                    641: 
                    642: // デストラクタ
1.1.1.11  root      643: SCSIDisk::~SCSIDisk()
1.1       root      644: {
                    645: }
                    646: 
                    647: // 初期化
                    648: bool
1.1.1.11  root      649: SCSIDisk::Init()
1.1       root      650: {
1.1.1.2   root      651:        const std::string keybody = string_format("%s-id%d-",
1.1       root      652:                host->GetConfigName(), myid);
                    653:        const std::string imgkey = keybody + "image";
1.1.1.11  root      654:        const std::string wikey  = keybody + "writeignore";
1.1       root      655:        const std::string wpkey  = keybody + "writeprotect";
1.1.1.5   root      656:        const std::string stkey  = keybody + "seektime";
1.1       root      657: 
1.1.1.11  root      658:        // デバイス種別(v[0])とパス(v[1])に分解。
                    659:        // デバイス種別はすでにチェックしてあるのでここでは不要。
1.1.1.3   root      660:        const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.11  root      661:        const std::string& imageval = itemimg.AsString();
                    662:        auto v = string_split(imageval, ',', 2);
                    663:        std::string filename;
                    664:        if (v.size() > 1) {
                    665:                filename = string_trim(v[1]);
                    666:        }
                    667: 
                    668:        // 固定デバイス(HD)ならファイル名は省略不可
                    669:        if (!IsRemovableDevice() && filename.empty()) {
1.1.1.2   root      670:                itemimg.Err("imagefile not specified");
1.1       root      671:                return false;
                    672:        }
                    673: 
1.1.1.14  root      674:        // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意
                    675:        if (IsRemovableDevice()) {
                    676:                gScheduler->ConnectMessage(MessageID::SCSIDEV_LOAD(myid), this,
                    677:                        ToMessageCallback(&SCSIDisk::LoadCallback));
                    678:                gScheduler->ConnectMessage(MessageID::SCSIDEV_UNLOAD(myid), this,
                    679:                        ToMessageCallback(&SCSIDisk::UnloadCallback));
                    680:        }
                    681: 
1.1.1.11  root      682:        // オープン前に writeignore をチェック。
                    683:        // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
                    684:        // 行わないといけない。うーん。
                    685:        //
                    686:        // 変数名は元々 -writeprotect だったが -writeignore に変更した。
                    687:        // 単にリネームするだけだと、開発中に古いバージョンを二分探索とかする
                    688:        // 必要が出来た場合に面倒なので、当面は古いほうも面倒を見る。
                    689:        // 十分いらなくなった頃に削除する。
                    690:        //
                    691:        // wi指定     wp指定
                    692:        // なし               なし  wi を採用。
                    693:        // なし               あり  wp を採用。ただし警告
                    694:        // あり               なし  wi を採用。
                    695:        // あり               あり  wi を採用。ここは警告不要でいいだろう
                    696:        //
                    697:        const ConfigItem& wi = gConfig->Find(wikey);
                    698:        const ConfigItem& wp = gConfig->Find(wpkey);
                    699:        if (wi.GetFrom() == ConfigItem::FromInitial &&
                    700:            wp.GetFrom() != ConfigItem::FromInitial   )
                    701:        {
                    702:                // -writeignore (新)がなく -writeprotect (旧) だけが指定されると警告。
                    703:                // エラー停止まではしなくていいか。
                    704:                warnx("Warning: '%s' is obsolete. Use new '%s'",
                    705:                        wpkey.c_str(), wikey.c_str());
                    706: 
                    707:                write_ignore = wp.AsInt();
                    708:        } else {
                    709:                write_ignore = wi.AsInt();
1.1       root      710:        }
                    711: 
1.1.1.11  root      712:        // 設定時点でファイルが指定されていればオープン
                    713:        if (filename.empty() == false) {
1.1.1.14  root      714:                std::string path;
                    715:                if (filename[0] == '/') {
                    716:                        path = filename;
                    717:                } else {
                    718:                        // 相対パスなら VM ディレクトリから
                    719:                        path = gMainApp.GetVMDir() + filename;
                    720:                }
                    721: 
1.1.1.11  root      722:                if (LoadDisk(path) == false) {
                    723:                        return false;
                    724:                }
                    725:        } else {
                    726:                // メディアがロードされないままでも起動時は必ず通知を投げる。
                    727:                // ステータスパネルはこの UIMessage でのみ状態を更新するため。
                    728:                MediaChanged();
1.1       root      729:        }
                    730: 
1.1.1.5   root      731:        // 設定は [msec]、変数は [nsec]。
                    732:        seektime = gConfig->Find(stkey).AsInt();
                    733:        seektime *= 1_msec;
                    734: 
1.1       root      735:        return true;
                    736: }
                    737: 
1.1.1.11  root      738: // 電源オン
1.1.1.14  root      739: void
                    740: SCSIDisk::ResetHard(bool poweron)
1.1.1.11  root      741: {
1.1.1.14  root      742:        inherited::ResetHard(poweron);
1.1.1.11  root      743: 
1.1.1.14  root      744:        if (poweron) {
                    745:                // メディアの取り出し禁止を解除
                    746:                PreventMediumRemoval(false);
                    747:        }
1.1.1.11  root      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: {
1.1.1.15! root      766:        bool unloaded;
1.1.1.11  root      767:        off_t size;
                    768:        bool w_ok;
                    769:        bool read_only;
                    770: 
                    771:        // すでにあればクローズ (ここでは通知は行わない)
1.1.1.15! root      772:        unloaded = UnloadDisk_internal();
1.1.1.11  root      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;
1.1.1.15! root      861:        // リムーバブルデバイスでだけログを出す
        !           862:        if (IsRemovableDevice()) {
        !           863:                putlog(1, "Medium loaded");
1.1.1.11  root      864:        }
1.1.1.15! root      865: 
        !           866:  done:
1.1.1.11  root      867:        if (medium_loaded == false) {
                    868:                // ここで状態をクリアする
                    869:                Clear();
                    870:        }
1.1.1.15! root      871: 
        !           872:        // 変化があれば通知
        !           873:        if (unloaded || medium_loaded) {
        !           874:                MediaChanged();
        !           875:        }
1.1.1.11  root      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: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    934: void
                    935: SCSIDisk::LoadDiskUI(const std::string& pathname_)
                    936: {
                    937:        new_pathname = pathname_;
1.1.1.14  root      938:        gScheduler->SendMessage(MessageID::SCSIDEV_LOAD(GetMyID()));
1.1.1.11  root      939: }
                    940: 
                    941: // メディアを排出する。
                    942: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    943: void
                    944: SCSIDisk::UnloadDiskUI(bool force)
                    945: {
1.1.1.14  root      946:        gScheduler->SendMessage(MessageID::SCSIDEV_UNLOAD(GetMyID()), force);
1.1.1.11  root      947: }
                    948: 
1.1.1.14  root      949: // メディア挿入メッセージコールバック
1.1.1.11  root      950: void
1.1.1.14  root      951: SCSIDisk::LoadCallback(MessageID msgid, uint32 arg)
1.1.1.11  root      952: {
                    953:        if (LoadDisk(new_pathname) == false) {
                    954:                // 失敗したら UI に通知
                    955:                UIMessage::Post(UIMessage::SCSI_MEDIA_FAILED);
                    956:        }
                    957: }
                    958: 
1.1.1.14  root      959: // メディア排出メッセージコールバック
1.1.1.11  root      960: void
1.1.1.14  root      961: SCSIDisk::UnloadCallback(MessageID msgid, uint32 arg)
1.1.1.11  root      962: {
1.1.1.14  root      963:        bool force = arg;
1.1.1.11  root      964:        UnloadDisk(force);
                    965: }
                    966: 
1.1       root      967: // SCSI コマンドのディスパッチャ
                    968: void
1.1.1.11  root      969: SCSIDisk::DispatchCmd()
1.1       root      970: {
                    971:        switch (cmdseq[0]) {
                    972:         case SCSI::Command::Read6:                     // 0x08
1.1.1.11  root      973:         case SCSI::Command::Read10:            // 0x28
                    974:                cmd.reset(new SCSICmdRead(this));
1.1       root      975:                break;
                    976: 
                    977:         case SCSI::Command::Write6:            // 0x0a
1.1.1.11  root      978:         case SCSI::Command::Write10:           // 0x2a
                    979:                if (IsWriteableDevice()) {
                    980:                        cmd.reset(new SCSICmdWrite(this));
                    981:                } else {
                    982:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    983:                }
1.1       root      984:                break;
                    985: 
1.1.1.4   root      986:         case SCSI::Command::StartStopUnit:     // 0x1b
                    987:                cmd.reset(new SCSICmdStartStopUnit(this));
                    988:                break;
                    989: 
1.1.1.11  root      990:         case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
                    991:                if (IsRemovableDevice() == false) {
                    992:                        // HD は取り出せないので、とりあえず。
                    993:                        // XXX 要調査
                    994:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    995:                        return;
                    996:                }
                    997:                cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
                    998:                break;
                    999: 
1.1       root     1000:         case SCSI::Command::ReadCapacity:      // 0x25
1.1.1.11  root     1001:                // DA デバイスでは Read Capacity (0x25)、
                   1002:                // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
                   1003:                // 今の所同じ動作なので区別せず処理する。
1.1       root     1004:                cmd.reset(new SCSICmdReadCapacity(this));
                   1005:                break;
                   1006: 
1.1.1.11  root     1007:         case SCSI::Command::SynchronizeCache:  // 0x35
                   1008:                // このクラスは、物理ドライブにあるようなディスクとの間の
                   1009:                // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
                   1010:                // しろという指示に対しては、黙って成功するだけでいい気がする。
                   1011:                // このコマンドはオプションだが NetBSD カーネルが使う。
                   1012:                //
                   1013:                // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
                   1014:                // とりあえず黙って成功しておく。
                   1015:                cmd.reset(new SCSICmd(this));
1.1       root     1016:                break;
                   1017: 
1.1.1.11  root     1018:         case SCSI::Command::ReadTOC:           // 0x43
                   1019:                if (devtype == SCSI::DevType::CD) {
                   1020:                        cmd.reset(new SCSICmdReadTOC(this));
                   1021:                } else {
                   1022:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                   1023:                }
                   1024:                break;
                   1025: 
                   1026:         case SCSI::Command::ReadFormatCapacities:      // 0x23
                   1027:                // SCSI-2 規格でないので詳細不明。
                   1028:                // ディスク用か共通かも分からないけど、とりあえず
                   1029:         case SCSI::Command::ReadDiscInformation:       // 0x51
                   1030:                // CD-ROM デバイスのオプションコマンドで
                   1031:                // CD-R ライター用(?)らしいので無視する。
                   1032:                cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7   root     1033:                break;
                   1034: 
1.1       root     1035:         default:
                   1036:                // 見付からなければ基本クラス側に任せる
                   1037:                inherited::DispatchCmd();
                   1038:                break;
                   1039:        }
                   1040: }
                   1041: 
1.1.1.11  root     1042: // Inquiry データを返す
                   1043: bool
                   1044: SCSIDisk::Inquiry(std::vector<uint8>& buf, int lun)
                   1045: {
                   1046:        uint8 inqtype;
                   1047:        const char *prodname;
                   1048: 
                   1049:        switch (devtype) {
                   1050:         case SCSI::DevType::HD:
                   1051:                inqtype = SCSI::InquiryDeviceType::DirectAccess;
                   1052:                prodname = "SCSIHD";
                   1053:                break;
                   1054:         case SCSI::DevType::CD:
                   1055:                inqtype = SCSI::InquiryDeviceType::CDROM;
                   1056:                prodname = "SCSICD";
                   1057:                break;
                   1058:         case SCSI::DevType::MO:
                   1059:                inqtype = SCSI::InquiryDeviceType::MO;
                   1060:                prodname = "SCSIMO";
                   1061:                break;
                   1062:         default:
1.1.1.14  root     1063:                PANIC("Unexpected devtype=%d", (int)devtype);
1.1.1.11  root     1064:        }
                   1065: 
                   1066:        buf[0] = inqtype;
                   1067:        if (lun != 0) {
                   1068:                buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
                   1069:        }
                   1070:        buf[1] = 0;
                   1071:        if (IsRemovableDevice()) {
                   1072:                buf[1] |= 0x80;
                   1073:        }
                   1074:        buf[2] = 2;             // SCSI-2
                   1075:        buf[3] = 2;             // Response Data Format = SCSI-2
                   1076:        buf[4] = buf.size() - 4;        // 追加データ長
                   1077: 
                   1078:        // 文字列はゼロ終端ではなく、余りは空白で埋める
                   1079:        memset(&buf[8], ' ', 36 - 8);
                   1080:        memcpy(&buf[8],  "NONO", 4);
                   1081:        memcpy(&buf[16], prodname, strlen(prodname));
                   1082:        memcpy(&buf[32], "0", 1);
                   1083: 
                   1084:        return true;
                   1085: }
                   1086: 
                   1087: // ModeSense コマンドのデバイス固有パラメータを返す
                   1088: uint8
                   1089: SCSIDisk::GetDeviceSpecificParam() const
                   1090: {
                   1091:        uint8 param = 0;
                   1092: 
                   1093:        // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
                   1094:        // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
                   1095:        //
                   1096:        //      7   6   5     4    3   2   1   0
                   1097:        //    +---+---+---+------+---+---+---+---+
                   1098:        // DA |WP |   0   |DPOFUA|       0       | WP: Write Protect
                   1099:        //    +---+---+---+------+---+---+---+---+
                   1100:        //
                   1101:        //    +---+---+---+------+---+---+---+---+
                   1102:        // MO |WP |   0   |DPOFUA|     0     |EBC| EBC: Enable Blank Check
                   1103:        //    +---+---+---+------+---+---+---+---+
                   1104:        //
                   1105:        //    +---+---+---+------+---+---+---+---+
                   1106:        // CD |     0     |DPOFUA|       0       |
                   1107:        //    +---+---+---+------+---+---+---+---+
                   1108: 
                   1109:        if (GetDevType() == SCSI::DevType::CD) {
                   1110:                // CD には WP ビットの定義がない
                   1111:        } else {
                   1112:                // DA, MO は WP ビットの定義がある。
                   1113:                if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
                   1114:                        param |= 0x80;
                   1115:                }
                   1116:        }
                   1117: 
                   1118:        return param;
                   1119: }
                   1120: 
                   1121: bool
                   1122: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
                   1123: {
                   1124:        if (__predict_false((bool)image == false)) {
                   1125:                return false;
                   1126:        }
1.1.1.12  root     1127:        return image.Read(buf.data(), start, buf.size());
1.1.1.11  root     1128: }
                   1129: 
                   1130: bool
                   1131: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
                   1132: {
                   1133:        if (__predict_false((bool)image == false)) {
                   1134:                return false;
                   1135:        }
                   1136: 
1.1.1.12  root     1137:        if (GetWriteMode() >= RW::WriteProtect) {
1.1.1.11  root     1138:                // 書き込み不可
                   1139:                return false;
                   1140:        }
1.1.1.12  root     1141:        return image.Write(buf.data(), start, buf.size());
1.1.1.11  root     1142: }
                   1143: 
                   1144: off_t
                   1145: SCSIDisk::GetSize() const
                   1146: {
                   1147:        if (__predict_false((bool)image == false)) {
                   1148:                return 0;
                   1149:        }
1.1.1.12  root     1150:        return image.GetSize();
1.1.1.11  root     1151: }
                   1152: 
1.1.1.2   root     1153: // 指定の場所を自由に読み出す
                   1154: // (エミュレータ特権アクセス)
                   1155: bool
1.1.1.11  root     1156: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2   root     1157: {
1.1.1.11  root     1158:        if (__predict_false((bool)image == false)) {
1.1.1.2   root     1159:                return false;
                   1160:        }
1.1.1.12  root     1161:        return image.Read(buf, start, len);
1.1.1.2   root     1162: }
                   1163: 
1.1.1.11  root     1164: // メディアの取り出し可否(取り出し禁止)を設定する
                   1165: void
                   1166: SCSIDisk::PreventMediumRemoval(bool val)
                   1167: {
                   1168:        // 状態が変わったところでログを表示
                   1169:        if (medium_removal_prevented == false && val == true) {
                   1170:                putlog(1, "Medium removal prevented");
                   1171:        } else if (medium_removal_prevented == true && val == false) {
                   1172:                putlog(1, "Medium removal allowed");
                   1173:        }
                   1174:        medium_removal_prevented = val;
                   1175: }
1.1.1.14  root     1176: 
                   1177: // メディアの書き込みモードを文字列で返す
                   1178: const char *
                   1179: SCSIDisk::GetWriteModeStr() const
                   1180: {
                   1181:        switch (write_mode) {
                   1182:         case RW::ReadOnly:             return "ReadOnly";
                   1183:         case RW::WriteProtect: return "WriteProtected";
                   1184:         case RW::Writeable:    return "Writeable";
                   1185:         case RW::WriteIgnore:  return "WriteIgnore";
                   1186:         default:
                   1187:                __unreachable();
                   1188:        }
                   1189: }

unix.superglobalmegacorp.com

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