|
|
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();
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:
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_;
1.1.1.12! root 773: if (image.CreateHandler(pathname) == false) {
1.1.1.11 root 774: goto done;
775: }
776:
1.1.1.12! root 777: if (image.GetInfo(&size, &w_ok) == false) {
1.1.1.11 root 778: goto done;
779: }
780:
781: // セクタ単位になっていること
782: if (size % blocksize != 0) {
783: warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%d)",
784: pathname.c_str(), (uintmax_t)size, blocksize);
785: goto done;
786: }
787: // メディアごとの最大サイズを越えていないこと
788: switch (GetDevType()) {
789: case SCSI::DevType::HD:
790: // LBA (32bit) * 512 byte/sector
791: if (size >= 0x200'0000'0000ULL) {
792: warnx("%s: Bad image size(%ju): Too large",
793: pathname.c_str(), (uintmax_t)size);
794: goto done;
795: }
796: break;
797: case SCSI::DevType::CD:
798: // https://cdrfaq.org/faq07.html#S7-6
799: // 63min = 283,500 [sect] = 553.7MB CD-ROM = 635.9MB CD-DA
800: // 74min = 333,000 [sect] = 650.3MB CD-ROM = 746.9MB CD-DA
801: // 80min = 360,000 [sect] = 703.1MB CD-ROM = 807.4MB CD-DA
802: // 90min = 405,000 [sect] = 791.0MB CD-ROM = 908.4MB CD-DA
803: // 99min = 445,500 [sect] = 870.1MB CD-ROM = 999.3MB CD-DA
804: if (size >= 445500 * 2048) {
805: warnx("%s: Bad image size(%ju): Too Large",
806: pathname.c_str(), (uintmax_t)size);
807: goto done;
808: }
809: break;
810: case SCSI::DevType::MO:
811: // 128M は 25 [sect] * 10,000 [sect/track] * 512 [byte/sect]
812: // 230M は 25 [sect] * 17,853 [sect/track] * 512 [byte/sect]
813: // 540M は 25 [sect] * 41,660 [sect/track] * 512 [byte/sect]
814: // 640M は 17 [sect] * 18,256 [sect/track] * 2048 [byte/sect]
815: // 1.3G は 17 [sect] * 35,638 [sect/track] * 2048 [byte/sect]
816: // 2.3G は 17 [sect] * 62,538 [sect/track] * 2048 [byte/sect]
817: //
818: // 今の所 512 byte/sector フォーマットのみ対応しておく。
819: if (size >= 25 * 41660 * 512) {
820: warnx("%s: Bad image size(%ju): 640M or larger MO not supported",
821: pathname.c_str(), (uintmax_t)size);
822: goto done;
823: }
824: break;
825: default:
826: __unreachable();
827: }
828:
829: // 書き込みモードをここで確定
830: if (IsWriteableDevice()) {
831: // 書き込み可能デバイスの場合
832: if (w_ok) {
833: if (write_ignore) {
834: write_mode = RW::WriteIgnore;
835: } else {
836: write_mode = RW::Writeable;
837: }
838: } else {
839: write_mode = RW::WriteProtect;
840: }
841: } else {
842: // 読み込み専用デバイスの場合
843: write_mode = RW::ReadOnly;
844: }
845:
846: // ここでオープン
847: read_only = (GetWriteMode() != RW::Writeable);
1.1.1.12! root 848: if (image.Open(read_only, write_ignore) == false) {
1.1.1.11 root 849: goto done;
850: }
851:
852: // 書き込み無視なら一応ログ出力
853: if (GetWriteMode() == RW::WriteIgnore) {
854: putmsg(0, "write is ignored");
855: }
856:
857: medium_loaded = true;
858: changed = true;
859: done:
860: if (changed) {
861: // HD 初期化時はログに出さない
862: if (IsRemovableDevice()) {
863: putlog(1, "Medium loaded");
864: }
865:
866: // 変化があれば通知
867: MediaChanged();
868: }
869: if (medium_loaded == false) {
870: // ここで状態をクリアする
871: Clear();
872: }
873: return medium_loaded;
874: }
875:
876: // バックエンドのディスクイメージを閉じる。
877: // オープンされてなければ何もしない。
878: void
879: SCSIDisk::UnloadDisk(bool force)
880: {
881: if (IsMediumRemovalAllowed() || force) {
882: if (UnloadDisk_internal()) {
883: // 実際に閉じたら通知
884: MediaChanged();
885: }
886: }
887: }
888:
889: // バックエンドのディスクイメージを閉じる (内部用)。
890: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
891: // ここでは通知は行わない。
892: bool
893: SCSIDisk::UnloadDisk_internal()
894: {
895: if ((bool)image == false) {
896: return false;
897: }
898:
899: Clear();
900:
901: // 取り出し禁止はされてても解除する
902: if (IsMediumRemovalPrevented()) {
903: // XXX PreventMediumRemoval() を呼ぶかどうするか
904: medium_removal_prevented = false;
905: }
906:
907: putlog(1, "Medium unloaded");
908: return true;
909: }
910:
911: // ディスク状態をクリアする
912: // (メディアを取り外した時など)
913: void
914: SCSIDisk::Clear()
915: {
916: medium_loaded = false;
1.1.1.12! root 917: image.Close();
1.1.1.11 root 918: pathname.clear();
919: write_mode = RW::Writeable;
920: }
921:
922: // メディア状態変更を UI に通知する
923: void
924: SCSIDisk::MediaChanged() const
925: {
926: UIMessage::Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID());
927: }
928:
929:
930: // メディアを挿入する。
931: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
932: void
933: SCSIDisk::LoadDiskUI(const std::string& pathname_)
934: {
935: new_pathname = pathname_;
936: event->func = (DeviceCallback_t)&SCSIDisk::LoadCallback;
937: event->Start();
938: }
939:
940: // メディアを排出する。
941: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
942: void
943: SCSIDisk::UnloadDiskUI(bool force)
944: {
945: event->func = (DeviceCallback_t)&SCSIDisk::UnloadCallback;
946: event->code = force;
947: event->Start();
948: }
949:
950: // メディア挿入コールバック
951: void
952: SCSIDisk::LoadCallback(Event& ev)
953: {
954: if (LoadDisk(new_pathname) == false) {
955: // 失敗したら UI に通知
956: UIMessage::Post(UIMessage::SCSI_MEDIA_FAILED);
957: }
958: }
959:
960: // メディア排出コールバック
961: void
962: SCSIDisk::UnloadCallback(Event& ev)
963: {
964: bool force = ev.code;
965: UnloadDisk(force);
966: }
967:
1.1 root 968: // SCSI コマンドのディスパッチャ
969: void
1.1.1.11 root 970: SCSIDisk::DispatchCmd()
1.1 root 971: {
972: switch (cmdseq[0]) {
973: case SCSI::Command::Read6: // 0x08
1.1.1.11 root 974: case SCSI::Command::Read10: // 0x28
975: cmd.reset(new SCSICmdRead(this));
1.1 root 976: break;
977:
978: case SCSI::Command::Write6: // 0x0a
1.1.1.11 root 979: case SCSI::Command::Write10: // 0x2a
980: if (IsWriteableDevice()) {
981: cmd.reset(new SCSICmdWrite(this));
982: } else {
983: cmd.reset(new SCSICmdNotSupportedCommand(this));
984: }
1.1 root 985: break;
986:
1.1.1.4 root 987: case SCSI::Command::StartStopUnit: // 0x1b
988: cmd.reset(new SCSICmdStartStopUnit(this));
989: break;
990:
1.1.1.11 root 991: case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
992: if (IsRemovableDevice() == false) {
993: // HD は取り出せないので、とりあえず。
994: // XXX 要調査
995: cmd.reset(new SCSICmdNotSupportedCommand(this));
996: return;
997: }
998: cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
999: break;
1000:
1.1 root 1001: case SCSI::Command::ReadCapacity: // 0x25
1.1.1.11 root 1002: // DA デバイスでは Read Capacity (0x25)、
1003: // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
1004: // 今の所同じ動作なので区別せず処理する。
1.1 root 1005: cmd.reset(new SCSICmdReadCapacity(this));
1006: break;
1007:
1.1.1.11 root 1008: case SCSI::Command::SynchronizeCache: // 0x35
1009: // このクラスは、物理ドライブにあるようなディスクとの間の
1010: // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
1011: // しろという指示に対しては、黙って成功するだけでいい気がする。
1012: // このコマンドはオプションだが NetBSD カーネルが使う。
1013: //
1014: // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
1015: // とりあえず黙って成功しておく。
1016: cmd.reset(new SCSICmd(this));
1.1 root 1017: break;
1018:
1.1.1.11 root 1019: case SCSI::Command::ReadTOC: // 0x43
1020: if (devtype == SCSI::DevType::CD) {
1021: cmd.reset(new SCSICmdReadTOC(this));
1022: } else {
1023: cmd.reset(new SCSICmdNotSupportedCommand(this));
1024: }
1025: break;
1026:
1027: case SCSI::Command::ReadFormatCapacities: // 0x23
1028: // SCSI-2 規格でないので詳細不明。
1029: // ディスク用か共通かも分からないけど、とりあえず
1030: case SCSI::Command::ReadDiscInformation: // 0x51
1031: // CD-ROM デバイスのオプションコマンドで
1032: // CD-R ライター用(?)らしいので無視する。
1033: cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7 root 1034: break;
1035:
1.1 root 1036: default:
1037: // 見付からなければ基本クラス側に任せる
1038: inherited::DispatchCmd();
1039: break;
1040: }
1041: }
1042:
1.1.1.11 root 1043: // Inquiry データを返す
1044: bool
1045: SCSIDisk::Inquiry(std::vector<uint8>& buf, int lun)
1046: {
1047: uint8 inqtype;
1048: const char *prodname;
1049:
1050: switch (devtype) {
1051: case SCSI::DevType::HD:
1052: inqtype = SCSI::InquiryDeviceType::DirectAccess;
1053: prodname = "SCSIHD";
1054: break;
1055: case SCSI::DevType::CD:
1056: inqtype = SCSI::InquiryDeviceType::CDROM;
1057: prodname = "SCSICD";
1058: break;
1059: case SCSI::DevType::MO:
1060: inqtype = SCSI::InquiryDeviceType::MO;
1061: prodname = "SCSIMO";
1062: break;
1063: default:
1064: assert(false);
1065: }
1066:
1067: buf[0] = inqtype;
1068: if (lun != 0) {
1069: buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
1070: }
1071: buf[1] = 0;
1072: if (IsRemovableDevice()) {
1073: buf[1] |= 0x80;
1074: }
1075: buf[2] = 2; // SCSI-2
1076: buf[3] = 2; // Response Data Format = SCSI-2
1077: buf[4] = buf.size() - 4; // 追加データ長
1078:
1079: // 文字列はゼロ終端ではなく、余りは空白で埋める
1080: memset(&buf[8], ' ', 36 - 8);
1081: memcpy(&buf[8], "NONO", 4);
1082: memcpy(&buf[16], prodname, strlen(prodname));
1083: memcpy(&buf[32], "0", 1);
1084:
1085: return true;
1086: }
1087:
1088: // ModeSense コマンドのデバイス固有パラメータを返す
1089: uint8
1090: SCSIDisk::GetDeviceSpecificParam() const
1091: {
1092: uint8 param = 0;
1093:
1094: // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
1095: // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
1096: //
1097: // 7 6 5 4 3 2 1 0
1098: // +---+---+---+------+---+---+---+---+
1099: // DA |WP | 0 |DPOFUA| 0 | WP: Write Protect
1100: // +---+---+---+------+---+---+---+---+
1101: //
1102: // +---+---+---+------+---+---+---+---+
1103: // MO |WP | 0 |DPOFUA| 0 |EBC| EBC: Enable Blank Check
1104: // +---+---+---+------+---+---+---+---+
1105: //
1106: // +---+---+---+------+---+---+---+---+
1107: // CD | 0 |DPOFUA| 0 |
1108: // +---+---+---+------+---+---+---+---+
1109:
1110: if (GetDevType() == SCSI::DevType::CD) {
1111: // CD には WP ビットの定義がない
1112: } else {
1113: // DA, MO は WP ビットの定義がある。
1114: if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
1115: param |= 0x80;
1116: }
1117: }
1118:
1119: return param;
1120: }
1121:
1122: bool
1123: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
1124: {
1125: if (__predict_false((bool)image == false)) {
1126: return false;
1127: }
1.1.1.12! root 1128: return image.Read(buf.data(), start, buf.size());
1.1.1.11 root 1129: }
1130:
1131: bool
1132: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
1133: {
1134: if (__predict_false((bool)image == false)) {
1135: return false;
1136: }
1137:
1.1.1.12! root 1138: if (GetWriteMode() >= RW::WriteProtect) {
1.1.1.11 root 1139: // 書き込み不可
1140: return false;
1141: }
1.1.1.12! root 1142: return image.Write(buf.data(), start, buf.size());
1.1.1.11 root 1143: }
1144:
1145: off_t
1146: SCSIDisk::GetSize() const
1147: {
1148: if (__predict_false((bool)image == false)) {
1149: return 0;
1150: }
1.1.1.12! root 1151: return image.GetSize();
1.1.1.11 root 1152: }
1153:
1.1.1.2 root 1154: // 指定の場所を自由に読み出す
1155: // (エミュレータ特権アクセス)
1156: bool
1.1.1.11 root 1157: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2 root 1158: {
1.1.1.11 root 1159: if (__predict_false((bool)image == false)) {
1.1.1.2 root 1160: return false;
1161: }
1.1.1.12! root 1162: return image.Read(buf, start, len);
1.1.1.2 root 1163: }
1164:
1.1.1.11 root 1165: // メディアの取り出し可否(取り出し禁止)を設定する
1166: void
1167: SCSIDisk::PreventMediumRemoval(bool val)
1168: {
1169: // 状態が変わったところでログを表示
1170: if (medium_removal_prevented == false && val == true) {
1171: putlog(1, "Medium removal prevented");
1172: } else if (medium_removal_prevented == true && val == false) {
1173: putlog(1, "Medium removal allowed");
1174: }
1175: medium_removal_prevented = val;
1176: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.