L4Re - L4 Runtime Environment
vbus
1 // vi:ft=cpp
2 /*
3  * (c) 2011 Adam Lackorzynski <adam@os.inf.tu-dresden.de>
4  * economic rights: Technische Universit├Ąt Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #pragma once
11 
12 #include <l4/vbus/vbus.h>
13 #include <l4/vbus/vbus_pm.h>
14 #include <l4/sys/icu>
15 
16 #include <l4/re/dataspace>
17 #include <l4/re/dma_space>
18 #include <l4/re/event>
19 #include <l4/re/inhibitor>
20 
21 /**
22  * \defgroup api_l4re_vbus Vbus API
23  * \ingroup api_l4re
24  * C++ interface of the Vbus API.
25  *
26  * The virtual bus (Vbus) is a hierarchical (tree) structure of device nodes
27  * where each device has a set of resources attached to it. Each virtual bus
28  * provides an Icu (\ref l4_icu_api) for interrupt handling.
29  *
30  * The Vbus interface allows a client to find and query devices present on his
31  * virtual bus. After obtaining a device handle for a specific device the
32  * client can enumerate its resources.
33  *
34  * \includefile{l4/vbus/vbus}
35  *
36  * Refer to \ref l4vbus_module for the C API.
37  */
38 namespace L4vbus {
39 
40 class Vbus;
41 
42 /**
43  * \ingroup api_l4re_vbus
44  * Power-management API mixin.
45  */
46 template<typename DEC>
47 class Pm
48 {
49 private:
50  DEC const *self() const { return static_cast<DEC const *>(this); }
51  DEC *self() { return static_cast<DEC *>(this); }
52 public:
53  /**
54  * Suspend the module.
55  */
56  int pm_suspend() const
57  { return l4vbus_pm_suspend(self()->bus_cap().cap(), self()->dev_handle()); }
58 
59  /**
60  * Resume the module.
61  */
62  int pm_resume() const
63  { return l4vbus_pm_resume(self()->bus_cap().cap(), self()->dev_handle()); }
64 };
65 
66 
67 /**
68  * \ingroup api_l4re_vbus
69  * Device on a L4vbus::Vbus
70  */
71 class Device : public Pm<Device>
72 {
73 public:
74  Device() : _dev(L4VBUS_NULL) {}
75 
76  Device(L4::Cap<Vbus> bus, l4vbus_device_handle_t dev)
77  : _bus(bus), _dev(dev) {}
78 
79  /**
80  * Access the Vbus capability of the underlying virtual bus.
81  * \return the capability to the underlying Vbus.
82  */
83  L4::Cap<Vbus> bus_cap() const { return _bus; }
84 
85  /**
86  * Access the device handle of this device.
87  * \return the device handle for this device.
88  *
89  * The device handle is used to directly address the device on its virtual
90  * bus.
91  */
92  l4vbus_device_handle_t dev_handle() const { return _dev; }
93 
94 
95  /**
96  * Find a device by the human interface identifier (HID).
97  *
98  * This function searches the vbus for a device with the given HID and
99  * returns a handle to the first matching device. The HID usually conforms to
100  * an ACPI HID or a Linux device tree compatible identifier.
101  *
102  * It is possible to have multiple devices with the same HID on a vbus. In
103  * order to find all matching devices this function has to be called
104  * repeatedly with `child` pointing to the device found in the previous
105  * iteration. The iteration starts at `child` that might be any device node
106  * in the tree.
107  *
108  * \param[in, out] child Handle of the device from where in the device
109  * tree the search should start. To start searching
110  * from the beginning `child` must be initialized
111  * using the default (#L4VBUS_NULL). If a matching
112  * device is found its handle is returned through
113  * this parameter.
114  * \param hid HID of the device
115  * \param depth Maximum depth for the recursive lookup
116  * \param[out] devinfo Device information structure (might be NULL)
117  *
118  * \retval >= 0 A device with the given HID was found.
119  * \retval -L4_ENOENT No device with the given HID could be found on
120  * the vbus.
121  * \retval -L4_EINVAL Invalid or no HID provided.
122  * \retval -L4_ENODEV Function called on a non-existing device.
123  */
124  int device_by_hid(Device *child, char const *hid,
125  int depth = L4VBUS_MAX_DEPTH,
126  l4vbus_device_t *devinfo = 0) const
127  {
128  child->_bus = _bus;
129  return l4vbus_get_device_by_hid(_bus.cap(), _dev, &child->_dev, hid,
130  depth, devinfo);
131  }
132 
133  /**
134  * Find next child following `child`.
135  *
136  * \param[in, out] child Handle of the device that precedes the device
137  * that shall be found. To start from the beginning
138  * `child` must be initialized using the default
139  * (#L4VBUS_NULL).
140  * \param depth Depth to look for
141  * \param[out] devinfo device information (might be NULL)
142  *
143  * \return 0 on success, else failure
144  */
145  int next_device(Device *child, int depth = L4VBUS_MAX_DEPTH,
146  l4vbus_device_t *devinfo = 0) const
147  {
148  child->_bus = _bus;
149  return l4vbus_get_next_device(_bus.cap(), _dev, &child->_dev, depth,
150  devinfo);
151  }
152 
153  /**
154  * Obtain detailed information about a Vbus device.
155  *
156  * \param[out] devinfo Information structure which contains details about
157  * the device. The pointer might be NULL after a
158  * successfull call.
159  *
160  * \retval 0 Success.
161  * \retval -L4_ENODEV No device with the given device handle `dev` could be
162  * found.
163  */
164  int device(l4vbus_device_t *devinfo) const
165  { return l4vbus_get_device(_bus.cap(), _dev, devinfo); }
166 
167  /**
168  * Obtain the resource description of an individual device resource.
169  *
170  * \param res_idx Index of the resource for which the resource
171  * description should be returned. The total number of
172  * resources for a device is available in the
173  * l4vbus_device_t structure that is returned by
174  * L4vbus::Device::device_by_hid() and
175  * L4vbus::Device::next_device().
176  * \param[out] res Descriptor of the resource.
177  *
178  * This function returns the resource descriptor of an individual device
179  * resource selected by the `res_idx` parameter.
180  *
181  * \retval 0 Success.
182  * \retval -L4_ENOENT Invalid resource index `res_idx`.
183  */
184  int get_resource(int res_idx, l4vbus_resource_t *res) const
185  {
186  return l4vbus_get_resource(_bus.cap(), _dev, res_idx, res);
187  }
188 
189  /**
190  * Check if the given device has a compatibility ID (CID) or HID that
191  * matches \a cid.
192  *
193  * \param cid the compatibility ID to test
194  * \return 1 when the given ID (\a cid) matches this device,
195  * 0 when the given ID does not match,
196  * <0 on error.
197  */
198  int is_compatible(char const *cid) const
199  { return l4vbus_is_compatible(_bus.cap(), _dev, cid); }
200 
201  /**
202  * Test if two devices are the same Vbus device.
203  * \return true if the two devices are the same, false else.
204  */
205  bool operator == (Device const &o) const
206  {
207  return _bus == o._bus && _dev == o._dev;
208  }
209 
210  /**
211  * Test if two devices are not the same.
212  * \return true if the two devices are different, false else.
213  */
214  bool operator != (Device const &o) const
215  {
216  return _bus != o._bus || _dev != o._dev;
217  }
218 
219 protected:
220  L4::Cap<Vbus> _bus; /*!< The Vbus capability (where this device is
221  located on). */
222  l4vbus_device_handle_t _dev; ///< The device handle for this device.
223 };
224 
225 /**
226  * \ingroup api_l4re_vbus
227  * Vbus Interrupt controller API.
228  *
229  * Allows to access the underlying L4::Icu capability managing IRQs for
230  * the L4vbus::Vbus.
231  *
232  * \see L4::Icu
233  */
234 class Icu : public Device
235 {
236 public:
237  /**
238  * Request the L4::Icu capability for this Vbus ICU.
239  */
240  int vicu(L4::Cap<L4::Icu> icu) const
241  {
242  return l4vbus_vicu_get_cap(_bus.cap(), _dev, icu.cap());
243  }
244 };
245 
246 /**
247  * The virtual bus (Vbus) interface.
248  *
249  * \ingroup api_l4re_vbus
250  *
251  * \see \link api_l4re_vbus L4Re Vbus API \endlink
252  */
253 class Vbus : public L4::Kobject_3t<Vbus, L4Re::Dataspace, L4Re::Inhibitor, L4Re::Event>
254 {
255 public:
256 
257  /**
258  * Request the given resource from the bus.
259  * \param res The resource that shall be requested from the bus.
260  * \param flags The flags for the request.
261  * \return >=0 on success, <0 on error.
262  */
263  int request_resource(l4vbus_resource_t *res, int flags = 0) const
264  {
265  return l4vbus_request_resource(cap(), res, flags);
266  }
267 
268  /**
269  * Release the given resource from the bus.
270  * \param res The resource that shall be requested from the bus.
271  * \return >=0 on success, <0 on error.
272  */
273  int release_resource(l4vbus_resource_t *res) const
274  {
275  return l4vbus_release_resource(cap(), res);
276  }
277 
278  /**
279  * Get the root device of the device tree of this bus.
280  * \return A Vbus device representing the root of the device tree.
281  */
282  Device root() const
283  {
284  return Device(L4::Cap<Vbus>(cap()), L4VBUS_ROOT_BUS);
285  }
286 
287  /**
288  * Assign an L4Re::Dma_space to a DMA domain.
289  * \param domain_id DMA domain ID (resource address of DMA domain found on
290  * the vBUS). If the value is ~0U the DMA space of the whole
291  * vBUS is used.
292  * \param flags A combination of #L4vbus_dma_domain_assign_flags.
293  * \param dma_space The DMA space capability to bind or unbind, this must
294  * be an L4Re::Dma_space
295  *
296  * \retval 0 Operation completed successfully.
297  * \retval -L4_ENOENT The vbus does not support a global DMA domain or no DMA
298  * domain could be found.
299  * \retval -L4_EINVAL Invalid argument used.
300  * \retval -L4_EBUSY DMA domain is already active, this means another DMA
301  * space is already assigned.
302  */
303  int assign_dma_domain(unsigned domain_id, unsigned flags,
304  L4::Cap<L4Re::Dma_space> dma_space) const
305  {
306  flags |= L4VBUS_DMAD_L4RE_DMA_SPACE;
307  flags &= ~L4VBUS_DMAD_KERNEL_DMA_SPACE;
308  return l4vbus_assign_dma_domain(cap(), domain_id, flags, dma_space.cap());
309  }
310 
311  /**
312  * Assign a kernel DMA space to a DMA domain.
313  * \param domain_id DMA domain ID (resource address of DMA domain found on
314  * the vBUS). If the value is ~0U the DMA space of the whole
315  * vBUS is used.
316  * \param flags A combination of #L4vbus_dma_domain_assign_flags.
317  * \param dma_space The DMA space capability to bind or unbind, this must be
318  * a kernel DMA space (L4::Task created with
319  * L4_PROTO_DMA_SPACE)
320  *
321  * \retval 0 Operation completed successfully.
322  * \retval -L4_ENOENT The vbus does not support a global DMA domain or no DMA
323  * domain could be found.
324  * \retval -L4_EINVAL Invalid argument used.
325  * \retval -L4_EBUSY DMA domain is already active, this means another DMA
326  * space is already assigned.
327  */
328  int assign_dma_domain(unsigned domain_id, unsigned flags,
329  L4::Cap<L4::Task> dma_space) const
330  {
331  flags |= L4VBUS_DMAD_KERNEL_DMA_SPACE;
332  flags &= ~L4VBUS_DMAD_L4RE_DMA_SPACE;
333  return l4vbus_assign_dma_domain(cap(), domain_id, flags, dma_space.cap());
334  }
335 
336  typedef L4::Typeid::Raw_ipc<Vbus> Rpcs;
337 };
338 
339 } // namespace L4vbus