-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Documentation: port "Device Drivers vs. Bus Drivers and GPIO Drivers"…
… from wiki port page from wiki: https://cwiki.apache.org/confluence/display/NUTTX/Device+Drivers+vs.+Bus+Drivers+and+GPIO+Drivers
- Loading branch information
1 parent
5f91a9a
commit ccf73a8
Showing
3 changed files
with
213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
============== | ||
Device Drivers | ||
============== | ||
|
||
Standard Device Drivers | ||
======================= | ||
|
||
Device drivers should be implemented in the RTOS and used by applications. | ||
Drivers provide access to device functionality for applications. This is a | ||
necessary part of the modular RTOS design. In the NuttX directory structure, | ||
share-able device drivers reside under ``drivers/`` and custom drivers reside in | ||
the board-specific directories at ``nuttx/boards/<arch>/<chip>/<board>/src`` or | ||
``nuttx/boards/<arch>/<chip>/drivers`` that are built into the RTOS. | ||
|
||
Bus Drivers | ||
=========== | ||
|
||
There a many things that get called drivers in OS; NuttX makes a distinction | ||
between device drivers and bus drivers. For example, SPI, PCI, PCMCIA, USB, | ||
Ethernet, etc. are buses and not devices. You will never find a device driver | ||
for a bus in the NuttX architecture. | ||
|
||
In most devices architectures, devices reside on a bus. A bus is a transport | ||
layer that connects the device residing on the bus to a device driver. | ||
The bus is managed by a bus driver. The device driver uses the facilities of | ||
the bus driver transport layer to interact with the device. | ||
|
||
Consider SPI. SPI is a bus. It provides a serial bus to which many devices may | ||
be connected. An SPI device resides on the SPI bus in the sense that is shares | ||
the same MISO, MOSI, and clock lines with other devices on the SPI bus (but in | ||
SPI, it will have its own dedicated chip select discrete). | ||
|
||
Although we typically use the same term driver to refer to both bus drivers and | ||
device drivers, there is one big, fundamental difference: applications interact | ||
only with devices drivers and never with bus drivers. Applications never talk | ||
directly to PCI, PCMCIA, USB, Ethernet, nor with I2C, SPI, or GPIOs. Applications | ||
interface through device drivers that use PCI, PCMCIA, USB, Ethernet, I2C, or SPI. | ||
Bus drivers only exist to support the communication between the device driver and | ||
the device on the bus. | ||
|
||
Back to SPI... There will never be an application accessible interface to SPI. | ||
If your application were to use SPI directly, then you would have have embedded | ||
a device driver in your application and would have violated the RTOS functional | ||
partition. | ||
|
||
Test Drivers | ||
============ | ||
|
||
It would be possible to provide character driver, such as SPI driver, that could | ||
perform bus level accesses on behalf of an application. There are not many cases | ||
where this would be acceptable, however. One possibility would be to support | ||
support testing of bus drivers. | ||
There is, an example for I2S here: ``drivers/audio/i2schar.c`` with a test case | ||
here ``apps/examples/i2schar``. I2S is, of course, very similar to SPI. | ||
This interface exists only for testing purposes and would probably not be | ||
possible to build any meaningless application with it. | ||
|
||
The I2C Tool | ||
------------ | ||
|
||
Of course, like most rules, there are lots of violations. I2C is another bus and | ||
the the I2C "driver" is another transport similar in many ways to SPI. For I2C, | ||
there is an application at ``apps/system/i2c`` alled the "I2C tool" that will allow | ||
you access I2C devices from the command line. This is not really just a test tool | ||
and not a real part of an application. | ||
|
||
And there is a fundamental flaw in the I2C tool: it uses NuttX internal interfaces | ||
and violates the functional partitioning. NuttX has three build mode: (1) A flat | ||
build where there is no enforcement of RTOS boundaries. In that flat build, | ||
the I2C tool works fine. And (2) a kernel build mode and (3) a protected build mode. | ||
In bothof these latter cases, the OS interfaces are strictly enforced. In the kernel | ||
pand protected build modes, the I2C tool is not available because it cannot access | ||
those NuttX internal interfaces. | ||
|
||
User Space Drivers | ||
================== | ||
|
||
Above, it was stated that if your application were to use a bus directly, then you | ||
would have have embedded a device driver in your application and would violate | ||
the RTOS functional partition. Such device built into user applications are | ||
referred to as user space drivers in some contexts. There is no plan or intent | ||
to support user space drivers in NuttX. | ||
|
||
Communication Devices | ||
===================== | ||
|
||
What about interface like CAN and UARTs? Why are those exposed as drivers when | ||
SPI and I2C are not? | ||
|
||
Semantics are difficult. The general principles that are maintain in | ||
the RTOS are clear, but sometimes applying principles in a black and white way | ||
is not easy in a world with shades of grey. (And if the principles get in the | ||
way of good design then the principles should change). | ||
|
||
In the case of true buses that support generic devices, the principle | ||
is a good one. But there are grey areas too. | ||
|
||
CAN seems similar to Ethernet. Both are network interfaces of sorts. You | ||
wouldn't interface directly with Ethernet driver because you need to go | ||
through a network stack of some type. The OSI model prevents it. | ||
|
||
UARTs are communication devices. There is no RS-232 bus with devices connected | ||
to it. Rather there are peers on the bus that you communicate with. This does not | ||
preclude a UART from being used as a low level transfer for a device driver | ||
(as with the driver for a wireless modules). Nor does it preclude a stack layer | ||
like Modbus from being inserted in the path. | ||
|
||
CAN differs from Ethernet in that it really is a direct peer-to-peer | ||
communication, more like a UART. Although you can support a stack like CANOPen | ||
on CAN. Currently CAN can be used as a simple character device, or as a network | ||
interface using SocketCAN. | ||
|
||
Communication devices support a fundamental peer-to-peer model. CAN and UARTs | ||
are basically serial interfaces. But so are SPI, I2C, and USB. But those latter | ||
serial interfaces clearly have a host/device, master/slave model associated with | ||
them. It make perfectly good sense to think of them as buses that support device | ||
interfaces. | ||
|
||
I/O Expander | ||
============ | ||
|
||
An I/O expander is device that interfaces with the MCU, usually via I2C, and | ||
provides additional discrete inputs and outputs. The same rules apply: | ||
|
||
* **GPIOS are Board-Specific**. Nothing in the system should now about GPIOs | ||
except for board specific logic. GPIOs can change from board-toboard. They | ||
can come and go. They can be replaced by GPIO expanders. Your (portable) | ||
application should not have any knowledge about how any discrete I/O is | ||
implemented on the board. There will never be GPIO drivers as a part of | ||
the NuttX architecture. | ||
|
||
* **Common Drivers are Board-Independent**. Nor should common drivers | ||
(like those in ``drivers/``) know anything about GPIOs. In ALL cases, | ||
the board specific implementation in the board directories creates | ||
a "lower half" driver and binds that "lower half" driver with an common | ||
"upper half" driver to initialize the driver. Only the board logic has | ||
any kind of GPIO knowledge; not the application and not the common | ||
"upper half driver". | ||
|
||
* **I2C and SPI Drivers are Internal Bus Drivers**. Similarly I2C and SPI | ||
drivers are not accessible to applications. These are NOT device drivers | ||
but are bus drivers. They should not be accessed directly by applications. | ||
Rather, again, the board-specific logic generates a "lower half" driver | ||
that provides a common I2C or SPI interface and binds that with | ||
an "upper half" driver to initialize the driver. | ||
|
||
None of those rules change if you use an I/O expander, things just get | ||
more convoluted. | ||
|
||
Example Architecture | ||
-------------------- | ||
|
||
Consider this case for some ``<board>``: | ||
|
||
#. A discrete joystick is implemented as set of buttons: UP, DOWN, LEFT, RIGHT, | ||
and CENTER. The state of each the buttons is sensed as a GPIO input. | ||
|
||
#. The GPIO button inputs go to I2C I/O expander at say, | ||
``drivers/ioexpander/myexpander.c``, and finally to | ||
|
||
#. The discrete joystick driver "upper half" driver (``drivers/input/djoystick.c``). | ||
|
||
Implementation Details | ||
---------------------- | ||
|
||
These should be implemented in the following, flexible, portable, layered architecture: | ||
|
||
#. In the end, the application would interact only with a joystick driver | ||
interface via standard open/close/read/ioctl operations. It would receive | ||
pjoystick information as described in ``include/nuttx/input/djoystick.h.`` | ||
|
||
#. The discrete joystick driver would have been initialized by logic in some | ||
file like ``boards/<arch>/xyz/<board>/src/xyz_djoystick.c`` when the system | ||
was initialized. ``zyz_joystick.c`` would have created instance of | ||
the ``struct djoy_lowerhalf_s`` "lower half" interface as described in | ||
``nuttx/include/nuttx/input/djoystick.h`` and would have passed that | ||
interface instance to the ``drivers/input/djoystick.c`` "upper half" driver | ||
to initialize it. | ||
|
||
#. As part of the creation of the ``struct djoy_lowerhalf_s`` "lower half" | ||
interface instance, logic in ``xyz_djoystick.c`` would have done the following: | ||
It would have created an I2C driver instance by called MCU specific I2C initialization | ||
logic then passed this I2C driver instance to the I/O expander initialization interface | ||
in ``drivers/ioexpander/myexpander.c`` to create the I/O expander interface instance. | ||
|
||
Note that the I/O expander interface should NOT be a normal character driver. | ||
It should NOT be accessed via open/close/read/write/ioctl. Rather, it should return | ||
an instance of a some ``struct ioexpander_s`` interface. That I/O expander interface | ||
would be described in ``nuttx/include/ioexpander/ioexpander.h``. It is an internal | ||
operating system interface and would never be available to application logic. | ||
|
||
After receiving the I/O expander interface instance, the "lower half" discrete | ||
joystick interface would retain this internally as private data. Nothing in the | ||
system other than this "lower half" discrete joystick driver needs to know how | ||
the joystick is connected on board. | ||
|
||
#. After creating the "upper half" discrete joystick interface interface, | ||
the "lower half" discrete joystick interface would enable interrupts from | ||
the I/O expander device. | ||
|
||
#. When a key is pressed, the "lower half" discrete joystick driver would receive | ||
an interrupt from the I/O expander. It would then interact with the I/O driver | ||
to obtain the current discrete button depressions. The I/O expander driver would | ||
interact with I2C to obtain those button settings. Then the discrete joystick | ||
interface callback will be called, providing the discrete joystick "upper half" | ||
driver with the joystick input. | ||
|
||
#. The "upper half" discrete joystick character driver would then return the encoded | ||
joystick input to the application in response to a ``read()`` from application code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters