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