Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7ffdb64
feat(sbp2): add session registry and command flow
gly11 May 27, 2026
166b8a7
fix(sbp2): harden command submission lifecycle
gly11 May 27, 2026
c1c3ec5
fix(sbp2): correct status block wire format
gly11 May 28, 2026
575a23f
fix(sbp2): harden command ORB configuration
gly11 May 28, 2026
26bf1fa
fix(sbp2): start reconnect timeout after ACK
gly11 May 28, 2026
26d98f2
fix(sbp2): release page table ranges on destruction
gly11 May 28, 2026
81c6172
fix(sbp2): decode unit characteristics fields
gly11 May 28, 2026
8164308
fix(sbp2): guard session async callbacks with weak ownership
gly11 May 28, 2026
138c7f1
fix(sbp2): release failed command orb resources
gly11 May 28, 2026
d93376b
fix(sbp2): preserve inquiry failure status
gly11 May 28, 2026
7f9dc8b
fix(sbp2): reject duplicate target sessions
gly11 May 28, 2026
9bb9464
test(sbp2): cover registry target info and release edges
gly11 May 31, 2026
0213018
fix(sbp2): validate session owner for client calls
gly11 May 31, 2026
00b109e
fix(sbp2): release sessions before address ranges
gly11 May 31, 2026
1c96fe1
fix(sbp2): reject short session state outputs
gly11 May 31, 2026
11ddc22
fix(sbp2): harden command user client ABI
gly11 May 31, 2026
71e7b25
docs(sbp2): clarify session registry handles
gly11 May 31, 2026
4d50ee4
fix(sbp2): harden address-space bounds and labels
gly11 May 31, 2026
c53fcd2
fix(sbp2): guard remote-write callback lifetimes
gly11 May 31, 2026
98b0a90
fix(sbp2): retain read-block response backing
gly11 May 31, 2026
d7da945
fix(service): clear packet handlers before protocol teardown
gly11 May 31, 2026
5dcbd82
fix(service): use one block-write routing path
gly11 May 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions ASFWDriver/ASFWDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
#include "Async/AsyncSubsystem.hpp"
#include "Async/DMAMemoryImpl.hpp"
#include "Async/Interfaces/IFireWireBus.hpp"
#include "Async/PacketHelpers.hpp"
#include "Async/ResponseCode.hpp"
#include "Audio/AudioCoordinator.hpp"
#include "Bus/SelfIDCapture.hpp"
#include "Common/DriverKitOwnership.hpp"
Expand Down Expand Up @@ -224,32 +222,6 @@ kern_return_t IMPL(ASFWDriver, Start) {
ASFW_LOG(Controller, "✅ FCPResponseRouter initialized");
}

if (ctx.deps.fcpResponseRouter && ctx.deps.asyncSubsystem) {
if (auto* router = ctx.deps.asyncSubsystem->GetPacketRouter()) {
router->RegisterRequestHandler(
0x1, // tCode for Block Write Request
[fcpRouter =
ctx.deps.fcpResponseRouter.get()](const ASFW::Async::ARPacketView& packet) {
if (fcpRouter) {
const ASFW::Protocols::Ports::BlockWriteRequestView request{
.sourceID = packet.sourceID,
.destOffset = ASFW::Async::ExtractDestOffset(packet.header),
.payload = packet.payload,
};

const auto disposition = fcpRouter->RouteBlockWrite(request);
if (disposition ==
ASFW::Protocols::Ports::BlockWriteDisposition::kAddressError) {
return ASFW::Async::ResponseCode::AddressError;
}
return ASFW::Async::ResponseCode::Complete;
}
return ASFW::Async::ResponseCode::NoResponse;
});
ASFW_LOG(Controller, "✅ FCPResponseRouter wired to PacketRouter (tCode 0x1)");
}
}

DriverWiring::EnsureSbp2Deps(ctx);

if (ctx.deps.speedPolicy) {
Expand Down
4 changes: 4 additions & 0 deletions ASFWDriver/Async/AsyncSubsystemInterrupts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Contexts/ATRequestContext.hpp"
#include "Contexts/ATResponseContext.hpp"
#include "Tx/ResponseSender.hpp"

#include "../Logging/Logging.hpp"
#include "../Shared/Memory/DMAMemoryManager.hpp"
Expand All @@ -22,6 +23,9 @@ uint32_t AsyncSubsystem::DrainTxCompletions(const char* reason) {
return;
}
while (auto completion = ctx->ScanCompletion()) {
if (completion->isResponseContext && responseSender_) {
responseSender_->OnTxCompletion(*completion);
}
tracking_->OnTxCompletion(*completion);
++drained;
}
Expand Down
5 changes: 4 additions & 1 deletion ASFWDriver/Async/AsyncSubsystemLifecycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,10 @@ void AsyncSubsystem::Teardown(bool disableHardware) {
txnMgr_.reset();
}

responseSender_.reset();
if (responseSender_) {
responseSender_->ClearOutstandingResponses();
responseSender_.reset();
}
descriptorBuilder_ = nullptr;
descriptorBuilderResponse_ = nullptr;
packetBuilder_.reset();
Expand Down
29 changes: 26 additions & 3 deletions ASFWDriver/Async/Tx/ResponseSender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "../Engine/ContextManager.hpp"
#include "../Contexts/ATResponseContext.hpp"
#include "../Track/TxCompletion.hpp"
#include "../Tx/DescriptorBuilder.hpp"
#include "../Tx/Submitter.hpp"
#include "../../Bus/GenerationTracker.hpp"
Expand Down Expand Up @@ -50,7 +51,8 @@ void ResponseSender::SendResponse(const ARPacketView& request,
const uint32_t* header,
std::size_t headerBytes,
uint64_t payloadDeviceAddress,
std::size_t payloadLength) noexcept {
std::size_t payloadLength,
std::shared_ptr<void> payloadLease) noexcept {
// Per IEEE 1394, broadcast requests (destID=0xFFFF) do not get responses.
if (request.destID == 0xFFFF) {
ASFW_LOG_V3(Async, "ResponseSender: skip response for broadcast destID=0xFFFF");
Expand Down Expand Up @@ -78,8 +80,16 @@ void ResponseSender::SendResponse(const ARPacketView& request,
return;
}

const auto* leaseKey = chain.last;
if (payloadLease && leaseKey) {
responsePayloadLeases_[leaseKey] = std::move(payloadLease);
}

const auto submitRes = submitter_.submit_tx_chain(atRspCtx, std::move(chain));
if (submitRes.kr != kIOReturnSuccess) {
if (leaseKey) {
responsePayloadLeases_.erase(leaseKey);
}
ASFW_LOG_ERROR(
Async,
"ResponseSender: submit_tx_chain failed (tCode=0x%x kr=0x%x)",
Expand Down Expand Up @@ -147,7 +157,8 @@ void ResponseSender::SendReadQuadletResponse(const ARPacketView& request,
void ResponseSender::SendReadBlockResponse(const ARPacketView& request,
ResponseCode rcode,
uint64_t payloadDeviceAddress,
uint32_t payloadLength) noexcept {
uint32_t payloadLength,
std::shared_ptr<void> payloadLease) noexcept {
if (request.tCode != 0x5) {
ASFW_LOG_V3(Async,
"ResponseSender: skip RdBlockResp for non-read-block tCode=0x%x",
Expand Down Expand Up @@ -177,7 +188,19 @@ void ResponseSender::SendReadBlockResponse(const ARPacketView& request,
header,
sizeof(header),
responsePayloadAddress,
responsePayloadLen);
responsePayloadLen,
std::move(payloadLease));
}

void ResponseSender::OnTxCompletion(const TxCompletion& completion) noexcept {
if (!completion.isResponseContext || !completion.descriptor) {
return;
}
responsePayloadLeases_.erase(completion.descriptor);
}

void ResponseSender::ClearOutstandingResponses() noexcept {
responsePayloadLeases_.clear();
}

} // namespace ASFW::Async
17 changes: 15 additions & 2 deletions ASFWDriver/Async/Tx/ResponseSender.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <cstddef>
#include <cstdint>
#include <memory>
#include <unordered_map>

#include "../ResponseCode.hpp"
#include "../Rx/PacketRouter.hpp"
Expand All @@ -11,9 +13,11 @@ namespace ASFW::Async {
class DescriptorBuilder;
class ATResponseContext;
class IFireWireBusInfo;
struct TxCompletion;
namespace Bus { class GenerationTracker; }
namespace Engine { class ContextManager; }
namespace Tx { class Submitter; }
namespace HW { struct OHCIDescriptor; }

/// Utility to build and send Write Response (WrResp) packets for incoming AR requests.
class ResponseSender {
Expand All @@ -36,7 +40,14 @@ class ResponseSender {
void SendReadBlockResponse(const ARPacketView& request,
ResponseCode rcode,
uint64_t payloadDeviceAddress,
uint32_t payloadLength) noexcept;
uint32_t payloadLength,
std::shared_ptr<void> payloadLease = {}) noexcept;

/// Release response payload leases after AT response descriptor completion.
void OnTxCompletion(const TxCompletion& completion) noexcept;

/// Drop any retained response payload leases during response subsystem teardown.
void ClearOutstandingResponses() noexcept;

private:
void SendResponse(const ARPacketView& request,
Expand All @@ -45,12 +56,14 @@ class ResponseSender {
const uint32_t* header,
std::size_t headerBytes,
uint64_t payloadDeviceAddress,
std::size_t payloadLength) noexcept;
std::size_t payloadLength,
std::shared_ptr<void> payloadLease = {}) noexcept;

DescriptorBuilder& builder_;
Tx::Submitter& submitter_;
Engine::ContextManager& ctxMgr_;
Bus::GenerationTracker& generationTracker_;
std::unordered_map<const HW::OHCIDescriptor*, std::shared_ptr<void>> responsePayloadLeases_;
};

} // namespace ASFW::Async
6 changes: 6 additions & 0 deletions ASFWDriver/Controller/ControllerCore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class FCPResponseRouter;

namespace ASFW::Protocols::SBP2 {
class AddressSpaceManager;
class SBP2SessionRegistry;
}

namespace ASFW::IRM {
Expand Down Expand Up @@ -96,6 +97,7 @@ class ControllerCore {
std::shared_ptr<ASFW::Protocols::AVC::AVCDiscovery> avcDiscovery;
std::shared_ptr<ASFW::Protocols::AVC::FCPResponseRouter> fcpResponseRouter;
std::shared_ptr<ASFW::Protocols::SBP2::AddressSpaceManager> sbp2AddressSpaceManager;
std::shared_ptr<ASFW::Protocols::SBP2::SBP2SessionRegistry> sbp2SessionRegistry;

std::shared_ptr<ASFW::IRM::IRMClient> irmClient;

Expand Down Expand Up @@ -137,9 +139,13 @@ class ControllerCore {
Protocols::AVC::IAVCDiscovery* GetAVCDiscovery() const;
void SetAVCDiscovery(std::shared_ptr<Protocols::AVC::AVCDiscovery> avcDiscovery);
void SetFCPResponseRouter(std::shared_ptr<Protocols::AVC::FCPResponseRouter> fcpResponseRouter);

Protocols::SBP2::AddressSpaceManager* GetSbp2AddressSpaceManager() const;
Protocols::SBP2::SBP2SessionRegistry* GetSBP2SessionRegistry() const;
void SetSbp2AddressSpaceManager(
std::shared_ptr<Protocols::SBP2::AddressSpaceManager> sbp2AddressSpaceManager);
void SetSBP2SessionRegistry(
std::shared_ptr<Protocols::SBP2::SBP2SessionRegistry> sbp2SessionRegistry);

IRM::IRMClient* GetIRMClient() const;
void SetIRMClient(std::shared_ptr<IRM::IRMClient> client);
Expand Down
23 changes: 22 additions & 1 deletion ASFWDriver/Controller/ControllerCoreDiscovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "../Protocols/AVC/AVCDiscovery.hpp"
#include "../Protocols/AVC/CMP/CMPClient.hpp"
#include "../Protocols/Audio/DeviceProtocolFactory.hpp"
#include "../Protocols/SBP2/SBP2SessionRegistry.hpp"
#include "../Scheduling/Scheduler.hpp"
#include "../Version/DriverVersion.hpp"
#include "ControllerStateMachine.hpp"
Expand Down Expand Up @@ -116,6 +117,18 @@ void ControllerCore::OnTopologyReady(const TopologySnapshot& snap) {
// CMP (PCR) operations target a *specific device's* plug registers, not the IRM node.
// Device-scoped CMP wiring is done at stream start time (IsochService).
}

if (deps_.sbp2SessionRegistry) {
deps_.sbp2SessionRegistry->OnBusReset(static_cast<uint16_t>(snap.generation));
}

// NOTE: CSR STATE_SET CMSTR write removed. Apple IOFireWireFamily does NOT write
// CSR STATE_SET via async transactions — it uses the OHCI LinkControl register
// directly (kCycleMaster bit), which ASFWDriver already sets in kDefaultLinkControl
// during controller initialization. Async loopback to the local node does not work
// in ASFWDriver (always returns timeout), so the previous implementation was a no-op.
// OHCI hardware generates cycle-start packets automatically when the node is root
// and kCycleMaster is set in LinkControl.
}

// NOLINTNEXTLINE(readability-function-cognitive-complexity)
Expand Down Expand Up @@ -218,14 +231,22 @@ void ControllerCore::OnDiscoveryScanComplete(Discovery::Generation gen,
} else if (deps_.deviceManager && zeroRomScanInconclusive) {
ASFW_LOG(Discovery,
"ROM scan for gen=%u produced 0 ROMs but topology still has remote "
"link-active nodes; keeping existing devices until a conclusive scan",
"link-active nodes keeping existing devices until a conclusive scan",
gen.value);
}

ASFW_LOG(Discovery, "═══════════════════════════════════════");
ASFW_LOG(Discovery, "Discovery complete: %zu devices processed in gen=%u", roms.size(),
gen.value);
ASFW_LOG(Discovery, "═══════════════════════════════════════════════════════");

if (deps_.sbp2SessionRegistry && !zeroRomScanInconclusive) {
deps_.sbp2SessionRegistry->RefreshTargets(gen);
} else if (deps_.sbp2SessionRegistry) {
ASFW_LOG(Discovery,
"Skipping SBP-2 target refresh for inconclusive zero-ROM scan gen=%u",
gen.value);
}
}

} // namespace ASFW::Driver
11 changes: 10 additions & 1 deletion ASFWDriver/Controller/ControllerCoreFacades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,25 @@ void ControllerCore::SetFCPResponseRouter(
deps_.fcpResponseRouter = std::move(fcpResponseRouter);
}

IRM::IRMClient* ControllerCore::GetIRMClient() const { return deps_.irmClient.get(); }

Protocols::SBP2::AddressSpaceManager* ControllerCore::GetSbp2AddressSpaceManager() const {
return deps_.sbp2AddressSpaceManager.get();
}

Protocols::SBP2::SBP2SessionRegistry* ControllerCore::GetSBP2SessionRegistry() const {
return deps_.sbp2SessionRegistry.get();
}

void ControllerCore::SetSbp2AddressSpaceManager(
std::shared_ptr<Protocols::SBP2::AddressSpaceManager> sbp2AddressSpaceManager) {
deps_.sbp2AddressSpaceManager = std::move(sbp2AddressSpaceManager);
}

IRM::IRMClient* ControllerCore::GetIRMClient() const { return deps_.irmClient.get(); }
void ControllerCore::SetSBP2SessionRegistry(
std::shared_ptr<Protocols::SBP2::SBP2SessionRegistry> sbp2SessionRegistry) {
deps_.sbp2SessionRegistry = std::move(sbp2SessionRegistry);
}

void ControllerCore::SetIRMClient(std::shared_ptr<IRM::IRMClient> client) {
deps_.irmClient = std::move(client);
Expand Down
1 change: 1 addition & 0 deletions ASFWDriver/Logging/Logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ os_log_t AVC() { static os_log_t log = MakeCategory("avc"); return
os_log_t Isoch() { static os_log_t log = MakeCategory("isoch"); return log; }
os_log_t Audio() { static os_log_t log = MakeCategory("audio"); return log; }
os_log_t DICE() { static os_log_t log = MakeCategory("dice"); return log; }
os_log_t SBP2() { static os_log_t log = MakeCategory("sbp2"); return log; }

} // namespace ASFW::Driver::Logging
1 change: 1 addition & 0 deletions ASFWDriver/Logging/Logging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ os_log_t AVC();
os_log_t Isoch();
os_log_t Audio();
os_log_t DICE();
os_log_t SBP2();
} // namespace ASFW::Driver::Logging

// ----- time helpers (header-only, safe in DriverKit) -----
Expand Down
Loading
Loading