|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2020 [email protected] ! 4: // ! 5: ! 6: // SCSI バス ! 7: ! 8: #include "scsibus.h" ! 9: #include "scsidev.h" ! 10: ! 11: // time 時間後に呼び出すコールバックをセットする。 ! 12: // func の書式が面倒なのを省略して書きたいため。 ! 13: #define CallAfter(func_, name_, time_) do { \ ! 14: event.func = (DeviceCallback_t)&SCSIBus::func_; \ ! 15: event.SetName(name_); \ ! 16: event.code = 0; \ ! 17: event.time = time_; \ ! 18: gScheduler->AddEvent(&event); \ ! 19: } while (0) ! 20: ! 21: // コンストラクタ ! 22: SCSIBus::SCSIBus() ! 23: { ! 24: logname = "scsibus"; ! 25: devname = "SCSIBus"; ! 26: ! 27: event.dev = this; ! 28: event.SetName("SCSIBus"); // 複数出来たら名前どうするべか ! 29: } ! 30: ! 31: // デストラクタ ! 32: SCSIBus::~SCSIBus() ! 33: { ! 34: } ! 35: ! 36: // このバスにデバイスを接続する (初期化時に使う) ! 37: bool ! 38: SCSIBus::Attach(SCSIDevice *dev, int id) ! 39: { ! 40: if (device[id] != NULL) { ! 41: putlog(0, "id=%d already exist?", id); ! 42: return false; ! 43: } ! 44: putlog(3, "Attach #%d", id); ! 45: ! 46: // バスとデバイスは相互に接続する。どちらも所有のないただのポインタ。 ! 47: device[id] = dev; ! 48: dev->Attach(this, id); ! 49: ! 50: return true; ! 51: } ! 52: ! 53: // RST を上げる ! 54: void ! 55: SCSIBus::AssertRST() ! 56: { ! 57: putlog(4, "%s", __func__); ! 58: ! 59: // すでに上がってたら何もしない ! 60: if (rst) { ! 61: return; ! 62: } ! 63: ! 64: rst = true; ! 65: ! 66: // 即バスリセットを全員に通知 ! 67: for (int i = 0; i < 8; i++) { ! 68: if (device[i]) { ! 69: device[i]->BusResetCB(); ! 70: } ! 71: } ! 72: ! 73: // コールバック終了後に内部状態もクリア ! 74: init_id = -1; ! 75: target_id = -1; ! 76: } ! 77: ! 78: // RST を下げる ! 79: void ! 80: SCSIBus::NegateRST() ! 81: { ! 82: putlog(4, "%s", __func__); ! 83: ! 84: rst = false; ! 85: ! 86: // 特にすることはない ! 87: } ! 88: ! 89: // SEL を上げる ! 90: void ! 91: SCSIBus::AssertSEL(uint id) ! 92: { ! 93: putlog(4, "%s(#%d)", __func__, id); ! 94: ! 95: // すでに上がってたら何もしない ! 96: if ((sel & (1 << id))) { ! 97: return; ! 98: } ! 99: ! 100: sel |= (1 << id); ! 101: ! 102: if (GetPhase() == SCSI::Phase::BusFree) { ! 103: // バスフリーフェーズなら、アービトレーションなしでセレクション ! 104: // フェーズに移行。 ! 105: // ! 106: // 仕様Phase |Free |<---- Selection ---->| ! 107: // ! 108: // BSY ~~\__________________________/~~~~~~~ ! 109: // ! 110: // Data -----------< Initiator&Target ID >--- ! 111: // ! 112: // SEL ________________/~~~~~~~~~~~~~~~~\___ ! 113: // ^イマココ ! 114: // ! 115: // 実装Phase |<- Free ->|<-- Selection ->| ! 116: // ! 117: // SCSI 本にはデータバスが変化したところから Selection フェーズと ! 118: // 書いてあるように読めるけど(上)、ここでは SEL がアサートされた ! 119: // ところからとする(下)。 ! 120: ! 121: SetPhase(SCSI::Phase::Selection); ! 122: ! 123: // SEL を上げた人がイニシエータ ! 124: init_id = id; ! 125: ! 126: // 即セレクション開始。 ! 127: CallAfter(SelectionStart, "SCSIBus Selection Start", 0); ! 128: ! 129: } else if (GetPhase() == SCSI::Phase::Arbitration) { ! 130: // アービトレーションフェーズなら、 ! 131: // アービトレーションに勝ってセレクションフェーズに移行途中。 ! 132: // この場合はセレクションフェーズに移行するのはここではなく ! 133: // BSY の立ち下がりなので、そっち参照。 ! 134: // ! 135: // 仕様Phase Arbitration |---|<--- Selection -->| ! 136: // ! 137: // BSY /~~~~~~~~~~~~~~~\_______________/~~~~~~ ! 138: // ! 139: // Data <InitiatorID>< Initiator&Target ID >--- ! 140: // ! 141: // SEL __________/~~~~~~~~~~~~~~~~~~~~~~~~\___ ! 142: // ^イマココ ! 143: // ! 144: // 実装Phase <-Arbitration ->|<--- Selection -->| ! 145: ! 146: // SEL を上げた人がイニシエータなことだけはここで覚えておく。 ! 147: init_id = id; ! 148: ! 149: } else { ! 150: // どうする? ! 151: PANIC("AssertSEL in %s phase\n", GetPhaseName()); ! 152: } ! 153: } ! 154: ! 155: // SEL を下げる ! 156: void ! 157: SCSIBus::NegateSEL(uint id) ! 158: { ! 159: putlog(4, "%s(#%d)", __func__, id); ! 160: ! 161: // すでに下りてたら何もしない ! 162: if ((sel & (1 << id)) == 0) { ! 163: return; ! 164: } ! 165: ! 166: sel &= ~(1 << id); ! 167: ! 168: if (GetPhase() == SCSI::Phase::Selection) { ! 169: // セレクションフェーズで SEL を下げる ! 170: if (bsy) { ! 171: // BSY が立っていれば、ターゲットがセレクションに応答したので ! 172: // イニシエータが SEL を下げて、これから任意時間後にターゲットが ! 173: // 情報転送フェーズに移行する。 ! 174: ! 175: CallAfter(StartTransfer, "SCSIBus Transfer Start", 400_nsec); ! 176: } else { ! 177: // BSY が立ってなければ、ターゲットが応答しなかったことに ! 178: // イニシエータがあきらめて SEL を下げたので、何もせず ! 179: // バスフリーフェーズに移行。 ! 180: BusFree(id); ! 181: } ! 182: } else { ! 183: // どうする? ! 184: PANIC("NegateSEL in %s phase\n", GetPhaseName()); ! 185: } ! 186: } ! 187: ! 188: // BSY を上げる ! 189: void ! 190: SCSIBus::AssertBSY(uint id) ! 191: { ! 192: putlog(4, "%s(#%d)", __func__, id); ! 193: ! 194: // すでに上がってたら何もしない ! 195: if ((bsy & (1 << id))) { ! 196: return; ! 197: } ! 198: ! 199: bsy |= (1 << id); ! 200: ! 201: if (GetPhase() == SCSI::Phase::BusFree) { ! 202: // バスフリーフェーズだったらアービトレーション開始 ! 203: ! 204: // 実際のアービトレーションは、 ! 205: // バスフリーを検出してから規定時間経過後に、データバスに自身の ID を ! 206: // 出して BSY を立てる。規定時間内に他のデバイスもアービトレーションに ! 207: // 参加すれば ID の高い方が勝つ。負けた方は規定時間内にアービトレー ! 208: // ションを取り下げる。規定期間経って勝っていればアービトレーション ! 209: // 成功となり、セレクションフェーズに移る。 ! 210: // のだが、エミュレータ上では、今の所アービトレーションで競合が起きる ! 211: // ことはないため、アービトレーションは規定時間経過して必ず成功する。 ! 212: // ちゃんとしたアービトレーションを実装する場合はたぶんこれだけでは ! 213: // 済まないと思うので書き換えること。 ! 214: ! 215: SetPhase(SCSI::Phase::Arbitration); ! 216: ! 217: } else if (GetPhase() == SCSI::Phase::Arbitration) { ! 218: // アービトレーションフェーズだったら、アービトレーションに参加。 ! 219: // 今は起きない。 ! 220: PANIC("AssertBSY(#%d) in arbitration", id); ! 221: ! 222: } else if (GetPhase() == SCSI::Phase::Selection) { ! 223: // セレクションフェーズだったら、転送フェーズに移行中。 ! 224: // ターゲットが BSY をアサートしたので次は規定時間経過後に ! 225: // SelectionAck() を呼び出してイニシエータに SEL を下げさせる。 ! 226: ! 227: CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec); ! 228: } else { ! 229: // どうする? ! 230: PANIC("AssertBSY(#%d) in %s phase\n", id, GetPhaseName()); ! 231: } ! 232: } ! 233: ! 234: // BSY を下げる ! 235: void ! 236: SCSIBus::NegateBSY(uint id) ! 237: { ! 238: putlog(4, "%s(#%d)", __func__, id); ! 239: ! 240: // すでに下りてたら何もしない ! 241: if ((bsy & (1 << id)) == 0) { ! 242: return; ! 243: } ! 244: ! 245: bsy &= ~(1 << id); ! 246: ! 247: if (GetPhase() == SCSI::Phase::Transfer) { ! 248: // 転送フェーズから BSY を下げたらバスフリーに移行。 ! 249: BusFree(id); ! 250: ! 251: } else if (GetPhase() == SCSI::Phase::Arbitration) { ! 252: // アービトレーションフェーズなら、 ! 253: // イニシエータが BSY を下ろすことでセレクションフェーズに移行。 ! 254: // ! 255: // この少し上の AssertSEL() 中のタイムチャート参照。 ! 256: ! 257: SetPhase(SCSI::Phase::Selection); ! 258: ! 259: // 即セレクション開始。 ! 260: CallAfter(SelectionStart, "SCSIBus Selection Start", 0); ! 261: ! 262: } else { ! 263: // どうする? ! 264: PANIC("NegateBSY(#%d) in %s phase\n", id, GetPhaseName()); ! 265: } ! 266: } ! 267: ! 268: // MSG, CD, IO 信号線を変更する。 ! 269: void ! 270: SCSIBus::SetXfer(uint8 val) ! 271: { ! 272: uint8 oldxfer = xfer; ! 273: ! 274: xfer = val & (SCSI::MSG | SCSI::CD | SCSI::IO); ! 275: if (oldxfer != xfer) { ! 276: if (GetPhase() == SCSI::Phase::Transfer) { ! 277: putlog(4, "SetXfer(%x:%s)", xfer, SCSI::GetXferPhaseName(xfer)); ! 278: } else { ! 279: putlog(4, "SetXfer(%x)", xfer); ! 280: } ! 281: } ! 282: } ! 283: ! 284: // REQ を上げる ! 285: void ! 286: SCSIBus::AssertREQ() ! 287: { ! 288: if (GetPhase() == SCSI::Phase::Transfer) { ! 289: // REQ を上げると言いつつ、ここで転送レートの時間調整をしているので ! 290: // (scsibus.h の解説参照) REQ を実際に上げるのはイベント発生後。 ! 291: // XXX 本来ターゲットデバイスがタイマー持っててやるべきだけど、 ! 292: // イベントがもう一つ増えるのもうざいので。どのみち、ここの REQ ! 293: // 通知はイベントにしないといけないので。 ! 294: ! 295: uint64 after = 0; ! 296: uint64 now = gScheduler->GetVirtTime(); ! 297: if (now < last_req_time + 1_usec) { ! 298: after = (last_req_time + 1_usec) - now; ! 299: } ! 300: CallAfter(TransferReq, "SCSIBus AssertREQ", after); ! 301: } else { ! 302: // 転送フェーズでないのに上げることはないと思うけど ! 303: // とりあえず通知もせずその場で上げておくか? ! 304: putlog(4, "%s", __func__); ! 305: req = true; ! 306: } ! 307: } ! 308: ! 309: // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて ! 310: // イニシエータの TransferReqCB を呼び出すところ。 ! 311: void ! 312: SCSIBus::TransferReq(int dummy) ! 313: { ! 314: putlog(4, "AssertREQ"); ! 315: req = true; ! 316: ! 317: assert(device[init_id]); ! 318: device[init_id]->TransferReqCB(); ! 319: } ! 320: ! 321: // REQ を下げる ! 322: void ! 323: SCSIBus::NegateREQ() ! 324: { ! 325: putlog(4, "%s", __func__); ! 326: req = false; ! 327: } ! 328: ! 329: // ACK を上げる ! 330: void ! 331: SCSIBus::AssertACK() ! 332: { ! 333: putlog(4, "%s", __func__); ! 334: ack = true; ! 335: ! 336: if (GetPhase() == SCSI::Phase::Transfer) { ! 337: assert(device[target_id]); ! 338: device[target_id]->TransferCB(); ! 339: } ! 340: } ! 341: ! 342: // ACK を下げる ! 343: void ! 344: SCSIBus::NegateACK() ! 345: { ! 346: putlog(4, "%s", __func__); ! 347: ack = false; ! 348: ! 349: if (GetPhase() == SCSI::Phase::Transfer) { ! 350: assert(device[target_id]); ! 351: device[target_id]->TransferAckCB(); ! 352: } ! 353: } ! 354: ! 355: // ATN を上げる ! 356: void ! 357: SCSIBus::AssertATN() ! 358: { ! 359: putlog(4, "%s", __func__); ! 360: atn = true; ! 361: } ! 362: ! 363: // ATN を下げる ! 364: void ! 365: SCSIBus::NegateATN() ! 366: { ! 367: putlog(4, "%s", __func__); ! 368: atn = false; ! 369: } ! 370: ! 371: // バスフリーフェーズに移行。 ! 372: // id はこのバスフリーを行ったデバイス (0..7) ! 373: void ! 374: SCSIBus::BusFree(int id) ! 375: { ! 376: putlog(4, "%s", __func__); ! 377: ! 378: SetPhase(SCSI::Phase::BusFree); ! 379: ! 380: // 即全デバイスに通知。 ! 381: for (int i = 0; i < countof(device); i++) { ! 382: if (device[i]) { ! 383: device[i]->BusFreeCB(id); ! 384: } ! 385: } ! 386: ! 387: // コールバック終了後に内部状態もクリア ! 388: init_id = -1; ! 389: target_id = -1; ! 390: ! 391: // 通知された各デバイスは規定時間内に他の信号線も取り下げなければ ! 392: // ならない。 ! 393: } ! 394: ! 395: // セレクションから情報転送フェーズまで ! 396: // ! 397: // 仕様Phase <- Selection ->| ! 398: // ! 399: // BSY _______________/~~~~~~~~~ ! 400: // ! 401: // Data Initiator&Target ID >---- ! 402: // ! 403: // SEL ~~~~~~~~~~~~~~~~~~~~~~\__ ! 404: // ! 405: // -(1)->|--(2)-->|-(3)->| ! 406: // ! 407: // セレクションフェーズ開始後、(ターゲット)デバイスは、SEL がアサート、 ! 408: // (IO がネゲート)、データバスの自身の ID がアサート、が 400nsec 以上 ! 409: // 継続することで、自身がターゲットに選択されていることを認識する。(1) ! 410: // ターゲットは (1) から 200usec 以内に BSY をアサートする。(2) ! 411: // イニシエータは (2) から 90nsec 以上後に SEL (とデータ) を下げる。(3) ! 412: // ! 413: // 実装では、セレクションフェーズ開始が SelectionStart() の呼び出しに対応。 ! 414: // ! 415: // ターゲットデバイスが存在していない場合はタイムアウトとなるが、これは ! 416: // ホストが処理するのでバスは何もしない。 ! 417: // ターゲットデバイスが存在していれば SelectionStart() から 400nsec 後 + ! 418: // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック ! 419: // を呼び出す(1+2)。 ! 420: // ! 421: // Selection() コールバック(が呼び出す device->SelectionCB()) は通常 BSY を ! 422: // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック ! 423: // を呼び出す(3)。 ! 424: // ! 425: // SelectionAck() コールバック(が呼び出す device->SelectionAckCB()) は通常 ! 426: // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に ! 427: // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして ! 428: // デバイスを情報転送フェーズに移行させる。 ! 429: ! 430: // セレクションフェーズ開始。 ! 431: void ! 432: SCSIBus::SelectionStart() ! 433: { ! 434: // この時点でデータバスには自身の ID と相手の ID が立っているはず。 ! 435: // 一方 sel は SEL 信号線を立てた人のビットを持っているので、 ! 436: // これでターゲットを特定する。 ! 437: uint target_mask = data ^ sel; ! 438: if (__builtin_popcount(target_mask) != 1) { ! 439: // データバスから自身を除いた結果立ってるビットが1個以外だったら ! 440: // 誰も応答しないので、何もせず帰ってよい。 ! 441: putlog(3, "%s invalid databus=$%02x", __func__, data); ! 442: return; ! 443: } ! 444: ! 445: // ターゲット ID はここで取得 ! 446: target_id = DecodeID(target_mask); ! 447: if (device[target_id] == NULL) { ! 448: // ターゲットデバイスがいなければ、バスは何もしない。 ! 449: // (その結果イニシエータでタイムアウトが起きるはず) ! 450: putlog(4, "%s (No target)", __func__); ! 451: return; ! 452: } ! 453: ! 454: // ターゲットデバイスがいれば、規定時間経過後ターゲットに通知。 ! 455: putlog(4, "%s", __func__); ! 456: CallAfter(Selected, "SCSIBus Selected", 100_usec); ! 457: } ! 458: ! 459: // SelectionStart() にてターゲットが選択されて規定時間経ったので ! 460: // そろそろターゲットが BSY を立てる頃に呼ばれるコールバック。 ! 461: void ! 462: SCSIBus::Selected(int dummy) ! 463: { ! 464: // ターゲットに通知。ターゲットがいることは確定している。 ! 465: assert(device[target_id]); ! 466: device[target_id]->SelectionCB(); ! 467: } ! 468: ! 469: // ターゲットが BSY を立てて規定時間経ったので ! 470: // そろそろイニシエータが SEL を下げることに呼ばれるコールバック。 ! 471: void ! 472: SCSIBus::SelectionAck(int dummy) ! 473: { ! 474: // イニシエータに通知。 ! 475: assert(device[init_id]); ! 476: device[init_id]->SelectionAckCB(); ! 477: } ! 478: ! 479: // 情報転送フェーズ開始を通知する。 ! 480: // BSY を上げて情報転送フェーズに移行したところで呼ばれる。 ! 481: void ! 482: SCSIBus::StartTransfer(int dummy) ! 483: { ! 484: SetPhase(SCSI::Phase::Transfer); ! 485: ! 486: assert(device[target_id]); ! 487: device[target_id]->StartTransferCB(); ! 488: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.