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

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

unix.superglobalmegacorp.com

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