Documentation: Add docs for lower-half of netdriver
Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
parent
d19486e1f8
commit
9ab0d8cce6
@ -10,6 +10,7 @@ Network Support
|
||||
pkt.rst
|
||||
nat.rst
|
||||
netdev.rst
|
||||
netdriver.rst
|
||||
netguardsize.rst
|
||||
slip.rst
|
||||
wqueuedeadlocks.rst
|
||||
|
@ -8,6 +8,13 @@ Network Devices
|
||||
interface and is passed to the network via
|
||||
``netdev_register()``.
|
||||
|
||||
- ``include/nuttx/net/netdev_lowerhalf.h``. (Recommended for new
|
||||
drivers, see :ref:`Network Drivers <netdriver>`)
|
||||
This header file defines the interface between the network device
|
||||
and the network stack. The network device is a lower-half driver
|
||||
that provides the network stack with the ability to send and receive
|
||||
packets.
|
||||
|
||||
IP Addresses
|
||||
============
|
||||
|
||||
|
237
Documentation/components/net/netdriver.rst
Normal file
237
Documentation/components/net/netdriver.rst
Normal file
@ -0,0 +1,237 @@
|
||||
.. _netdriver:
|
||||
|
||||
===============
|
||||
Network Drivers
|
||||
===============
|
||||
|
||||
The NuttX network driver is split into two parts:
|
||||
|
||||
#. An "upper half", generic driver that provides the common network
|
||||
interface to application level code, and
|
||||
#. A "lower half", platform-specific driver that implements the
|
||||
low-level timer controls to implement the network functionality.
|
||||
|
||||
Files supporting network driver can be found in the following locations:
|
||||
|
||||
- **Interface Definition**. The header file for the NuttX network
|
||||
driver resides at ``include/nuttx/net/netdev_lowerhalf.h``. This
|
||||
header file includes the interface between the "upper half" and
|
||||
"lower half" drivers.
|
||||
- **"Upper Half" Driver**. The generic, "upper half" network driver
|
||||
resides at ``drivers/net/netdev_upperhalf.c``.
|
||||
- **"Lower Half" Drivers**. Platform-specific network drivers reside
|
||||
in ``arch/<architecture>/src/<hardware>`` or ``drivers/net``
|
||||
directory for the specific processor ``<architecture>`` and for
|
||||
the specific ``<chip>`` network peripheral devices.
|
||||
|
||||
**Special Note**: Not all network drivers are implemented with this
|
||||
architecture. Known lower-half drivers:
|
||||
``arch/sim/src/sim/sim_netdriver.c``, ``drivers/virtio/virtio-net.c``
|
||||
|
||||
How to change full network driver into lower-half one
|
||||
=====================================================
|
||||
|
||||
We have many network drivers that are implemented as full network drivers
|
||||
with ``include/nuttx/net/netdev.h``, we can change them into lower-half
|
||||
drivers to remove the common code (which is already in upper-half driver).
|
||||
Here is a guide to do so:
|
||||
|
||||
1. Change ``struct net_driver_s`` to ``struct netdev_lowerhalf_s`` in
|
||||
the network driver structure. If you really need to touch some fields
|
||||
inside ``struct net_driver_s`` like MAC address, you can access them
|
||||
through ``struct netdev_lowerhalf_s::netdev``.
|
||||
2. Change the function names called in the network driver file to the names
|
||||
with prefix ``netdev_lower_``, e.g. ``netdev_lower_register`` and
|
||||
``netdev_lower_carrier_on``.
|
||||
3. Change the core functions called by work queue like ``txpoll`` as
|
||||
``transmit`` and ``receive`` in the ``netdev_ops_s`` structure. You may
|
||||
need to change ``memcpy`` for ``d_buf`` into ``netpkt_copyin`` and
|
||||
``netpkt_copyout``.
|
||||
|
||||
- Note that the ``receive`` function just need to return the received
|
||||
packet instead of calling functions like ``ipv4_input`` or doing reply.
|
||||
The upper-half will call ``receive`` to get all packets until it
|
||||
returns ``NULL`` and send these packets into the network stack.
|
||||
- Also remember to call ``netpkt_free`` for the transmitted packets.
|
||||
|
||||
4. Remove work queues related to send and receive, and replace them
|
||||
with calling ``netdev_lower_txdone`` and ``netdev_lower_rxready``.
|
||||
Then the upper-half driver will call ``transmit`` and ``receive`` to
|
||||
send/get packets.
|
||||
5. Remove any buffer related to ``d_buf``, and make sure ``d_buf`` is not
|
||||
used in the lower-half driver.
|
||||
6. Remove ``txavail`` function, the upper-half driver will call ``transmit``
|
||||
when it has packets to send.
|
||||
7. Remove the statistics macros like ``NETDEV_TXPACKETS``, ``NETDEV_TXDONE``,
|
||||
``NETDEV_RXPACKETS`` or ``NETDEV_RXDROPPED``, these macros are well
|
||||
handled in upper-half. But you may still keep macros like
|
||||
``NETDEV_TXTIMEOUTS`` and ``NETDEV_RXERRORS`` because the upper-half
|
||||
cannot know whether these error happens.
|
||||
8. Find a suitable ``quota`` for the driver, and set it in the driver
|
||||
initialization function. The quota is the maximum number of buffers
|
||||
that the driver can hold at the same time. For example, if the TX quota
|
||||
is set to 5, it means that if the driver has 5 unreleased packets
|
||||
(``netpkt_free``), the upper-half will not call ``transmit`` until they
|
||||
are released.
|
||||
|
||||
- Note: An exception is that if the net stack is replying for RX packet,
|
||||
this replied packet will always be put into ``transmit``, which may
|
||||
exceed the TX quota temporarily.
|
||||
|
||||
"Lower Half" Example
|
||||
====================
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct <chip>_priv_s
|
||||
{
|
||||
/* This holds the information visible to the NuttX network */
|
||||
|
||||
struct netdev_lowerhalf_s dev;
|
||||
|
||||
...
|
||||
};
|
||||
|
||||
static const struct netdev_ops_s g_ops =
|
||||
{
|
||||
.ifup = <chip>_ifup,
|
||||
.ifdown = <chip>_ifdown,
|
||||
.transmit = <chip>_transmit,
|
||||
.receive = <chip>_receive,
|
||||
.addmac = <chip>_addmac,
|
||||
.rmmac = <chip>_rmmac,
|
||||
.ioctl = <chip>_ioctl
|
||||
};
|
||||
|
||||
/* The Wi-Fi driver registration function can be implemented as follows,
|
||||
* where <chip> refers to the chip name. netdev_lower_register() is the
|
||||
* network device interface provided by upper-half drivers to register
|
||||
* network device drivers.
|
||||
*/
|
||||
|
||||
int <chip>_netdev_init(FAR struct <chip>_priv_s *priv)
|
||||
{
|
||||
FAR struct netdev_lowerhalf_s *dev = &priv->dev;
|
||||
|
||||
dev->ops = &g_ops;
|
||||
|
||||
/* The maximum number of buffers that the driver can hold
|
||||
* at the same time. For example, if the TX quota is set to 5, it
|
||||
* means that if the driver has 5 unreleased packets (netpkt_free),
|
||||
* the upper layer will not call transmit until they are released.
|
||||
* After the rx quota is used up and no new buffer can be allocated
|
||||
* (netpkt_alloc), it needs to notify the upper layer
|
||||
* (netdev_lower_rxready) and restore the quota by submitting buffer
|
||||
* back through receive function.
|
||||
* If the driver processes each packet individually (without
|
||||
* accumulating multiple packets before sending/receiving), it can be
|
||||
* set to 1.
|
||||
*/
|
||||
|
||||
dev->quota[NETPKT_TX] = 1;
|
||||
dev->quota[NETPKT_RX] = 1;
|
||||
|
||||
return netdev_lower_register(dev, NET_LL_ETHERNET);
|
||||
}
|
||||
|
||||
/* The transmit function can be implemented as follows, where <chip>
|
||||
* refers to the chip name.
|
||||
*/
|
||||
|
||||
static int <chip>_transmit(FAR struct netdev_lowerhalf_s *dev,
|
||||
FAR netpkt_t *pkt)
|
||||
{
|
||||
FAR struct <chip>_priv_s *priv = (FAR struct <chip>_priv_s *)dev;
|
||||
unsigned int len = netpkt_getdatalen(dev, pkt);
|
||||
|
||||
#if you want to do offloading
|
||||
if (!netpkt_is_fragmented(pkt))
|
||||
{
|
||||
/* Contiguous memory, just use data pointer */
|
||||
|
||||
FAR uint8_t *databuf = netpkt_getdata(dev, pkt);
|
||||
FAR uint8_t *devbuf = databuf - sizeof(struct <chip>_txhead_s);
|
||||
|
||||
/* Do Transmit. Note: `databuf` points to the L2 data, and there is
|
||||
* a reserved memory with size of `CONFIG_NET_LL_GUARDSIZE` before
|
||||
* databuf to be used for driver header, drivers can just fill data
|
||||
* there (`devbuf`) and start the transmission.
|
||||
*/
|
||||
|
||||
...
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Copyout the L2 data and transmit. */
|
||||
|
||||
uint8_t devbuf[1600];
|
||||
netpkt_copyout(dev, devbuf, pkt, len, 0);
|
||||
|
||||
/* Do Transmit */
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void <chip>_txdone_interrupt(FAR struct <chip>_priv_s *priv)
|
||||
{
|
||||
FAR struct netdev_lowerhalf_s *dev = &priv->dev;
|
||||
|
||||
/* Perform some processing in the driver (if necessary) */
|
||||
|
||||
...
|
||||
|
||||
/* Free the buffer and notify the upper layer */
|
||||
|
||||
netpkt_free(dev, pkt, NETPKT_TX);
|
||||
netdev_lower_txdone(dev);
|
||||
}
|
||||
|
||||
/* The receive function can be implemented as follows, where <chip>
|
||||
* refers to the chip name.
|
||||
*/
|
||||
|
||||
static void <chip>_rxready_interrupt(FAR struct <chip>_priv_s *priv)
|
||||
{
|
||||
FAR struct netdev_lowerhalf_s *dev = &priv->dev;
|
||||
netdev_lower_rxready(dev);
|
||||
}
|
||||
|
||||
static FAR netpkt_t *<chip>_receive(FAR struct netdev_lowerhalf_s *dev)
|
||||
{
|
||||
/* It is also possible to allocate the pkt and receive the data in
|
||||
* advance, and then call rxready and return pkt through receive
|
||||
*/
|
||||
|
||||
FAR netpkt_t *pkt = netpkt_alloc(dev, NETPKT_RX);
|
||||
|
||||
if (pkt)
|
||||
{
|
||||
#if NETPKT_BUFLEN > 15xx && you want to do offloading
|
||||
/* Write directly to the buffer inside pkt, len corresponds to the
|
||||
* length of L2 data (need the NETPKT_BUFLEN to be large enough to
|
||||
* hold the data). The `<chip>_rxhead_s` is the driver header before
|
||||
* the actual data (maybe you don't have).
|
||||
*/
|
||||
|
||||
len = receive_data_into(netpkt_getbase(pkt));
|
||||
netpkt_resetreserved(&priv->dev, pkt, sizeof(struct <chip>_rxhead_s));
|
||||
netpkt_setdatalen(&priv->dev, pkt, len);
|
||||
#else
|
||||
uint8_t devbuf[1600];
|
||||
|
||||
/* Copy from src, len corresponds to the length of L2 data, you can
|
||||
* always use this method to receive data. The `<chip>_rxhead_s` is
|
||||
* the driver header before the actual data (maybe you don't have).
|
||||
*/
|
||||
|
||||
len = receive_data_into(devbuf);
|
||||
netpkt_copyin(dev, pkt, devbuf + sizeof(struct <chip>_rxhead_s), len, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
return pkt;
|
||||
}
|
Loading…
Reference in New Issue
Block a user