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