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