L4Re Operating System Framework
Interface and Usage Documentation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
virtio-i2c-device
1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * Copyright (C) 2024 Kernkonzept GmbH.
4 * Author(s): Martin Kuettler <martin.kuettler@kernkonzept.com>
5 *
6 * License: see LICENSE.spdx (in this directory or the directories above)
7 */
8
9#pragma once
10
11#include <l4/re/error_helper>
12#include <l4/sys/cxx/ipc_epiface>
13
14#include <l4/l4virtio/server/virtio>
15#include <l4/l4virtio/server/l4virtio>
16#include <l4/l4virtio/l4virtio>
17
18#include <l4/re/error_helper>
19#include <l4/re/util/object_registry>
20#include <l4/re/util/br_manager>
21#include <l4/sys/cxx/ipc_epiface>
22
23#include <vector>
24#include <l4/cxx/pair>
25
26namespace L4virtio {
27namespace Svr {
28
29enum : l4_uint8_t
30{
31 I2c_msg_ok = 0,
32 I2c_msg_err = 1,
33};
34
35struct I2c_request_flags
36{
37 l4_uint32_t raw;
38
39 CXX_BITFIELD_MEMBER(0, 0, fail_next, raw);
40 CXX_BITFIELD_MEMBER(1, 1, m_rd, raw);
41};
42static_assert(sizeof(I2c_request_flags) == 4,
43 "I2c_request_flags contains padding bytes.");
44
45struct I2c_out_hdr
46{
47 l4_uint16_t addr;
48 l4_uint16_t padding;
49 I2c_request_flags flags;
50};
51static_assert(sizeof(I2c_out_hdr) == 8, "I2c_out_hdr contains padding bytes.");
52
53struct I2c_in_hdr
54{
55 l4_uint8_t status;
56};
57static_assert(sizeof(I2c_in_hdr) == 1, "I2c_in_hdr contains padding bytes.");
58
59struct I2c_req
60{
61 struct I2c_out_hdr out_hdr;
62 unsigned buf_len;
63 l4_uint8_t* buf;
64 struct I2c_in_hdr *in_hdr;
65
66 unsigned write_size;
67
68 void set_status(l4_uint8_t status)
69 {
70 in_hdr->status = status;
71 }
72};
73
84template <typename Request_handler,
85 typename Epiface = L4virtio::Device>
87 public L4::Epiface_t<Virtio_i2c<Request_handler,
88 Epiface>,
89 Epiface>
90{
91private:
92 enum
93 {
94 Num_request_queues = 1,
95 queue_size = 128,
96 };
97
98public:
99 using I2c_request_handler = Request_handler;
100
106 class Host_irq : public L4::Irqep_t<Host_irq>
107 {
108 public:
109 explicit Host_irq(Virtio_i2c *i2c) : L4::Irqep_t<Host_irq>(), _i2c(i2c) {}
110
111 void handle_irq()
112 {
113 _i2c->handle_queue();
114 }
115
116 private:
117 Virtio_i2c *_i2c;
118 };
119
124 {
125 public:
126
127 struct Data_buffer : public L4virtio::Svr::Data_buffer
128 {
129 Data_buffer()
130 {
131 pos = nullptr;
132 left = 0;
133 }
134 // This constructor is called from within start, so make it available.
135 Data_buffer(L4virtio::Svr::Driver_mem_region const *r,
138 {
139 pos = static_cast<char *>(r->local(d.addr));
140 left = d.len;
141 }
142
143 };
144
145 Request_processor(L4virtio::Svr::Virtqueue *q, I2c_request_handler *hndlr,
146 Virtio_i2c *i2c)
147 : _q(q), _req_handler(hndlr), _i2c(i2c), _head(), _req(),
148 _fail_next(false)
149 {}
150
151 bool init_queue()
152 {
153 auto r = _q->next_avail();
154
155 if (L4_UNLIKELY(!r))
156 return false;
157
158 _head = start(_i2c->mem_info(), r, &_req);
159
160 return true;
161 }
162
172 I2c_req get_request()
173 {
174 I2c_req request;
175 memcpy(&request.out_hdr, _req.pos, sizeof(I2c_out_hdr));
176
177 // number of bytes to be written in the answer.
178 request.write_size = sizeof(I2c_in_hdr);
179 request.buf_len = 0;
180
181 Data_buffer req;
182 // 2nd part: either the optional buffer or the in_hdr
183 if (next(_i2c->mem_info(), &req))
184 {
185
186 request.buf_len += req.left;
187 request.buf = reinterpret_cast<l4_uint8_t *>(req.pos);
188 }
189
190 // 3rd part: in_hdr
191 if (next(_i2c->mem_info(), &req))
192 {
193 // 2nd part was indeed a buffer
194 if (request.out_hdr.flags.m_rd())
195 request.write_size += request.buf_len;
196
197 // actual 3rd part
198 request.in_hdr = reinterpret_cast<I2c_in_hdr *>(req.pos);
199 }
200 else
201 {
202 // no 3rd part, 2nd part is in_hdr;
203 request.in_hdr = reinterpret_cast<I2c_in_hdr *>(request.buf);
204 request.buf = nullptr;
205 request.buf_len = 0;
206 }
207
208 return request;
209 }
210
211 void handle_request()
212 {
213 if (!_head)
214 if (!init_queue())
215 return;
216
217 using Consumed_entry =
219 std::vector<Consumed_entry> consumed;
220
221 for (;;)
222 {
223 auto r = get_request();
224 if (_fail_next)
225 {
226 r.set_status(I2c_msg_err);
227 _fail_next = r.out_hdr.flags.fail_next();
228 }
229 else
230 {
231 bool ok;
232 l4_uint16_t i2c_addr = r.out_hdr.addr >> 1;
233 if (r.out_hdr.flags.m_rd())
234 ok = _req_handler->handle_read(i2c_addr, r.buf, r.buf_len);
235 else
236 ok = _req_handler->handle_write(i2c_addr, r.buf, r.buf_len);
237 if (ok)
238 {
239 r.set_status(I2c_msg_ok);
240 _fail_next = false;
241 }
242 else
243 {
244 r.set_status(I2c_msg_err);
245 _fail_next = r.out_hdr.flags.fail_next();
246 }
247 }
248 consumed.emplace_back(_head, r.write_size);
249 if (!init_queue())
250 break;
251 }
252
253 _q->finish(consumed.begin(), consumed.end(), _i2c);
254
255 _head = Virtqueue::Head_desc();
256 }
257
258 private:
260 I2c_request_handler *_req_handler;
261 Virtio_i2c *_i2c;
263 Data_buffer _req;
264 bool _fail_next;
265 };
266
267 struct Features : public L4virtio::Svr::Dev_config::Features
268 {
269 Features() = default;
270 Features(l4_uint32_t raw) : L4virtio::Svr::Dev_config::Features(raw) {}
271
272 // This feature is mandatory. The driver is requested to abort communication
273 // if this is not offered.
274 CXX_BITFIELD_MEMBER(0, 0, zero_length_request, raw);
275 };
276
277 Virtio_i2c(I2c_request_handler *hndlr, L4Re::Util::Object_registry *registry)
278 : L4virtio::Svr::Device(&_dev_config),
279 _dev_config(L4VIRTIO_VENDOR_KK, L4VIRTIO_ID_I2C, Num_request_queues),
280 _req_handler(hndlr),
281 _host_irq(this),
282 _request_processor(&_q, hndlr, this)
283 {
284 init_mem_info(2);
285 reset_queue_config(0, queue_size);
286 setup_queue(&_q, 0, queue_size);
287 registry->register_irq_obj(&_host_irq);
288
289 Features hf(0);
290 hf.ring_indirect_desc() = true;
291 hf.zero_length_request() = true;
292 _dev_config.host_features(0) = hf.raw;
293 _dev_config.set_host_feature(L4VIRTIO_FEATURE_VERSION_1);
294 _dev_config.reset_hdr();
295 }
296
297 void notify_queue(L4virtio::Svr::Virtqueue *)
298 {
299 if (_q.no_notify_guest())
300 return;
301
302 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
303 L4Re::chkipc(_notify_guest_irq->trigger(), "trigger guest irq");
304 }
305
306 void handle_queue()
307 {
308 _request_processor.handle_request();
309 }
310
311 void reset() override
312 {
313 }
314
315 bool check_queues() override
316 {
317 return true;
318 }
319
320 int reconfig_queue(unsigned idx) override
321 {
322 if (idx != 0)
323 return -L4_ERANGE;
324
325 setup_queue(&_q, 0, queue_size);
326
327 return L4_EOK;
328 }
329
331 {
332 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
333 _notify_guest_irq->trigger();
334 }
335
336 L4::Ipc_svr::Server_iface *server_iface() const override
337 {
339 }
340
341 long op_set_status(L4virtio::Device::Rights r, unsigned status)
342 {
343 return L4virtio::Svr::Device::op_set_status(r, status);
344 }
345
346 long op_config_queue(L4virtio::Device::Rights r, unsigned queue)
347 {
348 return L4virtio::Svr::Device::op_config_queue(r, queue);
349 }
350
351 long op_device_config(L4virtio::Device::Rights r,
353 l4_addr_t &ds_offset)
354 {
355 return L4virtio::Svr::Device::op_device_config(r, config_ds, ds_offset);
356 }
357
359 {
360 return L4::cap_cast<L4::Irq>(_host_irq.obj_cap());
361 }
362
364 {
365 _notify_guest_irq = L4Re::chkcap
366 (server_iface()->template rcv_cap<L4::Irq>(0));
367
368 L4Re::chksys(server_iface()->realloc_rcv_cap(0));
369 }
370
371
372private:
373 L4virtio::Svr::Dev_config_t<L4virtio::Svr::No_custom_data>_dev_config;
374 I2c_request_handler *_req_handler;
376 Host_irq _host_irq;
377 L4::Cap<L4::Irq> _notify_guest_irq;
378 Request_processor _request_processor;
379};
380
381} // namespace Svr
382} // namespace L4virtio
A registry that manages server objects and their attached IPC gates for a single server loop for a sp...
L4::Cap< L4::Irq > register_irq_obj(L4::Epiface *o) override
Register a handler for an interrupt.
C++ interface for capabilities.
Definition capability.h:224
Capability type for RPC interfaces (see L4::Cap<T>).
Definition ipc_types:699
Interface for server-loop related functions.
Definition ipc_epiface:37
IPC interface for virtio over L4 IPC.
Definition l4virtio:42
Server-side L4-VIRTIO device stub.
Definition l4virtio:802
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
Definition l4virtio:1047
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
Definition l4virtio:1012
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition l4virtio:898
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Trigger reset for the configuration space for queue idx.
Definition l4virtio:1002
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:570
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:501
I2c_req get_request()
Linux prepares the I2C request in three data parts: 1st: out_hdr 2nd: buffer (optional) 3rd: in_hdr.
A server implementation of the virtio-i2c protocol.
void trigger_driver_config_irq() override
callback for triggering configuration change notification IRQ
void register_single_driver_irq() override
callback for registering a single guest IRQ for all queues (old-style)
void reset() override
reset callback, called for doing a device reset
bool check_queues() override
callback for checking if the queues at DRIVER_OK transition
L4::Cap< L4::Irq > device_notify_irq() const override
callback to gather the device notification IRQ (old-style)
int reconfig_queue(unsigned idx) override
callback for client queue-config request
VIRTIO request, essentially a descriptor from the available ring.
Definition virtio:94
Virtqueue implementation for the device.
Definition virtio:88
Request next_avail()
Get the next available descriptor from the available ring.
Definition virtio:136
void finish(Head_desc &d, QUEUE_OBSERVER *o, l4_uint32_t len=0)
Add a descriptor to the used ring, and notify an observer.
Definition virtio:240
Descriptor in the descriptor table.
Definition virtqueue:87
l4_uint32_t len
Length of described buffer.
Definition virtqueue:109
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:108
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:413
Error helper.
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition l4int.h:25
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
@ L4_ERANGE
Range error.
Definition err.h:48
@ L4_EOK
Ok.
Definition err.h:32
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:275
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition virtio.h:99
@ L4VIRTIO_ID_I2C
I2C device.
Definition virtio.h:79
@ L4VIRTIO_IRQ_STATUS_VRING
VRING IRQ pending flag.
Definition virtio.h:110
@ L4VIRTIO_IRQ_STATUS_CONFIG
CONFIG IRQ pending flag.
Definition virtio.h:111
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:72
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:149
l4_msgtag_t chkipc(l4_msgtag_t tag, char const *extra="", l4_utcb_t *utcb=l4_utcb())
Test a message tag for IPC errors.
Definition error_helper:180
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Pair implementation.
Epiface implementation for Kobject-based interface implementations.
Definition ipc_epiface:504
Server_iface * server_iface() const
Get pointer to server interface at which the object is currently registered.
Definition ipc_epiface:213
Epiface implementation for interrupt handlers.
Definition ipc_epiface:283
Cap< L4::Irq > obj_cap() const
Get the (typed) capability to this object.
Definition ipc_epiface:294
l4_msgtag_t trigger(l4_utcb_t *utcb=l4_utcb()) noexcept
Trigger the object.
Definition irq:91
Abstract data buffer.
Definition virtio:307
l4_uint32_t left
Bytes left in buffer.
Definition virtio:309
char * pos
Current buffer position.
Definition virtio:308
Type for device feature bitmap.
Definition virtio:67
l4_uint32_t raw
The raw value of the features bitmap.
Definition virtio:68
Pair of two values.
Definition pair:28