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