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

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.23! root       65:        SetName(string_format("(SCSIHost.%s)", domain_->GetDomainName()));
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);
1.1.1.23! root      571:        const char *domainname = domain->GetDomainName();
1.1.1.11  root      572: 
1.1.1.23! root      573:        // ドライブ自身のログ表示名は "HD(SPC0-6)" のような形式にする。
        !           574:        // ログ指定はそれに合わせて "spc0-6" のような完全形と
        !           575:        // "hd6" のような短縮形を用意してみる。
        !           576:        // SCSI コントローラ2台目ではこの短縮形だけなくす?
        !           577:        //
1.1.1.11  root      578:        // コンストラクタの初期化子では devtypename を組み込みにくいので、
                    579:        // コンストラクタ本文でログ名などを設定する。その時の作法に
                    580:        // 従っているのですこし面倒な手法が必要。
                    581:        // lib/object.h 参照。
1.1.1.23! root      582:        std::string name = string_format("%s-%u", domainname, id);
        !           583:        SetName(string_format("%s(%s)", devtypename, name.c_str()));
1.1.1.11  root      584:        ClearAlias();
1.1.1.23! root      585:        AddAlias(name);
1.1.1.18  root      586:        AddAlias(string_format("%s%u", devtypename, id));
1.1.1.11  root      587: 
1.1.1.23! root      588:        // image はコンストラクタがないので、このあたりで代入する。
        !           589:        image.SetName(name);
        !           590: 
1.1.1.11  root      591:        switch (devtype) {
                    592:         case SCSI::DevType::HD:
                    593:                removable_device = false;
                    594:                writeable_device = true;
                    595:                blocksize = 512;
                    596:                break;
                    597:         case SCSI::DevType::CD:
                    598:                removable_device = true;
                    599:                writeable_device = false;
                    600:                blocksize = 2048;
                    601:                break;
                    602:         case SCSI::DevType::MO:
                    603:                removable_device = true;
                    604:                writeable_device = true;
                    605:                blocksize = 512;
                    606:                break;
                    607:         default:
1.1.1.18  root      608:                PANIC("corrupted devtype=%u", (uint)devtype);
1.1.1.11  root      609:        }
1.1.1.10  root      610: 
1.1       root      611: }
                    612: 
                    613: // デストラクタ
1.1.1.11  root      614: SCSIDisk::~SCSIDisk()
1.1       root      615: {
                    616: }
                    617: 
                    618: // 初期化
                    619: bool
1.1.1.11  root      620: SCSIDisk::Init()
1.1       root      621: {
1.1.1.18  root      622:        const std::string keybody = string_format("%s-id%u-",
1.1.1.20  root      623:                domain->GetConfigName(), myid);
1.1       root      624:        const std::string imgkey = keybody + "image";
1.1.1.11  root      625:        const std::string wikey  = keybody + "writeignore";
1.1.1.5   root      626:        const std::string stkey  = keybody + "seektime";
1.1       root      627: 
1.1.1.11  root      628:        // デバイス種別(v[0])とパス(v[1])に分解。
                    629:        // デバイス種別はすでにチェックしてあるのでここでは不要。
1.1.1.3   root      630:        const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.11  root      631:        const std::string& imageval = itemimg.AsString();
                    632:        auto v = string_split(imageval, ',', 2);
                    633:        std::string filename;
                    634:        if (v.size() > 1) {
                    635:                filename = string_trim(v[1]);
                    636:        }
                    637: 
                    638:        // 固定デバイス(HD)ならファイル名は省略不可
                    639:        if (!IsRemovableDevice() && filename.empty()) {
1.1.1.2   root      640:                itemimg.Err("imagefile not specified");
1.1       root      641:                return false;
                    642:        }
                    643: 
1.1.1.14  root      644:        // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意
                    645:        if (IsRemovableDevice()) {
1.1.1.16  root      646:                scheduler->ConnectMessage(MessageID::SCSIDEV_LOAD(myid), this,
1.1.1.17  root      647:                        ToMessageCallback(&SCSIDisk::LoadMessage));
1.1.1.16  root      648:                scheduler->ConnectMessage(MessageID::SCSIDEV_UNLOAD(myid), this,
1.1.1.17  root      649:                        ToMessageCallback(&SCSIDisk::UnloadMessage));
1.1.1.14  root      650:        }
                    651: 
1.1.1.11  root      652:        // オープン前に writeignore をチェック。
                    653:        // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
                    654:        // 行わないといけない。うーん。
1.1.1.23! root      655:        write_ignore = gConfig->Find(wikey).AsBool();
1.1       root      656: 
1.1.1.21  root      657:        // writeignore が決まったので状態を初期化。
                    658:        Clear();
                    659: 
1.1.1.11  root      660:        // 設定時点でファイルが指定されていればオープン
                    661:        if (filename.empty() == false) {
1.1.1.17  root      662:                std::string path = gMainApp.NormalizePath(filename);
1.1.1.11  root      663:                if (LoadDisk(path) == false) {
1.1.1.23! root      664:                        itemimg.Err("Could not open %s", path.c_str());
1.1.1.11  root      665:                        return false;
                    666:                }
1.1       root      667:        }
                    668: 
1.1.1.23! root      669:        // 平均シークタイム。設定は [msec]、変数は [tsec]。
1.1.1.20  root      670:        // virtio-scsi にはない。
                    671:        if (strncmp(stkey.c_str(), "virtio", 6) != 0) {
                    672:                seektime = gConfig->Find(stkey).AsInt();
                    673:                seektime *= 1_msec;
                    674:        }
1.1.1.5   root      675: 
1.1       root      676:        return true;
                    677: }
                    678: 
1.1.1.11  root      679: // 電源オン
1.1.1.14  root      680: void
                    681: SCSIDisk::ResetHard(bool poweron)
1.1.1.11  root      682: {
1.1.1.14  root      683:        inherited::ResetHard(poweron);
1.1.1.11  root      684: 
1.1.1.14  root      685:        if (poweron) {
                    686:                // メディアの取り出し禁止を解除
                    687:                PreventMediumRemoval(false);
                    688:        }
1.1.1.11  root      689: }
                    690: 
1.1.1.18  root      691: // バスリセットされた (SCSIBus から呼ばれる)
1.1.1.11  root      692: void
1.1.1.18  root      693: SCSIDisk::OnBusReset()
1.1.1.11  root      694: {
                    695:        // メディアの取り出し禁止を解除
                    696:        PreventMediumRemoval(false);
                    697: 
1.1.1.18  root      698:        inherited::OnBusReset();
1.1.1.11  root      699: }
                    700: 
                    701: // バックエンドのディスクイメージを開く。
                    702: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
                    703: // のではないが、medium_loaded が同じ値になるので使いまわしている)
                    704: bool
                    705: SCSIDisk::LoadDisk(const std::string& pathname_)
                    706: {
1.1.1.15  root      707:        bool unloaded;
1.1.1.11  root      708:        off_t size;
                    709:        bool read_only;
                    710: 
                    711:        // すでにあればクローズ (ここでは通知は行わない)
1.1.1.15  root      712:        unloaded = UnloadDisk_internal();
1.1.1.11  root      713: 
                    714:        // 新しいイメージをオープン(する準備)
                    715:        pathname = pathname_;
1.1.1.12  root      716:        if (image.CreateHandler(pathname) == false) {
1.1.1.11  root      717:                goto done;
                    718:        }
                    719: 
1.1.1.22  root      720:        // 書き込みモードをここで確定。
                    721:        //
                    722:        // デバイス特性   イメージ    write_ignore    write_mode
                    723:        // ------------ ----------      ------------ :  ----------
                    724:        // 書込み可         書込み可    no                              Writeable
                    725:        // 〃                  〃                     yes                             WriteIgnore
                    726:        // 〃                  書込み不可 no                              WriteProtect (A)
                    727:        // 〃                  〃                     yes                             WriteIgnore  (B)
                    728:        // 書込み不可      *                       *                               ReadOnly
                    729:        //
                    730:        // (A) は「ライトプロテクトした MO を入れた」状態。
                    731:        // (B) は「書き換えたくないので書き込み権を落としてあるイメージファイルを
                    732:        // あたかも書き込めるように見せたい」。(WI マークが出る)
1.1.1.18  root      733:        if (IsWriteableDevice()) {
                    734:                // 書き込み可能デバイスの場合
1.1.1.22  root      735:                if (write_ignore) {
                    736:                        write_mode = RW::WriteIgnore;
                    737:                } else {
1.1.1.23! root      738:                        int w_ok = image.IsWriteable();
        !           739:                        if (w_ok < 0) {
        !           740:                                goto done;
        !           741:                        } else if (w_ok) {
1.1.1.18  root      742:                                write_mode = RW::Writeable;
1.1.1.22  root      743:                        } else {
                    744:                                write_mode = RW::WriteProtect;
1.1.1.18  root      745:                        }
                    746:                }
                    747:        } else {
                    748:                // 読み込み専用デバイスの場合
                    749:                write_mode = RW::ReadOnly;
                    750:        }
                    751: 
                    752:        // ここでオープン
                    753:        read_only = (GetWriteMode() != RW::Writeable);
                    754:        if (image.Open(read_only, write_ignore) == false) {
                    755:                goto done;
                    756:        }
                    757: 
                    758:        size = image.GetSize();
                    759: 
1.1.1.11  root      760:        // セクタ単位になっていること
                    761:        if (size % blocksize != 0) {
1.1.1.18  root      762:                warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%u)",
1.1.1.11  root      763:                        pathname.c_str(), (uintmax_t)size, blocksize);
                    764:                goto done;
                    765:        }
                    766:        // メディアごとの最大サイズを越えていないこと
                    767:        switch (GetDevType()) {
                    768:         case SCSI::DevType::HD:
                    769:                // LBA (32bit) * 512 byte/sector
                    770:                if (size >= 0x200'0000'0000ULL) {
                    771:                        warnx("%s: Bad image size(%ju): Too large",
                    772:                                pathname.c_str(), (uintmax_t)size);
                    773:                        goto done;
                    774:                }
                    775:                break;
                    776:         case SCSI::DevType::CD:
                    777:                // https://cdrfaq.org/faq07.html#S7-6
                    778:                // 63min = 283,500 [sect] = 553.7MB CD-ROM = 635.9MB CD-DA
                    779:                // 74min = 333,000 [sect] = 650.3MB CD-ROM = 746.9MB CD-DA
                    780:                // 80min = 360,000 [sect] = 703.1MB CD-ROM = 807.4MB CD-DA
                    781:                // 90min = 405,000 [sect] = 791.0MB CD-ROM = 908.4MB CD-DA
                    782:                // 99min = 445,500 [sect] = 870.1MB CD-ROM = 999.3MB CD-DA
                    783:                if (size >= 445500 * 2048) {
                    784:                        warnx("%s: Bad image size(%ju): Too Large",
                    785:                                pathname.c_str(), (uintmax_t)size);
                    786:                        goto done;
                    787:                }
                    788:                break;
                    789:         case SCSI::DevType::MO:
                    790:                // 128M は 25 [sect] * 10,000 [sect/track] * 512 [byte/sect]
                    791:                // 230M は 25 [sect] * 17,853 [sect/track] * 512 [byte/sect]
                    792:                // 540M は 25 [sect] * 41,660 [sect/track] * 512 [byte/sect]
                    793:                // 640M は 17 [sect] * 18,256 [sect/track] * 2048 [byte/sect]
                    794:                // 1.3G は 17 [sect] * 35,638 [sect/track] * 2048 [byte/sect]
                    795:                // 2.3G は 17 [sect] * 62,538 [sect/track] * 2048 [byte/sect]
                    796:                //
                    797:                // 今の所 512 byte/sector フォーマットのみ対応しておく。
                    798:                if (size >= 25 * 41660 * 512) {
                    799:                        warnx("%s: Bad image size(%ju): 640M or larger MO not supported",
                    800:                                pathname.c_str(), (uintmax_t)size);
                    801:                        goto done;
                    802:                }
                    803:                break;
                    804:         default:
1.1.1.18  root      805:                PANIC("corrupted devtype=%u", (uint)devtype);
1.1.1.11  root      806:        }
                    807: 
                    808:        // 書き込み無視なら一応ログ出力
                    809:        if (GetWriteMode() == RW::WriteIgnore) {
                    810:                putmsg(0, "write is ignored");
                    811:        }
                    812: 
                    813:        medium_loaded = true;
1.1.1.15  root      814:        // リムーバブルデバイスでだけログを出す
                    815:        if (IsRemovableDevice()) {
                    816:                putlog(1, "Medium loaded");
1.1.1.11  root      817:        }
1.1.1.15  root      818: 
                    819:  done:
1.1.1.11  root      820:        if (medium_loaded == false) {
                    821:                // ここで状態をクリアする
                    822:                Clear();
                    823:        }
1.1.1.15  root      824: 
                    825:        // 変化があれば通知
                    826:        if (unloaded || medium_loaded) {
                    827:                MediaChanged();
                    828:        }
1.1.1.11  root      829:        return medium_loaded;
                    830: }
                    831: 
                    832: // バックエンドのディスクイメージを閉じる。
                    833: // オープンされてなければ何もしない。
                    834: void
                    835: SCSIDisk::UnloadDisk(bool force)
                    836: {
                    837:        if (IsMediumRemovalAllowed() || force) {
                    838:                if (UnloadDisk_internal()) {
                    839:                        // 実際に閉じたら通知
                    840:                        MediaChanged();
                    841:                }
                    842:        }
                    843: }
                    844: 
                    845: // バックエンドのディスクイメージを閉じる (内部用)。
                    846: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
                    847: // ここでは通知は行わない。
                    848: bool
                    849: SCSIDisk::UnloadDisk_internal()
                    850: {
                    851:        if ((bool)image == false) {
                    852:                return false;
                    853:        }
                    854: 
                    855:        Clear();
                    856: 
                    857:        // 取り出し禁止はされてても解除する
                    858:        if (IsMediumRemovalPrevented()) {
                    859:                // XXX PreventMediumRemoval() を呼ぶかどうするか
                    860:                medium_removal_prevented = false;
                    861:        }
                    862: 
                    863:        putlog(1, "Medium unloaded");
                    864:        return true;
                    865: }
                    866: 
                    867: // ディスク状態をクリアする
                    868: // (メディアを取り外した時など)
                    869: void
                    870: SCSIDisk::Clear()
                    871: {
                    872:        medium_loaded = false;
1.1.1.12  root      873:        image.Close();
1.1.1.11  root      874:        pathname.clear();
1.1.1.21  root      875: 
                    876:        if (IsWriteableDevice()) {
                    877:                if (write_ignore) {
                    878:                        write_mode = RW::WriteIgnore;
                    879:                } else {
                    880:                        write_mode = RW::Writeable;
                    881:                }
                    882:        } else {
                    883:                write_mode = RW::ReadOnly;
                    884:        }
1.1.1.11  root      885: }
                    886: 
                    887: // メディア状態変更を UI に通知する
                    888: void
                    889: SCSIDisk::MediaChanged() const
                    890: {
1.1.1.22  root      891:        gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID());
1.1.1.11  root      892: }
                    893: 
                    894: // メディアを挿入する。
                    895: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    896: void
                    897: SCSIDisk::LoadDiskUI(const std::string& pathname_)
                    898: {
                    899:        new_pathname = pathname_;
1.1.1.16  root      900:        scheduler->SendMessage(MessageID::SCSIDEV_LOAD(GetMyID()));
1.1.1.11  root      901: }
                    902: 
                    903: // メディアを排出する。
                    904: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    905: void
                    906: SCSIDisk::UnloadDiskUI(bool force)
                    907: {
1.1.1.16  root      908:        scheduler->SendMessage(MessageID::SCSIDEV_UNLOAD(GetMyID()), force);
1.1.1.11  root      909: }
                    910: 
1.1.1.14  root      911: // メディア挿入メッセージコールバック
1.1.1.11  root      912: void
1.1.1.17  root      913: SCSIDisk::LoadMessage(MessageID msgid, uint32 arg)
1.1.1.11  root      914: {
                    915:        if (LoadDisk(new_pathname) == false) {
                    916:                // 失敗したら UI に通知
1.1.1.22  root      917:                gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_FAILED);
1.1.1.11  root      918:        }
                    919: }
                    920: 
1.1.1.14  root      921: // メディア排出メッセージコールバック
1.1.1.11  root      922: void
1.1.1.17  root      923: SCSIDisk::UnloadMessage(MessageID msgid, uint32 arg)
1.1.1.11  root      924: {
1.1.1.14  root      925:        bool force = arg;
1.1.1.11  root      926:        UnloadDisk(force);
                    927: }
                    928: 
1.1.1.20  root      929: // SCSI コマンドのディスパッチャ。
                    930: // C++ の例外は呼び出し側で捕捉している。
1.1       root      931: void
1.1.1.11  root      932: SCSIDisk::DispatchCmd()
1.1       root      933: {
                    934:        switch (cmdseq[0]) {
                    935:         case SCSI::Command::Read6:                     // 0x08
1.1.1.11  root      936:         case SCSI::Command::Read10:            // 0x28
                    937:                cmd.reset(new SCSICmdRead(this));
1.1       root      938:                break;
                    939: 
                    940:         case SCSI::Command::Write6:            // 0x0a
1.1.1.11  root      941:         case SCSI::Command::Write10:           // 0x2a
                    942:                if (IsWriteableDevice()) {
                    943:                        cmd.reset(new SCSICmdWrite(this));
                    944:                } else {
                    945:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    946:                }
1.1       root      947:                break;
                    948: 
1.1.1.4   root      949:         case SCSI::Command::StartStopUnit:     // 0x1b
                    950:                cmd.reset(new SCSICmdStartStopUnit(this));
                    951:                break;
                    952: 
1.1.1.11  root      953:         case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
                    954:                if (IsRemovableDevice() == false) {
                    955:                        // HD は取り出せないので、とりあえず。
                    956:                        // XXX 要調査
                    957:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    958:                        return;
                    959:                }
                    960:                cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
                    961:                break;
                    962: 
1.1       root      963:         case SCSI::Command::ReadCapacity:      // 0x25
1.1.1.11  root      964:                // DA デバイスでは Read Capacity (0x25)、
                    965:                // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
                    966:                // 今の所同じ動作なので区別せず処理する。
1.1       root      967:                cmd.reset(new SCSICmdReadCapacity(this));
                    968:                break;
                    969: 
1.1.1.11  root      970:         case SCSI::Command::SynchronizeCache:  // 0x35
                    971:                // このクラスは、物理ドライブにあるようなディスクとの間の
                    972:                // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
                    973:                // しろという指示に対しては、黙って成功するだけでいい気がする。
                    974:                // このコマンドはオプションだが NetBSD カーネルが使う。
                    975:                //
                    976:                // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
                    977:                // とりあえず黙って成功しておく。
                    978:                cmd.reset(new SCSICmd(this));
1.1       root      979:                break;
                    980: 
1.1.1.11  root      981:         case SCSI::Command::ReadTOC:           // 0x43
                    982:                if (devtype == SCSI::DevType::CD) {
                    983:                        cmd.reset(new SCSICmdReadTOC(this));
                    984:                } else {
                    985:                        cmd.reset(new SCSICmdNotSupportedCommand(this));
                    986:                }
                    987:                break;
                    988: 
                    989:         case SCSI::Command::ReadFormatCapacities:      // 0x23
                    990:                // SCSI-2 規格でないので詳細不明。
                    991:                // ディスク用か共通かも分からないけど、とりあえず
                    992:         case SCSI::Command::ReadDiscInformation:       // 0x51
                    993:                // CD-ROM デバイスのオプションコマンドで
                    994:                // CD-R ライター用(?)らしいので無視する。
                    995:                cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7   root      996:                break;
                    997: 
1.1       root      998:         default:
                    999:                // 見付からなければ基本クラス側に任せる
                   1000:                inherited::DispatchCmd();
                   1001:                break;
                   1002:        }
                   1003: }
                   1004: 
1.1.1.11  root     1005: // Inquiry データを返す
                   1006: bool
1.1.1.18  root     1007: SCSIDisk::Inquiry(std::vector<uint8>& buf, uint lun)
1.1.1.11  root     1008: {
                   1009:        uint8 inqtype;
                   1010:        const char *prodname;
                   1011: 
                   1012:        switch (devtype) {
                   1013:         case SCSI::DevType::HD:
                   1014:                inqtype = SCSI::InquiryDeviceType::DirectAccess;
                   1015:                prodname = "SCSIHD";
                   1016:                break;
                   1017:         case SCSI::DevType::CD:
                   1018:                inqtype = SCSI::InquiryDeviceType::CDROM;
                   1019:                prodname = "SCSICD";
                   1020:                break;
                   1021:         case SCSI::DevType::MO:
                   1022:                inqtype = SCSI::InquiryDeviceType::MO;
                   1023:                prodname = "SCSIMO";
                   1024:                break;
                   1025:         default:
1.1.1.18  root     1026:                PANIC("Unexpected devtype=%u", (uint)devtype);
1.1.1.11  root     1027:        }
                   1028: 
                   1029:        buf[0] = inqtype;
                   1030:        if (lun != 0) {
                   1031:                buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
                   1032:        }
                   1033:        buf[1] = 0;
                   1034:        if (IsRemovableDevice()) {
                   1035:                buf[1] |= 0x80;
                   1036:        }
                   1037:        buf[2] = 2;             // SCSI-2
                   1038:        buf[3] = 2;             // Response Data Format = SCSI-2
                   1039:        buf[4] = buf.size() - 4;        // 追加データ長
                   1040: 
                   1041:        // 文字列はゼロ終端ではなく、余りは空白で埋める
                   1042:        memset(&buf[8], ' ', 36 - 8);
                   1043:        memcpy(&buf[8],  "NONO", 4);
                   1044:        memcpy(&buf[16], prodname, strlen(prodname));
                   1045:        memcpy(&buf[32], "0", 1);
                   1046: 
                   1047:        return true;
                   1048: }
                   1049: 
                   1050: // ModeSense コマンドのデバイス固有パラメータを返す
                   1051: uint8
                   1052: SCSIDisk::GetDeviceSpecificParam() const
                   1053: {
                   1054:        uint8 param = 0;
                   1055: 
                   1056:        // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
                   1057:        // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
                   1058:        //
                   1059:        //      7   6   5     4    3   2   1   0
                   1060:        //    +---+---+---+------+---+---+---+---+
                   1061:        // DA |WP |   0   |DPOFUA|       0       | WP: Write Protect
                   1062:        //    +---+---+---+------+---+---+---+---+
                   1063:        //
                   1064:        //    +---+---+---+------+---+---+---+---+
                   1065:        // MO |WP |   0   |DPOFUA|     0     |EBC| EBC: Enable Blank Check
                   1066:        //    +---+---+---+------+---+---+---+---+
                   1067:        //
                   1068:        //    +---+---+---+------+---+---+---+---+
                   1069:        // CD |     0     |DPOFUA|       0       |
                   1070:        //    +---+---+---+------+---+---+---+---+
                   1071: 
                   1072:        if (GetDevType() == SCSI::DevType::CD) {
                   1073:                // CD には WP ビットの定義がない
                   1074:        } else {
                   1075:                // DA, MO は WP ビットの定義がある。
                   1076:                if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
                   1077:                        param |= 0x80;
                   1078:                }
                   1079:        }
                   1080: 
                   1081:        return param;
                   1082: }
                   1083: 
                   1084: bool
                   1085: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
                   1086: {
                   1087:        if (__predict_false((bool)image == false)) {
                   1088:                return false;
                   1089:        }
1.1.1.12  root     1090:        return image.Read(buf.data(), start, buf.size());
1.1.1.11  root     1091: }
                   1092: 
                   1093: bool
                   1094: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
                   1095: {
                   1096:        if (__predict_false((bool)image == false)) {
                   1097:                return false;
                   1098:        }
                   1099: 
1.1.1.12  root     1100:        if (GetWriteMode() >= RW::WriteProtect) {
1.1.1.11  root     1101:                // 書き込み不可
                   1102:                return false;
                   1103:        }
1.1.1.12  root     1104:        return image.Write(buf.data(), start, buf.size());
1.1.1.11  root     1105: }
                   1106: 
                   1107: off_t
                   1108: SCSIDisk::GetSize() const
                   1109: {
                   1110:        if (__predict_false((bool)image == false)) {
                   1111:                return 0;
                   1112:        }
1.1.1.12  root     1113:        return image.GetSize();
1.1.1.11  root     1114: }
                   1115: 
1.1.1.2   root     1116: // 指定の場所を自由に読み出す
                   1117: // (エミュレータ特権アクセス)
                   1118: bool
1.1.1.11  root     1119: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2   root     1120: {
1.1.1.11  root     1121:        if (__predict_false((bool)image == false)) {
1.1.1.2   root     1122:                return false;
                   1123:        }
1.1.1.12  root     1124:        return image.Read(buf, start, len);
1.1.1.2   root     1125: }
                   1126: 
1.1.1.11  root     1127: // メディアの取り出し可否(取り出し禁止)を設定する
                   1128: void
                   1129: SCSIDisk::PreventMediumRemoval(bool val)
                   1130: {
                   1131:        // 状態が変わったところでログを表示
                   1132:        if (medium_removal_prevented == false && val == true) {
                   1133:                putlog(1, "Medium removal prevented");
                   1134:        } else if (medium_removal_prevented == true && val == false) {
                   1135:                putlog(1, "Medium removal allowed");
                   1136:        }
                   1137:        medium_removal_prevented = val;
                   1138: }
1.1.1.14  root     1139: 
                   1140: // メディアの書き込みモードを文字列で返す
                   1141: const char *
                   1142: SCSIDisk::GetWriteModeStr() const
                   1143: {
                   1144:        switch (write_mode) {
                   1145:         case RW::ReadOnly:             return "ReadOnly";
                   1146:         case RW::WriteProtect: return "WriteProtected";
                   1147:         case RW::Writeable:    return "Writeable";
                   1148:         case RW::WriteIgnore:  return "WriteIgnore";
                   1149:         default:
1.1.1.16  root     1150:                PANIC("corrupted write_mode=%d", (int)write_mode);
1.1.1.14  root     1151:        }
                   1152: }

unix.superglobalmegacorp.com

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