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

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

unix.superglobalmegacorp.com

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