|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.