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