|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2025 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ! 8: // SCSI ドメイン ! 9: // ! 10: ! 11: // SCSI ドメインは、1台のイニシエータとそれに繋がるターゲットからなるシステム ! 12: // 一式を指すことにする。ここには SCSIBus は含まない。 ! 13: // ! 14: // HBA (SPCとか) ! 15: // | ! 16: // +----> .domain ................................................ ! 17: // | (SCSIDomain) : ! 18: // | : +----------------+ +------------+ +------------+ : ! 19: // | .parent |.initiator | |.target[0] | |.target[6] | : ! 20: // |<--------|(SCSIHostDevice)| |(SCSITarget)| |(SCSITarget)| : ! 21: // | : +----------------+ +------------+ +------------+ : ! 22: // | : ^ | ^ | ^ | : ! 23: // | ..........|.|...............|.|.............|.|......... ! 24: // | | | | | | | ! 25: // | | |.bus | |.bus | |.bus ! 26: // +----> .bus .....|.|...............|.|.............|.|......... ! 27: // (SCSIBus) | v | v | v : ! 28: // : .device[7] .device[0] .device[6] : ! 29: // : (SCSIDevice) (SCSIDevice) (SCSIDevice) : ! 30: // ........................................................ ! 31: // ! 32: // 初期化 (Create) 時に SCSIDomain は設定ファイルから自分のドメインの ! 33: // 各 .target[] と .initiator を生成する。 ! 34: // HBA が SCSIBus を持つタイプであれば Init 時に ! 35: // SCSIDomain::AttachTargets(bus)、SCSIBus::Refresh() ですべてのターゲットを ! 36: // バスに参加させる。 ! 37: // HBA が SCSIBus を持たないタイプであれば GetDevices() でターゲット情報を ! 38: // 手元に持っておく。 ! 39: // ! 40: // 電源オン後、イニシエータの SCSI ID が確定したら HBA は ! 41: // SCSIDomain::AttachInitiator(bus, id)、SCSIBus::Refresh() でイニシエータを ! 42: // バスに参加させる。 ! 43: ! 44: #include "scsidomain.h" ! 45: #include "scsi.h" ! 46: #include "scsibus.h" ! 47: #include "scsidev.h" ! 48: #include "config.h" ! 49: #include "monitor.h" ! 50: ! 51: // コンストラクタ ! 52: SCSIDomain::SCSIDomain(IODevice *parent_, const char *config_name_) ! 53: : inherited(OBJ_SCSI) ! 54: { ! 55: parent = parent_; ! 56: config_name = config_name_; ! 57: ! 58: monitor = gMonitorManager->Regist(ID_MONITOR_SCSIDEVS, this); ! 59: monitor->func = ToMonitorCallback(&SCSIDomain::MonitorUpdate); ! 60: // サイズは Create で決まる ! 61: } ! 62: ! 63: // デストラクタ ! 64: SCSIDomain::~SCSIDomain() ! 65: { ! 66: } ! 67: ! 68: // 動的なコンストラクション ! 69: bool ! 70: SCSIDomain::Create() ! 71: { ! 72: // 設定を読み込む。 ! 73: for (int id = 0; id < MaxDevices; id++) { ! 74: const std::string key = string_format("%s-id%u-image", ! 75: config_name, id); ! 76: const ConfigItem& item = gConfig->Find(key); ! 77: const std::string& image = item.AsString(); ! 78: ! 79: // 空ならデバイスなし ! 80: if (image.empty()) { ! 81: // 未接続部分のパラメータは減らしておくか。 ! 82: // --show-config するとさすがに無駄に多くて邪魔なので。 ! 83: const std::string wikey = string_format("%s-id%u-writeignore", ! 84: config_name, id); ! 85: gConfig->Delete(wikey); ! 86: ! 87: // -seektime は virtio-scsi にはない。 ! 88: if (strncmp(config_name, "virtio", 6) != 0) { ! 89: const std::string stkey = string_format("%s-id%u-seektime", ! 90: config_name, id); ! 91: gConfig->Delete(stkey); ! 92: } ! 93: continue; ! 94: } ! 95: ! 96: // ',' より前がデバイス種別 (ここではパスは不要) ! 97: auto v = string_split(image, ','); ! 98: // 比較のため前後の空白を取り除いて小文字にする ! 99: std::string type = string_trim(v[0]); ! 100: type = string_tolower(type); ! 101: ! 102: SCSI::DevType target_devtype; ! 103: if (type == "hd") { ! 104: target_devtype = SCSI::DevType::HD; ! 105: } else if (type == "cd") { ! 106: target_devtype = SCSI::DevType::CD; ! 107: } else if (type == "mo") { ! 108: target_devtype = SCSI::DevType::MO; ! 109: } else { ! 110: item.Err("Invaild device type '%s'", type.c_str()); ! 111: return false; ! 112: } ! 113: ! 114: try { ! 115: target[id].reset(new SCSIDisk(this, id, target_devtype)); ! 116: } catch (...) { } ! 117: if ((bool)target[id] == false) { ! 118: warnx("Failed to initialize target[%u] at %s", id, __method__); ! 119: return false; ! 120: } ! 121: putlog(3, "Attach #%u", id); ! 122: } ! 123: ! 124: // イニシエータを生成。 ! 125: try { ! 126: initiator.reset(new SCSIHostDevice(this, parent)); ! 127: } catch (...) { } ! 128: if ((bool)initiator == false) { ! 129: warnx("Failed to initialize SCSHostDevice at %s", __method__); ! 130: return false; ! 131: } ! 132: ! 133: // この時点ではイニシエータに ID がないため失敗しないはず。 ! 134: bool r = RefreshDevices(); ! 135: assert(r == true); ! 136: ! 137: // イニシエータだけ必要な行数が異なるが、この時点の ! 138: // connected_devices はイニシエータを含まないので都合がいい。 ! 139: monitor->SetSize(75, connected_devices.size() * 4 + 1); ! 140: ! 141: return true; ! 142: } ! 143: ! 144: // 初期化。 ! 145: bool ! 146: SCSIDomain::Init() ! 147: { ! 148: // 初期化ではないけど、ログが使えるようになったここでデバッグ表示。 ! 149: if (loglevel >= 1) { ! 150: const auto& conn = GetConnectedDevices(); ! 151: for (const auto dev : conn) { ! 152: putmsgn("%s target #%u %s", config_name, dev->GetMyID(), ! 153: SCSI::GetDevTypeName(dev->GetDevType())); ! 154: } ! 155: } ! 156: ! 157: return true; ! 158: } ! 159: ! 160: // すべてのターゲットをバスに参加させる。 ! 161: // バスを持つホストデバイスが Init() 時に呼ぶ。 ! 162: void ! 163: SCSIDomain::AttachTargets(SCSIBus *bus) ! 164: { ! 165: for (uint id = 0; id < MaxDevices; id++) { ! 166: if (target[id]) { ! 167: target[id]->Attach(bus); ! 168: } ! 169: } ! 170: } ! 171: ! 172: // イニシエータに ID を付与し、(あれば)バスに参加させる。 ! 173: // bus は NULL でもよい。 ! 174: // 成功すれば true を返す。 ! 175: // 失敗すればイニシエータの ID を -1 に設定して false を返す。 ! 176: bool ! 177: SCSIDomain::AttachInitiator(SCSIBus *bus, uint id_) ! 178: { ! 179: initiator->SetID(id_); ! 180: ! 181: // ドメインに反映させる。 ! 182: if (RefreshDevices() == false) { ! 183: // 失敗したらイニシエータを切り離しておく。 ! 184: initiator->SetID(-1); ! 185: RefreshDevices(); ! 186: return false; ! 187: } ! 188: ! 189: if (bus) { ! 190: initiator->Attach(bus); ! 191: } ! 192: ! 193: return true; ! 194: } ! 195: ! 196: // initiator と target[] から device[] と connected_devices を作り直す。 ! 197: // target[] はイニシエータを含まないターゲットデバイス。 ! 198: // device[] はターゲットと ID の確定したイニシエータを指す穴あきの配列。 ! 199: // connected_devices はイニシエータも含む ID 順の可変長リスト。 ! 200: // initiator か target[] に増減があるか、initiator の ID が確定した時に ! 201: // 呼ぶこと。 ! 202: // initiator の ID が他と衝突すれば false を返す。この場合 device[] と ! 203: // connected_devices の状態は不定。 ! 204: bool ! 205: SCSIDomain::RefreshDevices() ! 206: { ! 207: // initiator と taget[] から device[] を作る。 ! 208: device.fill(NULL); ! 209: for (int id = 0; id < MaxDevices; id++) { ! 210: if ((bool)target[id]) { ! 211: device[id] = target[id].get(); ! 212: } ! 213: if (initiator->GetMyID() == id) { ! 214: if (device[id]) { ! 215: return false; ! 216: } ! 217: device[id] = initiator.get(); ! 218: } ! 219: } ! 220: ! 221: // device[] から connected_devices を作る。 ! 222: connected_devices.clear(); ! 223: for (uint id = 0; id < MaxDevices; id++) { ! 224: if (device[id]) { ! 225: connected_devices.push_back(device[id]); ! 226: } ! 227: } ! 228: ! 229: return true; ! 230: } ! 231: ! 232: // (イニシエータを除く) デバイス数を返す。 ! 233: uint ! 234: SCSIDomain::GetTargetCount() const ! 235: { ! 236: uint count = 0; ! 237: ! 238: for (const auto& t : target) { ! 239: if ((bool)t) { ! 240: count++; ! 241: } ! 242: } ! 243: ! 244: return count; ! 245: } ! 246: ! 247: // SCSI デバイスモニタ ! 248: // (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので ! 249: // とりあえずここで) ! 250: void ! 251: SCSIDomain::MonitorUpdate(Monitor *, TextScreen& screen) ! 252: { ! 253: int y; ! 254: ! 255: // 012345678901234567890123456789012345678901234567890123456789012345678901234 ! 256: // ID6: HD(2048MB) ! 257: // MediumLoaded Off LogicalBlock 2048 ImageSize 2,147,483,648 ! 258: // RemovalPrevent Off WriteProtected ! 259: ! 260: ! 261: screen.Clear(); ! 262: ! 263: y = 0; ! 264: for (const auto dev : connected_devices) { ! 265: std::string desc = SCSI::GetDevTypeName(dev->GetDevType()); ! 266: std::string pathname; ! 267: ! 268: const SCSIDisk *disk = dynamic_cast<const SCSIDisk*>(dev); ! 269: if (disk && disk->IsMediumLoaded()) { ! 270: desc += string_format("(%uMB)", ! 271: (uint)(disk->GetSize() / 1024 / 1024)); ! 272: pathname = disk->GetPathName(); ! 273: } ! 274: screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str()); ! 275: ! 276: #if 1 ! 277: (void)pathname; ! 278: #else ! 279: // パス名の非ASCIIどうするか ! 280: if (!pathname.empty()) { ! 281: if (pathname.size() > screen.GetCol() - 16 - 3) { ! 282: pathname = pathname.substr(0, screen.GetCol() - 16 - 3); ! 283: pathname += "..."; ! 284: } ! 285: screen.Puts(16, y, pathname.c_str()); ! 286: } ! 287: #endif ! 288: y++; ! 289: ! 290: // イニシエータならここまで。(今の所イニシエータとディスクしかない) ! 291: if (disk == NULL) { ! 292: continue; ! 293: } ! 294: ! 295: const int x1 = 5; ! 296: const int x2 = 29; ! 297: const int x3 = 50; ! 298: ! 299: // 1行目 ! 300: screen.Print(x1, y, ! 301: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), ! 302: "MediumLoaded %s", ! 303: disk->IsMediumLoaded() ? "On" : "Off"); ! 304: screen.Print(x2, y, "LogicalBlock %u", disk->GetBlocksize()); ! 305: std::string imagesize = format_number(disk->GetSize()); ! 306: screen.Print(x3, y, "ImageSize %15s", imagesize.c_str()); ! 307: y++; ! 308: ! 309: // 2行目 ! 310: screen.Print(x1, y, ! 311: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), ! 312: "RemovalPrevent %s", ! 313: disk->IsMediumRemovalPrevented() ? "On" : "Off"); ! 314: ! 315: screen.Puts(x2, y, ! 316: (disk->IsMediumLoaded() ? TA::Normal : TA::Disable), ! 317: disk->GetWriteModeStr()); ! 318: y++; ! 319: ! 320: y++; ! 321: } ! 322: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.