mirror of https://github.com/ARMmbed/mbed-os.git
Add EMAC driver README.md with porting guide
parent
fbd920777b
commit
953f1b615a
|
@ -0,0 +1,185 @@
|
|||
# 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.
|
Loading…
Reference in New Issue