|
|
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.11! root 7: // ログレベルは
! 8: // 1: SCSI コマンドの概要。
! 9: // デバイスの動作のうち大きな変更のもの
! 10: // 2: SCSI コマンドの詳細、ダンプ。
! 11: // デバイスの動作のうち些細なもの、あるいは頻繁なもの
! 12: // 3: フェーズ遷移
! 13: // 4: データも含める
1.1 root 14:
1.1.1.11! root 15: #include "scsicmd.h"
1.1 root 16: #include "scsidev.h"
1.1.1.2 root 17: #include "config.h"
18: #include "mainapp.h"
1.1.1.11! root 19: #include "uimessage.h"
1.1 root 20:
21: //
1.1.1.11! root 22: // SCSI デバイス共通部分
1.1 root 23: //
24:
25: // コンストラクタ
1.1.1.10 root 26: SCSIDevice::SCSIDevice(const std::string& objname_)
27: : inherited(objname_)
1.1 root 28: {
29: }
30:
31: // デストラクタ
32: SCSIDevice::~SCSIDevice()
33: {
34: }
35:
36: //
37: // SCSI ホストデバイス
38: //
39:
40: // コンストラクタ
1.1.1.10 root 41: SCSIHostDevice::SCSIHostDevice(const std::string& objname_)
42: : inherited(objname_)
1.1 root 43: {
1.1.1.5 root 44: devtype = SCSI::DevType::Initiator;
45:
1.1.1.11! root 46: monitor_devs.func = (MonitorCallback_t)&SCSIHostDevice::MonitorUpdateDevs;
! 47: // サイズは Create で決まる
! 48: monitor_devs.Regist(ID_MONITOR_SCSIDEVS);
! 49:
1.1 root 50: // バスデバイスを作成する。
51: // ただし自身がバスに参加するのは継承側で行う
52: // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。
53: scsibus.reset(new SCSIBus());
54: }
55:
56: // デストラクタ
57: SCSIHostDevice::~SCSIHostDevice()
58: {
59: }
60:
1.1.1.2 root 61: // 設定ファイルに使われるこのホストデバイスを示す接頭語を設定する
62: // (継承クラス側のコンストラクタで config_name を設定後に呼ぶこと)
63: void
64: SCSIHostDevice::SetConfigName(const char *name_)
65: {
66: config_name = name_;
67: }
68:
1.1 root 69: bool
70: SCSIHostDevice::Create()
71: {
72: // 設定を読み込む。
73: for (int id = 0; id < 8; id++) {
1.1.1.2 root 74: const std::string key = string_format("%s-id%d-image",
1.1 root 75: GetConfigName(), id);
1.1.1.3 root 76: const ConfigItem& item = gConfig->Find(key);
1.1.1.2 root 77: const std::string& image = item.AsString();
78:
79: // 空ならデバイスなし
80: if (image.length() == 0) {
1.1.1.5 root 81: // 未接続部分のパラメータは減らしておくか。
82: // --show-config するとさすがに無駄に多くて邪魔なので。
1.1.1.11! root 83: const std::string wikey = string_format("%s-id%d-writeignore",
! 84: GetConfigName(), id);
1.1.1.5 root 85: const std::string wpkey = string_format("%s-id%d-writeprotect",
86: GetConfigName(), id);
87: const std::string stkey = string_format("%s-id%d-seektime",
88: GetConfigName(), id);
1.1.1.11! root 89: gConfig->Delete(wikey);
1.1.1.5 root 90: gConfig->Delete(wpkey);
91: gConfig->Delete(stkey);
1.1.1.2 root 92: continue;
93: }
94:
95: // ',' より前がデバイス種別 (ここではパスは不要)
1.1.1.11! root 96: auto v = string_split(image, ',');
! 97: // 比較のため前後の空白を取り除いて小文字にする
! 98: std::string type = string_trim(v[0]);
! 99: type = string_tolower(type);
1.1.1.2 root 100:
1.1.1.11! root 101: SCSI::DevType target_devtype;
1.1.1.2 root 102: if (type == "hd") {
1.1.1.11! root 103: target_devtype = SCSI::DevType::HD;
! 104: } else if (type == "cd") {
! 105: target_devtype = SCSI::DevType::CD;
! 106: } else if (type == "mo") {
! 107: target_devtype = SCSI::DevType::MO;
1.1.1.2 root 108: } else {
109: item.Err("invaild device type '%s'", type.c_str());
1.1.1.11! root 110: return false;
1.1 root 111: }
1.1.1.11! root 112: target[id].reset(new SCSIDisk(this, id, target_devtype));
! 113: scsibus->Attach(target[id].get(), id);
1.1 root 114: }
115:
116: return true;
117: }
118:
1.1.1.11! root 119: // 初期化。
! 120: // 継承クラスが connected_devices をセットした後で呼ぶこと。
1.1 root 121: bool
122: SCSIHostDevice::Init()
123: {
124: // 初期化ではないけどログレベルがセットできたここでデバッグ表示。
125: // -L オプションの処理は Create() の後なので。
126: if (loglevel >= 1) {
127: for (int id = 0; id < 8; id++) {
128: std::string typestr;
129: if (target[id]) {
1.1.1.11! root 130: typestr = SCSI::GetDevTypeName(target[id]->GetDevType());
1.1.1.8 root 131: putmsgn("%s target #%d %s", GetConfigName(), id,
1.1 root 132: typestr.c_str());
133: }
134: }
135: }
136:
1.1.1.11! root 137: // connected_devices によってモニタの大きさを決定。
! 138: monitor_devs.SetSize(80, (connected_devices.size() - 1) * 4 + 1);
! 139:
1.1 root 140: return true;
141: }
142:
143: // バスフリーコールバック (SCSIBus から呼ばれる)
144: void
145: SCSIHostDevice::BusFreeCB(int id)
146: {
147: // 実際にはバスフリーを検出したデバイスは各自自身が送出している信号を
148: // 下げるのだが、色々面倒なので、ここではホストのバスフリーコールバックが
149: // 一括して全部の線を下げる。
150: // ただしさすがにログがうるさいので、上がってる線だけ下げる。
151:
152: uint32 data;
153: while ((data = GetBSY()) != 0) {
154: bus->NegateBSY(DecodeID(data));
155: }
156: while ((data = GetSEL()) != 0) {
157: bus->NegateSEL(DecodeID(data));
158: }
159: if (GetREQ()) {
160: NegateREQ();
161: }
162: if (GetACK()) {
163: NegateACK();
164: }
165: if (GetATN()) {
166: NegateATN();
167: }
168: SetXfer(0);
169: SetData(0);
170: }
171:
1.1.1.11! root 172: // SCSI デバイスモニタ
! 173: // (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので
! 174: // とりあえずここで)
! 175: void
! 176: SCSIHostDevice::MonitorUpdateDevs(Monitor *, TextScreen& screen)
1.1 root 177: {
1.1.1.11! root 178: int x;
! 179: int y;
1.1 root 180:
1.1.1.11! root 181: screen.Clear();
1.1.1.4 root 182:
1.1.1.11! root 183: y = 0;
! 184: for (const auto dev : connected_devices) {
! 185: std::string desc = SCSI::GetDevTypeName(dev->GetDevType());
! 186: std::string pathname;
! 187:
! 188: SCSIDisk *disk = dynamic_cast<SCSIDisk*>(dev);
! 189: if (disk && disk->IsMediumLoaded()) {
! 190: desc += string_format("(%dMB)",
! 191: (int)(disk->GetSize() / 1024 / 1024));
! 192: pathname = disk->GetPathName();
! 193: }
! 194: screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str());
! 195:
! 196: #if 1
! 197: (void)pathname;
! 198: #else
! 199: // パス名の非ASCIIどうするか
! 200: if (!pathname.empty()) {
! 201: if (pathname.size() > screen.GetCol() - 16 - 3) {
! 202: pathname = pathname.substr(0, screen.GetCol() - 16 - 3);
! 203: pathname += "...";
! 204: }
! 205: screen.Puts(16, y, pathname.c_str());
1.1.1.4 root 206: }
1.1.1.11! root 207: #endif
! 208: y++;
1.1 root 209:
1.1.1.11! root 210: // イニシエータならここまで。(今の所イニシエータとディスクしかない)
! 211: if (disk == NULL) {
! 212: continue;
1.1.1.7 root 213: }
214:
1.1.1.11! root 215: // 1列目
! 216: x = 5;
! 217: screen.Print(x, y,
! 218: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
! 219: "MediumLoaded %s",
! 220: disk->IsMediumLoaded() ? "On" : "Off");
! 221: screen.Print(x, y + 1,
! 222: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
! 223: "RemovalPrevent %s",
! 224: disk->IsMediumRemovalPrevented() ? "On" : "Off");
! 225:
! 226: // 2列目
! 227: x = 29;
! 228: const char *wstr;
! 229: switch (disk->GetWriteMode()) {
! 230: case SCSIDisk::RW::ReadOnly: wstr = "ReadOnly"; break;
! 231: case SCSIDisk::RW::WriteProtect: wstr = "WriteProtected"; break;
! 232: case SCSIDisk::RW::Writeable: wstr = "Writeable"; break;
! 233: case SCSIDisk::RW::WriteIgnore: wstr = "WriteIgnored"; break;
! 234: default: __unreachable();
! 235: }
! 236: screen.Puts(x, y,
! 237: (disk->IsMediumLoaded() ? TA::Normal : TA::Disable), wstr);
! 238:
! 239: // 3列目
! 240: x = 50;
! 241: uint64 v = disk->GetSize();
! 242: std::string bytes;
! 243: if (v < 1000) {
! 244: bytes = string_format("%u", (uint32)v);
! 245: } else if (v < 1000 * 1000) {
! 246: bytes = string_format("%u,%03u",
! 247: (uint)(v / 1000),
! 248: (uint)(v % 1000));
! 249: } else if (v < 1000 * 1000 * 1000) {
! 250: bytes = string_format("%u,%03u,%03u",
! 251: (uint)(v / 1000 / 1000),
! 252: (uint)((v / 1000) % 1000),
! 253: (uint)(v % 1000));
! 254: } else if (v < 1000ULL * 1000 * 1000 * 1000) {
! 255: bytes = string_format("%u,%03u,%03u,%03u",
! 256: (uint)(v / 1000 / 1000 / 1000),
! 257: (uint)((v / 1000 / 1000) % 1000),
! 258: (uint)((v / 1000) % 1000),
! 259: (uint)(v % 1000));
! 260: } else {
! 261: bytes = string_format("%ju", (uintmax_t)v);
1.1.1.7 root 262: }
1.1.1.11! root 263: screen.Print(x, y, "TotalBytes %s", bytes.c_str());
! 264: screen.Print(x, y + 1, "LogicalBlock %d", disk->GetBlocksize());
1.1.1.7 root 265:
1.1.1.11! root 266: y += 3;
1.1 root 267: }
1.1.1.11! root 268: }
1.1 root 269:
270:
271: //
272: // SCSI ターゲットデバイスの共通部分
273: //
274:
275: // コンストラクタ
1.1.1.11! root 276: SCSITarget::SCSITarget(const std::string& objname_, SCSIHostDevice *h, int id)
1.1.1.10 root 277: : inherited(objname_)
1.1 root 278: {
279: // ホストへのリンク (所有関係のないポインタ)
280: host = h;
281: myid = id;
282: }
283:
284: // デストラクタ
285: SCSITarget::~SCSITarget()
286: {
287: }
288:
1.1.1.9 root 289: // 電源オン
290: bool
291: SCSITarget::PowerOn()
292: {
293: ClearSense();
294: cmdseq.clear();
295: cmdlen = 0;
296: cmd.reset();
297: bytecount = 0;
298: lastbyte = false;
299:
300: return true;
301: }
302:
1.1.1.4 root 303: // バスリセットされた (SCSIBus から呼ばれる)
304: void
305: SCSITarget::BusResetCB()
306: {
1.1.1.11! root 307: // センスキーをクリア
! 308: ClearSense();
! 309:
1.1.1.5 root 310: // リセットされたので持ってる信号を全部解放。
311: // 一応自分が変えていい信号線だけを落とす。
312: switch (GetPhase()) {
313: case SCSI::Phase::BusFree:
314: break;
315: case SCSI::Phase::Arbitration:
316: case SCSI::Phase::Selection:
317: case SCSI::Phase::Reselection:
318: NegateSEL();
319: NegateBSY();
320: break;
321: case SCSI::Phase::Transfer:
322: NegateBSY();
323: break;
324: }
1.1.1.4 root 325: }
326:
1.1 root 327: // セレクションで選択された (SCSIBus から呼ばれる)
328: void
329: SCSITarget::SelectionCB()
330: {
331: // ターゲットが、自身が選択されていることを認識してから BSY を
332: // 立てるまでの時間はもうバス側でカウントしてあるので、ここでは
333: // ただちに BSY を上げてよい。
334: putlog(4, "Selection Callback");
335: AssertBSY();
336: }
337:
338: // 情報転送フェーズを開始する (SCSIBus から呼ばれる)
339: void
340: SCSITarget::StartTransferCB()
341: {
342: // 最初はコマンドフェーズ。(ATN はとりあえず放置)
343: putlog(4, "Start Command Phase");
344: cmdseq.clear();
345: cmdlen = 0;
346: SetXfer(SCSI::XferPhase::Command);
347: AssertREQ();
348: }
349:
350: // 情報転送フェーズ間の遷移について
351: //
352: // 情報転送フェーズは TransferCB() で1バイトずつ転送を行い、所定のバイト数
353: // の転送が完了すればフェーズを遷移する。遷移方法は入出力方向別で6通り。
354: // o IN -> IN
355: // o IN -> OUT
356: // o IN -> END
357: // o OUT -> OUT
358: // o OUT -> IN
359: // o OUT -> END
360: //
361: // IN -> IN の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを
362: // 変更して REQ を立てるところまで。
363: //
364: // IN -> OUT の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを
365: // 変更して (おそらく {Phase}Begin() を発行してから)、データをセットし REQ
366: // を立てる?
367: //
368: // IN -> END の遷移。TransferCB() で前フェーズの最終バイト受信後、BSY を
369: // 下げて終了。
370: //
371: // OUT -> OUT の遷移。前フェーズの最終バイトをイニシエータが受信後に
372: // TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
373: // を発行してから)、データをセットし REQ を立てる?
374: //
375: // OUT -> IN の遷移。前フェーズの最終バイトをイニシエータが受信後に
376: // TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
377: // を発行してから)、REQ を立てる?
378: //
379: // OUT -> END の遷移。前フェーズの最終バイトをイニシエータが受信後に
380: // TransferCB() が呼ばれるので、BSY を下げる?
381:
382: // 情報転送フェーズでイニシエータが ACK を上げた。(SCSIBus から呼ばれる)
383: // OUT (イニシエータ → ターゲット方向) なら、ターゲットが上げた REQ に応答
384: // してイニシエータがデータをデータバスに乗せてからこれをコールしてくる。
385: // IN (ターゲット → イニシエータ方向) なら、ターゲットがデータをデータバスに
386: // 乗せて REQ を上げたのをイニシエータが受信した後でこれをコールしてくる。
387: void
388: SCSITarget::TransferCB()
389: {
390: uint8 data;
391: SCSI::XferPhase xfer;
392:
393: // このフェーズの最終バイトか
394: lastbyte = false;
395:
396: // このフェーズの1バイト処理
397: xfer = GetXfer();
398: if (xfer == SCSI::XferPhase::Command) {
399: // コマンドフェーズだけいろいろ特殊なので別処理。
400: // 受信バッファが異なる、1バイト目で受信長が決まるなど。
401: data = GetData();
402: putlog(4, "TransferCB: cmdseq[%02d] = $%02x",
403: (int)cmdseq.size(), data);
404: cmdseq.push_back(data);
405: if (cmdseq.size() == 1) {
406: // 1バイト目ならこのコマンドが何バイトか調べる
407: switch (cmdseq[0] >> 5) {
408: case 0: cmdlen = 6; break;
409: case 1: cmdlen = 10; break;
410: case 2: cmdlen = 10; break;
411: case 5: cmdlen = 12; break;
412: default:
413: // 3,4 はリサーブ、6,7 はベンダー定義。XXX 来たらどうする?
414: cmdlen = 6;
415: break;
416: }
417: } else if (cmdseq.size() >= cmdlen) {
1.1.1.11! root 418: if (loglevel >= 2
! 419: #if defined(NO_READWRITE_LOG)
! 420: && (cmdseq[0] != SCSI::Command::Read6 &&
! 421: cmdseq[0] != SCSI::Command::Read10 &&
! 422: cmdseq[0] != SCSI::Command::Write6 &&
! 423: cmdseq[0] != SCSI::Command::Write10)
! 424: #endif
! 425: ) {
1.1 root 426: std::string cmdbuf;
427: const char *name = SCSI::GetCommandName(cmdseq[0]);
428: cmdbuf += string_format("Command \"%s\"", name ?: "?");
429: for (const auto& v : cmdseq) {
1.1.1.11! root 430: cmdbuf += string_format(" %02x", v);
1.1 root 431: }
1.1.1.8 root 432: putlogn("%s", cmdbuf.c_str());
1.1 root 433: }
434: // 最終バイトを受信したらコマンドを選択
435: DispatchCmd();
436: lastbyte = true;
437: }
438: } else if (GetIO() == false) {
439: // OUT 方向 (ターゲットが受信側)
440: // bytecount は減算方向。
441: cmd->buf.push_back(GetData());
442: bytecount--;
443: if (bytecount == 0) {
444: lastbyte = true;
445: }
446: } else {
447: // IN 方向 (ターゲットが送信側)
448: // bytecount は加算方向。
449: // データは前のループの終わり (TransferAckCB()の下のほう) で
450: // セットしているのでここでは引き取られた分をカウントする。
451: bytecount++;
452: if (bytecount >= cmd->buf.size()) {
453: lastbyte = true;
454: }
455: }
456:
457: NegateREQ();
458: }
459:
460: // 情報転送フェーズでイニシエータが ACK を下げた。(SCSIBus から呼ばれる)
461: void
462: SCSITarget::TransferAckCB()
463: {
464: SCSI::XferPhase xfer = GetXfer();
465:
466: // これが最終バイトだったら、フェーズ完了 → フェーズ切り替え。
467: if (lastbyte) {
468: SCSI::XferPhase next;
469: // フェーズ完了
470: switch (xfer) {
471: case SCSI::XferPhase::Command:
472: next = cmd->Command(cmdseq);
473: break;
474: case SCSI::XferPhase::DataOut:
475: next = cmd->DoneDataOut();
476: break;
477: case SCSI::XferPhase::DataIn:
478: next = cmd->DoneDataIn();
479: break;
480: case SCSI::XferPhase::MsgOut:
481: next = cmd->DoneMsgOut();
482: break;
483: case SCSI::XferPhase::MsgIn:
484: next = cmd->DoneMsgIn();
485: break;
486: case SCSI::XferPhase::Status:
487: next = cmd->DoneStatus();
488: break;
489: default:
490: PANIC("TransferCB unknown xfer done %d", (int)xfer);
491: }
1.1.1.11! root 492: putlog(3, "%s done", SCSI::GetXferPhaseName(xfer));
1.1 root 493:
1.1.1.5 root 494: // 簡略化のため、フェーズ完了時だけ REQ を引き伸ばすことができる
495: if (cmd->optime != 0) {
496: bus->SetOneshotReqWait(cmd->optime);
497: cmd->optime = 0;
498: }
499:
1.1 root 500: if (next != xfer) {
501: // End ならバスフリーにして終了。
502: if (next == SCSI::XferPhase::End) {
503: NegateBSY();
504: return;
505: }
506:
507: // (必要なら)フェーズを更新
508: SetXfer(next);
509: xfer = next;
510:
511: // 新しいフェーズの開始処理
512: // XXX ここで bytecount とかをリセットできればいいのだが
513: // Command フェーズで Data フェーズの準備をすることが多いので
514: // 悩ましい。
515: switch (xfer) {
516: case SCSI::XferPhase::DataOut:
517: cmd->buf.clear();
518: bytecount = cmd->recvbytes;
519: putlog(3, "DataOut begin (%d bytes)", bytecount);
520: break;
521: case SCSI::XferPhase::DataIn:
522: bytecount = 0;
523: putlog(3, "DataIn begin (%d bytes)", (int)cmd->buf.size());
524: break;
525: case SCSI::XferPhase::MsgOut:
526: cmd->BeginMsgOut();
527: cmd->buf.clear();
528: bytecount = cmd->recvbytes;
529: putlog(3, "MsgOut begin (%d bytes)", bytecount);
530: break;
531: case SCSI::XferPhase::MsgIn:
532: cmd->BeginMsgIn();
533: bytecount = 0;
534: putlog(3, "MsgIn begin (%d bytes)", (int)cmd->buf.size());
535: break;
536: case SCSI::XferPhase::Status:
537: cmd->BeginStatus();
538: bytecount = 0;
539: putlog(3, "Status begin (%d bytes)", (int)cmd->buf.size());
540: break;
541: default:
542: PANIC("TransferCB unknown xfer begin %d", (int)xfer);
543: }
544: }
545: }
546:
547: // IN 方向 (ターゲットが送信側) ならここでデータをバスにセット。
548: if (GetIO()) {
549: SetData(cmd->buf[bytecount]);
550: putlog(4, "SetData $%02x", GetData());
551: }
552:
553: // 次のバイト転送を行うために REQ を上げる。
554: AssertREQ();
555: }
556:
557: // SCSI コマンドのディスパッチャ
558: void
559: SCSITarget::DispatchCmd()
560: {
561: switch (cmdseq[0]) {
562: case SCSI::Command::TestUnitReady: // 0x00
1.1.1.11! root 563: cmd.reset(new SCSICmd(this));
1.1 root 564: break;
565:
1.1.1.2 root 566: case SCSI::Command::RezeroUnit: // 0x01
1.1.1.11! root 567: cmd.reset(new SCSICmd(this));
1.1.1.2 root 568: break;
569:
1.1 root 570: case SCSI::Command::RequestSense: // 0x03
571: cmd.reset(new SCSICmdRequestSense(this));
572: break;
573:
574: case SCSI::Command::Inquiry: // 0x12
575: cmd.reset(new SCSICmdInquiry(this));
576: break;
577:
578: case SCSI::Command::ModeSelect6: // 0x15
1.1.1.11! root 579: case SCSI::Command::ModeSelect10: // 0x55
! 580: cmd.reset(new SCSICmdModeSelect(this));
1.1 root 581: break;
582:
1.1.1.2 root 583: case SCSI::Command::ModeSense6: // 0x1a
1.1.1.11! root 584: case SCSI::Command::ModeSense10: // 0x5a
! 585: cmd.reset(new SCSICmdModeSense(this));
1.1 root 586: break;
587:
588: default:
589: // サポートしていないコマンド
590: const char *cname = SCSI::GetCommandName(cmdseq[0]);
591: std::string name;
592: if (cname) {
593: name = string_format(" %s", cname);
594: }
595: putlog(0, "未実装 SCSI コマンド $%02x%s len=%d",
596: cmdseq[0], name.c_str(), (int)cmdseq.size());
597:
598: cmd.reset(new SCSICmdNotSupportedCommand(this));
599: break;
600: }
601: }
602:
1.1.1.11! root 603:
1.1 root 604: //
1.1.1.11! root 605: // SCSI Disk
1.1 root 606: //
607:
608: // コンストラクタ
1.1.1.11! root 609: SCSIDisk::SCSIDisk(SCSIHostDevice *h, int id, SCSI::DevType devtype_)
! 610: : inherited("", h, id)
1.1 root 611: {
1.1.1.11! root 612: devtype = devtype_;
! 613:
! 614: const char *devtypename = SCSI::GetDevTypeName(devtype);
! 615:
! 616: // コンストラクタの初期化子では devtypename を組み込みにくいので、
! 617: // コンストラクタ本文でログ名などを設定する。その時の作法に
! 618: // 従っているのですこし面倒な手法が必要。
! 619: // lib/object.h 参照。
! 620: SetName(string_format("SCSI%s%d", devtypename, id));
! 621: ClearAlias();
! 622: AddAlias(GetName());
! 623: AddAlias(string_format("%s%d", devtypename, id));
! 624:
! 625: switch (devtype) {
! 626: case SCSI::DevType::HD:
! 627: removable_device = false;
! 628: writeable_device = true;
! 629: blocksize = 512;
! 630: break;
! 631: case SCSI::DevType::CD:
! 632: removable_device = true;
! 633: writeable_device = false;
! 634: blocksize = 2048;
! 635: break;
! 636: case SCSI::DevType::MO:
! 637: removable_device = true;
! 638: writeable_device = true;
! 639: blocksize = 512;
! 640: break;
! 641: default:
! 642: __unreachable();
! 643: }
1.1.1.10 root 644:
1.1.1.11! root 645: // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意
! 646: if (IsRemovableDevice()) {
! 647: event = new Event(this);
! 648: event->SetName(GetName() + " MediaChange");
! 649: }
1.1 root 650: }
651:
652: // デストラクタ
1.1.1.11! root 653: SCSIDisk::~SCSIDisk()
1.1 root 654: {
655: }
656:
657: // 初期化
658: bool
1.1.1.11! root 659: SCSIDisk::Init()
1.1 root 660: {
1.1.1.2 root 661: const std::string keybody = string_format("%s-id%d-",
1.1 root 662: host->GetConfigName(), myid);
663: const std::string imgkey = keybody + "image";
1.1.1.11! root 664: const std::string wikey = keybody + "writeignore";
1.1 root 665: const std::string wpkey = keybody + "writeprotect";
1.1.1.5 root 666: const std::string stkey = keybody + "seektime";
1.1 root 667:
1.1.1.11! root 668: // デバイス種別(v[0])とパス(v[1])に分解。
! 669: // デバイス種別はすでにチェックしてあるのでここでは不要。
1.1.1.3 root 670: const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.11! root 671: const std::string& imageval = itemimg.AsString();
! 672: auto v = string_split(imageval, ',', 2);
! 673: std::string filename;
! 674: if (v.size() > 1) {
! 675: filename = string_trim(v[1]);
! 676: }
! 677:
! 678: // 固定デバイス(HD)ならファイル名は省略不可
! 679: if (!IsRemovableDevice() && filename.empty()) {
1.1.1.2 root 680: itemimg.Err("imagefile not specified");
1.1 root 681: return false;
682: }
683:
1.1.1.11! root 684: // オープン前に writeignore をチェック。
! 685: // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
! 686: // 行わないといけない。うーん。
! 687: //
! 688: // 変数名は元々 -writeprotect だったが -writeignore に変更した。
! 689: // 単にリネームするだけだと、開発中に古いバージョンを二分探索とかする
! 690: // 必要が出来た場合に面倒なので、当面は古いほうも面倒を見る。
! 691: // 十分いらなくなった頃に削除する。
! 692: //
! 693: // wi指定 wp指定
! 694: // なし なし wi を採用。
! 695: // なし あり wp を採用。ただし警告
! 696: // あり なし wi を採用。
! 697: // あり あり wi を採用。ここは警告不要でいいだろう
! 698: //
! 699: const ConfigItem& wi = gConfig->Find(wikey);
! 700: const ConfigItem& wp = gConfig->Find(wpkey);
! 701: if (wi.GetFrom() == ConfigItem::FromInitial &&
! 702: wp.GetFrom() != ConfigItem::FromInitial )
! 703: {
! 704: // -writeignore (新)がなく -writeprotect (旧) だけが指定されると警告。
! 705: // エラー停止まではしなくていいか。
! 706: warnx("Warning: '%s' is obsolete. Use new '%s'",
! 707: wpkey.c_str(), wikey.c_str());
! 708:
! 709: write_ignore = wp.AsInt();
! 710: } else {
! 711: write_ignore = wi.AsInt();
1.1 root 712: }
713:
1.1.1.11! root 714: // 設定時点でファイルが指定されていればオープン
! 715: if (filename.empty() == false) {
! 716: const std::string path = gMainApp.GetVMDir() + filename;
! 717: if (LoadDisk(path) == false) {
! 718: return false;
! 719: }
! 720: } else {
! 721: // メディアがロードされないままでも起動時は必ず通知を投げる。
! 722: // ステータスパネルはこの UIMessage でのみ状態を更新するため。
! 723: MediaChanged();
1.1 root 724: }
725:
1.1.1.5 root 726: // 設定は [msec]、変数は [nsec]。
727: seektime = gConfig->Find(stkey).AsInt();
728: seektime *= 1_msec;
729:
1.1 root 730: return true;
731: }
732:
1.1.1.11! root 733: // 電源オン
! 734: bool
! 735: SCSIDisk::PowerOn()
! 736: {
! 737: if (inherited::PowerOn() == false) {
! 738: return false;
! 739: }
! 740:
! 741: // メディアの取り出し禁止を解除
! 742: PreventMediumRemoval(false);
! 743:
! 744: return true;
! 745: }
! 746:
! 747: // バスリセット
! 748: void
! 749: SCSIDisk::BusResetCB()
! 750: {
! 751: // メディアの取り出し禁止を解除
! 752: PreventMediumRemoval(false);
! 753:
! 754: inherited::BusResetCB();
! 755: }
! 756:
! 757: // バックエンドのディスクイメージを開く。
! 758: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
! 759: // のではないが、medium_loaded が同じ値になるので使いまわしている)
! 760: bool
! 761: SCSIDisk::LoadDisk(const std::string& pathname_)
! 762: {
! 763: bool changed = false;
! 764: off_t size;
! 765: bool w_ok;
! 766: bool read_only;
! 767:
! 768: // すでにあればクローズ (ここでは通知は行わない)
! 769: changed = UnloadDisk_internal();
! 770:
! 771: // 新しいイメージをオープン(する準備)
! 772: pathname = pathname_;
! 773: try {
! 774: image.reset(DiskImage::Create(pathname));
! 775: } catch (...) {
! 776: goto done;
! 777: }
! 778:
! 779: if (image->GetInfo(&size, &w_ok) < 0) {
! 780: goto done;
! 781: }
! 782:
! 783: // セクタ単位になっていること
! 784: if (size % blocksize != 0) {
! 785: warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%d)",
! 786: pathname.c_str(), (uintmax_t)size, blocksize);
! 787: goto done;
! 788: }
! 789: // メディアごとの最大サイズを越えていないこと
! 790: switch (GetDevType()) {
! 791: case SCSI::DevType::HD:
! 792: // LBA (32bit) * 512 byte/sector
! 793: if (size >= 0x200'0000'0000ULL) {
! 794: warnx("%s: Bad image size(%ju): Too large",
! 795: pathname.c_str(), (uintmax_t)size);
! 796: goto done;
! 797: }
! 798: break;
! 799: case SCSI::DevType::CD:
! 800: // https://cdrfaq.org/faq07.html#S7-6
! 801: // 63min = 283,500 [sect] = 553.7MB CD-ROM = 635.9MB CD-DA
! 802: // 74min = 333,000 [sect] = 650.3MB CD-ROM = 746.9MB CD-DA
! 803: // 80min = 360,000 [sect] = 703.1MB CD-ROM = 807.4MB CD-DA
! 804: // 90min = 405,000 [sect] = 791.0MB CD-ROM = 908.4MB CD-DA
! 805: // 99min = 445,500 [sect] = 870.1MB CD-ROM = 999.3MB CD-DA
! 806: if (size >= 445500 * 2048) {
! 807: warnx("%s: Bad image size(%ju): Too Large",
! 808: pathname.c_str(), (uintmax_t)size);
! 809: goto done;
! 810: }
! 811: break;
! 812: case SCSI::DevType::MO:
! 813: // 128M は 25 [sect] * 10,000 [sect/track] * 512 [byte/sect]
! 814: // 230M は 25 [sect] * 17,853 [sect/track] * 512 [byte/sect]
! 815: // 540M は 25 [sect] * 41,660 [sect/track] * 512 [byte/sect]
! 816: // 640M は 17 [sect] * 18,256 [sect/track] * 2048 [byte/sect]
! 817: // 1.3G は 17 [sect] * 35,638 [sect/track] * 2048 [byte/sect]
! 818: // 2.3G は 17 [sect] * 62,538 [sect/track] * 2048 [byte/sect]
! 819: //
! 820: // 今の所 512 byte/sector フォーマットのみ対応しておく。
! 821: if (size >= 25 * 41660 * 512) {
! 822: warnx("%s: Bad image size(%ju): 640M or larger MO not supported",
! 823: pathname.c_str(), (uintmax_t)size);
! 824: goto done;
! 825: }
! 826: break;
! 827: default:
! 828: __unreachable();
! 829: }
! 830:
! 831: // 書き込みモードをここで確定
! 832: if (IsWriteableDevice()) {
! 833: // 書き込み可能デバイスの場合
! 834: if (w_ok) {
! 835: if (write_ignore) {
! 836: write_mode = RW::WriteIgnore;
! 837: } else {
! 838: write_mode = RW::Writeable;
! 839: }
! 840: } else {
! 841: write_mode = RW::WriteProtect;
! 842: }
! 843: } else {
! 844: // 読み込み専用デバイスの場合
! 845: write_mode = RW::ReadOnly;
! 846: }
! 847:
! 848: // ここでオープン
! 849: read_only = (GetWriteMode() != RW::Writeable);
! 850: if (image->Open(read_only) == false) {
! 851: goto done;
! 852: }
! 853:
! 854: // 書き込み無視なら一応ログ出力
! 855: if (GetWriteMode() == RW::WriteIgnore) {
! 856: putmsg(0, "write is ignored");
! 857: }
! 858:
! 859: medium_loaded = true;
! 860: changed = true;
! 861: done:
! 862: if (changed) {
! 863: // HD 初期化時はログに出さない
! 864: if (IsRemovableDevice()) {
! 865: putlog(1, "Medium loaded");
! 866: }
! 867:
! 868: // 変化があれば通知
! 869: MediaChanged();
! 870: }
! 871: if (medium_loaded == false) {
! 872: // ここで状態をクリアする
! 873: Clear();
! 874: }
! 875: return medium_loaded;
! 876: }
! 877:
! 878: // バックエンドのディスクイメージを閉じる。
! 879: // オープンされてなければ何もしない。
! 880: void
! 881: SCSIDisk::UnloadDisk(bool force)
! 882: {
! 883: if (IsMediumRemovalAllowed() || force) {
! 884: if (UnloadDisk_internal()) {
! 885: // 実際に閉じたら通知
! 886: MediaChanged();
! 887: }
! 888: }
! 889: }
! 890:
! 891: // バックエンドのディスクイメージを閉じる (内部用)。
! 892: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
! 893: // ここでは通知は行わない。
! 894: bool
! 895: SCSIDisk::UnloadDisk_internal()
! 896: {
! 897: if ((bool)image == false) {
! 898: return false;
! 899: }
! 900:
! 901: Clear();
! 902:
! 903: // 取り出し禁止はされてても解除する
! 904: if (IsMediumRemovalPrevented()) {
! 905: // XXX PreventMediumRemoval() を呼ぶかどうするか
! 906: medium_removal_prevented = false;
! 907: }
! 908:
! 909: putlog(1, "Medium unloaded");
! 910: return true;
! 911: }
! 912:
! 913: // ディスク状態をクリアする
! 914: // (メディアを取り外した時など)
! 915: void
! 916: SCSIDisk::Clear()
! 917: {
! 918: medium_loaded = false;
! 919: image.reset();
! 920: pathname.clear();
! 921: write_mode = RW::Writeable;
! 922: }
! 923:
! 924: // メディア状態変更を UI に通知する
! 925: void
! 926: SCSIDisk::MediaChanged() const
! 927: {
! 928: UIMessage::Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID());
! 929: }
! 930:
! 931:
! 932: // メディアを挿入する。
! 933: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
! 934: void
! 935: SCSIDisk::LoadDiskUI(const std::string& pathname_)
! 936: {
! 937: new_pathname = pathname_;
! 938: event->func = (DeviceCallback_t)&SCSIDisk::LoadCallback;
! 939: event->Start();
! 940: }
! 941:
! 942: // メディアを排出する。
! 943: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
! 944: void
! 945: SCSIDisk::UnloadDiskUI(bool force)
! 946: {
! 947: event->func = (DeviceCallback_t)&SCSIDisk::UnloadCallback;
! 948: event->code = force;
! 949: event->Start();
! 950: }
! 951:
! 952: // メディア挿入コールバック
! 953: void
! 954: SCSIDisk::LoadCallback(Event& ev)
! 955: {
! 956: if (LoadDisk(new_pathname) == false) {
! 957: // 失敗したら UI に通知
! 958: UIMessage::Post(UIMessage::SCSI_MEDIA_FAILED);
! 959: }
! 960: }
! 961:
! 962: // メディア排出コールバック
! 963: void
! 964: SCSIDisk::UnloadCallback(Event& ev)
! 965: {
! 966: bool force = ev.code;
! 967: UnloadDisk(force);
! 968: }
! 969:
1.1 root 970: // SCSI コマンドのディスパッチャ
971: void
1.1.1.11! root 972: SCSIDisk::DispatchCmd()
1.1 root 973: {
974: switch (cmdseq[0]) {
975: case SCSI::Command::Read6: // 0x08
1.1.1.11! root 976: case SCSI::Command::Read10: // 0x28
! 977: cmd.reset(new SCSICmdRead(this));
1.1 root 978: break;
979:
980: case SCSI::Command::Write6: // 0x0a
1.1.1.11! root 981: case SCSI::Command::Write10: // 0x2a
! 982: if (IsWriteableDevice()) {
! 983: cmd.reset(new SCSICmdWrite(this));
! 984: } else {
! 985: cmd.reset(new SCSICmdNotSupportedCommand(this));
! 986: }
1.1 root 987: break;
988:
1.1.1.4 root 989: case SCSI::Command::StartStopUnit: // 0x1b
990: cmd.reset(new SCSICmdStartStopUnit(this));
991: break;
992:
1.1.1.11! root 993: case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
! 994: if (IsRemovableDevice() == false) {
! 995: // HD は取り出せないので、とりあえず。
! 996: // XXX 要調査
! 997: cmd.reset(new SCSICmdNotSupportedCommand(this));
! 998: return;
! 999: }
! 1000: cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
! 1001: break;
! 1002:
1.1 root 1003: case SCSI::Command::ReadCapacity: // 0x25
1.1.1.11! root 1004: // DA デバイスでは Read Capacity (0x25)、
! 1005: // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
! 1006: // 今の所同じ動作なので区別せず処理する。
1.1 root 1007: cmd.reset(new SCSICmdReadCapacity(this));
1008: break;
1009:
1.1.1.11! root 1010: case SCSI::Command::SynchronizeCache: // 0x35
! 1011: // このクラスは、物理ドライブにあるようなディスクとの間の
! 1012: // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
! 1013: // しろという指示に対しては、黙って成功するだけでいい気がする。
! 1014: // このコマンドはオプションだが NetBSD カーネルが使う。
! 1015: //
! 1016: // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
! 1017: // とりあえず黙って成功しておく。
! 1018: cmd.reset(new SCSICmd(this));
1.1 root 1019: break;
1020:
1.1.1.11! root 1021: case SCSI::Command::ReadTOC: // 0x43
! 1022: if (devtype == SCSI::DevType::CD) {
! 1023: cmd.reset(new SCSICmdReadTOC(this));
! 1024: } else {
! 1025: cmd.reset(new SCSICmdNotSupportedCommand(this));
! 1026: }
! 1027: break;
! 1028:
! 1029: case SCSI::Command::ReadFormatCapacities: // 0x23
! 1030: // SCSI-2 規格でないので詳細不明。
! 1031: // ディスク用か共通かも分からないけど、とりあえず
! 1032: case SCSI::Command::ReadDiscInformation: // 0x51
! 1033: // CD-ROM デバイスのオプションコマンドで
! 1034: // CD-R ライター用(?)らしいので無視する。
! 1035: cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7 root 1036: break;
1037:
1.1 root 1038: default:
1039: // 見付からなければ基本クラス側に任せる
1040: inherited::DispatchCmd();
1041: break;
1042: }
1043: }
1044:
1.1.1.11! root 1045: // Inquiry データを返す
! 1046: bool
! 1047: SCSIDisk::Inquiry(std::vector<uint8>& buf, int lun)
! 1048: {
! 1049: uint8 inqtype;
! 1050: const char *prodname;
! 1051:
! 1052: switch (devtype) {
! 1053: case SCSI::DevType::HD:
! 1054: inqtype = SCSI::InquiryDeviceType::DirectAccess;
! 1055: prodname = "SCSIHD";
! 1056: break;
! 1057: case SCSI::DevType::CD:
! 1058: inqtype = SCSI::InquiryDeviceType::CDROM;
! 1059: prodname = "SCSICD";
! 1060: break;
! 1061: case SCSI::DevType::MO:
! 1062: inqtype = SCSI::InquiryDeviceType::MO;
! 1063: prodname = "SCSIMO";
! 1064: break;
! 1065: default:
! 1066: assert(false);
! 1067: }
! 1068:
! 1069: buf[0] = inqtype;
! 1070: if (lun != 0) {
! 1071: buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
! 1072: }
! 1073: buf[1] = 0;
! 1074: if (IsRemovableDevice()) {
! 1075: buf[1] |= 0x80;
! 1076: }
! 1077: buf[2] = 2; // SCSI-2
! 1078: buf[3] = 2; // Response Data Format = SCSI-2
! 1079: buf[4] = buf.size() - 4; // 追加データ長
! 1080:
! 1081: // 文字列はゼロ終端ではなく、余りは空白で埋める
! 1082: memset(&buf[8], ' ', 36 - 8);
! 1083: memcpy(&buf[8], "NONO", 4);
! 1084: memcpy(&buf[16], prodname, strlen(prodname));
! 1085: memcpy(&buf[32], "0", 1);
! 1086:
! 1087: return true;
! 1088: }
! 1089:
! 1090: // ModeSense コマンドのデバイス固有パラメータを返す
! 1091: uint8
! 1092: SCSIDisk::GetDeviceSpecificParam() const
! 1093: {
! 1094: uint8 param = 0;
! 1095:
! 1096: // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
! 1097: // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
! 1098: //
! 1099: // 7 6 5 4 3 2 1 0
! 1100: // +---+---+---+------+---+---+---+---+
! 1101: // DA |WP | 0 |DPOFUA| 0 | WP: Write Protect
! 1102: // +---+---+---+------+---+---+---+---+
! 1103: //
! 1104: // +---+---+---+------+---+---+---+---+
! 1105: // MO |WP | 0 |DPOFUA| 0 |EBC| EBC: Enable Blank Check
! 1106: // +---+---+---+------+---+---+---+---+
! 1107: //
! 1108: // +---+---+---+------+---+---+---+---+
! 1109: // CD | 0 |DPOFUA| 0 |
! 1110: // +---+---+---+------+---+---+---+---+
! 1111:
! 1112: if (GetDevType() == SCSI::DevType::CD) {
! 1113: // CD には WP ビットの定義がない
! 1114: } else {
! 1115: // DA, MO は WP ビットの定義がある。
! 1116: if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
! 1117: param |= 0x80;
! 1118: }
! 1119: }
! 1120:
! 1121: return param;
! 1122: }
! 1123:
! 1124: bool
! 1125: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
! 1126: {
! 1127: if (__predict_false((bool)image == false)) {
! 1128: return false;
! 1129: }
! 1130: return image->Read(buf.data(), start, buf.size());
! 1131: }
! 1132:
! 1133: bool
! 1134: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
! 1135: {
! 1136: if (__predict_false((bool)image == false)) {
! 1137: return false;
! 1138: }
! 1139:
! 1140: if (GetWriteMode() == RW::Writeable) {
! 1141: return image->Write(buf.data(), start, buf.size());
! 1142: } else if (GetWriteMode() == RW::WriteIgnore) {
! 1143: // 書き込み無視
! 1144: return true;
! 1145: } else {
! 1146: // 書き込み不可
! 1147: return false;
! 1148: }
! 1149: }
! 1150:
! 1151: off_t
! 1152: SCSIDisk::GetSize() const
! 1153: {
! 1154: if (__predict_false((bool)image == false)) {
! 1155: return 0;
! 1156: }
! 1157: return image->GetSize();
! 1158: }
! 1159:
1.1.1.2 root 1160: // 指定の場所を自由に読み出す
1161: // (エミュレータ特権アクセス)
1162: bool
1.1.1.11! root 1163: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2 root 1164: {
1.1.1.11! root 1165: if (__predict_false((bool)image == false)) {
1.1.1.2 root 1166: return false;
1167: }
1.1.1.11! root 1168:
! 1169: if (start + len > GetSize()) {
1.1.1.2 root 1170: return false;
1171: }
1.1.1.11! root 1172: return image->Read(buf, start, len);
1.1.1.2 root 1173: }
1174:
1.1.1.11! root 1175: // メディアの取り出し可否(取り出し禁止)を設定する
! 1176: void
! 1177: SCSIDisk::PreventMediumRemoval(bool val)
! 1178: {
! 1179: // 状態が変わったところでログを表示
! 1180: if (medium_removal_prevented == false && val == true) {
! 1181: putlog(1, "Medium removal prevented");
! 1182: } else if (medium_removal_prevented == true && val == false) {
! 1183: putlog(1, "Medium removal allowed");
! 1184: }
! 1185: medium_removal_prevented = val;
! 1186: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.