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