Doc: migration 5
Migrate pages: * Delayed ACK and TCP Performance * TCP Network Performance * NxWM Threading * Framebuffer Character Driver From Confluence wiki to official wiki
This commit is contained in:
parent
6dd313939f
commit
dbeaec4cdc
12 changed files with 788 additions and 0 deletions
195
Documentation/components/net/delay_act_and_tcp_perf.rst
Normal file
195
Documentation/components/net/delay_act_and_tcp_perf.rst
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
===============================
|
||||
Delayed ACK and TCP Performance
|
||||
===============================
|
||||
|
||||
.. warning::
|
||||
Migrated from:
|
||||
https://cwiki.apache.org/confluence/display/NUTTX/Delayed+ACK+and+TCP+Performance
|
||||
|
||||
uIP and NuttX
|
||||
=============
|
||||
|
||||
The heart of the NuttX IP stack derived from Adam Dunkel's tiny `uIP
|
||||
stack <http://sourceforge.net/projects/uip-stack/>`_ back at version 1.0.
|
||||
The NuttX TCP/IP stack contains the uIP TCP state machine and
|
||||
some uIP "ways of doing things," but otherwise, there is
|
||||
now little in common between these two designs.
|
||||
|
||||
**NOTE**: uIP is also built into Adam Dunkel's
|
||||
`Contiki <http://contiki.sourceforge.net/docs/2.6/a01793.html>`_
|
||||
operating system.
|
||||
|
||||
uIP, Delayed ACKs, and Split Packets
|
||||
====================================
|
||||
|
||||
In uIP, TCP packets are sent and ACK'ed one at a time.
|
||||
That is, after one TCP packet is sent, the next packet cannot
|
||||
be sent until the previous packet has been ACKed by the
|
||||
receiving side.
|
||||
The TCP protocol, of course, supports sending multiple packets
|
||||
which can be ACKed be the receiving time asynchronously.
|
||||
This one-packet-at-a-time logic is a simplification in the
|
||||
uIP design; because of this, uIP needs only a single packet
|
||||
buffer any you can use uIP in even the tiniest environments.
|
||||
This is a good thing for the objectives of uIP.
|
||||
|
||||
Improvements in packet buffering is the essential improvement
|
||||
that you get if upgrade from Adam Dunkel's uIP to his
|
||||
`lwIP <http://savannah.nongnu.org/projects/lwip/>`_ stack.
|
||||
The price that you pay is in memory usage.
|
||||
|
||||
This one-at-a-time packet transfer does create a performance
|
||||
problem for uIP:
|
||||
RFC 1122 states that a host may delay ACKing a packet for up
|
||||
to 500ms but must respond with an ACK to every second segment.
|
||||
In the baseline uIP, the effectively adds a one half second
|
||||
delay between the transfer of every packet to a recipient
|
||||
that employs this delayed ACK policy!
|
||||
|
||||
uIP has an option to work around this:
|
||||
It has logic that can be enable to split each packet into half,
|
||||
sending half as much data in each packet.
|
||||
Sending more, smaller packets does not sound like a performance
|
||||
improvement.
|
||||
This tricks the recipient that follows RFC 1122 into receiving
|
||||
the two, smaller back-to-back packets and ACKing the second
|
||||
immediately.
|
||||
References: `uip-split.c <http://contiki.sourceforge.net/docs/2.6/a00427_source.html>`_
|
||||
and `uip-split.h <http://contiki.sourceforge.net/docs/2.6/a00428.html>`_.
|
||||
|
||||
The NuttX TCP/IP Stack and Delay ACKs
|
||||
=====================================
|
||||
|
||||
The NuttX, low-level TCP/IP stack does not have the
|
||||
limitations of the uIP TCP/IP stack.
|
||||
It can send numerous TCP/IP packets regardless of
|
||||
whether they have been ACKed or not.
|
||||
That is because in NuttX, the accounting for which
|
||||
packets have been ACKed and which have not has been
|
||||
moved to a higher level in the architecture.
|
||||
|
||||
NuttX includes a standard, BSD socket interface on top
|
||||
of the low-level TCP/IP stack.
|
||||
It is in this higer-level, socket layer where the ACK
|
||||
accounting is done, specifically in the function
|
||||
`send() <http://pubs.opengroup.org/onlinepubs/009695399/functions/send.html>`_.
|
||||
If you send a large, multi-packet buffer via ``send()``,
|
||||
it will be broken up into individual packets and each
|
||||
packet will be sent as quickly as possible, with no
|
||||
concern for whether the previous packet has been ACKed or not.
|
||||
|
||||
However, the NuttX ``send()`` function will not return to
|
||||
the caller until the final packet has been ACKed.
|
||||
It does this to assure that the callers data was sent
|
||||
successfully (or not).
|
||||
This behavior means that if an odd number of packets
|
||||
were sent, there could still be a delay after the final
|
||||
packet before ``send()`` receives the ACK and returns.
|
||||
|
||||
So the NuttX approach is similar to the uIP way of doing
|
||||
things, but does add one more buffer, the user provided
|
||||
buffer to ``send()``, that can be used to improve TCP/IP
|
||||
performance (of course, this user provided buffer is
|
||||
also required by in order to be compliant with ``send()``""
|
||||
`specification <http://pubs.opengroup.org/onlinepubs/009695399/functions/send.html>`_.
|
||||
|
||||
The NuttX Split Packet Configuration
|
||||
====================================
|
||||
|
||||
But what happens if all of the user buffer is smaller than
|
||||
the MSS of one TCP packet?
|
||||
Suppose the MTU is 1500 and the user I/O buffer is only
|
||||
512 bytes?
|
||||
In this case, ``send()`` performance degenerates to the same
|
||||
behavior as uIP:
|
||||
An ACK is required for each packet before ``send()`` can
|
||||
return and before ``send()`` can be called again to send
|
||||
the next packet.
|
||||
|
||||
And the fix? A fix has recently been contributed by
|
||||
Yan T that works in a similar way to uIP split
|
||||
packet logic:
|
||||
In ``send()``, the logic normally tries to send a full packet
|
||||
of data each time it has the opportunity to do so.
|
||||
However, if the configuration option ``CONFIG_NET_TCP_SPLIT=y``
|
||||
is defined, the behavior of ``send()`` will change in the
|
||||
following way:
|
||||
|
||||
* ``send()`` will keep track of `even` and `odd` packets; `even`
|
||||
packets being those that we do not expect to be ACKed
|
||||
and `odd` packets being the those that we do expect to
|
||||
be ACKed.
|
||||
* ``send()`` will then reduce the size of even packets as
|
||||
necessary to assure that an even number of packets is
|
||||
always sent. Every call to send will result in an even
|
||||
number of packets being sent.
|
||||
|
||||
This clever solution tricks the RFC 1122 recipient in the
|
||||
same way that uIP split logic does.
|
||||
So if you are working with hosts the following the RFC 1122
|
||||
ACKing behavior and you have MSS sizes that are larger that
|
||||
the average size of the user buffers, then your throughput
|
||||
can probably be greatly improved by enabling ``CONFIG_NET_TCP_SPLIT=y``
|
||||
|
||||
NOTE: NuttX is `not` an RFC 1122 recipient; NuttX will ACK
|
||||
every TCP/IP packet that it receives.
|
||||
|
||||
Write Buffering
|
||||
===============
|
||||
|
||||
The best technical solution to the delayed ACK problem
|
||||
would be to support `write buffering`.
|
||||
Write buffering is enabled with ``CONFIG_NET_TCP_WRITE_BUFFERS``.
|
||||
If this option is selected, the NuttX networking layer will
|
||||
pre-allocate several write buffers at system initialization
|
||||
time. The sending a buffer of data then works like this:
|
||||
|
||||
* ``send()`` (1) obtains a pre-allocated write buffer from a free
|
||||
list, and then (2) simply copies the buffer of data that the
|
||||
user wishes to send into the allocated write buffer. If no
|
||||
write buffer is available, ``send()`` would have to block waiting
|
||||
for free write buffer space.
|
||||
* ``send()`` then (3) adds the write buffer to a queue of outgoing
|
||||
data for a TCP socket. Each open TCP socket has to support
|
||||
such a queue. ``send()`` could then (4) return success to the
|
||||
caller (even thought the transfer could still fail later).
|
||||
* Logic outside of the ``send()`` implementation manages the actual
|
||||
transfer of data from the write buffer. When the Ethernet
|
||||
driver is able to send a packet on the TCP connection, this
|
||||
external logic (5) copies a packet of data from the write
|
||||
buffer so that the Ethernet driver can perform the
|
||||
transmission (a `zero-copy` implementation would be preferable).
|
||||
Note that the data has to remain in the write buffer for now;
|
||||
it may need to be re-transmitted.
|
||||
* This external logic would also manage the receipt TCP ACKs.
|
||||
When TCP peer acknowledges the receipt of data, the
|
||||
acknowledged portion of the data can the (6) finally
|
||||
be deleted from the write buffer.
|
||||
|
||||
The following options configure TCP write buffering:
|
||||
|
||||
* ``CONFIG_NET_TCP_WRITE_BUFSIZE``: The size of one TCP write buffer.
|
||||
* ``CONFIG_NET_NTCP_WRITE_BUFFERS``: The number of TCP
|
||||
write buffers (may be zero to disable TCP/IP write buffering)
|
||||
|
||||
NuttX also supports TCP read-ahead buffering. This option
|
||||
is enabled with ``CONFIG_NET_TCP_READAHEAD``. TCP read-ahead
|
||||
buffer is necessary on TCP connections; otherwise data
|
||||
received while there is no ``recv()`` in place would be lost.
|
||||
For consistency, it would be best if such a TCP write
|
||||
buffer implementation worked in a manner similar to the
|
||||
existing TCP read-ahead buffering.
|
||||
|
||||
The following lists the NuttX configuration options
|
||||
available to configure the TCP read-ahead buffering feature:
|
||||
|
||||
* ``CONFIG_NET_TCP_READAHEAD_BUFSIZE``: The size of one TCP read-ahead buffer.
|
||||
* ``CONFIG_NET_NTCP_READAHEAD_BUFFERS``: The number of TCP
|
||||
read-ahead buffers (may be zero to disable TCP/IP read-ahead buffering)
|
||||
|
||||
A future enhancement is to combine the TCP write buffer
|
||||
management logic and the TCP read-ahead buffer management
|
||||
so that one common pool of buffers can be used for both
|
||||
functions (this would probably also require additional logic
|
||||
to `throttle` read-buffering so that received messages do not
|
||||
consume all of the buffers).
|
||||
|
|
@ -16,6 +16,8 @@ Network Support
|
|||
netlink.rst
|
||||
slip.rst
|
||||
wqueuedeadlocks.rst
|
||||
tcp_network_perf.rst
|
||||
delay_act_and_tcp_perf.rst
|
||||
|
||||
``net`` Directory Structure ::
|
||||
|
||||
|
|
|
|||
193
Documentation/components/net/tcp_network_perf.rst
Normal file
193
Documentation/components/net/tcp_network_perf.rst
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
=======================
|
||||
TCP Network Performance
|
||||
=======================
|
||||
|
||||
.. warning::
|
||||
Migrated from:
|
||||
https://cwiki.apache.org/confluence/display/NUTTX/TCP+Network+Performance
|
||||
|
||||
|
||||
(Abstracted and extended from a discussion from the NuttX Google group)
|
||||
|
||||
Question
|
||||
========
|
||||
|
||||
For some unknown reason, I am seeing poor TCP network performance.
|
||||
|
||||
Answer
|
||||
======
|
||||
|
||||
First let's talk about TCP send performance.
|
||||
|
||||
Source of Performance Bottlenecks
|
||||
---------------------------------
|
||||
|
||||
General TCP send performance is not determined by the TCP stack as much
|
||||
as it is by the network device driver. Bad network performance is due
|
||||
to time lost `BETWEEN` packet transfers. The packet transfers themselves
|
||||
go at the wire speed*. So if you want to improve performance on a
|
||||
given network, you have to reduce time lost between transfers.
|
||||
There is no other way.
|
||||
|
||||
Ignoring Ethernet issues like collisions, back-off delays,
|
||||
inter-packet gaps (IPG), etc.
|
||||
|
||||
The time between packets is limited primarily by the buffering
|
||||
design of the network driver. If you want to improve performance,
|
||||
then you must improve the buffering at the network driver.
|
||||
You need to support many full size (1500 byte) packet buffers.
|
||||
You must be able to query the network for new data to transfer,
|
||||
and queue those transfers in packet buffers. In order to reach
|
||||
peak performance, the network driver must have the next transfer
|
||||
buffered and ready-to-go before the previous transfer is finished
|
||||
to minimize the GAP between packet transfers.
|
||||
|
||||
Different network devices also support more or less efficient
|
||||
interfaces: The worst performing support interfaces that can
|
||||
handle only one packet at a time, the best performing are able
|
||||
to retain linked lists of packet buffers in memory and perform
|
||||
scatter-gather DMA for a sequence of packets.
|
||||
|
||||
In the NuttX TCP stack, you can also improve performance by
|
||||
enabling TCP write buffering. But the driver is the real key.
|
||||
|
||||
It would be good to have a real in-depth analysis of the
|
||||
network stack performance to identify bottlenecks and
|
||||
generate ideas for performance improvement. No one has
|
||||
ever done that. If I were aware of any stack related
|
||||
performance issue, I would certainly address it.
|
||||
|
||||
RFC 1122
|
||||
--------
|
||||
|
||||
There is one important feature missing the NuttX TCP that
|
||||
can help when there is no write buffering: Without write
|
||||
buffering send() will not return until the transfer has
|
||||
been ACKed by the recipient. But under RFC 1122, the host
|
||||
need not ACK each packet immediately; the host may wait
|
||||
for 500 MS before ACKing. This combination can cause very
|
||||
slow performance when small, non-buffered transfers are
|
||||
made to an RFC 1122 client. However, the RFC 1122 must
|
||||
ACK at least every second (odd) packet so sequences of
|
||||
packets with write buffering enabled do not suffer from
|
||||
this problem.
|
||||
|
||||
`Update: RFC 1122 support was added to the NuttX TCP
|
||||
stack with commit 66ef6d143a627738ad7f3ce1c065f9b1f3f303b0
|
||||
in December of 2019. That, however, that affects only
|
||||
received packet ACK behavior and has no impact on transmitted
|
||||
packet performance; write buffering is still recommended.`
|
||||
|
||||
TCPBlaster
|
||||
----------
|
||||
|
||||
I created a new test application at ``apps/examples/tcpblaster`` to
|
||||
measure TCP performance and collected some data for the
|
||||
configuration that happens to be on my desk. The `tcpblaster`
|
||||
test gives you the read and write transfer rates in ``Kb/sec``
|
||||
(I won't mention the numbers because I don't believe they
|
||||
would translate any other setup and, hence, would be
|
||||
misleading).
|
||||
|
||||
There is a nifty `TCP Throughput Tool <https://www.switch.ch/network/tools/tcp_throughput/>`_
|
||||
that gives some theoretical upper limits on performance.
|
||||
The tool needs to know the ``MSS`` (which is the Ethernet
|
||||
packet size that you configured minus the size of the
|
||||
Ethernet header, 14), the round-trip time (``RTT``)in
|
||||
milliseconds (which you can
|
||||
get from the Linux host ping), and a loss constant (which
|
||||
I left at the default). With these values, I can determine
|
||||
that the throughput for the NuttX TCP stack is approximately
|
||||
at the theoretical limits. You should not be able to do
|
||||
better any better than that (actually, it performs above
|
||||
the theoretical limit, but I suppose that is why it is
|
||||
"theoretical").
|
||||
|
||||
So, If you are unhappy with your network performance, the I
|
||||
suggest you run the `tcpblaster` test, use that data
|
||||
(along with the ``RTT`` from ping) with the
|
||||
`TCP Throughput Tool <https://www.switch.ch/network/tools/tcp_throughput/>`_.
|
||||
If you are still unhappy with the performance, don't go
|
||||
immediately pointing fingers at the stack (which everyone does).
|
||||
Instead, you should focus on optimizing your network
|
||||
configuration settings and reviewing the buffer handling
|
||||
of the Ethernet driver in you MCU.
|
||||
|
||||
If you do discover any significant performance issues
|
||||
with the stack I will of course gladly help you resolve
|
||||
them. Or if you have ideas for improved performance,
|
||||
I would also be happy to hear those.
|
||||
|
||||
What about Receive Performance?
|
||||
-------------------------------
|
||||
|
||||
All of the above discussion concerns `transmit performance`,
|
||||
i.e., "How fast can we send data over the network?" The other
|
||||
side is receive performance. Receive performance is very
|
||||
different thing. In this case it is the remote peer who is
|
||||
in complete control of the rate at which packets appear on
|
||||
the network and, hence, responsible for all of the raw bit
|
||||
transfer rates.
|
||||
|
||||
However, we might also redefine performance as the number of
|
||||
bytes that were `successfully` transferred. In order for the
|
||||
bytes to be successfully transferred they must be successfully
|
||||
received and processed on the NuttX target. If we fail in
|
||||
this if the packet is `lost` or `dropped`. A packet is lost if
|
||||
the network driver is not prepared to receive the packet when
|
||||
it was sent. A packet is dropped by the network if it is
|
||||
received but could not be processed either because there
|
||||
is some logical issue with the packet (not the case here)
|
||||
or if we have no space to buffer the newly received packet.
|
||||
|
||||
If a TCP packet is lost or dropped, then the penalty is big:
|
||||
The packet will not be ACKed, the remote peer may send a
|
||||
few more out-of-sequence packets which will also be dropped.
|
||||
Eventually, the remote peer will time out and retransmit
|
||||
the data from the point of the lost packet.
|
||||
|
||||
There is logic in the TCP protocol to help manage these data
|
||||
overruns. The TCP header includes a TCP `receive window` which
|
||||
tells the remote peer how much data the receiver is able to
|
||||
buffer. This value is sent in the ACK to each received
|
||||
packet. If well tuned, this receive window could possibly
|
||||
prevent packets from being lost due to the lack of
|
||||
read-ahead storage. This is a little better. The remote
|
||||
peer will hold off sending data instead of timing out and
|
||||
re-transmitting. But this is still a loss of performance;
|
||||
the gap between the transfer of packets caused by the hold-off
|
||||
will result in a reduced transfer rate.
|
||||
|
||||
So the issues for good reception are buffering and processing
|
||||
time. Buffering again applies to handling within the driver
|
||||
but unlike the transmit performance, this is not typically
|
||||
the bottleneck. And there is also a NuttX configuration
|
||||
option that controls `read-ahead` buffering of TCP packets.
|
||||
The buffering in the driver must be optimized to avoid lost
|
||||
packets; the ` buffering can be tuned to minimize
|
||||
the number packets dropped because we have no space to buffer them.
|
||||
|
||||
But the key to receive perform is management of processing
|
||||
delays. Small processing delays can occur in the network
|
||||
driver or in the TCP stack. But the major source of
|
||||
processing delay is the application which is the ultimate
|
||||
consumer of the incoming data. Imagine, for example,
|
||||
and FTP application that is receiving a file over a
|
||||
TCP and writing the file into FLASH memory. The primary
|
||||
bottleneck here will be the write to FLASH memory which
|
||||
is out of the control of software.
|
||||
|
||||
We obtain optimal receive performance when the processing
|
||||
delays keep up with the rate of the incoming packets.
|
||||
If the processing data rate is even slightly slower
|
||||
then the receive data rate, then there will be a
|
||||
growing `backlog` of buffered, incoming data to be
|
||||
processed. If this backlog continues to grow then
|
||||
eventually our ability to buffer data will be exhausted,
|
||||
packets will be held off or dropped, and performance
|
||||
will deteriorate. In an environment where a high-end,
|
||||
remote peer is interacting with the low-end, embedded
|
||||
system, that remote peer can easily overrun the
|
||||
embedded system due to the embedded system's limited
|
||||
buffering space, its much lower processing capability,
|
||||
and its slower storage peripherals.
|
||||
BIN
Documentation/components/nxgraphics/GraphicsInterfaces.png
Normal file
BIN
Documentation/components/nxgraphics/GraphicsInterfaces.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
383
Documentation/components/nxgraphics/framebuffer_char_driver.rst
Normal file
383
Documentation/components/nxgraphics/framebuffer_char_driver.rst
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
============================
|
||||
Framebuffer Character Driver
|
||||
============================
|
||||
|
||||
.. warning::
|
||||
Migrated from:
|
||||
https://cwiki.apache.org/confluence/display/NUTTX/Framebuffer+Character+Driver
|
||||
|
||||
|
||||
NX Graphics
|
||||
===========
|
||||
|
||||
NuttX has supported higher level graphics for some time with
|
||||
the OS's :doc:`/components/nxgraphics/index` and application
|
||||
oriented :doc:`NxWidgets </applications/graphics/nxwidgets/index>` and the tiny
|
||||
window manager :doc:`NxWM </applications/graphics/nxwm/index>`.
|
||||
These are higher level in the sense that the primary
|
||||
graphical function is to support windowing and control
|
||||
of tools and toolbars within windows. These graphics
|
||||
tools often do not meet the needs
|
||||
of developers with very low end graphics and minimal display
|
||||
requirements.
|
||||
|
||||
Figure 1
|
||||
========
|
||||
|
||||
The framebuffer character driver, along with the option LCD
|
||||
framebuffer interface, is an optional lighter-weight graphics interface.
|
||||
|
||||
.. image:: GraphicsInterfaces.png
|
||||
|
||||
Framebuffer Character Driver details
|
||||
====================================
|
||||
|
||||
A `framebuffer character driver` has been recently been added
|
||||
to bypass the complexity of `NX` and to provide a direct
|
||||
application interface to the framebuffer graphic device.
|
||||
The framebuffer buffer character devices, as with all
|
||||
character devices, provides the interface to the graphics
|
||||
device via stand POSIX VFS commands (``open()``, ``close()``,
|
||||
``read()``, ``write()``, ``seek()``, ...), through IOCTL commands,
|
||||
and for this driver via the ``mmap()`` function. These
|
||||
interfaces are described below,
|
||||
|
||||
The framebuffer character driver is located in the NuttX
|
||||
source tree at ``drivers/video/fb.c``. It is enabled in the
|
||||
build with ``CONFIG_VIDEO_FB=y``. In order to register the
|
||||
framebuffer driver, you will need to include logic in the
|
||||
your board-specific start-up function that calls
|
||||
``fb_register()`` That code sequence might look something
|
||||
like:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include <nuttx/video/fb.h>
|
||||
|
||||
#ifdef CONFIG_VIDEO_FB
|
||||
/* Initialize and register the simulated framebuffer driver */
|
||||
|
||||
ret = fb_register(0, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "ERROR: fb_register() failed: %d\n", ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
The ``fb_register()`` function takes two parameters:
|
||||
|
||||
* `display`. The display number for the case of boards
|
||||
supporting multiple displays or for hardware that supports
|
||||
multiple layers (each layer is consider a display). Typically zero.
|
||||
* `plane`. Identifies the color plane on hardware that supports
|
||||
separate framebuffer "planes" for each color component.
|
||||
Should be zero because no planar hardware is currently
|
||||
supported by NuttX.
|
||||
|
||||
``fb_register()`` will register the framebuffer character device
|
||||
at ``/dev/fb`` `N` where `N` is the display number if the devices
|
||||
supports only a single plane. If the hardware supports
|
||||
multiple color planes, then the device will be registered
|
||||
at ``/dev/fb`` `N-M` where `N` is the again display number but `M`
|
||||
is the display plane.
|
||||
|
||||
There is a simple example at ``apps/examples/fb`` that provides
|
||||
an illustration of most of the following interfacing methods.
|
||||
|
||||
POSIX Interfaces
|
||||
================
|
||||
|
||||
The interaction with the framebuffer character driver via POSIX
|
||||
VFS interface calls is the same as for other character drivers.
|
||||
The only aspect that might require some additional discussion
|
||||
is the use of ``read()``, ``write()``, and ``seek()``.
|
||||
|
||||
* ``read()`` returns data from the framebuffer memory and
|
||||
updates the file position based on the number of bytes read.
|
||||
* ``write()`` puts data into the framebuffer memory and
|
||||
also updates the file position.
|
||||
|
||||
That file position is initially set to the position
|
||||
zero meaning the beginning of the framebuffer. It is
|
||||
advanced each time you ``read()`` from or ``write()`` to the
|
||||
framebuffer. Is also updated by ``seek()``:
|
||||
|
||||
* ``seek()`` sets the file position to any desired
|
||||
location within the framebuffer.
|
||||
|
||||
The file position is in units of `bytes`. This can be
|
||||
confusing because other positional data may be in units
|
||||
`pixels`. Pixels have different `depth` in different displays,
|
||||
that is, different graphic hardware may support pixels with
|
||||
differing bits-per-pixel. The pixel depth can be obtained
|
||||
using one of the IOCTL commands listed below. Since the file
|
||||
position is in bytes, the bits-per-pixel must be taken account
|
||||
when using ``read()``, ``write()``, and ``seek()``. The usual conversion
|
||||
from pixels to bytes is:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
start_byte = (start_pixel * bits_per_pixel) >> 3;
|
||||
end_byte = (end_pixel * bits_per_pixel + 7) >> 3;
|
||||
|
||||
While the framebuffer may be accessed with these POSIX interfaces,
|
||||
a more typical way of interacting with the framebuffer from an
|
||||
application would involve use of ``mmap()`` as described below.
|
||||
|
||||
IOCTL Commands
|
||||
==============
|
||||
|
||||
* ``FBIOGET_VIDEOINFO``. Get color plane info. Its argument is
|
||||
pointer a writable instance of ``struct fb_videoinfo_s``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct fb_videoinfo_s
|
||||
{
|
||||
uint8_t fmt; /* see FB_FMT_* */
|
||||
fb_coord_t xres; /* Horizontal resolution in pixel columns */
|
||||
fb_coord_t yres; /* Vertical resolution in pixel rows */
|
||||
uint8_t nplanes; /* Number of color planes supported */
|
||||
};
|
||||
|
||||
* ``FBIOGET_PLANEINFO``. Get video plane info. It received
|
||||
a pointer to a writable instance of ``struct fb_planeinfo_s`` as its argument:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct fb_planeinfo_s
|
||||
{
|
||||
FAR void *fbmem; /* Start of frame buffer memory */
|
||||
uint32_t fblen; /* Length of frame buffer memory in bytes */
|
||||
fb_coord_t stride; /* Length of a line in bytes */
|
||||
uint8_t display; /* Display number */
|
||||
uint8_t bpp; /* Bits per pixel */
|
||||
};
|
||||
|
||||
* ``FBIOGET_CMAP`` and ``FBIOPUT_CMAP``. Get/Put RGB color mapping.
|
||||
These commands are available only if the hardware and
|
||||
framebuffer driver support color mapping (``CONFIG_FB_CMAP=y``).
|
||||
They each take a pointer to an instance of ``struct fb_cmap_s``
|
||||
as an argument (writeable for ``FBIOGET_CMAP`` and read-only
|
||||
for ``FBIOPUT_CMAP``).
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifdef CONFIG_FB_CMAP
|
||||
struct fb_cmap_s
|
||||
{
|
||||
uint16_t first; /* Offset offset first color entry in tables */
|
||||
uint16_t len; /* Number of color entries in tables */
|
||||
|
||||
/* Tables of color component. Any may be NULL if not used */
|
||||
|
||||
uint8_t *red; /* Table of 8-bit red values */
|
||||
uint8_t *green; /* Table of 8-bit green values */
|
||||
uint8_t *blue; /* Table of 8-bit blue values */
|
||||
#ifdef CONFIG_FB_TRANSPARENCY
|
||||
uint8_t *transp; /* Table of 8-bit transparency */
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
* ``FBIOGET_CURSOR``. Get cursor attributes. This command is
|
||||
available only if the hardware and framebuffer driver
|
||||
support cursors (``CONFIG_FB_HWCURSOR=y``). It take a pointer
|
||||
to a writable instance of ``struct fb_cursorattrib_s``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifdef CONFIG_FB_HWCURSOR
|
||||
#ifdef CONFIG_FB_HWCURSORIMAGE
|
||||
struct fb_cursorimage_s
|
||||
{
|
||||
fb_coord_t width; /* Width of the cursor image in pixels */
|
||||
fb_coord_t height /* Height of the cursor image in pixels */
|
||||
const uint8_t *image; /* Pointer to image data */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct fb_cursorpos_s
|
||||
{
|
||||
fb_coord_t x; /* X position in pixels */
|
||||
fb_coord_t y; /* Y position in rows */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FB_HWCURSORSIZE
|
||||
struct fb_cursorsize_s
|
||||
{
|
||||
fb_coord_t h; /* Height in rows */
|
||||
fb_coord_t w; /* Width in pixels */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct fb_cursorattrib_s
|
||||
{
|
||||
#ifdef CONFIG_FB_HWCURSORIMAGE
|
||||
uint8_t fmt; /* Video format of cursor */
|
||||
#endif
|
||||
struct fb_cursorpos_s pos; /* Current cursor position */
|
||||
#ifdef CONFIG_FB_HWCURSORSIZE
|
||||
struct fb_cursorsize_s mxsize; /* Maximum cursor size */
|
||||
struct fb_cursorsize_s size; /* Current size */
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
* ``FBIOPUT_CURSOR``. Set cursor attributes. This command is
|
||||
available only if the hardware and framebuffer driver
|
||||
support cursors (``CONFIG_FB_HWCURSOR=y``). It take a
|
||||
pointer to a writable instance of ``struct fb_setcursor_s``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifdef CONFIG_FB_HWCURSOR
|
||||
struct fb_setcursor_s
|
||||
{
|
||||
uint8_t flags; /* See FB_CUR_* definitions */
|
||||
struct fb_cursorpos_s pos; /* Cursor position */
|
||||
#ifdef CONFIG_FB_HWCURSORSIZE
|
||||
struct fb_cursorsize_s size; /* Cursor size */
|
||||
#endif
|
||||
#ifdef CONFIG_FB_HWCURSORIMAGE
|
||||
struct fb_cursorimage_s img; /* Cursor image */
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
* ``FBIO_UPDATE``. This IOCTL command updates a rectangular region
|
||||
in the framebuffer. Some hardware requires that there be
|
||||
such a notification when a change is made to the
|
||||
framebuffer (see, for example, the discussion of LCD drivers
|
||||
below). This IOTCL command is if ``CONFIG_NX_UPDATE=y`` is
|
||||
defined. It takes a pointer to a read-only instance of
|
||||
``struct nxgl_rect_s`` that describes the region to be updated:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct nxgl_rect_s
|
||||
{
|
||||
struct nxgl_point_s pt1; /* Upper, left-hand corner */
|
||||
struct nxgl_point_s pt2; /* Lower, right-hand corner */
|
||||
};
|
||||
|
||||
``mmap()``
|
||||
==========
|
||||
|
||||
Above we talked about using ``read()``, ``write()``, and ``seek()`` to
|
||||
access the framebuffer. The simplest way to access the
|
||||
framebuffer, however, is by using the ``mmap()`` to map
|
||||
the framebuffer memory into the application memory
|
||||
space. The following ``mmap()`` command, for example, can
|
||||
be used to obtain a pointer to a read-able, write-able
|
||||
copy of the framebuffer:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
FAR void *fbmem;
|
||||
|
||||
fbmem = mmap(NULL, fblen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE, fd, 0);
|
||||
if (state.fbmem == MAP_FAILED)
|
||||
{
|
||||
/* Handle failure */
|
||||
...
|
||||
}
|
||||
|
||||
printf("Mapped FB: %p\n", fbmem);
|
||||
|
||||
Where fd is the file descriptor of the opened framebuffer
|
||||
character driver and ``fblen`` was obtained via an IOCTL
|
||||
command as described above. NOTE that the framebuffer
|
||||
buffer pointer is also available within the values
|
||||
returned by the IOCTL commands. The address is a
|
||||
kernel memory address and may not be valid in all
|
||||
build configurations. Hence, ``mmap()`` is the preferred,
|
||||
portable way to get the framebuffer address.
|
||||
|
||||
Framebuffer vs. LCD Graphics Drivers
|
||||
====================================
|
||||
|
||||
Framebuffer graphics drivers are very common in high-end CPUs
|
||||
but most low-end, embedded hardware will not support a
|
||||
framebuffer.
|
||||
|
||||
A framebuffer graphics driver supports a region of memory
|
||||
that is shared both by the software and by the graphics
|
||||
hardware. Any modification to the framebuffer memory
|
||||
results in a corresponding modification on the display
|
||||
with no intervening software interaction. Some video
|
||||
memory is dual ported to support concurrent video processor
|
||||
and application processor accesses; or perhaps the LCD
|
||||
peripheral just constantly DMAs the framebuffer memory
|
||||
to the graphics hardware.
|
||||
|
||||
Most low-end embedded MCUs have a much simpler hardware
|
||||
interface: The interface to the LCD may be through a simple
|
||||
parallel interface or, more commonly, through a slower serial
|
||||
interface such as SPI. In order to support such low-end
|
||||
hardware with the framebuffer character driver, a special
|
||||
software layer called the `Framebuffer LCD Front End` has
|
||||
been developed. This is the topic of the next paragraph.
|
||||
|
||||
LCD Framebuffer Front-End
|
||||
=========================
|
||||
|
||||
The `LCD Framebuffer Front-End` provides a standard NuttX
|
||||
framebuffer interface, but works on top of a standard
|
||||
parallel or serial LCD driver. It provides the framebuffer,
|
||||
the framebuffer interface, and the hooks to adapt the LCD
|
||||
driver. The LCD framebuffer front-end can be found in the
|
||||
NuttX source tree at ``drivers/lcd/lcd_framebuffer.c``.
|
||||
|
||||
In order to provide updates to the LCD hardware after
|
||||
updates to the framebuffer, the LCD framebuffer front-end
|
||||
must be notified when significant changes to the framebuffer
|
||||
have been made. This notification is supported when
|
||||
``CONFIG_NX_UPDATE=y`` is defined in the configuration. In
|
||||
this case, the LCD framebuffer front-end will support
|
||||
the special. OS-internal interface function ``nx_notify_rectangle()``
|
||||
which defines the rectangular region in the framebuffer that
|
||||
has been changed. In response to a call to ``nx_notify_rectangle()``
|
||||
will use the lower-level LCD interface to update only that
|
||||
rectangular region on the display.
|
||||
|
||||
This kind of update for standard LCD drivers is very efficient:
|
||||
It is usually more efficient to update a region on the
|
||||
display than it is for form a complex image with text and
|
||||
line drawing; the updated region seems to update very
|
||||
quickly because of that. In fact, many of the low-end
|
||||
LCD drivers already include an internal framebuffer to
|
||||
support this style of LCD update.
|
||||
|
||||
When used with LCD character driver, the ``nx_notify_rectangle()``
|
||||
function will be called by the character river in response
|
||||
to the ``FBIO_UPDATE IOCTL`` command.
|
||||
|
||||
Another advantage of the framebuffer, both the LCD internal
|
||||
framebuffer and the framebuffer character driver, is
|
||||
that super-efficient reading of the LCD display memory:
|
||||
The LCD display memory is not read at all! The read is
|
||||
from the copy in the framebuffer.
|
||||
|
||||
Of course, using both an LCD internal framebuffer with the
|
||||
framebuffer character drivers is wasteful; one framebuffer
|
||||
is enough!
|
||||
|
||||
As a caution, it is important to remember that a framebuffer
|
||||
can be quite large. For example, a 480x320 display with
|
||||
16-bit RGB pixels would require an allocated framebuffer
|
||||
of size 300 KiB. This is inappropriate with most small
|
||||
MCUs (unless they support external memory). For tiny displays,
|
||||
such as 128x64 1-bit monochromatic displays, the framebuffer
|
||||
memory usage is not bad: 1 KiB in that example.
|
||||
|
||||
Framebuffer Graphics Library
|
||||
============================
|
||||
|
||||
Now the missing part is some kind of application-space
|
||||
framebuffer graphics library. The NuttX framebuffer
|
||||
driver is superficially similar to the Linux framebuffer
|
||||
driver so there is a lot of support for Linux
|
||||
framebuffer graphics support that should be easily ported to
|
||||
NuttX – Perhaps DirectFB would be an GPL option? SDL with its
|
||||
MIT license might be a more compatible source for such a port.
|
||||
|
|
@ -171,6 +171,8 @@ NX Header Files
|
|||
nxtk.rst
|
||||
nxfonts.rst
|
||||
nxcursor.rst
|
||||
nxwm_threading.rst
|
||||
framebuffer_char_driver.rst
|
||||
sample.rst
|
||||
appendix.rst
|
||||
|
||||
|
|
|
|||
13
Documentation/components/nxgraphics/nxwm_threading.rst
Normal file
13
Documentation/components/nxgraphics/nxwm_threading.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
==============
|
||||
NxWM Threading
|
||||
==============
|
||||
|
||||
.. warning::
|
||||
Migrated from:
|
||||
https://cwiki.apache.org/confluence/display/NUTTX/NxWM+Threading
|
||||
|
||||
.. image:: nxwm_threading_model/nxwm_theading_model_page_0.png
|
||||
.. image:: nxwm_threading_model/nxwm_theading_model_page_1.png
|
||||
.. image:: nxwm_threading_model/nxwm_theading_model_page_2.png
|
||||
.. image:: nxwm_threading_model/nxwm_theading_model_page_3.png
|
||||
.. image:: nxwm_threading_model/nxwm_theading_model_page_4.png
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 308 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 154 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 246 KiB |
Loading…
Add table
Reference in a new issue