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

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

unix.superglobalmegacorp.com

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