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

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

unix.superglobalmegacorp.com

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