Annotation of nono/vm/scsibus.cpp, revision 1.1.1.12

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.10  root        7: //
1.1       root        8: // SCSI バス
1.1.1.10  root        9: //
1.1       root       10: 
                     11: #include "scsibus.h"
                     12: #include "scsidev.h"
1.1.1.12! root       13: #include "config.h"
1.1.1.10  root       14: #include "scheduler.h"
1.1       root       15: 
                     16: // time 時間後に呼び出すコールバックをセットする。
                     17: // func の書式が面倒なのを省略して書きたいため。
                     18: #define CallAfter(func_, name_, time_) do { \
1.1.1.10  root       19:        event.func = ToEventCallback(&SCSIBus::func_); \
1.1       root       20:        event.time = time_; \
1.1.1.10  root       21:        event.SetName(name_); \
1.1.1.11  root       22:        scheduler->RestartEvent(event); \
1.1       root       23: } while (0)
                     24: 
                     25: // コンストラクタ
1.1.1.12! root       26: SCSIBus::SCSIBus(SCSIHostDevice *parent_, const char *config_name_)
1.1.1.11  root       27:        : inherited(OBJ_SCSIBUS)
1.1       root       28: {
1.1.1.12! root       29:        parent = parent_;
        !            30:        config_name = config_name_;
        !            31: 
        !            32:        monitor.func = ToMonitorCallback(&SCSIBus::MonitorUpdate);
        !            33:        // サイズは Create で決まる
        !            34:        monitor.Regist(ID_MONITOR_SCSIDEVS);
1.1       root       35: }
                     36: 
                     37: // デストラクタ
                     38: SCSIBus::~SCSIBus()
                     39: {
                     40: }
                     41: 
1.1.1.12! root       42: // 動的なコンストラクション
        !            43: bool
        !            44: SCSIBus::Create()
        !            45: {
        !            46:        const char *name = GetConfigName();
        !            47: 
        !            48:        // 設定を読み込む。
        !            49:        for (int id = 0; id < 8; id++) {
        !            50:                const std::string key = string_format("%s-id%u-image", name, id);
        !            51:                const ConfigItem& item = gConfig->Find(key);
        !            52:                const std::string& image = item.AsString();
        !            53: 
        !            54:                // 空ならデバイスなし
        !            55:                if (image.empty()) {
        !            56:                        // 未接続部分のパラメータは減らしておくか。
        !            57:                        // --show-config するとさすがに無駄に多くて邪魔なので。
        !            58:                        const std::string wikey = string_format("%s-id%u-writeignore",
        !            59:                                name, id);
        !            60:                        const std::string wpkey = string_format("%s-id%u-writeprotect",
        !            61:                                name, id);
        !            62:                        const std::string stkey = string_format("%s-id%u-seektime",
        !            63:                                name, id);
        !            64:                        gConfig->Delete(wikey);
        !            65:                        gConfig->Delete(wpkey);
        !            66:                        gConfig->Delete(stkey);
        !            67:                        continue;
        !            68:                }
        !            69: 
        !            70:                // ',' より前がデバイス種別 (ここではパスは不要)
        !            71:                auto v = string_split(image, ',');
        !            72:                // 比較のため前後の空白を取り除いて小文字にする
        !            73:                std::string type = string_trim(v[0]);
        !            74:                type = string_tolower(type);
        !            75: 
        !            76:                SCSI::DevType target_devtype;
        !            77:                if (type == "hd") {
        !            78:                        target_devtype = SCSI::DevType::HD;
        !            79:                } else if (type == "cd") {
        !            80:                        target_devtype = SCSI::DevType::CD;
        !            81:                } else if (type == "mo") {
        !            82:                        target_devtype = SCSI::DevType::MO;
        !            83:                } else {
        !            84:                        item.Err("Invaild device type '%s'", type.c_str());
        !            85:                        return false;
        !            86:                }
        !            87:                target[id].reset(new SCSIDisk(this, id, target_devtype));
        !            88:                AttachDevice(target[id].get(), id);
        !            89:        }
        !            90: 
        !            91:        // ここの connected_devices はイニシエータを含んでいない数
        !            92:        monitor.SetSize(75, connected_devices.size() * 4 + 1);
        !            93: 
        !            94:        return true;
        !            95: }
        !            96: 
        !            97: // 初期化
        !            98: bool
        !            99: SCSIBus::Init()
        !           100: {
        !           101:        if (inherited::Init() == false) {
        !           102:                return false;
        !           103:        }
        !           104: 
        !           105:        event.SetName("SCSIBus");       // 複数出来たら名前どうするべか
        !           106:        scheduler->RegistEvent(event);
        !           107: 
        !           108:        return true;
        !           109: }
        !           110: 
1.1.1.10  root      111: // 電源オン/リセット
1.1.1.4   root      112: void
1.1.1.10  root      113: SCSIBus::ResetHard(bool poweron)
1.1.1.4   root      114: {
1.1.1.10  root      115:        if (poweron) {
                    116:                phase = SCSI::Phase::BusFree;
                    117:                rst = false;
                    118:                req = false;
                    119:                ack = false;
                    120:                atn = false;
                    121:                sel = 0;
                    122:                bsy = 0;
                    123:                xfer = 0;
                    124:                data = 0;
                    125: 
                    126:                last_req_time = 0;
                    127:                req_wait = 0;
                    128:                init_id = -1;
                    129:                target_id = -1;
                    130:        }
                    131: 
1.1.1.4   root      132:        // ここでのイベントは実際にはイニシエータかターゲットのどちらかが
                    133:        // 時間を伴う処理をしている最中であることを示しているものなので、
                    134:        // イニシエータがリセットされたこととバスの動作とは本来関係ないのだが
                    135:        // ここでは特にイニシエータに次のバイトを転送させる、という目的で
                    136:        // タイマーイベントが使われており、本体をリセットしたのに入れ違いで
                    137:        // イニシエータの転送処理が呼び出されてしまうというような事故を
                    138:        // 防ぐためにもここでタイマーを停止する。
1.1.1.11  root      139:        scheduler->StopEvent(event);
1.1.1.4   root      140: }
                    141: 
1.1.1.12! root      142: // デバイスを接続する。
        !           143: // id に先客がいれば false を返す。成功すれば true を返す。
1.1       root      144: bool
1.1.1.12! root      145: SCSIBus::AttachDevice(SCSIDevice *dev, int id)
1.1       root      146: {
                    147:        if (device[id] != NULL) {
                    148:                return false;
                    149:        }
1.1.1.12! root      150:        putlog(3, "Attach #%u", id);
1.1       root      151: 
                    152:        device[id] = dev;
1.1.1.12! root      153:        MakeConnectedDevices();
        !           154: 
        !           155:        // 紐付けをデバイスにも通知。
        !           156:        dev->OnAttached(this, id);
1.1       root      157: 
                    158:        return true;
                    159: }
                    160: 
1.1.1.12! root      161: // device[] から connected_device を作り直す。
        !           162: // device[] はイニシエータを含む固定長の配列。
        !           163: // connected_device はイニシエータを (あれば) 含む ID 順の可変長リスト。
        !           164: void
        !           165: SCSIBus::MakeConnectedDevices()
        !           166: {
        !           167:        connected_devices.clear();
        !           168: 
        !           169:        for (int id = 0; id < 8; id++) {
        !           170:                if (device[id]) {
        !           171:                        connected_devices.push_back(device[id]);
        !           172:                }
        !           173:        }
        !           174: }
        !           175: 
        !           176: // 接続済みの (イニシエータを除く) デバイス数を返す。
        !           177: uint
        !           178: SCSIBus::GetTargetCount() const
        !           179: {
        !           180:        uint count = 0;
        !           181: 
        !           182:        for (int id = 0; id < 8; id++) {
        !           183:                if ((bool)target[id]) {
        !           184:                        count++;
        !           185:                }
        !           186:        }
        !           187: 
        !           188:        return count;
        !           189: }
        !           190: 
1.1       root      191: // RST を上げる
                    192: void
                    193: SCSIBus::AssertRST()
                    194: {
                    195:        putlog(4, "%s", __func__);
                    196: 
                    197:        // すでに上がってたら何もしない
                    198:        if (rst) {
                    199:                return;
                    200:        }
                    201: 
                    202:        rst = true;
                    203: 
                    204:        // 即バスリセットを全員に通知
                    205:        for (int i = 0; i < 8; i++) {
                    206:                if (device[i]) {
1.1.1.12! root      207:                        device[i]->OnBusReset();
1.1       root      208:                }
                    209:        }
                    210: 
                    211:        // コールバック終了後に内部状態もクリア
                    212:        init_id = -1;
                    213:        target_id = -1;
                    214: }
                    215: 
                    216: // RST を下げる
                    217: void
                    218: SCSIBus::NegateRST()
                    219: {
                    220:        putlog(4, "%s", __func__);
                    221: 
                    222:        rst = false;
                    223: 
                    224:        // 特にすることはない
                    225: }
                    226: 
                    227: // SEL を上げる
                    228: void
                    229: SCSIBus::AssertSEL(uint id)
                    230: {
1.1.1.12! root      231:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      232: 
                    233:        // すでに上がってたら何もしない
                    234:        if ((sel & (1 << id))) {
                    235:                return;
                    236:        }
                    237: 
                    238:        sel |= (1 << id);
                    239: 
                    240:        if (GetPhase() == SCSI::Phase::BusFree) {
                    241:                // バスフリーフェーズなら、アービトレーションなしでセレクション
                    242:                // フェーズに移行。
                    243:                //
                    244:                // 仕様Phase      |Free |<---- Selection ---->|
                    245:                //
                    246:                // BSY       ~~\__________________________/~~~~~~~
                    247:                //
                    248:                // Data      -----------< Initiator&Target ID >---
                    249:                //
                    250:                // SEL       ________________/~~~~~~~~~~~~~~~~\___
                    251:                //                           ^イマココ
                    252:                //
                    253:                // 実装Phase      |<- Free ->|<-- Selection ->|
                    254:                //
                    255:                // SCSI 本にはデータバスが変化したところから Selection フェーズと
                    256:                // 書いてあるように読めるけど(上)、ここでは SEL がアサートされた
                    257:                // ところからとする(下)。
                    258: 
                    259:                SetPhase(SCSI::Phase::Selection);
                    260: 
                    261:                // SEL を上げた人がイニシエータ
                    262:                init_id = id;
                    263: 
                    264:                // 即セレクション開始。
                    265:                CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
                    266: 
                    267:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    268:                // アービトレーションフェーズなら、
                    269:                // アービトレーションに勝ってセレクションフェーズに移行途中。
                    270:                // この場合はセレクションフェーズに移行するのはここではなく
                    271:                // BSY の立ち下がりなので、そっち参照。
                    272:                //
                    273:                // 仕様Phase Arbitration |---|<--- Selection -->|
                    274:                //
                    275:                // BSY       /~~~~~~~~~~~~~~~\_______________/~~~~~~
                    276:                //
                    277:                // Data      <InitiatorID>< Initiator&Target ID >---
                    278:                //
                    279:                // SEL       __________/~~~~~~~~~~~~~~~~~~~~~~~~\___
                    280:                //                     ^イマココ
                    281:                //
                    282:                // 実装Phase <-Arbitration ->|<--- Selection -->|
                    283: 
                    284:                // SEL を上げた人がイニシエータなことだけはここで覚えておく。
                    285:                init_id = id;
                    286: 
                    287:        } else {
1.1.1.6   root      288:                // それ以外(転送フェーズ)中に SEL が立てられたらどうすればいいか。
                    289:                // これは転送中に LUNA をリセットすると起きる。LUNA の PROM は SPC に
                    290:                // 対してバスリセットを発行せずに、セレクションを行おうとするため。
                    291:                // SCSI 本には載ってないので分からないが、このような規約違反はたいてい
                    292:                // イニシエータが検出してバスフリーかバスリセットを行うことが意図
                    293:                // されているんじゃないかと想像するけど、今回そのイニシエータが違反
                    294:                // してるので、どうするんだろうね。
                    295:                // 賢いターゲットデバイスだとこれを検出して自主的にバスフリーへ落ちる
                    296:                // だろうか。賢くないターゲットデバイスは検出せずにそのまま (そして
                    297:                // デッドロック) になるだろうか。
                    298: 
1.1.1.7   root      299: #if 1
                    300:                // XXX とりあえず LUNA で SCSI がなんか刺さった時に本体リセットでも
                    301:                // 直らない状況を真似するため、ターゲットは何もしないでおく。
                    302: #else
1.1.1.6   root      303:                // ターゲットデバイスは賢いので、全員イニシエータ様に忖度して
                    304:                // 即座にバスフリーへ移行するというのはどうか。
                    305:                for (uint i = 0; i < 8; i++) {
                    306:                        if (i != id) {
                    307:                                NegateBSY(i);
                    308:                        }
                    309:                }
1.1.1.7   root      310: #endif
1.1       root      311:        }
                    312: }
                    313: 
                    314: // SEL を下げる
                    315: void
                    316: SCSIBus::NegateSEL(uint id)
                    317: {
1.1.1.12! root      318:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      319: 
                    320:        // すでに下りてたら何もしない
                    321:        if ((sel & (1 << id)) == 0) {
                    322:                return;
                    323:        }
                    324: 
                    325:        sel &= ~(1 << id);
                    326: 
1.1.1.6   root      327:        if (GetPhase() == SCSI::Phase::BusFree) {
                    328:                // バスフリーフェーズで SEL を下げる
                    329: 
                    330:                // 通常は起きないはずだけど、転送フェーズ中に LUNA をリセットすると
                    331:                // -> LUNA の PROM が SEL を上げる (プロトコル違反)
                    332:                // -> ターゲットは転送フェーズ中なのに SEL が上がったのを検出して
                    333:                //    BSY を取り下げてバスフリーに移行する
                    334:                // -> バスフリーがイニシエータに通知され、イニシエータが SEL を下げる
                    335:                // となるので、このケースはスルーしてよい。
                    336: 
                    337:        } else if (GetPhase() == SCSI::Phase::Selection) {
1.1       root      338:                // セレクションフェーズで SEL を下げる
                    339:                if (bsy) {
                    340:                        // BSY が立っていれば、ターゲットがセレクションに応答したので
                    341:                        // イニシエータが SEL を下げて、これから任意時間後にターゲットが
                    342:                        // 情報転送フェーズに移行する。
                    343: 
                    344:                        CallAfter(StartTransfer, "SCSIBus Transfer Start", 400_nsec);
                    345:                } else {
                    346:                        // BSY が立ってなければ、ターゲットが応答しなかったことに
                    347:                        // イニシエータがあきらめて SEL を下げたので、何もせず
                    348:                        // バスフリーフェーズに移行。
                    349:                        BusFree(id);
                    350:                }
                    351:        } else {
1.1.1.6   root      352:                // 下げる分には無視してもいいはずだけど、どうする?
1.1.1.10  root      353:                VMPANIC("NegateSEL in %s phase", GetPhaseName());
1.1       root      354:        }
                    355: }
                    356: 
                    357: // BSY を上げる
                    358: void
                    359: SCSIBus::AssertBSY(uint id)
                    360: {
1.1.1.12! root      361:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      362: 
                    363:        // すでに上がってたら何もしない
                    364:        if ((bsy & (1 << id))) {
                    365:                return;
                    366:        }
                    367: 
                    368:        bsy |= (1 << id);
                    369: 
                    370:        if (GetPhase() == SCSI::Phase::BusFree) {
                    371:                // バスフリーフェーズだったらアービトレーション開始
                    372: 
                    373:                // 実際のアービトレーションは、
                    374:                // バスフリーを検出してから規定時間経過後に、データバスに自身の ID を
                    375:                // 出して BSY を立てる。規定時間内に他のデバイスもアービトレーションに
                    376:                // 参加すれば ID の高い方が勝つ。負けた方は規定時間内にアービトレー
                    377:                // ションを取り下げる。規定期間経って勝っていればアービトレーション
                    378:                // 成功となり、セレクションフェーズに移る。
                    379:                // のだが、エミュレータ上では、今の所アービトレーションで競合が起きる
                    380:                // ことはないため、アービトレーションは規定時間経過して必ず成功する。
                    381:                // ちゃんとしたアービトレーションを実装する場合はたぶんこれだけでは
                    382:                // 済まないと思うので書き換えること。
                    383: 
                    384:                SetPhase(SCSI::Phase::Arbitration);
                    385: 
                    386:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    387:                // アービトレーションフェーズだったら、アービトレーションに参加。
                    388:                // 今は起きない。
1.1.1.12! root      389:                VMPANIC("AssertBSY(#%u) in arbitration", id);
1.1       root      390: 
                    391:        } else if (GetPhase() == SCSI::Phase::Selection) {
                    392:                // セレクションフェーズだったら、転送フェーズに移行中。
                    393:                // ターゲットが BSY をアサートしたので次は規定時間経過後に
                    394:                // SelectionAck() を呼び出してイニシエータに SEL を下げさせる。
                    395: 
                    396:                CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec);
                    397:        } else {
                    398:                // どうする?
1.1.1.12! root      399:                VMPANIC("AssertBSY(#%u) in %s phase", id, GetPhaseName());
1.1       root      400:        }
                    401: }
                    402: 
                    403: // BSY を下げる
                    404: void
                    405: SCSIBus::NegateBSY(uint id)
                    406: {
1.1.1.12! root      407:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      408: 
                    409:        // すでに下りてたら何もしない
                    410:        if ((bsy & (1 << id)) == 0) {
                    411:                return;
                    412:        }
                    413: 
                    414:        bsy &= ~(1 << id);
                    415: 
                    416:        if (GetPhase() == SCSI::Phase::Transfer) {
                    417:                // 転送フェーズから BSY を下げたらバスフリーに移行。
                    418:                BusFree(id);
                    419: 
                    420:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    421:                // アービトレーションフェーズなら、
                    422:                // イニシエータが BSY を下ろすことでセレクションフェーズに移行。
                    423:                //
                    424:                // この少し上の AssertSEL() 中のタイムチャート参照。
                    425: 
                    426:                SetPhase(SCSI::Phase::Selection);
                    427: 
                    428:                // 即セレクション開始。
                    429:                CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
                    430: 
                    431:        } else {
                    432:                // どうする?
1.1.1.12! root      433:                VMPANIC("NegateBSY(#%u) in %s phase", id, GetPhaseName());
1.1       root      434:        }
                    435: }
                    436: 
                    437: // MSG, CD, IO 信号線を変更する。
                    438: void
                    439: SCSIBus::SetXfer(uint8 val)
                    440: {
                    441:        uint8 oldxfer = xfer;
                    442: 
                    443:        xfer = val & (SCSI::MSG | SCSI::CD | SCSI::IO);
                    444:        if (oldxfer != xfer) {
                    445:                if (GetPhase() == SCSI::Phase::Transfer) {
                    446:                        putlog(4, "SetXfer(%x:%s)", xfer, SCSI::GetXferPhaseName(xfer));
                    447:                } else {
                    448:                        putlog(4, "SetXfer(%x)", xfer);
                    449:                }
                    450:        }
                    451: }
                    452: 
                    453: // REQ を上げる
                    454: void
                    455: SCSIBus::AssertREQ()
                    456: {
                    457:        if (GetPhase() == SCSI::Phase::Transfer) {
                    458:                // REQ を上げると言いつつ、ここで転送レートの時間調整をしているので
                    459:                // (scsibus.h の解説参照) REQ を実際に上げるのはイベント発生後。
                    460:                // XXX 本来ターゲットデバイスがタイマー持っててやるべきだけど、
                    461:                // イベントがもう一つ増えるのもうざいので。どのみち、ここの REQ
                    462:                // 通知はイベントにしないといけないので。
                    463: 
                    464:                uint64 after = 0;
1.1.1.11  root      465:                uint64 now = scheduler->GetVirtTime();
1.1       root      466:                if (now < last_req_time + 1_usec) {
                    467:                        after = (last_req_time + 1_usec) - now;
                    468:                }
1.1.1.4   root      469:                // ウェイトが指示されていれば入れる (シークタイムなど)
                    470:                if (req_wait != 0) {
                    471:                        after += req_wait;
                    472:                        req_wait = 0;
                    473:                }
1.1.1.9   root      474:                last_req_time = now + after;
1.1       root      475:                CallAfter(TransferReq, "SCSIBus AssertREQ", after);
                    476:        } else {
                    477:                // 転送フェーズでないのに上げることはないと思うけど
                    478:                // とりあえず通知もせずその場で上げておくか?
                    479:                putlog(4, "%s", __func__);
                    480:                req = true;
                    481:        }
                    482: }
                    483: 
                    484: // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて
1.1.1.12! root      485: // イニシエータの OnTransferReq を呼び出すところ。
1.1       root      486: void
1.1.1.4   root      487: SCSIBus::TransferReq(Event& ev)
1.1       root      488: {
                    489:        putlog(4, "AssertREQ");
                    490:        req = true;
                    491: 
                    492:        assert(device[init_id]);
1.1.1.12! root      493:        device[init_id]->OnTransferReq();
1.1       root      494: }
                    495: 
                    496: // REQ を下げる
                    497: void
                    498: SCSIBus::NegateREQ()
                    499: {
                    500:        putlog(4, "%s", __func__);
                    501:        req = false;
                    502: }
                    503: 
                    504: // ACK を上げる
                    505: void
                    506: SCSIBus::AssertACK()
                    507: {
                    508:        putlog(4, "%s", __func__);
                    509:        ack = true;
                    510: 
                    511:        if (GetPhase() == SCSI::Phase::Transfer) {
                    512:                assert(device[target_id]);
1.1.1.12! root      513:                device[target_id]->OnTransfer();
1.1       root      514:        }
                    515: }
                    516: 
                    517: // ACK を下げる
                    518: void
                    519: SCSIBus::NegateACK()
                    520: {
                    521:        putlog(4, "%s", __func__);
                    522:        ack = false;
                    523: 
                    524:        if (GetPhase() == SCSI::Phase::Transfer) {
                    525:                assert(device[target_id]);
1.1.1.12! root      526:                device[target_id]->OnTransferAck();
1.1       root      527:        }
                    528: }
                    529: 
                    530: // ATN を上げる
                    531: void
                    532: SCSIBus::AssertATN()
                    533: {
                    534:        putlog(4, "%s", __func__);
                    535:        atn = true;
                    536: }
                    537: 
                    538: // ATN を下げる
                    539: void
                    540: SCSIBus::NegateATN()
                    541: {
                    542:        putlog(4, "%s", __func__);
                    543:        atn = false;
                    544: }
                    545: 
                    546: // バスフリーフェーズに移行。
                    547: // id はこのバスフリーを行ったデバイス (0..7)
                    548: void
1.1.1.12! root      549: SCSIBus::BusFree(uint id)
1.1       root      550: {
                    551:        putlog(4, "%s", __func__);
                    552: 
                    553:        SetPhase(SCSI::Phase::BusFree);
                    554: 
                    555:        // 即全デバイスに通知。
                    556:        for (int i = 0; i < countof(device); i++) {
                    557:                if (device[i]) {
1.1.1.12! root      558:                        device[i]->OnBusFree(id);
1.1       root      559:                }
                    560:        }
                    561: 
                    562:        // コールバック終了後に内部状態もクリア
                    563:        init_id = -1;
                    564:        target_id = -1;
                    565: 
                    566:        // 通知された各デバイスは規定時間内に他の信号線も取り下げなければ
                    567:        // ならない。
                    568: }
                    569: 
                    570: // セレクションから情報転送フェーズまで
                    571: //
                    572: // 仕様Phase <- Selection ->|
                    573: //
                    574: // BSY       _______________/~~~~~~~~~
                    575: //
                    576: // Data      Initiator&Target ID >----
                    577: //
                    578: // SEL       ~~~~~~~~~~~~~~~~~~~~~~\__
                    579: //
                    580: //           -(1)->|--(2)-->|-(3)->|
                    581: //
                    582: // セレクションフェーズ開始後、(ターゲット)デバイスは、SEL がアサート、
                    583: // (IO がネゲート)、データバスの自身の ID がアサート、が 400nsec 以上
                    584: // 継続することで、自身がターゲットに選択されていることを認識する。(1)
                    585: // ターゲットは (1) から 200usec 以内に BSY をアサートする。(2)
                    586: // イニシエータは (2) から 90nsec 以上後に SEL (とデータ) を下げる。(3)
                    587: //
                    588: // 実装では、セレクションフェーズ開始が SelectionStart() の呼び出しに対応。
                    589: //
                    590: // ターゲットデバイスが存在していない場合はタイムアウトとなるが、これは
                    591: // ホストが処理するのでバスは何もしない。
                    592: // ターゲットデバイスが存在していれば SelectionStart() から 400nsec 後 +
                    593: // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック
                    594: // を呼び出す(1+2)。
                    595: //
1.1.1.12! root      596: // Selection() コールバック(が呼び出す device->OnSelected()) は通常 BSY を
1.1       root      597: // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック
                    598: // を呼び出す(3)。
                    599: //
1.1.1.12! root      600: // SelectionAck() コールバック(が呼び出す device->OnSelectionAck()) は通常
1.1       root      601: // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に
                    602: // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして
                    603: // デバイスを情報転送フェーズに移行させる。
                    604: 
                    605: // セレクションフェーズ開始。
                    606: void
1.1.1.4   root      607: SCSIBus::SelectionStart(Event& ev)
1.1       root      608: {
                    609:        // この時点でデータバスには自身の ID と相手の ID が立っているはず。
                    610:        // 一方 sel は SEL 信号線を立てた人のビットを持っているので、
                    611:        // これでターゲットを特定する。
                    612:        uint target_mask = data ^ sel;
                    613:        if (__builtin_popcount(target_mask) != 1) {
                    614:                // データバスから自身を除いた結果立ってるビットが1個以外だったら
                    615:                // 誰も応答しないので、何もせず帰ってよい。
1.1.1.2   root      616:                putlog(3, "%s (No target specified; databus=$%02x)", __func__, data);
1.1       root      617:                return;
                    618:        }
                    619: 
                    620:        // ターゲット ID はここで取得
                    621:        target_id = DecodeID(target_mask);
                    622:        if (device[target_id] == NULL) {
                    623:                // ターゲットデバイスがいなければ、バスは何もしない。
                    624:                // (その結果イニシエータでタイムアウトが起きるはず)
                    625:                putlog(4, "%s (No target)", __func__);
                    626:                return;
                    627:        }
                    628: 
                    629:        // ターゲットデバイスがいれば、規定時間経過後ターゲットに通知。
                    630:        putlog(4, "%s", __func__);
                    631:        CallAfter(Selected, "SCSIBus Selected", 100_usec);
                    632: }
                    633: 
                    634: // SelectionStart() にてターゲットが選択されて規定時間経ったので
                    635: // そろそろターゲットが BSY を立てる頃に呼ばれるコールバック。
                    636: void
1.1.1.4   root      637: SCSIBus::Selected(Event& ev)
1.1       root      638: {
                    639:        // ターゲットに通知。ターゲットがいることは確定している。
                    640:        assert(device[target_id]);
1.1.1.12! root      641:        device[target_id]->OnSelected();
1.1       root      642: }
                    643: 
                    644: // ターゲットが BSY を立てて規定時間経ったので
1.1.1.4   root      645: // そろそろイニシエータが SEL を下げる頃に呼ばれるコールバック。
1.1       root      646: void
1.1.1.4   root      647: SCSIBus::SelectionAck(Event& ev)
1.1       root      648: {
                    649:        // イニシエータに通知。
                    650:        assert(device[init_id]);
1.1.1.12! root      651:        device[init_id]->OnSelectionAck();
1.1       root      652: }
                    653: 
                    654: // 情報転送フェーズ開始を通知する。
                    655: // BSY を上げて情報転送フェーズに移行したところで呼ばれる。
                    656: void
1.1.1.4   root      657: SCSIBus::StartTransfer(Event& ev)
1.1       root      658: {
                    659:        SetPhase(SCSI::Phase::Transfer);
                    660: 
                    661:        assert(device[target_id]);
1.1.1.12! root      662:        device[target_id]->OnStartTransfer();
        !           663: }
        !           664: 
        !           665: // SCSI デバイスモニタ
        !           666: // (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので
        !           667: // とりあえずここで)
        !           668: void
        !           669: SCSIBus::MonitorUpdate(Monitor *, TextScreen& screen)
        !           670: {
        !           671:        int y;
        !           672: 
        !           673: // 012345678901234567890123456789012345678901234567890123456789012345678901234
        !           674: // ID6: HD(2048MB)
        !           675: //      MediumLoaded   Off      LogicalBlock 2048    ImageSize   2,147,483,648
        !           676: //      RemovalPrevent Off      WriteProtected
        !           677: 
        !           678: 
        !           679:        screen.Clear();
        !           680: 
        !           681:        y = 0;
        !           682:        for (const auto dev : connected_devices) {
        !           683:                std::string desc = SCSI::GetDevTypeName(dev->GetDevType());
        !           684:                std::string pathname;
        !           685: 
        !           686:                const SCSIDisk *disk = dynamic_cast<const SCSIDisk*>(dev);
        !           687:                if (disk && disk->IsMediumLoaded()) {
        !           688:                        desc += string_format("(%uMB)",
        !           689:                                (uint)(disk->GetSize() / 1024 / 1024));
        !           690:                        pathname = disk->GetPathName();
        !           691:                }
        !           692:                screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str());
        !           693: 
        !           694: #if 1
        !           695:                (void)pathname;
        !           696: #else
        !           697:                // パス名の非ASCIIどうするか
        !           698:                if (!pathname.empty()) {
        !           699:                        if (pathname.size() > screen.GetCol() - 16 - 3) {
        !           700:                                pathname = pathname.substr(0, screen.GetCol() - 16 - 3);
        !           701:                                pathname += "...";
        !           702:                        }
        !           703:                        screen.Puts(16, y, pathname.c_str());
        !           704:                }
        !           705: #endif
        !           706:                y++;
        !           707: 
        !           708:                // イニシエータならここまで。(今の所イニシエータとディスクしかない)
        !           709:                if (disk == NULL) {
        !           710:                        continue;
        !           711:                }
        !           712: 
        !           713:                const int x1 = 5;
        !           714:                const int x2 = 29;
        !           715:                const int x3 = 50;
        !           716: 
        !           717:                // 1行目
        !           718:                screen.Print(x1, y,
        !           719:                        (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
        !           720:                        "MediumLoaded   %s",
        !           721:                        disk->IsMediumLoaded() ? "On" : "Off");
        !           722:                screen.Print(x2, y, "LogicalBlock %u", disk->GetBlocksize());
        !           723:                std::string imagesize = format_number(disk->GetSize());
        !           724:                screen.Print(x3, y, "ImageSize   %13s", imagesize.c_str());
        !           725:                y++;
        !           726: 
        !           727:                // 2行目
        !           728:                screen.Print(x1, y,
        !           729:                        (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
        !           730:                        "RemovalPrevent %s",
        !           731:                        disk->IsMediumRemovalPrevented() ? "On" : "Off");
        !           732: 
        !           733:                screen.Puts(x2, y,
        !           734:                        (disk->IsMediumLoaded() ? TA::Normal : TA::Disable),
        !           735:                        disk->GetWriteModeStr());
        !           736:                y++;
        !           737: 
        !           738:                y++;
        !           739:        }
1.1       root      740: }

unix.superglobalmegacorp.com

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