Hello,
I've been trying to write some drivers in L4Re, starting out with a GPIO driver for a currently-unsupported board, and I think I have mostly understood the concepts involved. Fortunately, there are already a couple of drivers to borrow ideas from...
pkg/io/io/server/src/drivers/gpio/bcm2835.cc pkg/io/io/server/src/drivers/gpio/omap.cc
And if I am not mistaken, such drivers can be accessed from other components by just doing something like this (testing for validity as we go):
vbus = l4re_env_get_cap("vbus"); l4vbus_get_device_by_hid(vbus, 0, &gpio_handle, gpio_hid, 0, 0); l4vbus_gpio_set(vbus, gpio_handle, gpio_reg, value);
It appears that if I just declare the device appropriately in my board's hw_devices.io file, I should be able to get a handle for it using code like the above. And all access can apparently be done via the l4vbus_gpio functions with this handle.
(I was also wondering if it was possible to actually instantiate or obtain a device directly instead of obtaining a handle, for code written in C++, but the above is easily good enough at the moment.)
But now I want to make another, non-GPIO, driver abstraction, instead of directly accessing the mapped memory regions concerned (which I see that some of the drivers do). It seems like I should be able to...
* define a class for the abstraction inheriting from Hw::Device * declare the device in hw_devices.io using this class * provide various properties in the hw_devices.io declaration (for configuration purposes) * read the properties in the actual class implementation * access registers in memory just as the GPIO drivers do
I think I can muddle through all this. But how should another component access methods provided by this driver? Do I have to implement functionality like that provided by the following files...?
pkg/io/libvbus/lib/src/vbus_gpio.cc pkg/io/libvbus/include/vbus_gpio-ops.h pkg/io/io/server/src/virt/gpio/vgpio.cc
Does any of this make any sense? :-)
I'm just getting started here, but thanks are due in advance for any advice anyone may have!
Paul
Hi Paul,
On 07/04/2017 11:11 PM, Paul Boddie wrote:
Hello,
I've been trying to write some drivers in L4Re, starting out with a GPIO driver for a currently-unsupported board, and I think I have mostly understood the concepts involved. Fortunately, there are already a couple of drivers to borrow ideas from...
pkg/io/io/server/src/drivers/gpio/bcm2835.cc pkg/io/io/server/src/drivers/gpio/omap.cc
And if I am not mistaken, such drivers can be accessed from other components by just doing something like this (testing for validity as we go):
vbus = l4re_env_get_cap("vbus"); l4vbus_get_device_by_hid(vbus, 0, &gpio_handle, gpio_hid, 0, 0); l4vbus_gpio_set(vbus, gpio_handle, gpio_reg, value);
It appears that if I just declare the device appropriately in my board's hw_devices.io file, I should be able to get a handle for it using code like the above. And all access can apparently be done via the l4vbus_gpio functions with this handle.
That's not the whole story. Devices declared in e.g. hw_devices.io describe physical (real) devices and their resources. The device handle that you obtain via the Vbus API points to a virtual device and only virtual devices can be assigned to a client's virtual bus. A virtual device can wrap a physical one but e.g. in the case of GPIOs it can be composed of individual (or ranges) of physical GPIO resources (= pins).
(I was also wondering if it was possible to actually instantiate or obtain a device directly instead of obtaining a handle, for code written in C++, but the above is easily good enough at the moment.)
But now I want to make another, non-GPIO, driver abstraction, instead of directly accessing the mapped memory regions concerned (which I see that some of the drivers do). It seems like I should be able to...
The first question that should be answered is, whether the driver (abstraction) has to be implemented inside io. Maybe a standalone driver with an appropriate API is better suited for your use case, because the Vbus API has its limitations and most likely you need to extend it anyways. Also from a security standpoint it is maybe worthwile to implement the driver in its own component.
Currently L4Re is lacking a proper device driver framework and we are still learning and exploring different directions. Maybe you would like to discuss your use case further so that we can guide you through that process?
Regards, Matthias.
- define a class for the abstraction inheriting from Hw::Device
- declare the device in hw_devices.io using this class
- provide various properties in the hw_devices.io declaration (for configuration purposes)
- read the properties in the actual class implementation
- access registers in memory just as the GPIO drivers do
I think I can muddle through all this. But how should another component access methods provided by this driver? Do I have to implement functionality like that provided by the following files...?
pkg/io/libvbus/lib/src/vbus_gpio.cc pkg/io/libvbus/include/vbus_gpio-ops.h pkg/io/io/server/src/virt/gpio/vgpio.cc
Does any of this make any sense? :-)
I'm just getting started here, but thanks are due in advance for any advice anyone may have!
Paul
On Wednesday 5. July 2017 22.53.35 Matthias Lange wrote:
On 07/04/2017 11:11 PM, Paul Boddie wrote:
It appears that if I just declare the device appropriately in my board's hw_devices.io file, I should be able to get a handle for it using code like the above. And all access can apparently be done via the l4vbus_gpio functions with this handle.
That's not the whole story. Devices declared in e.g. hw_devices.io describe physical (real) devices and their resources. The device handle that you obtain via the Vbus API points to a virtual device and only virtual devices can be assigned to a client's virtual bus. A virtual device can wrap a physical one but e.g. in the case of GPIOs it can be composed of individual (or ranges) of physical GPIO resources (= pins).
My understanding of this is that the hw_devices.io file, although I used the term "declare", is actually instantiating device objects using a Lua interface to the underlying C++ code. I hope that is correct.
Then, with regard to the virtual bus, my understanding of that is that it exposes functionality via the IPC mechanism. So, all invocations have to be marshalled via that mechanism to the Io server.
So, I define my GPIO devices (one per "port", similar to the OMAP functionality) in the hw_devices.io file, plus other devices (CPM, LCD) like this:
Io.hw_add_devices(function()
CPM = Hw.Cpm_jz4740_chip(function() Property.hid = "jz4740-cpm"; Property.exclk_freq = 12000000; -- 12 MHz compatible = {"mips,jz4740-cpm"}; Resource.regs = Res.mmio(0x10000000, 0x10000fff); end);
GPIO = Hw.Device(function() Property.hid = "JZ4740 GPIO";
PORTA = Hw.Gpio_jz4740_chip(function() Property.hid = "jz4740-gpio-PORTA"; Property.pins = 32; compatible = {"mips,jz4740-gpio"}; Resource.regs = Res.mmio(0x10010000, 0x100100ff); Resource.irq = Res.irq(28, Io.Resource.Irq_type_level_high); end);
... -- PORTB, PORTC, PORTD
end);
LCD = Hw.Device(function() Property.hid = "jz4740-lcd"; compatible = {"mips,jz4740-lcd"}; Resource.regs = Res.mmio(0x13050000, 0x1305ffff); end);
end)
I guess that this now means that the Io server will expose each of these devices, but that the APIs to access them will vary: the GPIO devices will be accessible via the l4vbus_gpio API, but the other devices will only support a more limited API, maybe access to resource details and perhaps "raw" communication, but nothing specific to the device type.
At the moment, I'm not even sure whether it makes sense to expose the LCD device, but I assume that this initialisation does some work to map the region defined for the "regs" resource (or to offer the capability of mapping this region), and this is necessary for the LCD driver to be able to obtain this region and access its contents.
(I was also wondering if it was possible to actually instantiate or obtain a device directly instead of obtaining a handle, for code written in C++, but the above is easily good enough at the moment.)
But now I want to make another, non-GPIO, driver abstraction, instead of directly accessing the mapped memory regions concerned (which I see that some of the drivers do). It seems like I should be able to...
The first question that should be answered is, whether the driver (abstraction) has to be implemented inside io.
I don't really know! The "device tree" aspect of it might be convenient, and it arguably lets me specify all the various device parameters in one place. I had thought that Io perhaps made devices available as separate servers, but I get the impression that this isn't the case.
Maybe a standalone driver with an appropriate API is better suited for your use case, because the Vbus API has its limitations and most likely you need to extend it anyways. Also from a security standpoint it is maybe worthwile to implement the driver in its own component.
I'm still not really very clear about how I would achieve this. Thinking about the LCD driver, which is merely following the form of the other LCD drivers in the L4Re distribution, I would only be using this other driver to access various peripheral registers.
I suppose that the simplest approach would be to define a generic device (as done above but using Hw.Device instead of Hw.Cpm_jz4740_chip), and then just use the memory mapping support to obtain the register memory region. Then I would just access the memory directly in the LCD driver. This is, after all, what I do to access the LCD registers in the LCD driver, and something has to access the memory directly.
But to make a better abstraction would either involve another Io-based device or, as you suggest, making a standalone driver in some form. Having seen the GPIO drivers, I was led along that route, but maybe it isn't the right one.
(I also get the impression that the LCD drivers are actually just combined with fb-drv and used as libraries, but maybe I misunderstood something in that regard.)
Currently L4Re is lacking a proper device driver framework and we are still learning and exploring different directions. Maybe you would like to discuss your use case further so that we can guide you through that process?
Well, briefly, all I'm trying to do at the moment is to add LCD panel support for a MIPS-based board, and this also seems to need GPIO and "CPM" (clock and power management) peripheral support. I've adapted code that was originally written as a Linux driver, deploying it in a standalone payload on this board, and the code does work in a minimal environment. So, I aim to work from that limited success by trying to get it to work in the L4 runtime environment.
Initially, I saw the following general advice about writing drivers:
http://os.inf.tu-dresden.de/pipermail/l4-hackers/2015/007475.html (define devices in hw_devices.io, query the vbus, map the memory)
http://os.inf.tu-dresden.de/pipermail/l4-hackers/2015/007489.html (mentions the client configuration and mapping the memory)
I previously also got some general advice myself:
http://os.inf.tu-dresden.de/pipermail/l4-hackers/2016/007755.html (mentions the vbus and mapping mechanisms)
So, I got the impression that the "device tree" approach was a sound one. I couldn't really find much other guidance about writing drivers, at least not particularly conveniently. If you know of some good resources to help settle my conceptual model, I would appreciate it.
Sorry if this is a bit confused, but I do feel that I have made some progress this time round, at least!
Paul
On Thursday 6. July 2017 01.05.58 Paul Boddie wrote:
On Wednesday 5. July 2017 22.53.35 Matthias Lange wrote:
That's not the whole story. Devices declared in e.g. hw_devices.io describe physical (real) devices and their resources. The device handle that you obtain via the Vbus API points to a virtual device and only virtual devices can be assigned to a client's virtual bus. A virtual device can wrap a physical one but e.g. in the case of GPIOs it can be composed of individual (or ranges) of physical GPIO resources (= pins).
I've been trying to use this API and it seems rather confusing, both when trying to initialise the virtual devices, and when actually trying to access them.
First of all, it appears that any device based on Hw::Gpio_device will cause any corresponding virtual device to have its "hid" set to "GPIO". This makes the general examples on virtual bus configuration misleading because one might think that doing a match on the original hid will yield a device reference, but the l4vbus_get_device_by_hid function actually needs "GPIO" to produce the virtual device.
Obviously, this renaming or re-aliasing is also not helpful where there are many separate GPIO devices. I noticed that the OMAP hardware device definitions provide multiple GPIO devices under a common container, and this seemed like a reasonable way of partitioning the different "PORT" collections in the hardware I'm describing. Here is the related example from before:
[...]
GPIO = Hw.Device(function() Property.hid = "JZ4740 GPIO";
PORTA = Hw.Gpio_jz4740_chip(function() Property.hid = "jz4740-gpio-PORTA"; Property.pins = 32; compatible = {"mips,jz4740-gpio"}; Resource.regs = Res.mmio(0x10010000, 0x100100ff); Resource.irq = Res.irq(28, Io.Resource.Irq_type_level_high); end); ... -- PORTB, PORTC, PORTD
end);
But having all such devices appear as "GPIO" in the virtual bus is rather inconvenient. If I knew what the intentions were for the API design in this regard, I might have a better idea of how it is supposed to be used, but there are no relevant, specific examples that I can find.
To get this working, then, I made a hid method in the virtual GPIO device that returns the actual device hid value. Then, the application is able to look up the device using what would be the expected hid value, not "GPIO".
Then there is the matter of resources. It seems, following the rather complicated framework code, that the virtual devices can be told how many pins they have by consulting the actual device. However, the resources associated with the actual device are not propagated to the virtual device. This is seen in the debugging output:
IO | gpio: [N12_GLOBAL__N_112Virtual_sbusE] IO | Resources: ==== start ==== IO | Resources: ===== end ===== IO | L4ICU: [N2Vi6Sw_icuE] IO | Resources: ==== start ==== IO | Resources: ===== end ===== IO | jz4780-gpio-PORTF.PORTF IO | Resources: ==== start ==== IO | Resources: ===== end ===== IO | jz4780-gpio-PORTD.PORTD IO | Resources: ==== start ==== IO | Resources: ===== end =====
Here, the latter two devices do have resources defined in the actual hardware description. Consequently, if I understand it correctly, the matter of "enabling" the pins is not carried out because there are no resources associated with these virtual devices.
(I guess this extra layer of functionality around enabling pins is to provide some kind of additional benefit, but I don't see anything that tells me what that benefit is, nor anything that indicates why the actual devices can't just handle pin validity since they have to do that anyway.)
I have tried adding code to initialise the resources from each actual device when initialising the corresponding virtual device. But I get the impression that what I should really be doing is to use some kind of factory that produces the "right" kind of resource objects that know about the particular "resource space" that can enable the pins. It isn't obvious to me what I should be doing to achieve this. I would have thought just mentioning the things in the Lua script would do it, but I guess not.
Maybe the method of describing these devices in a virtual bus is different from the other ways of describing devices, but I haven't seen a single example that might give me some hints. Indeed, the mechanisms employed don't seem to be documented, either, which means that one is left to chase down the details in the source code. That ceases to be particularly rewarding after a few hours.
Is there any guidance out there about this particular framework? Does anyone even use it?
Paul
l4-hackers@os.inf.tu-dresden.de