RPMsg inter-partition messaging
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Thomas Gazagnaire. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** {1 RPMsg endpoint info struct}
7
8 Matches Linux kernel [struct rpmsg_endpoint_info]:
9 {v
10 struct rpmsg_endpoint_info {
11 char name[32]; /* endpoint name */
12 __u32 src; /* source address */
13 __u32 dst; /* destination address */
14 };
15 v}
16
17 Encoded via Wire.Codec to avoid manual C struct packing. *)
18
19type endpoint_info = { name : string; src : int; dst : int }
20
21let f_ei_name = Wire.Field.v "name" (Wire.byte_array ~size:(Wire.int 32))
22let f_ei_src = Wire.Field.v "src" Wire.uint32
23let f_ei_dst = Wire.Field.v "dst" Wire.uint32
24
25let endpoint_info_codec =
26 Wire.Codec.v "rpmsg_endpoint_info"
27 (fun name src dst -> { name; src; dst })
28 Wire.Codec.
29 [
30 ( f_ei_name $ fun t ->
31 (* Pad/truncate name to exactly 32 bytes *)
32 let b = Bytes.make 32 '\x00' in
33 let len = min (String.length t.name) 31 in
34 Bytes.blit_string t.name 0 b 0 len;
35 Bytes.to_string b );
36 (f_ei_src $ fun t -> t.src);
37 (f_ei_dst $ fun t -> t.dst);
38 ]
39
40let endpoint_info_size = Wire.Codec.wire_size endpoint_info_codec
41
42(** RPMSG ioctl commands.
43
44 [RPMSG_CREATE_EPT_IOCTL = _IOW(0xb5, 0x1, struct rpmsg_endpoint_info)]
45 [RPMSG_DESTROY_EPT_IOCTL = _IOW(0xb5, 0x2, struct rpmsg_endpoint_info)]
46
47 _IOW(type, nr, size) = (1 << 30) | (size << 16) | (type << 8) | nr *)
48let iow typ nr size = (1 lsl 30) lor (size lsl 16) lor (typ lsl 8) lor nr
49
50let ept_ioctl = iow 0xb5 0x1 endpoint_info_size
51let destroy_ept_ioctl = iow 0xb5 0x2 endpoint_info_size
52
53external ioctl : Unix.file_descr -> int -> bytes -> int = "caml_rpmsg_ioctl"
54
55module Ctrl = struct
56 type t = { fd : Unix.file_descr }
57
58 let open_ ?(index = 0) () =
59 let path = Fmt.str "/dev/rpmsg_ctrl%d" index in
60 let fd = Unix.openfile path [ Unix.O_RDWR ] 0 in
61 { fd }
62
63 let encode_info ~name ~src ~dst =
64 let info = { name; src; dst } in
65 let buf = Bytes.create endpoint_info_size in
66 Wire.Codec.encode endpoint_info_codec info buf 0;
67 buf
68
69 let create_endpoint t ~name ~src ~dst =
70 let buf = encode_info ~name ~src ~dst in
71 ioctl t.fd ept_ioctl buf
72
73 let destroy_endpoint t ~name ~src ~dst =
74 let buf = encode_info ~name ~src ~dst in
75 ignore (ioctl t.fd destroy_ept_ioctl buf)
76
77 let close t = Unix.close t.fd
78end
79
80module Endpoint = struct
81 type t = {
82 recv_fn : max_size:int -> string option;
83 send_fn : string -> unit;
84 close_fn : unit -> unit;
85 }
86
87 let max_message_size = 496
88
89 let of_source_sink ~source ~sink =
90 let recv_fn ~max_size =
91 let buf = Cstruct.create max_size in
92 match Eio.Flow.single_read source buf with
93 | n -> Some (Cstruct.to_string ~len:n buf)
94 | exception End_of_file -> None
95 in
96 let send_fn msg = Eio.Flow.copy_string msg sink in
97 { recv_fn; send_fn; close_fn = ignore }
98
99 let open_ index =
100 let path = Fmt.str "/dev/rpmsg%d" index in
101 let fd = Unix.openfile path [ Unix.O_RDWR ] 0 in
102 fd
103
104 let recv t = t.recv_fn
105 let send t msg = t.send_fn msg
106 let close t = t.close_fn ()
107end