mirror of https://github.com/ARMmbed/mbed-os.git
186 lines
7.7 KiB
Markdown
186 lines
7.7 KiB
Markdown
# mbed OS Ethernet MAC (EMAC) drivers
|
|
|
|
This document describes how to port and test an Ethernet MAC (EMAC) driver to
|
|
mbed OS. It is based on work on the feature-emac branch as of mbed OS 5.8,
|
|
which is intended to be merged into mbed OS 5.9
|
|
|
|
The scope of this document is limited to Ethernet (IEEE 802.3) or Ethernet-like
|
|
devices such as Wi-Fi (IEEE 802.11), where the device presents a MAC interface
|
|
to send and receive frames, and this will be used by one of the onboard
|
|
network stacks that runs on mbed OS on the host processor.
|
|
|
|
(If the device has an off-board network stack, a driver would need to implement
|
|
`NetworkStack` directly instead to pass network calls to that offboard
|
|
stack).
|
|
|
|
## Abstractions
|
|
|
|
The EMAC interface is designed to abstract network stacks and drivers, and to
|
|
easily permit multiple instances. The key API classes are:
|
|
|
|
* `NetworkInterface` - an mbed OS network interface of any type
|
|
* `NetworkStack` - an mbed OS network stack of any type (may be off-board)
|
|
* `OnboardNetworkStack` - an on-board network stack
|
|
* `EMAC` - an Ethernet MAC device driver
|
|
* `EMACMemoryManager` - a memory manager used to pass data between driver and stack
|
|
* `EMACInterface`- a `NetworkInterface` that uses an `EMAC` driver and an `OnboardNetworkStack`
|
|
|
|
## The EMAC driver core
|
|
|
|
The first step in the port is to create a driver class that can be instantiated
|
|
to control your device. This must be derived from class `EMAC`.
|
|
This API is used by a network stack (or test framework) to control your driver.
|
|
|
|
The EMAC-derived driver would normally be installed in
|
|
features/netsocket/emac-drivers, often in a `TARGET_XXX` directory.
|
|
|
|
Class EMAC is entirely abstract - you need to implement about a dozen calls
|
|
to activate the driver, send and receive packets, and perform other control
|
|
and information functions.
|
|
|
|
There are also callback registration functions for upcalls from the driver - the
|
|
stack can register callback functions for packet reception and link status
|
|
changes.
|
|
|
|
|
|
## The EMAC memory manager
|
|
|
|
For the send and receive paths, data is transferred in memory buffers controlled
|
|
via an `EMACMemoryManager` object. The network stack using an EMAC driver
|
|
provides it with a reference to the memory manager in use before powering up -
|
|
this will be constant as long as the EMAC is powered up.
|
|
|
|
On the output call, the EMAC driver is given ownership of a buffer chain - it
|
|
must free the chain when it has finished with the data. The data may or may
|
|
not be contiguous. A driver can express alignment preferences for outgoing data,
|
|
but the network stack is not required to meet these prefernces, so a driver
|
|
relying on alignment may need a slow path that copies data into an aligned
|
|
(or contiguous) buffer.
|
|
|
|
For reception, the EMAC driver must allocate memory from the `EMACMemoryManager`
|
|
to store the received packets - this is then passed to the link input callback,
|
|
which will free it. By preference this memory should be allocated using the pool,
|
|
but if contiguous memory is required it can be allocated from the heap.
|
|
|
|
|
|
## EthernetInterface
|
|
|
|
If your driver is a pure Ethernet driver, there is no further implementation
|
|
required. The class `EthernetInterface` can use any `EMAC` driver to provide
|
|
an mbed OS `NetworkInterface`:
|
|
|
|
MyEMAC my_emac(params);
|
|
EthernetInterface net(&my_emac);
|
|
|
|
net.connect();
|
|
|
|
This will attach the default network stack (normally lwIP - the other
|
|
current alternative is Nanostack) to the specified EMAC driver, and provide all
|
|
the generic `NetworkInterface` and `NetworkStack` APIs.
|
|
|
|
## Being the default EMAC / EthernetInterface
|
|
|
|
To make your EMAC the default for applications you should define the static function
|
|
`EMAC::get_default_instance()` to return an instance of your emac, eg:
|
|
|
|
MBED_WEAK EMAC &EMAC::get_default_instance()
|
|
{
|
|
static MyEMAC my_emac(params);
|
|
return &my_emac;
|
|
}
|
|
|
|
This permits this example application code to work:
|
|
|
|
EthernetInterface net; // uses EMAC::get_default_instance()
|
|
net.connect();
|
|
|
|
This definition would normally be gated by a target label of some sort. As
|
|
target code, your definition of EMAC::get_default_instance() must be weak -
|
|
this permits it to be overridden by application code.
|
|
|
|
## Wi-Fi interfaces
|
|
|
|
As a Wi-Fi interface, a little more work is required - at a minimum you need
|
|
to implement the extra configuration calls in `WiFiInterface`. This
|
|
is because the network stacks and EMAC APIs are only related to the
|
|
Ethernet-like data path - they have no knowledge of any other configuration
|
|
mechanisms and assume they are already set up.
|
|
|
|
To do this, you should create a C++ class that inherits from both
|
|
`WiFiInterface` and `EMACInterface`. The `EMACInterface` is a helper class
|
|
that implements all the core `NetworkInterface` functionality for you. You
|
|
then just need to implement the extra `WiFiInterface` configuration methods.
|
|
|
|
For reference, note that `EthernetInterface` also derives from
|
|
`EMACInterface`, but has no extra code as there is no extra configuration
|
|
required.
|
|
|
|
As a Wi-fi driver, you will not normally be directly exposing your `EMAC` class -
|
|
it would not normally be declared as `EMAC::get_default_instance`, but you
|
|
would pass it to the constructor of your base `EMACInterface`. This then
|
|
will make it visible via the `get_emac` method. This is for test purposes,
|
|
meaning the test framework can do:
|
|
|
|
MyWiFiInterface net;
|
|
net.set_credentials();
|
|
EMAC &emac = net.get_emac();
|
|
do_emac_test(emac);
|
|
|
|
This must work in your driver - it must be possible to power up and use the
|
|
built-in EMAC directly without the `NetworkInterface::connect()` method being
|
|
invoked, as long as the credentials have been set. This structure will come naturally if
|
|
you just use the default `EMACInterface::connect()` implementation.
|
|
|
|
Note also that your constructor must allow the network stack to be specified using
|
|
the same form as `EthernetInterface`:
|
|
|
|
MyWiFiInterface(OnboardNetworkStack &stack = OnboardNetworkStack::get_default_instance());
|
|
|
|
## OnboardNetworkStack
|
|
|
|
The precise details of the `OnboardNetworkStack` API should not concern
|
|
an EMAC driver writer - it provides the mechanism to bind a driver to a stack, and
|
|
the APIs needed to implement a `NetworkInterface`, but this is handled by
|
|
`EMACInterface`, either as a base class of your own `XXXInterface` or as
|
|
the base of `EthernetInterface`.
|
|
|
|
## DEVICE_EMAC
|
|
|
|
At present, as an interim measure, targets providing `EMAC::get_default_instance()`
|
|
should add "EMAC" in `device_has` in their `targets.json`. This activates
|
|
network tests in CI builds.
|
|
|
|
This is subject to change, but is necessary in lieu of the previous typical
|
|
behaviour of gating tests on `FEATURE_LWIP`.
|
|
|
|
## Tuning memory allocations
|
|
|
|
Depending on its use of pool and heap memory, and other factors, a driver might
|
|
want to tune the configuration of particular network stacks. This can be done via
|
|
the `mbed_lib.json` of each network stack, using their `target_overrides`
|
|
section.
|
|
|
|
## Testing
|
|
|
|
The mbed OS tree contains Greentea-based tests that exercise the EMAC API
|
|
directly, and more general socket tests.
|
|
|
|
See here for general Greentea information: <https://github.com/ARMmbed/greentea>
|
|
|
|
See here for the emac tests:
|
|
<https://github.com/ARMmbed/mbed-os/tree/feature-emac/TESTS/network/emac>
|
|
|
|
Greentea socket tests are at:
|
|
<https://github.com/ARMmbed/mbed-os/tree/feature-emac/TESTS/netsocket>
|
|
|
|
The driver should also be exercised with real-world examples like
|
|
<https://github.com/ARMmbed/mbed-os-example-client>
|
|
|
|
The driver should also be tested with both network stacks available in mbed OS,
|
|
as they will use the driver somewhat differently - try with the JSON option
|
|
`nsapi.default-stack` set to each of `LWIP` and `NANOSTACK`.
|
|
|
|
Nanostack is IPv6 only. IPv6 operation should also be tested with lwIP, as this
|
|
is likely to reveal problems with multicast filtering that may not be spotted
|
|
by IPv4 or Nanostack.
|