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