Bus drivers
Where MCTP transport is the medium-independent packet layer,
these drivers are the media. Each subsystem under src/nv/ owns one
physical interface, and most follow the same shape: one FreeRTOS task per
bus/port, an IPC Queue of request items, an ISR path that enqueues work,
and a switch on the request type in the task’s main loop. The ones that
carry MCTP call nv::mctp::Driver::mctp_send() on ingress and are the
targets of nv::mctp::Driver::forward() on egress (see
MCTP transport).
This is a catalog page; citations are directory-level except where a specific class or entry point is worth pinning. I2C is the largest driver and gets the most detail.
I2C / SMBus — src/nv/i2c/
The largest bus subsystem (49 files). nv::i2c::Task
(src/nv/i2c/task.h:37) drives a hardware I2C controller (sys::i2c::Driver),
one task instance per physical port (I2c0–I2c8), and can act as
both bus controller (master) and target (slave). It is the primary
MCTP-over-SMBus binding: MCTP packets travel as SMBus block writes with a
trailing PEC byte, and a per-task routing table bridges them between
upstream and downstream ports. The directory also carries a library of
on-bus device drivers (temperature sensors, hot-swap/power controllers,
a Lattice CPLD programmer, EEPROM cache).
Role split: MCTP transport binding plus raw I2C request/response service for other tasks plus sensor/EEPROM polling. Completion is interrupt-driven, and it supports DMA-error reporting, bus recovery, and fault injection.
Key files:
src/nv/i2c/task.cpp/task.h— the bus task: MCTP tx/rx, routing, raw I2C dispatch, sensor/EEPROM polling, recovery.src/nv/i2c/common.h— shared payload types (I2cRequest,I2cResponse, buffers).src/nv/i2c/lattice_driver.cpp/.h— Lattice CPLD in-system programming over I2C.src/nv/i2c/sensor.cpp/.h—TempSensorbase plus concrete drivers (nct75, tmp1075, tmp432/461/468, emc1812).src/nv/i2c/powersensor/— hot-swap controller (HSC/HSCC) manager.src/nv/i2c/eeprom_cache.cpp,recovery.cpp,error_injection.cpp,smb_direct.cpp— EEPROM bridge cache, bus recovery, fault injection, and SMBus direct-command helpers.
Entry points:
Task::make()—src/nv/i2c/task.cpp:81(per-port factory).Task::start()—src/nv/i2c/task.cpp:408; the request dispatch is theswitch (request.type)atsrc/nv/i2c/task.cpp:451(MctpRx,MctpTx,MctpUpdateRoutingTable, raw I2C, sensor, recovery).Task::tx(const nv::mctp::Packet&)—src/nv/i2c/task.cpp:171, the MCTP send path (also called by the USB router).Task::forward(nv::mctp::Packet&, uint8_t)—src/nv/i2c/task.cpp:1231, the routing-table forward to USB / upstream I2C / downstream ports.struct I2cRequest—src/nv/i2c/common.h:35.- Device drivers:
class LatticeCpld(src/nv/i2c/lattice_driver.h:181),class TempSensor(src/nv/i2c/sensor.h:74),class DeviceManager(src/nv/i2c/powersensor/device_manager.h:91).
The MCTP Client values UsI2c and DsI2c0..7 all resolve to this driver;
nv::mctp::Driver::forward() reaches it via nv::i2c::Task::tx.
I3C — src/nv/i3c/
nv::i3c::Task (src/nv/i3c/task.h:40) drives an I3C controller
(nv::i3c::Driver, src/nv/i3c/driver.h:31) as I3C master with an
I2C-legacy fallback. It performs Dynamic Address Assignment and CCC
commands and, crucially, receives In-Band Interrupts (IBIs) that serve
two purposes: an IBI is the “MCTP packet available” signal for the
GPU-facing MCTP endpoint, and a separate IBI subtype drives the GPU
SMBus Post-Box Interface (SMBPBI) telemetry path.
Key files: task.cpp/task.h (IBI handling, MCTP routing, GPU reset,
sensor polling); driver.h (DAA/CCC, IBI, GPU/OCP helpers);
smbpbi.cpp/smbpbi.h (GPU SMBPBI protocol); gpu.cpp/gpu.h (GPU I2C
address derivation); topology_info.cpp (FRU/NVLink topology).
Entry points:
Task::start()main loop —src/nv/i3c/task.cpp:345, dispatching onswitch (request.type)atsrc/nv/i3c/task.cpp:374.Task::on_ibi(...)ISR handler —src/nv/i3c/task.cpp:396, which splits intoTask::handle_mctp_ibi(...)(src/nv/i3c/task.cpp:449) for MCTP and the GPU SMBPBI path.nv::i3c::smbpbi::start_smbpbi(...)—src/nv/i3c/smbpbi.cpp:41.
MCTP Client values DsI3c0/DsI3c1 resolve here; forwarded packets
arrive via nv::i3c::Task::tx. The driver also bridges onto the I2C queues
for legacy-addressed devices.
SPI — src/nv/spi/
nv::spi::Task (src/nv/spi/task.h:34) has two roles. As an MCTP
transport it is SPI master to a “Glacier” slave EC, running a custom
SPB (SPI Protocol Bus) mailbox protocol to carry MCTP packets. Separately, a
flashrom passthrough path drives an external SPI-NOR flash over eDMA,
fed from USB via LSTP framing (not MCTP).
Key files: task.cpp/task.h (MCTP-over-SPI task, up to ports
Spi0..2); spb.cpp/spb.h (the SPB mailbox/posted-read/write engine);
common.h (SPB constants and the MctpBinding SPI header);
flashrom.cpp + flashrom_task.cpp (external-flash passthrough);
ext_flash.cpp (JEDEC SPI-NOR abstraction).
Entry points:
Task::tx(const mctp::Packet&)—src/nv/spi/task.cpp:166(MCTP send, called by the USB MCTP router).class Spb—src/nv/spi/spb.h:99;spi_mctp_send/spi_mctp_recvwrap/unwrap MCTP packets in the SPIMctpBindingheader.class Flashrom—src/nv/spi/flashrom.h:65(the non-MCTP flash tunnel over USB-LSTP).
MCTP Client values Spi0..2 resolve here.
UART — src/nv/uart/
A deliberately thin, transmit-only wrapper over the platform
sys::uart::Driver, used as the serial console / logging output rather
than a message transport. The whole subsystem is a single header,
class Driver (src/nv/uart/driver.h:46) exposing Status tx(...)
(src/nv/uart/driver.h:50); the method bodies are per-SoC. Consumers are the
logger subsystem (fault logs, string logging). It carries no MCTP.
USB — src/nv/usb/
nv::usb::Task (src/nv/usb/task.h:67) is the USB device (target) driver
and the primary upstream MCTP endpoint as well as the MCTP bridge to
downstream buses. It multiplexes three functions over USB endpoints:
MCTP-over-USB (DMTF binding, id 0xB41A), a CP2112-compatible HID-to-SMBus
bridge, and LSTP (used to tunnel the SPI-flashrom traffic).
Key files: task.cpp/task.h (task loop, MCTP/HID/LSTP paths, vbus
handling); mctp_router.cpp/.h (downstream routing shared with the
proxy); hid_smb.cpp/.h (CP2112 HID-SMBus state machine);
i2c_backend.cpp (virtual→physical I2C mapping);
usb_mctp_header.h (UsbMctpHeader, UsbDmtfId); usb_stub.h (dual-core
stub that proxies to usb_proxy).
Entry points:
Task::main()event loop —src/nv/usb/task.cpp:73.Task::mctp_receive()—src/nv/usb/task.cpp:257, which callsroute_mctp_to_downstream(...)(src/nv/usb/mctp_router.cpp:34). On a routing-table hit it forwards toi2c::Task::tx/i3c::Task::tx/spi::Task::tx; otherwise it hands off tomctp::Driver::mctp_sendwith clientUsUsb.Task::usb_tx(...)outbound MCTP — the target ofnv::mctp::Driver::forward()forClient::UsUsb.struct UsbMctpHeader—src/nv/usb/usb_mctp_header.h:28;class HidSmb—src/nv/usb/hid_smb.h:26.
USB proxy — src/nv/usb_proxy/
On the dual-core (NCSI) build the bare-metal USB stack lives on core1; this
core0 task services that traffic after it crosses the C2C link.
nv::usb_proxy::Task (src/nv/usb_proxy/task.h:56) receives forwarded USB
data and (a) parses/routes MCTP to the MCTP driver, (b) emulates the CP2112
HID-to-I2C bridge, and (c) forwards LSTP channels. The whole file is gated
on #if NCSI_ENABLE.
Entry points:
Task::process_mctp_from_c2c(...)—src/nv/usb_proxy/task.cpp:202, which builds anmctp::Packetwith clientUsUsband callsmctp::Driver::mctp_send(...).dispatch_c2c_data(...)—src/nv/usb_proxy/task.cpp:622, the C2C fan-out that routes UsbTx→MCTP, UsbHid→HID, UsbAcm→UART bridge, and LstpRx→LSTP.
VCOM — src/nv/vcom/
USB Virtual COM endpoints. Two independent sub-drivers, neither of which carries MCTP:
src/nv/vcom/vruart/— a UART↔USB bridge.nv::vruart::BridgeTask(src/nv/vcom/vruart/task.h:31) runs a compile-time-selectedBridgealias (src/nv/vcom/vruart/bridge.h:32): eitherCdcBridge(raw CDC-ACM serial) orLstpBridge(UART tunnelled inside NVIDIA LSTP framing), chosen bynv::ipc::UartOverUsbProtocol.src/nv/vcom/gpio_mon/— a diagnostic GPIO monitor (nv::gpio_mon::Task,src/nv/vcom/gpio_mon/task.h:41) that streams GPIO state over a CDC VCOM endpoint using a small bespoke binary record protocol (full dump per 100 ms scan tick, per-bank interrupt records from GPIO ISRs).
SSIF — src/nv/ssif/
Despite living beside the MCTP bindings, SSIF here is the IPMI SSIF
(SMBus System Interface) slave, not MCTP-over-SMBus. nv::ssif::Ssif
(src/nv/ssif/ssif.h:123) acts as an I2C/SMBus slave at the BMC address and
implements the IPMI SSIF multi-part SMBus block-write / block-read command
codes with PEC validation, assembling IPMI request/response messages up to
254 bytes.
Once an inbound IPMI message is fully assembled, Ssif::handle_rx()
(src/nv/ssif/ssif.cpp:103) hands it up over LSTP via
nv::lstp::LstpRouter::send_ipmi(...) — the IPMI payload is wrapped in an
LstpHdr (struct Ssif::Packet, src/nv/ssif/ssif.h:130) and shipped over
the USB/LSTP path. Outbound responses arrive back through the Ssif queue
(Task::to_ssif, src/nv/ssif/task.cpp:62). SSIF is disabled by default —
Task::is_default_enabled() returns false (src/nv/ssif/task.cpp:91) —
and is enabled at runtime.
Interpretation: SSIF, the VCOM
vruartLSTP path, andusb_proxyall converge on the NVIDIA LSTP router rather than MCTP. LSTP is OpenSMA’s own USB tunnelling layer; MCTP and IPMI/SSIF are distinct message worlds that happen to share the USB and SMBus media.
Summary
| Driver | Path | Carries MCTP? | Key entry |
|---|---|---|---|
| I2C/SMBus | src/nv/i2c/ |
Yes (UsI2c, DsI2c0..7) |
Task::tx src/nv/i2c/task.cpp:171 |
| I3C | src/nv/i3c/ |
Yes (DsI3c0/1), via IBI |
Task::on_ibi src/nv/i3c/task.cpp:396 |
| SPI | src/nv/spi/ |
Yes (Spi0..2), SPB |
Task::tx src/nv/spi/task.cpp:166 |
| UART | src/nv/uart/ |
No (console/log) | Driver::tx src/nv/uart/driver.h:50 |
| USB | src/nv/usb/ |
Yes (UsUsb), + HID/LSTP |
Task::mctp_receive src/nv/usb/task.cpp:257 |
| USB proxy | src/nv/usb_proxy/ |
Yes (Core1 → MCTP) | dispatch_c2c_data src/nv/usb_proxy/task.cpp:622 |
| VCOM | src/nv/vcom/ |
No (CDC-ACM / LSTP / GPIO) | BridgeTask src/nv/vcom/vruart/task.h:31 |
| SSIF | src/nv/ssif/ |
No (IPMI over SMBus → LSTP) | Ssif::handle_rx src/nv/ssif/ssif.cpp:103 |
See MCTP transport for how the “Yes” rows plug into the transport core, and the section landing for the overview.