|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// SCSI デバイス
//
// Device Device
// | |
// v v
// IODevice SCSIDevice (SCSI デバイスとしての共通部分)
// | |
// v +-----+------+
// SPCDevice v v
// .scsi -> SCSIHostDevice SCSITarget (SCSI ホスト/ターゲットの共通部分)
// |
// v
// SCSIDisk (各デバイス、各ターゲット固有部分)
#pragma once
#include "diskimage.h"
#include "message.h"
#include "scsibus.h"
#include "scsicmd.h"
class SCSIDomain;
//
// SCSI デバイスとしての共通部分。
//
class SCSIDevice : public Device
{
using inherited = Device;
public:
explicit SCSIDevice(uint objid_);
~SCSIDevice() override;
// SCSI ID を返す
uint GetMyID() const { return myid; }
// デバイス種別を返す (主にデバッグ表示用)
SCSI::DevType GetDevType() const { return devtype; }
// バスに接続する。
void Attach(SCSIBus *bus_);
// 自身が接続しているバスを返す。
// イニシエータがバスに接続するのは VM 実行後なことに注意。
SCSIBus *GetBus() const { return bus; }
//
// 信号線制御
// (ソースコード的なショートカットのため)
//
bool GetRST() const { return bus->GetRST(); }
void AssertRST() { bus->AssertRST(); }
void NegateRST() { bus->NegateRST(); }
bool GetREQ() const { return bus->GetREQ(); }
void AssertREQ() { bus->AssertREQ(); }
void NegateREQ() { bus->NegateREQ(); }
bool GetACK() const { return bus->GetACK(); }
void AssertACK() { bus->AssertACK(); }
void NegateACK() { bus->NegateACK(); }
bool GetATN() const { return bus->GetATN(); }
void AssertATN() { bus->AssertATN(); }
void NegateATN() { bus->NegateATN(); }
uint8 GetSEL() const { return bus->GetSEL(); }
void AssertSEL() { bus->AssertSEL(myid); }
void NegateSEL() { bus->NegateSEL(myid); }
uint8 GetBSY() const { return bus->GetBSY(); }
void AssertBSY() { bus->AssertBSY(myid); }
void NegateBSY() { bus->NegateBSY(myid); }
bool GetMSG() const { return bus->GetMSG(); }
bool GetCD() const { return bus->GetCD(); }
bool GetIO() const { return bus->GetIO(); }
SCSI::XferPhase GetXfer() const { return bus->GetXfer(); }
void SetXfer(uint8 val) { bus->SetXfer(val); }
SCSI::Phase GetPhase() const { return bus->GetPhase(); }
uint8 GetData() const { return bus->GetData(); }
void SetData(uint8 val) const { bus->SetData(val); }
//
// SCSIBus から呼ばれる
//
// バスリセットされた (全員に通知される)
virtual void OnBusReset() { }
// バスフリーになった (全員に通知される)
virtual void OnBusFree(uint id) { }
// セレクションで選択された (ターゲットのみ通知される)
virtual void OnSelected() { }
// ターゲットがセレクションに応答した (イニシエータのみ通知される)
virtual void OnSelectionAck() { }
// 情報転送フェーズを開始する (ターゲットのみ通知される)
virtual void OnStartTransfer() { }
// 情報転送フェーズで REQ を立てた (イニシエータのみ通知される)
virtual void OnTransferReq() { }
// 情報転送フェーズで1バイト送受信が進んだ (ターゲットのみ通知される)
virtual void OnTransfer() { }
// 情報転送フェーズ(マニュアル転送)で ACK を下げた (ターゲットのみ通知)
virtual void OnTransferAck() { }
protected:
// 自身の ID
uint myid {};
// デバイス種別
SCSI::DevType devtype {};
// あれば自身が繋がっているバスへのポインタ。
// (イニシエータ自身も含めて) 所有ではない。
SCSIBus *bus {};
// 自身を管理しているドメイン(親) へのポインタ。
SCSIDomain *domain {};
};
//
// ホストデバイスの共通部分。
// イニシエータもしくは HBA (ホストバスアダプタ) に相当する部分。
//
class SCSIHostDevice : public SCSIDevice
{
using inherited = SCSIDevice;
public:
using SCSIHostCallback_t = void (Device::*)();
using BusFreeCallback_t = void (Device::*)(uint);
#define ToSCSIHostCallback(f) \
static_cast<SCSIHostDevice::SCSIHostCallback_t>(f)
#define ToBusFreeCallback(f) \
static_cast<SCSIHostDevice::BusFreeCallback_t>(f)
public:
SCSIHostDevice(SCSIDomain *domain_, IODevice *parent_);
~SCSIHostDevice() override;
// SCSI ID を設定する。(SCSIDomain からのみ呼ぶこと)
void SetID(uint myid_);
// SCSIBus からのコールバック関数を設定する。
void SetBusCallback(
BusFreeCallback_t busfree_,
SCSIHostCallback_t selack_,
SCSIHostCallback_t xferreq_);
private:
// SCSIBus から呼ばれる
// バスフリーになった (SCSIBus から全員に通知される)
void OnBusFree(uint id) override;
// ターゲットがセレクションに応答した (SCSIBus から呼ばれる)
void OnSelectionAck() override;
// 情報転送フェーズで REQ を立てた (イニシエータのみ通知される)
void OnTransferReq() override;
// 親クラス(コントローラ) へのコールバック。
// バスフリーを通知する
BusFreeCallback_t BusFreeCallback {};
// ターゲットがセレクションに応答したことを通知する
SCSIHostCallback_t SelectionAckCallback {};
// 転送フェーズでターゲットが REQ を立てたことを通知する
SCSIHostCallback_t TransferReqCallback {};
// コントローラデバイス。
IODevice *parent {};
};
//
// ターゲットデバイスの共通部分。
//
class SCSITarget : public SCSIDevice
{
using inherited = SCSIDevice;
public:
SCSITarget(SCSIDomain *, uint id);
~SCSITarget() override;
void ResetHard(bool poweron) override;
// Inquiry データを返す (Cmd から呼ばれる)
virtual bool Inquiry(std::vector<uint8>& buf, uint lun) = 0;
// 現在のコマンド列を返す (モニタ用)
std::vector<uint8> GetCmdSeq() const { return cmdseq; }
// コマンド列から SCSICmd を選択する。
// (VirtIOSCSI と ROM30 エミュレーションから使う)
SCSICmd *SelectCommand(const std::vector<uint8>& cmdseq_);
// 論理ブロック長
uint32 GetBlocksize() const { return blocksize; }
void SetBlocksize(uint32 val) { blocksize = val; }
// ModeSense コマンドのデバイス固有パラメータを返す
virtual uint8 GetDeviceSpecificParam() const { return 0; }
// センスキー
uint8 sensekey {};
// 上位バイトが ASC、下位バイトが ASCQ
uint16 senseasc {};
// センスキーをクリア
void ClearSense() {
sensekey = 0;
senseasc = 0;
}
// SCSIBus から呼ばれる
// バスリセットされた (全員に通知される)
void OnBusReset() override;
// セレクションで選択された (ターゲットのみ通知される)
void OnSelected() override;
// 情報転送フェーズを開始する (ターゲットのみ通知される)
void OnStartTransfer() override;
// 情報転送フェーズで1バイト送受信が進んだ (ターゲットのみ通知される)
void OnTransfer() override;
// 情報転送フェーズ(マニュアル転送)で ACK を下げた (ターゲットのみ通知)
void OnTransferAck() override;
protected:
// SCSI コマンドを選択する。内部用。
void SelectCommand();
// SCSI コマンドのディスパッチャ
virtual void DispatchCmd();
// コマンド
std::vector<uint8> cmdseq {};
// このコマンドの長さ。
// cmdseq の1バイト目で決まるやつのことで、受信目標バイト数。
// 今何バイト受信しているかは cmdseq.size()。
uint cmdlen {};
// 現在実行中の SCSI コマンド
std::unique_ptr<SCSICmd> cmd {};
// コマンド前のメッセージアウトフェーズ。
std::vector<uint8> msgseq {};
// 現在のフェーズでの転送カウント。
// IN と OUT でカウント方向が違うことに注意。
uint32 bytecount {};
// 現在処理中のバイトが(各)フェーズの最終バイトなら true
bool lastbyte {};
// 論理ブロック長
uint32 blocksize {};
};
//
// SCSI Disk (HD,CD,...)
//
class SCSIDisk : public SCSITarget
{
using inherited = SCSITarget;
public:
// メディアへの書き込みモード
//
// Device Media Ignore
// RO (RO) * ReadOnly
// RW RO * WriteProtect
// RW RW no Writeable
// RW RW yes WriteIgnore
enum class RW {
ReadOnly = 2, // 読み込み専用デバイス
WriteProtect = 1, // 書き込み禁止メディア
Writeable = 0, // 書き込み可能
WriteIgnore = -1, // 書き込み可能メディアで書き込みを無視
};
public:
SCSIDisk(SCSIDomain *, uint id, SCSI::DevType type);
~SCSIDisk() override;
bool Init() override;
void ResetHard(bool poweron) override;
void DispatchCmd() override;
bool Inquiry(std::vector<uint8>& buf, uint lun) override;
// ModeSense コマンドのデバイス固有パラメータを返す
uint8 GetDeviceSpecificParam() const override;
// buf にデータを読み込む。
// 単位はセクタではなくバイトなことに注意。
bool Read(std::vector<uint8>& buf, uint64 start);
// buf のデータを書き込む。
// 単位はセクタではなくバイトなことに注意。
bool Write(const std::vector<uint8>& buf, uint64 start);
// このデバイスのバイトサイズ
off_t GetSize() const;
// このデバイスをディスクイメージだと思うとして (いや実際そうだけど)
// 先頭から start バイト目から len バイト読み出して返す。
// 単位はセクタではなくバイトなことに注意。
// (エミュレータ特権アクセス)
bool Peek(void *buf, uint64 start, uint32 len) const;
// シーク時間[tsec]
uint64 GetSeekTime() const { return seektime; }
// リムーバブルデバイスなら true
bool IsRemovableDevice() const { return removable_device; }
// 書き込み可能デバイスなら true
bool IsWriteableDevice() const { return writeable_device; }
// メディアが入っていれば true
bool IsMediumLoaded() const { return medium_loaded; }
// メディアの書き込みモードを返す
RW GetWriteMode() const { return write_mode; }
// メディアの書き込みモードを文字列で返す (モニタ用)
const char *GetWriteModeStr() const;
// メディア(ディスク)イメージを開く/閉じる。(同一スレッドから呼ぶこと)
bool LoadDisk(const std::string& path_);
void UnloadDisk(bool force = false);
// メディア(ディスク)イメージを開く/閉じる。(UI スレッドから呼ぶ)
void LoadDiskUI(const std::string& path_);
void UnloadDiskUI(bool force);
// メディアの取り出し可否
bool IsMediumRemovalPrevented() const { return medium_removal_prevented; }
bool IsMediumRemovalAllowed() const { return !medium_removal_prevented; }
void PreventMediumRemoval(bool val);
// 現在ロードされているイメージファイルのフルパスを返す
const std::string& GetPathName() const { return pathname; }
// SCSIBus から呼ばれる
// バスリセットされた (全員に通知される)
void OnBusReset() override;
private:
// ディスクイメージを閉じる (内部用)
bool UnloadDisk_internal();
// ディスク状態をクリアする
void Clear();
// メディア状態変更を UI に通知する
void MediaChanged() const;
// メディア挿入/排出コールバック
void LoadMessage(MessageID, uint32);
void UnloadMessage(MessageID, uint32);
// イメージファイルのフルパス
std::string pathname {};
// 新しいイメージファイルのフルパス (UIから)
std::string new_pathname {};
// イメージファイル
DiskImage image {};
bool removable_device {}; // リムーバブルデバイスなら true
bool writeable_device {}; // 書き込み可能なデバイスなら true
bool write_ignore {}; // 書き込み無視オプション
bool medium_loaded {}; // メディアが挿入されていれば true
// メディアの書き込みモード
RW write_mode {};
// メディアの取り出しを禁止するなら true。
// XXX Bus Device Reset メッセージでも解除できるがメッセージは未実装
bool medium_removal_prevented {};
uint64 seektime {};
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.