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