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