1// SPDX-License-Identifier: GPL-3.0-or-later
2// io_t wrapper for IPC channels
3
4#include "mos/ipc/ipc_io.hpp"
5
6#include "mos/io/io.hpp"
7#include "mos/ipc/ipc.hpp"
8#include "mos/misc/panic.hpp"
9
10#include <mos/allocator.hpp>
11#include <mos_stdlib.hpp>
12
13struct ipc_server_io_t : mos::NamedType<"IPC.ServerIO">
14{
15 io_t control_io;
16 IPCServer *server;
17};
18
19static void ipc_control_io_close(io_t *io)
20{
21 if (io->type != IO_IPC)
22 mos_panic("ipc_control_io_close: io->type != IO_IPC"); // handle error cases in io_close
23
24 // we only deannounce the server, we don't free it
25 ipc_server_io_t *server_io = container_of(io, ipc_server_io_t, control_io);
26 ipc_server_close(server: server_io->server);
27 delete server_io;
28}
29
30static const io_op_t ipc_control_io_op = {
31 .close = ipc_control_io_close,
32};
33
34static size_t ipc_client_io_write(io_t *io, const void *buf, size_t size)
35{
36 ipc_conn_io_t *conn = container_of(io, ipc_conn_io_t, io);
37 return ipc_client_write(ipc: conn->ipc, buffer: buf, size);
38}
39
40static size_t ipc_client_io_read(io_t *io, void *buf, size_t size)
41{
42 ipc_conn_io_t *conn = container_of(io, ipc_conn_io_t, io);
43 return ipc_client_read(ipc: conn->ipc, buffer: buf, size);
44}
45
46static void ipc_client_io_close(io_t *io)
47{
48 if (io->type != IO_IPC)
49 mos_panic("ipc_client_io_close: io->type != IO_IPC"); // handle error cases in io_close
50
51 ipc_conn_io_t *conn = container_of(io, ipc_conn_io_t, io);
52 ipc_client_close_channel(ipc: conn->ipc);
53 delete conn;
54}
55
56static size_t ipc_server_io_write(io_t *io, const void *buf, size_t size)
57{
58 ipc_conn_io_t *conn = container_of(io, ipc_conn_io_t, io);
59 return ipc_server_write(ipc: conn->ipc, buffer: buf, size);
60}
61
62static size_t ipc_server_io_read(io_t *io, void *buf, size_t size)
63{
64 ipc_conn_io_t *conn = container_of(io, ipc_conn_io_t, io);
65 return ipc_server_read(ipc: conn->ipc, buffer: buf, size);
66}
67
68static void ipc_server_io_close(io_t *io)
69{
70 if (io->type != IO_IPC)
71 mos_panic("ipc_server_io_close: io->type != IO_IPC"); // handle error cases in io_close
72
73 ipc_conn_io_t *conn = container_of(io, ipc_conn_io_t, io);
74 ipc_server_close_channel(ipc: conn->ipc);
75 delete conn;
76}
77
78static const io_op_t ipc_client_io_op = {
79 .read = ipc_client_io_read,
80 .write = ipc_client_io_write,
81 .close = ipc_client_io_close,
82};
83
84static const io_op_t ipc_server_io_op = {
85 .read = ipc_server_io_read,
86 .write = ipc_server_io_write,
87 .close = ipc_server_io_close,
88};
89
90PtrResult<ipc_conn_io_t> ipc_conn_io_create(IPCDescriptor *ipc, bool is_server_side)
91{
92 ipc_conn_io_t *io = mos::create<ipc_conn_io_t>();
93 if (io == nullptr)
94 return -ENOMEM;
95
96 io->ipc = ipc;
97 io_init(io: &io->io, type: IO_IPC, flags: IO_READABLE | IO_WRITABLE, ops: is_server_side ? &ipc_server_io_op : &ipc_client_io_op);
98 return io;
99}
100
101PtrResult<io_t> ipc_create(const char *name, size_t max_pending_connections)
102{
103 auto server = ipc_server_create(name, max_pending_connections);
104 if (server.isErr())
105 return server.getErr();
106
107 ipc_server_io_t *io = mos::create<ipc_server_io_t>();
108 if (io == nullptr)
109 {
110 ipc_server_close(server: server.get());
111 return -ENOMEM;
112 }
113
114 io->server = server.get();
115 io_init(io: &io->control_io, type: IO_IPC, flags: IO_NONE, ops: &ipc_control_io_op);
116 return &io->control_io;
117}
118
119PtrResult<io_t> ipc_accept(io_t *server)
120{
121 if (server->type != IO_IPC)
122 return -EBADF; // not an ipc server
123
124 ipc_server_io_t *ipc_server = container_of(server, ipc_server_io_t, control_io);
125 auto ipc = ipc_server_accept(server: ipc_server->server);
126 if (ipc.isErr())
127 return ipc.getErr();
128
129 auto io = ipc_conn_io_create(ipc: ipc.get(), is_server_side: true);
130 if (io.isErr())
131 return io.getErr();
132
133 return &io->io;
134}
135
136PtrResult<io_t> ipc_connect(const char *name, size_t buffer_size)
137{
138 auto ipc = ipc_connect_to_server(name, buffer_size);
139 if (ipc.isErr())
140 return ipc.getErr();
141
142 auto connio = ipc_conn_io_create(ipc: ipc.get(), is_server_side: false);
143 if (connio.isErr())
144 return connio.getErr();
145
146 return &connio->io;
147}
148