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

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

unix.superglobalmegacorp.com

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