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

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

unix.superglobalmegacorp.com

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