|
|
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:
11: #include "scsibus.h"
12: #include "scsidev.h"
1.1.1.12 root 13: #include "config.h"
1.1.1.13! root 14: #include "monitor.h"
1.1.1.10 root 15: #include "scheduler.h"
1.1 root 16:
17: // time 時間後に呼び出すコールバックをセットする。
18: // func の書式が面倒なのを省略して書きたいため。
19: #define CallAfter(func_, name_, time_) do { \
1.1.1.10 root 20: event.func = ToEventCallback(&SCSIBus::func_); \
1.1 root 21: event.time = time_; \
1.1.1.10 root 22: event.SetName(name_); \
1.1.1.11 root 23: scheduler->RestartEvent(event); \
1.1 root 24: } while (0)
25:
26: // コンストラクタ
1.1.1.12 root 27: SCSIBus::SCSIBus(SCSIHostDevice *parent_, const char *config_name_)
1.1.1.11 root 28: : inherited(OBJ_SCSIBUS)
1.1 root 29: {
1.1.1.12 root 30: parent = parent_;
31: config_name = config_name_;
32:
1.1.1.13! root 33: monitor = gMonitorManager->Regist(ID_MONITOR_SCSIDEVS, this);
! 34: monitor->func = ToMonitorCallback(&SCSIBus::MonitorUpdate);
1.1.1.12 root 35: // サイズは Create で決まる
1.1 root 36: }
37:
38: // デストラクタ
39: SCSIBus::~SCSIBus()
40: {
41: }
42:
1.1.1.12 root 43: // 動的なコンストラクション
44: bool
45: SCSIBus::Create()
46: {
47: const char *name = GetConfigName();
48:
49: // 設定を読み込む。
50: for (int id = 0; id < 8; id++) {
51: const std::string key = string_format("%s-id%u-image", name, id);
52: const ConfigItem& item = gConfig->Find(key);
53: const std::string& image = item.AsString();
54:
55: // 空ならデバイスなし
56: if (image.empty()) {
57: // 未接続部分のパラメータは減らしておくか。
58: // --show-config するとさすがに無駄に多くて邪魔なので。
59: const std::string wikey = string_format("%s-id%u-writeignore",
60: name, id);
61: const std::string wpkey = string_format("%s-id%u-writeprotect",
62: name, id);
63: const std::string stkey = string_format("%s-id%u-seektime",
64: name, id);
65: gConfig->Delete(wikey);
66: gConfig->Delete(wpkey);
67: gConfig->Delete(stkey);
68: continue;
69: }
70:
71: // ',' より前がデバイス種別 (ここではパスは不要)
72: auto v = string_split(image, ',');
73: // 比較のため前後の空白を取り除いて小文字にする
74: std::string type = string_trim(v[0]);
75: type = string_tolower(type);
76:
77: SCSI::DevType target_devtype;
78: if (type == "hd") {
79: target_devtype = SCSI::DevType::HD;
80: } else if (type == "cd") {
81: target_devtype = SCSI::DevType::CD;
82: } else if (type == "mo") {
83: target_devtype = SCSI::DevType::MO;
84: } else {
85: item.Err("Invaild device type '%s'", type.c_str());
86: return false;
87: }
88: target[id].reset(new SCSIDisk(this, id, target_devtype));
89: AttachDevice(target[id].get(), id);
90: }
91:
92: // ここの connected_devices はイニシエータを含んでいない数
1.1.1.13! root 93: monitor->SetSize(75, connected_devices.size() * 4 + 1);
1.1.1.12 root 94:
95: return true;
96: }
97:
98: // 初期化
99: bool
100: SCSIBus::Init()
101: {
102: if (inherited::Init() == false) {
103: return false;
104: }
105:
106: event.SetName("SCSIBus"); // 複数出来たら名前どうするべか
107: scheduler->RegistEvent(event);
108:
109: return true;
110: }
111:
1.1.1.10 root 112: // 電源オン/リセット
1.1.1.4 root 113: void
1.1.1.10 root 114: SCSIBus::ResetHard(bool poweron)
1.1.1.4 root 115: {
1.1.1.10 root 116: if (poweron) {
117: phase = SCSI::Phase::BusFree;
118: rst = false;
119: req = false;
120: ack = false;
121: atn = false;
122: sel = 0;
123: bsy = 0;
124: xfer = 0;
125: data = 0;
126:
127: last_req_time = 0;
128: req_wait = 0;
129: init_id = -1;
130: target_id = -1;
131: }
132:
1.1.1.4 root 133: // ここでのイベントは実際にはイニシエータかターゲットのどちらかが
134: // 時間を伴う処理をしている最中であることを示しているものなので、
135: // イニシエータがリセットされたこととバスの動作とは本来関係ないのだが
136: // ここでは特にイニシエータに次のバイトを転送させる、という目的で
137: // タイマーイベントが使われており、本体をリセットしたのに入れ違いで
138: // イニシエータの転送処理が呼び出されてしまうというような事故を
139: // 防ぐためにもここでタイマーを停止する。
1.1.1.11 root 140: scheduler->StopEvent(event);
1.1.1.4 root 141: }
142:
1.1.1.12 root 143: // デバイスを接続する。
144: // id に先客がいれば false を返す。成功すれば true を返す。
1.1 root 145: bool
1.1.1.12 root 146: SCSIBus::AttachDevice(SCSIDevice *dev, int id)
1.1 root 147: {
148: if (device[id] != NULL) {
149: return false;
150: }
1.1.1.12 root 151: putlog(3, "Attach #%u", id);
1.1 root 152:
153: device[id] = dev;
1.1.1.12 root 154: MakeConnectedDevices();
155:
156: // 紐付けをデバイスにも通知。
157: dev->OnAttached(this, id);
1.1 root 158:
159: return true;
160: }
161:
1.1.1.12 root 162: // device[] から connected_device を作り直す。
163: // device[] はイニシエータを含む固定長の配列。
164: // connected_device はイニシエータを (あれば) 含む ID 順の可変長リスト。
165: void
166: SCSIBus::MakeConnectedDevices()
167: {
168: connected_devices.clear();
169:
170: for (int id = 0; id < 8; id++) {
171: if (device[id]) {
172: connected_devices.push_back(device[id]);
173: }
174: }
175: }
176:
177: // 接続済みの (イニシエータを除く) デバイス数を返す。
178: uint
179: SCSIBus::GetTargetCount() const
180: {
181: uint count = 0;
182:
183: for (int id = 0; id < 8; id++) {
184: if ((bool)target[id]) {
185: count++;
186: }
187: }
188:
189: return count;
190: }
191:
1.1 root 192: // RST を上げる
193: void
194: SCSIBus::AssertRST()
195: {
196: putlog(4, "%s", __func__);
197:
198: // すでに上がってたら何もしない
199: if (rst) {
200: return;
201: }
202:
203: rst = true;
204:
205: // 即バスリセットを全員に通知
206: for (int i = 0; i < 8; i++) {
207: if (device[i]) {
1.1.1.12 root 208: device[i]->OnBusReset();
1.1 root 209: }
210: }
211:
212: // コールバック終了後に内部状態もクリア
213: init_id = -1;
214: target_id = -1;
215: }
216:
217: // RST を下げる
218: void
219: SCSIBus::NegateRST()
220: {
221: putlog(4, "%s", __func__);
222:
223: rst = false;
224:
225: // 特にすることはない
226: }
227:
228: // SEL を上げる
229: void
230: SCSIBus::AssertSEL(uint id)
231: {
1.1.1.12 root 232: putlog(4, "%s(#%u)", __func__, id);
1.1 root 233:
234: // すでに上がってたら何もしない
235: if ((sel & (1 << id))) {
236: return;
237: }
238:
239: sel |= (1 << id);
240:
241: if (GetPhase() == SCSI::Phase::BusFree) {
242: // バスフリーフェーズなら、アービトレーションなしでセレクション
243: // フェーズに移行。
244: //
245: // 仕様Phase |Free |<---- Selection ---->|
246: //
247: // BSY ~~\__________________________/~~~~~~~
248: //
249: // Data -----------< Initiator&Target ID >---
250: //
251: // SEL ________________/~~~~~~~~~~~~~~~~\___
252: // ^イマココ
253: //
254: // 実装Phase |<- Free ->|<-- Selection ->|
255: //
256: // SCSI 本にはデータバスが変化したところから Selection フェーズと
257: // 書いてあるように読めるけど(上)、ここでは SEL がアサートされた
258: // ところからとする(下)。
259:
260: SetPhase(SCSI::Phase::Selection);
261:
262: // SEL を上げた人がイニシエータ
263: init_id = id;
264:
265: // 即セレクション開始。
266: CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
267:
268: } else if (GetPhase() == SCSI::Phase::Arbitration) {
269: // アービトレーションフェーズなら、
270: // アービトレーションに勝ってセレクションフェーズに移行途中。
271: // この場合はセレクションフェーズに移行するのはここではなく
272: // BSY の立ち下がりなので、そっち参照。
273: //
274: // 仕様Phase Arbitration |---|<--- Selection -->|
275: //
276: // BSY /~~~~~~~~~~~~~~~\_______________/~~~~~~
277: //
278: // Data <InitiatorID>< Initiator&Target ID >---
279: //
280: // SEL __________/~~~~~~~~~~~~~~~~~~~~~~~~\___
281: // ^イマココ
282: //
283: // 実装Phase <-Arbitration ->|<--- Selection -->|
284:
285: // SEL を上げた人がイニシエータなことだけはここで覚えておく。
286: init_id = id;
287:
288: } else {
1.1.1.6 root 289: // それ以外(転送フェーズ)中に SEL が立てられたらどうすればいいか。
290: // これは転送中に LUNA をリセットすると起きる。LUNA の PROM は SPC に
291: // 対してバスリセットを発行せずに、セレクションを行おうとするため。
292: // SCSI 本には載ってないので分からないが、このような規約違反はたいてい
293: // イニシエータが検出してバスフリーかバスリセットを行うことが意図
294: // されているんじゃないかと想像するけど、今回そのイニシエータが違反
295: // してるので、どうするんだろうね。
296: // 賢いターゲットデバイスだとこれを検出して自主的にバスフリーへ落ちる
297: // だろうか。賢くないターゲットデバイスは検出せずにそのまま (そして
298: // デッドロック) になるだろうか。
299:
1.1.1.7 root 300: #if 1
301: // XXX とりあえず LUNA で SCSI がなんか刺さった時に本体リセットでも
302: // 直らない状況を真似するため、ターゲットは何もしないでおく。
303: #else
1.1.1.6 root 304: // ターゲットデバイスは賢いので、全員イニシエータ様に忖度して
305: // 即座にバスフリーへ移行するというのはどうか。
306: for (uint i = 0; i < 8; i++) {
307: if (i != id) {
308: NegateBSY(i);
309: }
310: }
1.1.1.7 root 311: #endif
1.1 root 312: }
313: }
314:
315: // SEL を下げる
316: void
317: SCSIBus::NegateSEL(uint id)
318: {
1.1.1.12 root 319: putlog(4, "%s(#%u)", __func__, id);
1.1 root 320:
321: // すでに下りてたら何もしない
322: if ((sel & (1 << id)) == 0) {
323: return;
324: }
325:
326: sel &= ~(1 << id);
327:
1.1.1.6 root 328: if (GetPhase() == SCSI::Phase::BusFree) {
329: // バスフリーフェーズで SEL を下げる
330:
331: // 通常は起きないはずだけど、転送フェーズ中に LUNA をリセットすると
332: // -> LUNA の PROM が SEL を上げる (プロトコル違反)
333: // -> ターゲットは転送フェーズ中なのに SEL が上がったのを検出して
334: // BSY を取り下げてバスフリーに移行する
335: // -> バスフリーがイニシエータに通知され、イニシエータが SEL を下げる
336: // となるので、このケースはスルーしてよい。
337:
338: } else if (GetPhase() == SCSI::Phase::Selection) {
1.1 root 339: // セレクションフェーズで SEL を下げる
340: if (bsy) {
341: // BSY が立っていれば、ターゲットがセレクションに応答したので
342: // イニシエータが SEL を下げて、これから任意時間後にターゲットが
343: // 情報転送フェーズに移行する。
344:
345: CallAfter(StartTransfer, "SCSIBus Transfer Start", 400_nsec);
346: } else {
347: // BSY が立ってなければ、ターゲットが応答しなかったことに
348: // イニシエータがあきらめて SEL を下げたので、何もせず
349: // バスフリーフェーズに移行。
350: BusFree(id);
351: }
352: } else {
1.1.1.6 root 353: // 下げる分には無視してもいいはずだけど、どうする?
1.1.1.10 root 354: VMPANIC("NegateSEL in %s phase", GetPhaseName());
1.1 root 355: }
356: }
357:
358: // BSY を上げる
359: void
360: SCSIBus::AssertBSY(uint id)
361: {
1.1.1.12 root 362: putlog(4, "%s(#%u)", __func__, id);
1.1 root 363:
364: // すでに上がってたら何もしない
365: if ((bsy & (1 << id))) {
366: return;
367: }
368:
369: bsy |= (1 << id);
370:
371: if (GetPhase() == SCSI::Phase::BusFree) {
372: // バスフリーフェーズだったらアービトレーション開始
373:
374: // 実際のアービトレーションは、
375: // バスフリーを検出してから規定時間経過後に、データバスに自身の ID を
376: // 出して BSY を立てる。規定時間内に他のデバイスもアービトレーションに
377: // 参加すれば ID の高い方が勝つ。負けた方は規定時間内にアービトレー
378: // ションを取り下げる。規定期間経って勝っていればアービトレーション
379: // 成功となり、セレクションフェーズに移る。
380: // のだが、エミュレータ上では、今の所アービトレーションで競合が起きる
381: // ことはないため、アービトレーションは規定時間経過して必ず成功する。
382: // ちゃんとしたアービトレーションを実装する場合はたぶんこれだけでは
383: // 済まないと思うので書き換えること。
384:
385: SetPhase(SCSI::Phase::Arbitration);
386:
387: } else if (GetPhase() == SCSI::Phase::Arbitration) {
388: // アービトレーションフェーズだったら、アービトレーションに参加。
389: // 今は起きない。
1.1.1.12 root 390: VMPANIC("AssertBSY(#%u) in arbitration", id);
1.1 root 391:
392: } else if (GetPhase() == SCSI::Phase::Selection) {
393: // セレクションフェーズだったら、転送フェーズに移行中。
394: // ターゲットが BSY をアサートしたので次は規定時間経過後に
395: // SelectionAck() を呼び出してイニシエータに SEL を下げさせる。
396:
397: CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec);
398: } else {
399: // どうする?
1.1.1.12 root 400: VMPANIC("AssertBSY(#%u) in %s phase", id, GetPhaseName());
1.1 root 401: }
402: }
403:
404: // BSY を下げる
405: void
406: SCSIBus::NegateBSY(uint id)
407: {
1.1.1.12 root 408: putlog(4, "%s(#%u)", __func__, id);
1.1 root 409:
410: // すでに下りてたら何もしない
411: if ((bsy & (1 << id)) == 0) {
412: return;
413: }
414:
415: bsy &= ~(1 << id);
416:
417: if (GetPhase() == SCSI::Phase::Transfer) {
418: // 転送フェーズから BSY を下げたらバスフリーに移行。
419: BusFree(id);
420:
421: } else if (GetPhase() == SCSI::Phase::Arbitration) {
422: // アービトレーションフェーズなら、
423: // イニシエータが BSY を下ろすことでセレクションフェーズに移行。
424: //
425: // この少し上の AssertSEL() 中のタイムチャート参照。
426:
427: SetPhase(SCSI::Phase::Selection);
428:
429: // 即セレクション開始。
430: CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
431:
432: } else {
433: // どうする?
1.1.1.12 root 434: VMPANIC("NegateBSY(#%u) in %s phase", id, GetPhaseName());
1.1 root 435: }
436: }
437:
438: // MSG, CD, IO 信号線を変更する。
439: void
440: SCSIBus::SetXfer(uint8 val)
441: {
442: uint8 oldxfer = xfer;
443:
444: xfer = val & (SCSI::MSG | SCSI::CD | SCSI::IO);
445: if (oldxfer != xfer) {
446: if (GetPhase() == SCSI::Phase::Transfer) {
447: putlog(4, "SetXfer(%x:%s)", xfer, SCSI::GetXferPhaseName(xfer));
448: } else {
449: putlog(4, "SetXfer(%x)", xfer);
450: }
451: }
452: }
453:
454: // REQ を上げる
455: void
456: SCSIBus::AssertREQ()
457: {
458: if (GetPhase() == SCSI::Phase::Transfer) {
459: // REQ を上げると言いつつ、ここで転送レートの時間調整をしているので
460: // (scsibus.h の解説参照) REQ を実際に上げるのはイベント発生後。
461: // XXX 本来ターゲットデバイスがタイマー持っててやるべきだけど、
462: // イベントがもう一つ増えるのもうざいので。どのみち、ここの REQ
463: // 通知はイベントにしないといけないので。
464:
465: uint64 after = 0;
1.1.1.11 root 466: uint64 now = scheduler->GetVirtTime();
1.1 root 467: if (now < last_req_time + 1_usec) {
468: after = (last_req_time + 1_usec) - now;
469: }
1.1.1.4 root 470: // ウェイトが指示されていれば入れる (シークタイムなど)
471: if (req_wait != 0) {
472: after += req_wait;
473: req_wait = 0;
474: }
1.1.1.9 root 475: last_req_time = now + after;
1.1 root 476: CallAfter(TransferReq, "SCSIBus AssertREQ", after);
477: } else {
478: // 転送フェーズでないのに上げることはないと思うけど
479: // とりあえず通知もせずその場で上げておくか?
480: putlog(4, "%s", __func__);
481: req = true;
482: }
483: }
484:
485: // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて
1.1.1.12 root 486: // イニシエータの OnTransferReq を呼び出すところ。
1.1 root 487: void
1.1.1.4 root 488: SCSIBus::TransferReq(Event& ev)
1.1 root 489: {
490: putlog(4, "AssertREQ");
491: req = true;
492:
493: assert(device[init_id]);
1.1.1.12 root 494: device[init_id]->OnTransferReq();
1.1 root 495: }
496:
497: // REQ を下げる
498: void
499: SCSIBus::NegateREQ()
500: {
501: putlog(4, "%s", __func__);
502: req = false;
503: }
504:
505: // ACK を上げる
506: void
507: SCSIBus::AssertACK()
508: {
509: putlog(4, "%s", __func__);
510: ack = true;
511:
512: if (GetPhase() == SCSI::Phase::Transfer) {
513: assert(device[target_id]);
1.1.1.12 root 514: device[target_id]->OnTransfer();
1.1 root 515: }
516: }
517:
518: // ACK を下げる
519: void
520: SCSIBus::NegateACK()
521: {
522: putlog(4, "%s", __func__);
523: ack = false;
524:
525: if (GetPhase() == SCSI::Phase::Transfer) {
526: assert(device[target_id]);
1.1.1.12 root 527: device[target_id]->OnTransferAck();
1.1 root 528: }
529: }
530:
531: // ATN を上げる
532: void
533: SCSIBus::AssertATN()
534: {
535: putlog(4, "%s", __func__);
536: atn = true;
537: }
538:
539: // ATN を下げる
540: void
541: SCSIBus::NegateATN()
542: {
543: putlog(4, "%s", __func__);
544: atn = false;
545: }
546:
547: // バスフリーフェーズに移行。
548: // id はこのバスフリーを行ったデバイス (0..7)
549: void
1.1.1.12 root 550: SCSIBus::BusFree(uint id)
1.1 root 551: {
552: putlog(4, "%s", __func__);
553:
554: SetPhase(SCSI::Phase::BusFree);
555:
556: // 即全デバイスに通知。
557: for (int i = 0; i < countof(device); i++) {
558: if (device[i]) {
1.1.1.12 root 559: device[i]->OnBusFree(id);
1.1 root 560: }
561: }
562:
563: // コールバック終了後に内部状態もクリア
564: init_id = -1;
565: target_id = -1;
566:
567: // 通知された各デバイスは規定時間内に他の信号線も取り下げなければ
568: // ならない。
569: }
570:
571: // セレクションから情報転送フェーズまで
572: //
573: // 仕様Phase <- Selection ->|
574: //
575: // BSY _______________/~~~~~~~~~
576: //
577: // Data Initiator&Target ID >----
578: //
579: // SEL ~~~~~~~~~~~~~~~~~~~~~~\__
580: //
581: // -(1)->|--(2)-->|-(3)->|
582: //
583: // セレクションフェーズ開始後、(ターゲット)デバイスは、SEL がアサート、
584: // (IO がネゲート)、データバスの自身の ID がアサート、が 400nsec 以上
585: // 継続することで、自身がターゲットに選択されていることを認識する。(1)
586: // ターゲットは (1) から 200usec 以内に BSY をアサートする。(2)
587: // イニシエータは (2) から 90nsec 以上後に SEL (とデータ) を下げる。(3)
588: //
589: // 実装では、セレクションフェーズ開始が SelectionStart() の呼び出しに対応。
590: //
591: // ターゲットデバイスが存在していない場合はタイムアウトとなるが、これは
592: // ホストが処理するのでバスは何もしない。
593: // ターゲットデバイスが存在していれば SelectionStart() から 400nsec 後 +
594: // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック
595: // を呼び出す(1+2)。
596: //
1.1.1.12 root 597: // Selection() コールバック(が呼び出す device->OnSelected()) は通常 BSY を
1.1 root 598: // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック
599: // を呼び出す(3)。
600: //
1.1.1.12 root 601: // SelectionAck() コールバック(が呼び出す device->OnSelectionAck()) は通常
1.1 root 602: // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に
603: // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして
604: // デバイスを情報転送フェーズに移行させる。
605:
606: // セレクションフェーズ開始。
607: void
1.1.1.4 root 608: SCSIBus::SelectionStart(Event& ev)
1.1 root 609: {
610: // この時点でデータバスには自身の ID と相手の ID が立っているはず。
611: // 一方 sel は SEL 信号線を立てた人のビットを持っているので、
612: // これでターゲットを特定する。
613: uint target_mask = data ^ sel;
614: if (__builtin_popcount(target_mask) != 1) {
615: // データバスから自身を除いた結果立ってるビットが1個以外だったら
616: // 誰も応答しないので、何もせず帰ってよい。
1.1.1.2 root 617: putlog(3, "%s (No target specified; databus=$%02x)", __func__, data);
1.1 root 618: return;
619: }
620:
621: // ターゲット ID はここで取得
622: target_id = DecodeID(target_mask);
623: if (device[target_id] == NULL) {
624: // ターゲットデバイスがいなければ、バスは何もしない。
625: // (その結果イニシエータでタイムアウトが起きるはず)
626: putlog(4, "%s (No target)", __func__);
627: return;
628: }
629:
630: // ターゲットデバイスがいれば、規定時間経過後ターゲットに通知。
631: putlog(4, "%s", __func__);
632: CallAfter(Selected, "SCSIBus Selected", 100_usec);
633: }
634:
635: // SelectionStart() にてターゲットが選択されて規定時間経ったので
636: // そろそろターゲットが BSY を立てる頃に呼ばれるコールバック。
637: void
1.1.1.4 root 638: SCSIBus::Selected(Event& ev)
1.1 root 639: {
640: // ターゲットに通知。ターゲットがいることは確定している。
641: assert(device[target_id]);
1.1.1.12 root 642: device[target_id]->OnSelected();
1.1 root 643: }
644:
645: // ターゲットが BSY を立てて規定時間経ったので
1.1.1.4 root 646: // そろそろイニシエータが SEL を下げる頃に呼ばれるコールバック。
1.1 root 647: void
1.1.1.4 root 648: SCSIBus::SelectionAck(Event& ev)
1.1 root 649: {
650: // イニシエータに通知。
651: assert(device[init_id]);
1.1.1.12 root 652: device[init_id]->OnSelectionAck();
1.1 root 653: }
654:
655: // 情報転送フェーズ開始を通知する。
656: // BSY を上げて情報転送フェーズに移行したところで呼ばれる。
657: void
1.1.1.4 root 658: SCSIBus::StartTransfer(Event& ev)
1.1 root 659: {
660: SetPhase(SCSI::Phase::Transfer);
661:
662: assert(device[target_id]);
1.1.1.12 root 663: device[target_id]->OnStartTransfer();
664: }
665:
666: // SCSI デバイスモニタ
667: // (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので
668: // とりあえずここで)
669: void
670: SCSIBus::MonitorUpdate(Monitor *, TextScreen& screen)
671: {
672: int y;
673:
674: // 012345678901234567890123456789012345678901234567890123456789012345678901234
675: // ID6: HD(2048MB)
676: // MediumLoaded Off LogicalBlock 2048 ImageSize 2,147,483,648
677: // RemovalPrevent Off WriteProtected
678:
679:
680: screen.Clear();
681:
682: y = 0;
683: for (const auto dev : connected_devices) {
684: std::string desc = SCSI::GetDevTypeName(dev->GetDevType());
685: std::string pathname;
686:
687: const SCSIDisk *disk = dynamic_cast<const SCSIDisk*>(dev);
688: if (disk && disk->IsMediumLoaded()) {
689: desc += string_format("(%uMB)",
690: (uint)(disk->GetSize() / 1024 / 1024));
691: pathname = disk->GetPathName();
692: }
693: screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str());
694:
695: #if 1
696: (void)pathname;
697: #else
698: // パス名の非ASCIIどうするか
699: if (!pathname.empty()) {
700: if (pathname.size() > screen.GetCol() - 16 - 3) {
701: pathname = pathname.substr(0, screen.GetCol() - 16 - 3);
702: pathname += "...";
703: }
704: screen.Puts(16, y, pathname.c_str());
705: }
706: #endif
707: y++;
708:
709: // イニシエータならここまで。(今の所イニシエータとディスクしかない)
710: if (disk == NULL) {
711: continue;
712: }
713:
714: const int x1 = 5;
715: const int x2 = 29;
716: const int x3 = 50;
717:
718: // 1行目
719: screen.Print(x1, y,
720: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
721: "MediumLoaded %s",
722: disk->IsMediumLoaded() ? "On" : "Off");
723: screen.Print(x2, y, "LogicalBlock %u", disk->GetBlocksize());
724: std::string imagesize = format_number(disk->GetSize());
725: screen.Print(x3, y, "ImageSize %13s", imagesize.c_str());
726: y++;
727:
728: // 2行目
729: screen.Print(x1, y,
730: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
731: "RemovalPrevent %s",
732: disk->IsMediumRemovalPrevented() ? "On" : "Off");
733:
734: screen.Puts(x2, y,
735: (disk->IsMediumLoaded() ? TA::Normal : TA::Disable),
736: disk->GetWriteModeStr());
737: y++;
738:
739: y++;
740: }
1.1 root 741: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.