(*--------------------------------------------------------------------------- Copyright (c) 2025 Thomas Gazagnaire. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) (** {1 RPMsg endpoint info struct} Matches Linux kernel [struct rpmsg_endpoint_info]: {v struct rpmsg_endpoint_info { char name[32]; /* endpoint name */ __u32 src; /* source address */ __u32 dst; /* destination address */ }; v} Encoded via Wire.Codec to avoid manual C struct packing. *) type endpoint_info = { name : string; src : int; dst : int } let f_ei_name = Wire.Field.v "name" (Wire.byte_array ~size:(Wire.int 32)) let f_ei_src = Wire.Field.v "src" Wire.uint32 let f_ei_dst = Wire.Field.v "dst" Wire.uint32 let endpoint_info_codec = Wire.Codec.v "rpmsg_endpoint_info" (fun name src dst -> { name; src; dst }) Wire.Codec. [ ( f_ei_name $ fun t -> (* Pad/truncate name to exactly 32 bytes *) let b = Bytes.make 32 '\x00' in let len = min (String.length t.name) 31 in Bytes.blit_string t.name 0 b 0 len; Bytes.to_string b ); (f_ei_src $ fun t -> t.src); (f_ei_dst $ fun t -> t.dst); ] let endpoint_info_size = Wire.Codec.wire_size endpoint_info_codec (** RPMSG ioctl commands. [RPMSG_CREATE_EPT_IOCTL = _IOW(0xb5, 0x1, struct rpmsg_endpoint_info)] [RPMSG_DESTROY_EPT_IOCTL = _IOW(0xb5, 0x2, struct rpmsg_endpoint_info)] _IOW(type, nr, size) = (1 << 30) | (size << 16) | (type << 8) | nr *) let iow typ nr size = (1 lsl 30) lor (size lsl 16) lor (typ lsl 8) lor nr let ept_ioctl = iow 0xb5 0x1 endpoint_info_size let destroy_ept_ioctl = iow 0xb5 0x2 endpoint_info_size external ioctl : Unix.file_descr -> int -> bytes -> int = "caml_rpmsg_ioctl" module Ctrl = struct type t = { fd : Unix.file_descr } let open_ ?(index = 0) () = let path = Fmt.str "/dev/rpmsg_ctrl%d" index in let fd = Unix.openfile path [ Unix.O_RDWR ] 0 in { fd } let encode_info ~name ~src ~dst = let info = { name; src; dst } in let buf = Bytes.create endpoint_info_size in Wire.Codec.encode endpoint_info_codec info buf 0; buf let create_endpoint t ~name ~src ~dst = let buf = encode_info ~name ~src ~dst in ioctl t.fd ept_ioctl buf let destroy_endpoint t ~name ~src ~dst = let buf = encode_info ~name ~src ~dst in ignore (ioctl t.fd destroy_ept_ioctl buf) let close t = Unix.close t.fd end module Endpoint = struct type t = { recv_fn : max_size:int -> string option; send_fn : string -> unit; close_fn : unit -> unit; } let max_message_size = 496 let of_source_sink ~source ~sink = let recv_fn ~max_size = let buf = Cstruct.create max_size in match Eio.Flow.single_read source buf with | n -> Some (Cstruct.to_string ~len:n buf) | exception End_of_file -> None in let send_fn msg = Eio.Flow.copy_string msg sink in { recv_fn; send_fn; close_fn = ignore } let open_ index = let path = Fmt.str "/dev/rpmsg%d" index in let fd = Unix.openfile path [ Unix.O_RDWR ] 0 in fd let recv t = t.recv_fn let send t msg = t.send_fn msg let close t = t.close_fn () end