Add Doxygen pages for RT-Thread device-model drivers: platform/OFW, DM core (bus, dm, power), clk/regulator/reset/power-domain, PCI/PIC, Phye, DMA/hwcache, block/SCSI/ATA/NVMe/UFS/SDIO, SCMI, RPMsg, mailbox, NVMem, NUMA, syscon, thermal, input/LED/IIO/graphic, and related power/charger/supply docs. Expand device-driver INDEX.md with ~90 @subpage entries so all new pages appear in the driver chapter navigation. Update existing UART/SPI/RTC/pin/framework/dtc pages: UART docs moved from serial/ to uart/ (uart + uart_dm + uart_earlycon); SPI and RTC gain DM companion pages; pin links to pin_dm; framework points at DM topics. Focus on registration/probe flow and in-tree APIs; no standalone VirtIO subsystem page (transport code still incomplete). Test plan: build Doxygen for documentation/ and verify device-driver INDEX links resolve without missing @ref/@subpage warnings. Signed-off-by: GuEe-GUI <2991707448@qq.com>
11 KiB
Executable File
@page page_device_rpmsg RPMsg
@subpage page_device_rpmsg_char @subpage page_device_rpmsg_ns
RPMsg subsystem
Header: components/drivers/include/drivers/rpmsg.h. Core bus: components/drivers/rpmsg/rpmsg.c.
RPMsg is an endpoint-oriented IPC bus on top of a transport (VirtIO + shared memory + mailbox in the in-tree example). The core matches struct rt_rpmsg_device to struct rt_rpmsg_driver by id.name, then manages endpoints (name + src/dst addresses) and send via struct rt_rpmsg_ops.
| Layer | File | Role |
|---|---|---|
| RPMsg bus | rpmsg/rpmsg.c |
Endpoints, match rpmsg-char / rpmsg-name-service |
| VirtIO RPMsg device | virtio/virtio-rpmsg.c |
rt_rpmsg_ops, virtqueues, NS announce |
| AMP transport | rpmsg/rpmsg-rt-thread-virtio.c |
Shared mem, mailbox doorbell, VirtIO transport |
| Class drivers | rpmsg_char.c, rpmsg_ns.c |
Char devices + name service |
Kconfig: RT_USING_RPMSG, RT_RPMSG_RT_THREAD_VIRTIO (default y), RT_RPMSG_CHAR_*.
Complete stack (VirtIO example)
This is the reference integration in-tree: rpmsg-rt-thread-virtio.c + virtio-rpmsg.c.
DT: rt-thread,virtio-rpmsg (master or slave)
|
v
rpmsg_virtio_probe (platform)
|-- mbox: rt_mbox_request_by_index
|-- shmem: ioremap from "shmem" phandle
|-- slab DMA pool in shared RAM
|-- rt_virtio_device_register (device_id = VIRTIO_DEVICE_ID_RPMSG)
|
v
virtio_rpmsg_probe (virtio driver, RT_VIRTIO_DRIVER_EXPORT)
|-- virtio_rpmsg_vq_init: rx + tx virtqueues
|-- rt_rpmsg_device_register(id.name = "rpmsg-char")
|-- optional: second device "rpmsg-name-service" if VIRTIO_RPMSG_F_NS
|
v
rpmsg bus: match rpmsg_char_driver / rpmsg_ns_driver
|-- rpmsg_char_probe: control dev rpmsg_charN
|-- rpmsg_ns_probe: endpoint name-service @ 0x35
|
v
Application: RT_DEVICE_CTRL_RPMSG_CREATE_EPT or NS_CREATE from remote
|
v
rt_rpmsg_send -> virtio_rpmsg_send -> virtqueue kick -> mbox notify peer
Physical pieces
| Piece | DT / code | Role |
|---|---|---|
| Shared memory | shmem = <&soc_sram> |
Descriptor rings + virtio buffers + link registers at base |
| Mailbox | mboxes = <&mbox N> |
RPMSG_VIRTIO_EVENT_LINK (exchange queue pointers) and EVENT_QUEUE (kick) |
queue-max |
Both sides must match | Virtqueue depth |
master / slave |
Property on node | Master publishes queue offsets; slave reads peer TX ring for RX |
Boot role: rpmsg.mode=master or slave in /chosen bootargs (INIT_CORE_EXPORT rpmsg_mode_setup in rpmsg.c). Must match firmware on the other core. virtio_rpmsg_probe swaps RX/TX queue indices for slave vs master.
Device tree example (master + slave)
/ {
chosen {
bootargs = "rpmsg.mode=master"; /* or slave on the other OS */
};
soc_sram: memory@60000000 {
reg = <0x60000000 0x100000>;
};
soc_mailbox: mailbox@ ... { /* provider */ };
rpmsg_virtio0: rpmsg-virtio@0 {
compatible = "rt-thread,virtio-rpmsg";
shmem = <&soc_sram>;
mboxes = <&soc_mailbox 0>; /* master uses index 0 */
queue-max = <64>;
master;
};
};
Remote side (conceptually):
rpmsg_virtio1: rpmsg-virtio@0 {
compatible = "rt-thread,virtio-rpmsg";
shmem = <&soc_sram>; /* same region */
mboxes = <&soc_mailbox 1>; /* slave uses different mbox index */
queue-max = <64>;
slave;
};
After both sides probe: master writes MASTER_QUEUE_* offsets; EVENT_LINK makes slave map peer_tx_virtq and process inbound RPMsg frames.
Layer 1 — Register transport (rpmsg-rt-thread-virtio)
RT_PLATFORM_DRIVER_EXPORT(rpmsg_virtio_driver) — does not register rt_rpmsg_device itself.
rpmsg_virtio_probe checklist:
rt_mbox_request_by_index(&mbox_client, 0)—rx_callback = rpmsg_virtio_rx_callback- Parse
shmem,rt_ioremap_cached masterproperty →is_master, featureVIRTIO_RPMSG_F_NSonly on masterrt_slab_initon half of shmem (DMA buffers for virtqueues)rt_dma_device_set_ops— copy to/from shmem for non-coherent AMP- Fill
rt_virtio_device:VIRTIO_DEVICE_ID_RPMSG,rpmsg_virtio_trans rt_virtio_device_register(vdev)→ triggersvirtio_rpmsg_probe
Mailbox ISR (rpmsg_virtio_rx_callback):
EVENT_LINK: peer published descriptor ring addresses — wirepeer_tx_virtqEVENT_QUEUE:rt_virtqueue_isrfor local virtqueues- Peer TX avail ring progress → DMA tag fixup →
usedindex update (feeds virtio RX path)
Layer 2 — Register RPMsg device (virtio-rpmsg.c)
RT_VIRTIO_DRIVER_EXPORT(virtio_rpmsg_driver)
virtio_rpmsg_probe:
vrpmsg->parent.id.name = "rpmsg-char";
vrpmsg->parent.ops = &virtio_rpmsg_ops; /* create / destroy / send */
rt_rpmsg_device_register(&vrpmsg->parent);
if (VIRTIO_RPMSG_F_NS) {
vrpmsg->ns->id.name = "rpmsg-name-service";
rt_rpmsg_device_register(vrpmsg->ns);
}
struct rt_rpmsg_ops (backend contract)
| Op | virtio_rpmsg behavior |
|---|---|
create_endpoint |
Local: optional NS CREATE message to RT_RPMSG_NS_ADDR. Remote-created: src == RT_RPMSG_ADDR_ANY |
destroy_endpoint |
Optional NS DESTROY |
send |
Build struct virtio_rpmsg_hdr (src, dst, len), rt_virtqueue_add_outbuf, kick |
Wire header (virtio-rpmsg.h):
struct virtio_rpmsg_hdr {
le32 src, dst, reserved;
le16 len, flags;
u8 data[];
}; /* max payload VIRTIO_RPMSG_MAX_BUF_SIZE - sizeof(hdr) */
RX path (virtio_rpmsg_rx_done)
rt_virtqueue_read_bufon RX queue- Find endpoint:
find_endpoint(rdev, dst=hdr->dst, src=ANY)
Ifdst == RT_RPMSG_NS_ADDR→ usevrpmsg->nsdevice ept->rx_callback(rdev, hdr->src, data, len)- Re-queue buffer with
rt_virtqueue_add_inbuf
Layer 3 — RPMsg bus (rpmsg.c)
Register a class driver (char / NS / custom)
static const struct rt_rpmsg_device_id my_ids[] = {
{ .name = "rpmsg-char" }, /* must match rdev->id.name from backend */
{ },
};
static struct rt_rpmsg_driver my_driver = {
.ids = my_ids,
.probe = my_probe,
.remove = my_remove,
.rx_callback = my_default_rx, /* fallback if create_endpoint(..., NULL) */
};
RT_RPMSG_DRIVER_EXPORT(my_driver);
RT_RPMSG_DRIVER_EXPORT → INIT_DEVICE_EXPORT → rt_rpmsg_driver_register → probes all rt_rpmsg_device on bus with matching id.name.
Register an rt_rpmsg_device (custom backend)
If you implement a non-VirtIO link:
rdev->id.name = "rpmsg-char"; /* or your id, but char driver won't bind */
rdev->ops = &my_rpmsg_ops;
rt_rpmsg_device_register(rdev);
Bus probe: rt_dm_power_domain_attach then rdrv->probe(rdev).
Endpoints and addressing
struct rt_rpmsg_endpoint_info info;
rt_strncpy(info.name, "tty", RT_RPMSG_NAME_SIZE);
info.src = 0x400; /* local address (assigned by you or NS) */
info.dst = 0x401; /* remote peer */
ept = rt_rpmsg_create_endpoint(rdev, &info, my_rx_cb);
if (rt_is_err(ept))
return rt_ptr_err(ept);
rt_rpmsg_send(ept, buf, len);
| API | Role |
|---|---|
rt_rpmsg_create_endpoint |
ops->create_endpoint, insert on ept_nodes |
rt_rpmsg_destroy_endpoint |
Backend destroy + rt_free |
rt_rpmsg_find_endpoint |
Match src/dst/name wildcards (RT_RPMSG_ADDR_ANY) |
rt_rpmsg_send / sendto / send_wait |
ops->send under ept->lock |
RX callback: per-endpoint rx_cb, or driver-wide struct rt_rpmsg_driver::rx_callback if rx_cb == NULL.
Name service (summary)
Well-known address RT_RPMSG_NS_ADDR (0x35). Remote CREATE can auto-create endpoints (driver default RX logs only). Local CREATE from virtio_rpmsg_create_endpoint announces name+addr to peer when VIRTIO_RPMSG_F_NS.
Details: @ref page_device_rpmsg_ns.
Userspace / driver channel (summary)
- Open control device
rpmsg_charN(fromrpmsg_charprobe) RT_DEVICE_CTRL_RPMSG_CREATE_EPTwithstruct rt_rpmsg_endpoint_info- Open
rpmsg_<src>x<dst>,read/write
Details: @ref page_device_rpmsg_char.
Bring-up checklist
| Step | Check |
|---|---|
| 1 | RT_USING_RPMSG, RT_RPMSG_RT_THREAD_VIRTIO, mailbox + shmem drivers |
| 2 | Same queue-max, same shmem region, correct mboxes index per role |
| 3 | rpmsg.mode= bootarg matches remote |
| 4 | Master has master; property; slave has slave; |
| 5 | virtio_rpmsg_probe succeeds → rpmsg_char / rpmsg-name-service on bus |
| 6 | Create endpoint or wait for NS CREATE; verify rt_rpmsg_send returns OK |
| 7 | @ref page_device_hwspinlock / @ref page_device_mailbox ordering if shared with other AMP IPC |
Custom backend (non-VirtIO)
Implement struct rt_rpmsg_ops and register rt_rpmsg_device with an id.name your class driver matches (or reuse rpmsg-char):
create_endpoint: book-keep addresses / shared memory slotssend: push frame with src/dst header to remote- On RX interrupt: parse header →
rt_rpmsg_find_endpoint→ callept->rx_callback
Do not call rt_rpmsg_* from ISR if your send sleeps on virtqueue completion — use workqueue.
Pitfalls
| Issue | Mitigation |
|---|---|
id.name mismatch |
Backend rpmsg-char vs driver table rpmsg-char / rpmsg-raw |
| Master/slave queue swap | Wrong rpmsg.mode → no RX |
queue-max mismatch |
Corrupt rings |
| NS without feature | Master must advertise VIRTIO_RPMSG_F_NS |
| Endpoint without RX cb | create_endpoint(..., NULL) needs driver rx_callback |
| Buffer size | Payload ≤ VIRTIO_RPMSG_MAX_BUF_SIZE - sizeof(hdr) (512 default) |
NULL send addr |
RT_RPMSG_ADDR_ANY rejected in virtio_rpmsg_send |
| Power domain | Bus probe attaches domain; -RT_EEMPTY OK |
See also
- @ref page_device_rpmsg_char
- @ref page_device_rpmsg_ns
- @ref page_device_mailbox
- @ref page_device_hwspinlock
- @ref page_device_platform
components/drivers/rpmsg/rpmsg-rt-thread-virtio.ccomponents/drivers/virtio/virtio-rpmsg.ccomponents/drivers/virtio/virtio_config/virtio-rpmsg.h