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

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: 
1.1.1.15! root       11: // フェーズ遷移についてのメモ。
        !            12: //
        !            13: // イニシエータ(この時点ではまだ候補だが)が BSY を立てることで
        !            14: // (バスフリーフェーズから) アービトレーションフェーズに移る。
        !            15: // 現行では規定時間後に自動的にイニシエータが勝利する(バスは何もしない)。
        !            16: // イニシエータが SEL 立てて BSY を下ろすことで、セレクションフェーズに移る。
        !            17: // この間の遷移は以下の通り。
        !            18: // BSY SEL
        !            19: //   0   0 BusFree
        !            20: //         ↓ イニシエータ(候補)が BSY を立てる
        !            21: //   1   0 Arbitration (バスは何もしない)
        !            22: //         ↓ イニシエータが SEL を立てる
        !            23: //   1   1 Arbitration
        !            24: //         ↓ イニシエータがデータバスにターゲットをセットして BSY を下ろす
        !            25: //   0   1 Selection
        !            26: //
        !            27: // バスはセレクションフェーズ遷移後、規定時間でターゲットに通知。
        !            28: // ターゲットは即 BSY を立てるのでバスはこれをイニシエータに通知。
        !            29: // イニシエータは SEL を下げるのでバスはこれをターゲットに通知。
        !            30: // この間の遷移は以下の通り。
        !            31: // BSY SEL
        !            32: //   0   1 Selection (ターゲットに通知)
        !            33: //         ↓ ターゲットが BSY を立てる
        !            34: //   1   1 Selection (イニシエータに通知)
        !            35: //         ↓ イニシエータがデータバスと SEL を下げる
        !            36: //   1   0 Transfer (ターゲットに通知)
        !            37: //
        !            38: // バスは Transfer 後、規定時間でターゲットに通知。
        !            39: // ターゲットは転送フェーズ(の最初のコマンドフェーズ) に移行する。
        !            40: 
        !            41: // 転送フェーズについてのメモ。
        !            42: //
        !            43: //       -----> (実線) は関数呼び出し
        !            44: //       - - -> (破線) は関数からの戻り
        !            45: //       〜〜-> (波線) はイベントコールバックによる呼び出し
        !            46: //
        !            47: // マニュアル転送は以下のようになる。
        !            48: // 基本的にはターゲットが REQ を上げたことをイベントでイニシエータに通知し、
        !            49: // イニシエータは基本的に関数呼び出しでターゲットを操作することにする。
        !            50: //
        !            51: // 非同期転送時の REQ/ACK シーケンスに時間規定はないが、時間を考慮せずに
        !            52: // 実行してしまうと現実より速い転送が出来てしまう可能性があるので、ここでは
        !            53: // AssertREQ が TransferReqCB を呼び出すところでタイミングを調整する。
        !            54: // 具体的には前回の TransferReqCB から 1usec 以上空けるというのはどうか。
        !            55: //
        !            56: //       イニシエータ                        ターゲット
        !            57: //       ------------                        ----------
        !            58: //                              Event 〜〜-> StartTransferCB
        !            59: //                                            ↓
        !            60: //                                           REQ を上げる
        !            61: //       TransferReqCB <-〜〜〜 Event 〜〜〜
        !            62: //       ACK を上げる
        !            63: //                  +------ AssertACK() ---> TransferCB
        !            64: //                                           1バイト送受信
        !            65: //                                           REQ を下げる
        !            66: //                  < - - - - - - - - - - -  return;
        !            67: //       (誰かが)
        !            68: //       ACK を下げる
        !            69: //                  +------ NegateACK() ---> TransferAckCB
        !            70: //                                           REQ を上げる 〜〜+
        !            71: //                  < - - - - - - - - - - -  return;          :
        !            72: //                                                            :
        !            73: //       TransferReqCB <-〜〜〜 Event 〜〜〜〜〜〜〜〜〜〜〜〜+
        !            74: //
        !            75: // ハードウェア転送の場合は ACK 一往復を省略して以下のようにする。
        !            76: // Transfer() (TransferCB()) は ACK が立っていなければハードウェア転送と
        !            77: // 判断する。
        !            78: //
        !            79: //       イニシエータ                        ターゲット
        !            80: //       ------------                        ----------
        !            81: //                              Event 〜〜-> StartTransferCB
        !            82: //                                            ↓
        !            83: //                                           REQ を上げる
        !            84: //       TransferReqCB <-〜〜〜 Event 〜〜〜
        !            85: //                  |
        !            86: //                  +------- Transfer() ---> TransferCB
        !            87: //                                           1バイト送受信
        !            88: //                                           REQ を下げる
        !            89: //                                           REQ を上げる 〜〜+
        !            90: //                  < - - - - - - - - - - -  return;          :
        !            91: //                                                            :
        !            92: //       TransferReqCB <-〜〜〜 Event 〜〜〜〜〜〜〜〜〜〜〜〜+
        !            93: //
        !            94: 
1.1       root       95: #include "scsibus.h"
                     96: #include "scsidev.h"
1.1.1.15! root       97: #include "scsidomain.h"
1.1.1.10  root       98: #include "scheduler.h"
1.1       root       99: 
                    100: // time 時間後に呼び出すコールバックをセットする。
                    101: // func の書式が面倒なのを省略して書きたいため。
                    102: #define CallAfter(func_, name_, time_) do { \
1.1.1.10  root      103:        event.func = ToEventCallback(&SCSIBus::func_); \
1.1       root      104:        event.time = time_; \
1.1.1.10  root      105:        event.SetName(name_); \
1.1.1.11  root      106:        scheduler->RestartEvent(event); \
1.1       root      107: } while (0)
                    108: 
                    109: // コンストラクタ
1.1.1.15! root      110: SCSIBus::SCSIBus(SCSIDomain *domain_)
1.1.1.11  root      111:        : inherited(OBJ_SCSIBUS)
1.1       root      112: {
1.1.1.15! root      113:        domain = domain_;
1.1       root      114: }
                    115: 
                    116: // デストラクタ
                    117: SCSIBus::~SCSIBus()
                    118: {
                    119: }
                    120: 
1.1.1.12  root      121: // 初期化
                    122: bool
                    123: SCSIBus::Init()
                    124: {
1.1.1.15! root      125:        // 現時点で管理下にあるデバイス(ターゲット)をバスに接続。
        !           126:        // イニシエータはこの時点では ID 未定なのでバスには繋がらない。
        !           127:        Refresh();
        !           128: 
1.1.1.12  root      129:        event.SetName("SCSIBus");       // 複数出来たら名前どうするべか
                    130:        scheduler->RegistEvent(event);
                    131: 
                    132:        return true;
                    133: }
                    134: 
1.1.1.10  root      135: // 電源オン/リセット
1.1.1.4   root      136: void
1.1.1.10  root      137: SCSIBus::ResetHard(bool poweron)
1.1.1.4   root      138: {
1.1.1.10  root      139:        if (poweron) {
                    140:                phase = SCSI::Phase::BusFree;
                    141:                rst = false;
                    142:                req = false;
                    143:                ack = false;
                    144:                atn = false;
                    145:                sel = 0;
                    146:                bsy = 0;
                    147:                xfer = 0;
                    148:                data = 0;
                    149: 
                    150:                last_req_time = 0;
                    151:                req_wait = 0;
                    152:                init_id = -1;
                    153:                target_id = -1;
                    154:        }
                    155: 
1.1.1.4   root      156:        // ここでのイベントは実際にはイニシエータかターゲットのどちらかが
                    157:        // 時間を伴う処理をしている最中であることを示しているものなので、
                    158:        // イニシエータがリセットされたこととバスの動作とは本来関係ないのだが
                    159:        // ここでは特にイニシエータに次のバイトを転送させる、という目的で
                    160:        // タイマーイベントが使われており、本体をリセットしたのに入れ違いで
                    161:        // イニシエータの転送処理が呼び出されてしまうというような事故を
                    162:        // 防ぐためにもここでタイマーを停止する。
1.1.1.11  root      163:        scheduler->StopEvent(event);
1.1.1.4   root      164: }
                    165: 
1.1.1.15! root      166: // デバイス情報を更新する。
        !           167: // domain の device[] が変更されたら呼ぶこと。
1.1.1.12  root      168: void
1.1.1.15! root      169: SCSIBus::Refresh()
1.1.1.12  root      170: {
1.1.1.15! root      171:        domain->GetDevices(&device);
1.1.1.12  root      172: }
                    173: 
1.1       root      174: // RST を上げる
                    175: void
                    176: SCSIBus::AssertRST()
                    177: {
                    178:        putlog(4, "%s", __func__);
                    179: 
                    180:        // すでに上がってたら何もしない
                    181:        if (rst) {
                    182:                return;
                    183:        }
                    184: 
                    185:        rst = true;
                    186: 
                    187:        // 即バスリセットを全員に通知
1.1.1.15! root      188:        for (int i = 0; i < device.size(); i++) {
1.1       root      189:                if (device[i]) {
1.1.1.12  root      190:                        device[i]->OnBusReset();
1.1       root      191:                }
                    192:        }
                    193: 
                    194:        // コールバック終了後に内部状態もクリア
                    195:        init_id = -1;
                    196:        target_id = -1;
                    197: }
                    198: 
                    199: // RST を下げる
                    200: void
                    201: SCSIBus::NegateRST()
                    202: {
                    203:        putlog(4, "%s", __func__);
                    204: 
                    205:        rst = false;
                    206: 
                    207:        // 特にすることはない
                    208: }
                    209: 
                    210: // SEL を上げる
                    211: void
                    212: SCSIBus::AssertSEL(uint id)
                    213: {
1.1.1.12  root      214:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      215: 
                    216:        // すでに上がってたら何もしない
                    217:        if ((sel & (1 << id))) {
                    218:                return;
                    219:        }
                    220: 
                    221:        sel |= (1 << id);
                    222: 
                    223:        if (GetPhase() == SCSI::Phase::BusFree) {
                    224:                // バスフリーフェーズなら、アービトレーションなしでセレクション
                    225:                // フェーズに移行。
                    226:                //
                    227:                // 仕様Phase      |Free |<---- Selection ---->|
                    228:                //
                    229:                // BSY       ~~\__________________________/~~~~~~~
                    230:                //
                    231:                // Data      -----------< Initiator&Target ID >---
                    232:                //
                    233:                // SEL       ________________/~~~~~~~~~~~~~~~~\___
                    234:                //                           ^イマココ
                    235:                //
                    236:                // 実装Phase      |<- Free ->|<-- Selection ->|
                    237:                //
                    238:                // SCSI 本にはデータバスが変化したところから Selection フェーズと
                    239:                // 書いてあるように読めるけど(上)、ここでは SEL がアサートされた
                    240:                // ところからとする(下)。
                    241: 
                    242:                SetPhase(SCSI::Phase::Selection);
                    243: 
                    244:                // SEL を上げた人がイニシエータ
                    245:                init_id = id;
                    246: 
                    247:                // 即セレクション開始。
                    248:                CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
                    249: 
                    250:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    251:                // アービトレーションフェーズなら、
                    252:                // アービトレーションに勝ってセレクションフェーズに移行途中。
                    253:                // この場合はセレクションフェーズに移行するのはここではなく
                    254:                // BSY の立ち下がりなので、そっち参照。
                    255:                //
                    256:                // 仕様Phase Arbitration |---|<--- Selection -->|
                    257:                //
                    258:                // BSY       /~~~~~~~~~~~~~~~\_______________/~~~~~~
                    259:                //
                    260:                // Data      <InitiatorID>< Initiator&Target ID >---
                    261:                //
                    262:                // SEL       __________/~~~~~~~~~~~~~~~~~~~~~~~~\___
                    263:                //                     ^イマココ
                    264:                //
                    265:                // 実装Phase <-Arbitration ->|<--- Selection -->|
                    266: 
                    267:                // SEL を上げた人がイニシエータなことだけはここで覚えておく。
                    268:                init_id = id;
                    269: 
                    270:        } else {
1.1.1.6   root      271:                // それ以外(転送フェーズ)中に SEL が立てられたらどうすればいいか。
                    272:                // これは転送中に LUNA をリセットすると起きる。LUNA の PROM は SPC に
                    273:                // 対してバスリセットを発行せずに、セレクションを行おうとするため。
                    274:                // SCSI 本には載ってないので分からないが、このような規約違反はたいてい
                    275:                // イニシエータが検出してバスフリーかバスリセットを行うことが意図
                    276:                // されているんじゃないかと想像するけど、今回そのイニシエータが違反
                    277:                // してるので、どうするんだろうね。
                    278:                // 賢いターゲットデバイスだとこれを検出して自主的にバスフリーへ落ちる
                    279:                // だろうか。賢くないターゲットデバイスは検出せずにそのまま (そして
                    280:                // デッドロック) になるだろうか。
                    281: 
1.1.1.7   root      282: #if 1
                    283:                // XXX とりあえず LUNA で SCSI がなんか刺さった時に本体リセットでも
                    284:                // 直らない状況を真似するため、ターゲットは何もしないでおく。
                    285: #else
1.1.1.6   root      286:                // ターゲットデバイスは賢いので、全員イニシエータ様に忖度して
                    287:                // 即座にバスフリーへ移行するというのはどうか。
1.1.1.15! root      288:                for (uint i = 0; i < device.size(); i++) {
1.1.1.6   root      289:                        if (i != id) {
                    290:                                NegateBSY(i);
                    291:                        }
                    292:                }
1.1.1.7   root      293: #endif
1.1       root      294:        }
                    295: }
                    296: 
                    297: // SEL を下げる
                    298: void
                    299: SCSIBus::NegateSEL(uint id)
                    300: {
1.1.1.12  root      301:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      302: 
                    303:        // すでに下りてたら何もしない
                    304:        if ((sel & (1 << id)) == 0) {
                    305:                return;
                    306:        }
                    307: 
                    308:        sel &= ~(1 << id);
                    309: 
1.1.1.6   root      310:        if (GetPhase() == SCSI::Phase::BusFree) {
                    311:                // バスフリーフェーズで SEL を下げる
                    312: 
                    313:                // 通常は起きないはずだけど、転送フェーズ中に LUNA をリセットすると
                    314:                // -> LUNA の PROM が SEL を上げる (プロトコル違反)
                    315:                // -> ターゲットは転送フェーズ中なのに SEL が上がったのを検出して
                    316:                //    BSY を取り下げてバスフリーに移行する
                    317:                // -> バスフリーがイニシエータに通知され、イニシエータが SEL を下げる
                    318:                // となるので、このケースはスルーしてよい。
                    319: 
                    320:        } else if (GetPhase() == SCSI::Phase::Selection) {
1.1       root      321:                // セレクションフェーズで SEL を下げる
                    322:                if (bsy) {
                    323:                        // BSY が立っていれば、ターゲットがセレクションに応答したので
                    324:                        // イニシエータが SEL を下げて、これから任意時間後にターゲットが
                    325:                        // 情報転送フェーズに移行する。
                    326: 
                    327:                        CallAfter(StartTransfer, "SCSIBus Transfer Start", 400_nsec);
                    328:                } else {
                    329:                        // BSY が立ってなければ、ターゲットが応答しなかったことに
                    330:                        // イニシエータがあきらめて SEL を下げたので、何もせず
                    331:                        // バスフリーフェーズに移行。
                    332:                        BusFree(id);
                    333:                }
                    334:        } else {
1.1.1.6   root      335:                // 下げる分には無視してもいいはずだけど、どうする?
1.1.1.10  root      336:                VMPANIC("NegateSEL in %s phase", GetPhaseName());
1.1       root      337:        }
                    338: }
                    339: 
                    340: // BSY を上げる
                    341: void
                    342: SCSIBus::AssertBSY(uint id)
                    343: {
1.1.1.12  root      344:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      345: 
                    346:        // すでに上がってたら何もしない
                    347:        if ((bsy & (1 << id))) {
                    348:                return;
                    349:        }
                    350: 
                    351:        bsy |= (1 << id);
                    352: 
                    353:        if (GetPhase() == SCSI::Phase::BusFree) {
                    354:                // バスフリーフェーズだったらアービトレーション開始
                    355: 
                    356:                // 実際のアービトレーションは、
                    357:                // バスフリーを検出してから規定時間経過後に、データバスに自身の ID を
                    358:                // 出して BSY を立てる。規定時間内に他のデバイスもアービトレーションに
                    359:                // 参加すれば ID の高い方が勝つ。負けた方は規定時間内にアービトレー
                    360:                // ションを取り下げる。規定期間経って勝っていればアービトレーション
                    361:                // 成功となり、セレクションフェーズに移る。
                    362:                // のだが、エミュレータ上では、今の所アービトレーションで競合が起きる
                    363:                // ことはないため、アービトレーションは規定時間経過して必ず成功する。
                    364:                // ちゃんとしたアービトレーションを実装する場合はたぶんこれだけでは
                    365:                // 済まないと思うので書き換えること。
                    366: 
                    367:                SetPhase(SCSI::Phase::Arbitration);
                    368: 
                    369:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    370:                // アービトレーションフェーズだったら、アービトレーションに参加。
                    371:                // 今は起きない。
1.1.1.12  root      372:                VMPANIC("AssertBSY(#%u) in arbitration", id);
1.1       root      373: 
                    374:        } else if (GetPhase() == SCSI::Phase::Selection) {
                    375:                // セレクションフェーズだったら、転送フェーズに移行中。
                    376:                // ターゲットが BSY をアサートしたので次は規定時間経過後に
                    377:                // SelectionAck() を呼び出してイニシエータに SEL を下げさせる。
                    378: 
                    379:                CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec);
                    380:        } else {
                    381:                // どうする?
1.1.1.12  root      382:                VMPANIC("AssertBSY(#%u) in %s phase", id, GetPhaseName());
1.1       root      383:        }
                    384: }
                    385: 
                    386: // BSY を下げる
                    387: void
                    388: SCSIBus::NegateBSY(uint id)
                    389: {
1.1.1.12  root      390:        putlog(4, "%s(#%u)", __func__, id);
1.1       root      391: 
                    392:        // すでに下りてたら何もしない
                    393:        if ((bsy & (1 << id)) == 0) {
                    394:                return;
                    395:        }
                    396: 
                    397:        bsy &= ~(1 << id);
                    398: 
                    399:        if (GetPhase() == SCSI::Phase::Transfer) {
                    400:                // 転送フェーズから BSY を下げたらバスフリーに移行。
                    401:                BusFree(id);
                    402: 
                    403:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    404:                // アービトレーションフェーズなら、
                    405:                // イニシエータが BSY を下ろすことでセレクションフェーズに移行。
                    406:                //
                    407:                // この少し上の AssertSEL() 中のタイムチャート参照。
                    408: 
                    409:                SetPhase(SCSI::Phase::Selection);
                    410: 
                    411:                // 即セレクション開始。
                    412:                CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
                    413: 
                    414:        } else {
                    415:                // どうする?
1.1.1.12  root      416:                VMPANIC("NegateBSY(#%u) in %s phase", id, GetPhaseName());
1.1       root      417:        }
                    418: }
                    419: 
                    420: // MSG, CD, IO 信号線を変更する。
                    421: void
                    422: SCSIBus::SetXfer(uint8 val)
                    423: {
                    424:        uint8 oldxfer = xfer;
                    425: 
                    426:        xfer = val & (SCSI::MSG | SCSI::CD | SCSI::IO);
                    427:        if (oldxfer != xfer) {
                    428:                if (GetPhase() == SCSI::Phase::Transfer) {
                    429:                        putlog(4, "SetXfer(%x:%s)", xfer, SCSI::GetXferPhaseName(xfer));
                    430:                } else {
                    431:                        putlog(4, "SetXfer(%x)", xfer);
                    432:                }
                    433:        }
                    434: }
                    435: 
                    436: // REQ を上げる
                    437: void
                    438: SCSIBus::AssertREQ()
                    439: {
                    440:        if (GetPhase() == SCSI::Phase::Transfer) {
                    441:                // REQ を上げると言いつつ、ここで転送レートの時間調整をしているので
                    442:                // (scsibus.h の解説参照) REQ を実際に上げるのはイベント発生後。
                    443:                // XXX 本来ターゲットデバイスがタイマー持っててやるべきだけど、
                    444:                // イベントがもう一つ増えるのもうざいので。どのみち、ここの REQ
                    445:                // 通知はイベントにしないといけないので。
                    446: 
                    447:                uint64 after = 0;
1.1.1.11  root      448:                uint64 now = scheduler->GetVirtTime();
1.1       root      449:                if (now < last_req_time + 1_usec) {
                    450:                        after = (last_req_time + 1_usec) - now;
                    451:                }
1.1.1.4   root      452:                // ウェイトが指示されていれば入れる (シークタイムなど)
                    453:                if (req_wait != 0) {
                    454:                        after += req_wait;
                    455:                        req_wait = 0;
                    456:                }
1.1.1.9   root      457:                last_req_time = now + after;
1.1       root      458:                CallAfter(TransferReq, "SCSIBus AssertREQ", after);
                    459:        } else {
                    460:                // 転送フェーズでないのに上げることはないと思うけど
                    461:                // とりあえず通知もせずその場で上げておくか?
                    462:                putlog(4, "%s", __func__);
                    463:                req = true;
                    464:        }
                    465: }
                    466: 
                    467: // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて
1.1.1.12  root      468: // イニシエータの OnTransferReq を呼び出すところ。
1.1       root      469: void
1.1.1.4   root      470: SCSIBus::TransferReq(Event& ev)
1.1       root      471: {
                    472:        putlog(4, "AssertREQ");
                    473:        req = true;
                    474: 
                    475:        assert(device[init_id]);
1.1.1.12  root      476:        device[init_id]->OnTransferReq();
1.1       root      477: }
                    478: 
                    479: // REQ を下げる
                    480: void
                    481: SCSIBus::NegateREQ()
                    482: {
                    483:        putlog(4, "%s", __func__);
                    484:        req = false;
                    485: }
                    486: 
                    487: // ACK を上げる
                    488: void
                    489: SCSIBus::AssertACK()
                    490: {
                    491:        putlog(4, "%s", __func__);
                    492:        ack = true;
                    493: 
                    494:        if (GetPhase() == SCSI::Phase::Transfer) {
                    495:                assert(device[target_id]);
1.1.1.12  root      496:                device[target_id]->OnTransfer();
1.1       root      497:        }
                    498: }
                    499: 
                    500: // ACK を下げる
                    501: void
                    502: SCSIBus::NegateACK()
                    503: {
                    504:        putlog(4, "%s", __func__);
                    505:        ack = false;
                    506: 
                    507:        if (GetPhase() == SCSI::Phase::Transfer) {
                    508:                assert(device[target_id]);
1.1.1.12  root      509:                device[target_id]->OnTransferAck();
1.1       root      510:        }
                    511: }
                    512: 
                    513: // ATN を上げる
                    514: void
                    515: SCSIBus::AssertATN()
                    516: {
                    517:        putlog(4, "%s", __func__);
                    518:        atn = true;
                    519: }
                    520: 
                    521: // ATN を下げる
                    522: void
                    523: SCSIBus::NegateATN()
                    524: {
                    525:        putlog(4, "%s", __func__);
                    526:        atn = false;
                    527: }
                    528: 
                    529: // バスフリーフェーズに移行。
                    530: // id はこのバスフリーを行ったデバイス (0..7)
                    531: void
1.1.1.12  root      532: SCSIBus::BusFree(uint id)
1.1       root      533: {
                    534:        putlog(4, "%s", __func__);
                    535: 
                    536:        SetPhase(SCSI::Phase::BusFree);
                    537: 
                    538:        // 即全デバイスに通知。
1.1.1.15! root      539:        for (int i = 0; i < device.size(); i++) {
1.1       root      540:                if (device[i]) {
1.1.1.12  root      541:                        device[i]->OnBusFree(id);
1.1       root      542:                }
                    543:        }
                    544: 
                    545:        // コールバック終了後に内部状態もクリア
                    546:        init_id = -1;
                    547:        target_id = -1;
                    548: 
                    549:        // 通知された各デバイスは規定時間内に他の信号線も取り下げなければ
                    550:        // ならない。
                    551: }
                    552: 
                    553: // セレクションから情報転送フェーズまで
                    554: //
                    555: // 仕様Phase <- Selection ->|
                    556: //
                    557: // BSY       _______________/~~~~~~~~~
                    558: //
                    559: // Data      Initiator&Target ID >----
                    560: //
                    561: // SEL       ~~~~~~~~~~~~~~~~~~~~~~\__
                    562: //
                    563: //           -(1)->|--(2)-->|-(3)->|
                    564: //
                    565: // セレクションフェーズ開始後、(ターゲット)デバイスは、SEL がアサート、
                    566: // (IO がネゲート)、データバスの自身の ID がアサート、が 400nsec 以上
                    567: // 継続することで、自身がターゲットに選択されていることを認識する。(1)
                    568: // ターゲットは (1) から 200usec 以内に BSY をアサートする。(2)
                    569: // イニシエータは (2) から 90nsec 以上後に SEL (とデータ) を下げる。(3)
                    570: //
                    571: // 実装では、セレクションフェーズ開始が SelectionStart() の呼び出しに対応。
                    572: //
                    573: // ターゲットデバイスが存在していない場合はタイムアウトとなるが、これは
                    574: // ホストが処理するのでバスは何もしない。
                    575: // ターゲットデバイスが存在していれば SelectionStart() から 400nsec 後 +
                    576: // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック
                    577: // を呼び出す(1+2)。
                    578: //
1.1.1.12  root      579: // Selection() コールバック(が呼び出す device->OnSelected()) は通常 BSY を
1.1       root      580: // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック
                    581: // を呼び出す(3)。
                    582: //
1.1.1.12  root      583: // SelectionAck() コールバック(が呼び出す device->OnSelectionAck()) は通常
1.1       root      584: // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に
                    585: // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして
                    586: // デバイスを情報転送フェーズに移行させる。
                    587: 
                    588: // セレクションフェーズ開始。
                    589: void
1.1.1.4   root      590: SCSIBus::SelectionStart(Event& ev)
1.1       root      591: {
                    592:        // この時点でデータバスには自身の ID と相手の ID が立っているはず。
                    593:        // 一方 sel は SEL 信号線を立てた人のビットを持っているので、
                    594:        // これでターゲットを特定する。
                    595:        uint target_mask = data ^ sel;
                    596:        if (__builtin_popcount(target_mask) != 1) {
                    597:                // データバスから自身を除いた結果立ってるビットが1個以外だったら
                    598:                // 誰も応答しないので、何もせず帰ってよい。
1.1.1.2   root      599:                putlog(3, "%s (No target specified; databus=$%02x)", __func__, data);
1.1       root      600:                return;
                    601:        }
                    602: 
                    603:        // ターゲット ID はここで取得
                    604:        target_id = DecodeID(target_mask);
                    605:        if (device[target_id] == NULL) {
                    606:                // ターゲットデバイスがいなければ、バスは何もしない。
                    607:                // (その結果イニシエータでタイムアウトが起きるはず)
                    608:                putlog(4, "%s (No target)", __func__);
                    609:                return;
                    610:        }
                    611: 
                    612:        // ターゲットデバイスがいれば、規定時間経過後ターゲットに通知。
                    613:        putlog(4, "%s", __func__);
                    614:        CallAfter(Selected, "SCSIBus Selected", 100_usec);
                    615: }
                    616: 
                    617: // SelectionStart() にてターゲットが選択されて規定時間経ったので
                    618: // そろそろターゲットが BSY を立てる頃に呼ばれるコールバック。
                    619: void
1.1.1.4   root      620: SCSIBus::Selected(Event& ev)
1.1       root      621: {
                    622:        // ターゲットに通知。ターゲットがいることは確定している。
                    623:        assert(device[target_id]);
1.1.1.12  root      624:        device[target_id]->OnSelected();
1.1       root      625: }
                    626: 
                    627: // ターゲットが BSY を立てて規定時間経ったので
1.1.1.4   root      628: // そろそろイニシエータが SEL を下げる頃に呼ばれるコールバック。
1.1       root      629: void
1.1.1.4   root      630: SCSIBus::SelectionAck(Event& ev)
1.1       root      631: {
                    632:        // イニシエータに通知。
                    633:        assert(device[init_id]);
1.1.1.12  root      634:        device[init_id]->OnSelectionAck();
1.1       root      635: }
                    636: 
                    637: // 情報転送フェーズ開始を通知する。
                    638: // BSY を上げて情報転送フェーズに移行したところで呼ばれる。
                    639: void
1.1.1.4   root      640: SCSIBus::StartTransfer(Event& ev)
1.1       root      641: {
                    642:        SetPhase(SCSI::Phase::Transfer);
                    643: 
                    644:        assert(device[target_id]);
1.1.1.12  root      645:        device[target_id]->OnStartTransfer();
                    646: }
                    647: 

unix.superglobalmegacorp.com

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