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 if (r.out_hdr.flags.m_rd())
233 ok = _req_handler->handle_read(r.buf, r.buf_len);
234 else
235 ok = _req_handler->handle_write(r.buf, r.buf_len);
236 if (ok)
237 {
238 r.set_status(I2c_msg_ok);
239 _fail_next = false;
240 }
241 else
242 {
243 r.set_status(I2c_msg_err);
244 _fail_next = r.out_hdr.flags.fail_next();
245 }
246 }
247 consumed.emplace_back(_head, r.write_size);
248 if (!init_queue())
249 break;
250 }
251
252 _q->finish(consumed.begin(), consumed.end(), _i2c);
253
254 _head = Virtqueue::Head_desc();
255 }
256
257 private:
259 I2c_request_handler *_req_handler;
260 Virtio_i2c *_i2c;
262 Data_buffer _req;
263 bool _fail_next;
264 };
265
266 struct Features : public L4virtio::Svr::Dev_config::Features
267 {
268 Features() = default;
269 Features(l4_uint32_t raw) : L4virtio::Svr::Dev_config::Features(raw) {}
270
271 // This feature is mandatory. The driver is requested to abort communication
272 // if this is not offered.
273 CXX_BITFIELD_MEMBER(0, 0, zero_length_request, raw);
274 };
275
276 Virtio_i2c(I2c_request_handler *hndlr, L4Re::Util::Object_registry *registry)
277 : L4virtio::Svr::Device(&_dev_config),
278 _dev_config(L4VIRTIO_VENDOR_KK, L4VIRTIO_ID_I2C, Num_request_queues),
279 _req_handler(hndlr),
280 _host_irq(this),
281 _request_processor(&_q, hndlr, this)
282 {
283 init_mem_info(2);
284 reset_queue_config(0, queue_size);
285 setup_queue(&_q, 0, queue_size);
286 registry->register_irq_obj(&_host_irq);
287
288 Features hf(0);
289 hf.ring_indirect_desc() = true;
290 hf.zero_length_request() = true;
291 _dev_config.host_features(0) = hf.raw;
292 _dev_config.set_host_feature(L4VIRTIO_FEATURE_VERSION_1);
293 _dev_config.reset_hdr();
294 }
295
296 void notify_queue(L4virtio::Svr::Virtqueue *)
297 {
298 if (_q.no_notify_guest())
299 return;
300
301 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
302 L4Re::chkipc(_notify_guest_irq->trigger(), "trigger guest irq");
303 }
304
305 void handle_queue()
306 {
307 _request_processor.handle_request();
308 }
309
310 void reset() override
311 {
312 }
313
314 bool check_queues() override
315 {
316 return true;
317 }
318
319 int reconfig_queue(unsigned idx) override
320 {
321 if (idx != 0)
322 return -L4_ERANGE;
323
324 setup_queue(&_q, 0, queue_size);
325
326 return L4_EOK;
327 }
328
330 {
331 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
332 _notify_guest_irq->trigger();
333 }
334
335 L4::Ipc_svr::Server_iface *server_iface() const override
336 {
338 }
339
340 long op_set_status(L4virtio::Device::Rights r, unsigned status)
341 {
342 return L4virtio::Svr::Device::op_set_status(r, status);
343 }
344
345 long op_config_queue(L4virtio::Device::Rights r, unsigned queue)
346 {
347 return L4virtio::Svr::Device::op_config_queue(r, queue);
348 }
349
350 long op_device_config(L4virtio::Device::Rights r,
352 l4_addr_t &ds_offset)
353 {
354 return L4virtio::Svr::Device::op_device_config(r, config_ds, ds_offset);
355 }
356
358 {
359 return L4::cap_cast<L4::Irq>(_host_irq.obj_cap());
360 }
361
363 {
364 _notify_guest_irq = L4Re::chkcap
365 (server_iface()->template rcv_cap<L4::Irq>(0));
366
367 L4Re::chksys(server_iface()->realloc_rcv_cap(0));
368 }
369
370
371private:
372 L4virtio::Svr::Dev_config_t<L4virtio::Svr::No_custom_data>_dev_config;
373 I2c_request_handler *_req_handler;
375 Host_irq _host_irq;
376 L4::Cap<L4::Irq> _notify_guest_irq;
377 Request_processor _request_processor;
378};
379
380} // namespace Svr
381} // 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:219
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:796
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
Definition l4virtio:1041
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
Definition l4virtio:1006
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition l4virtio:892
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Trigger reset for the configuration space for queue idx.
Definition l4virtio:996
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