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