|
| 1 | +.. _mac_address_config: |
| 2 | + |
| 3 | +MAC Address Configuration |
| 4 | +************************* |
| 5 | + |
| 6 | +Ethernet drivers can delegate most MAC address handling during initialization to |
| 7 | +:c:struct:`net_eth_mac_config` and :c:func:`net_eth_mac_load`. The structure |
| 8 | +is typically stored in the driver configuration and initialized with |
| 9 | +:c:macro:`NET_ETH_MAC_DT_CONFIG_INIT` or :c:macro:`NET_ETH_MAC_DT_INST_CONFIG_INIT`, |
| 10 | +which translate the devicetree properties into one of the following behaviors: |
| 11 | + |
| 12 | +* :c:enumerator:`NET_ETH_MAC_STATIC` – use the full ``local-mac-address`` property. |
| 13 | +* :c:enumerator:`NET_ETH_MAC_RANDOM` – generate a random locally administered MAC address, |
| 14 | + optionally using the bytes provided in ``zephyr,mac-address-prefix`` as the first octets. |
| 15 | +* :c:enumerator:`NET_ETH_MAC_NVMEM` – read the remaining bytes from the ``"mac-address"`` |
| 16 | + :ref:`NVMEM<nvmem>` cell, again optionally prefixed by ``zephyr,mac-address-prefix``. |
| 17 | +* :c:enumerator:`NET_ETH_MAC_DEFAULT` – fall back to the driver's default logic (for |
| 18 | + example, a factory-programmed MAC address stored in peripheral registers). |
| 19 | + |
| 20 | +Driver integration |
| 21 | +================== |
| 22 | + |
| 23 | +Embed the :c:struct:`net_eth_mac_config` structure inside the driver's configuration |
| 24 | +and a static buffer inside the driver's data: |
| 25 | + |
| 26 | +.. code-block:: c |
| 27 | +
|
| 28 | + struct my_eth_config { |
| 29 | + struct net_eth_mac_config mac_cfg; |
| 30 | + /* more config fields */ |
| 31 | + }; |
| 32 | +
|
| 33 | + struct my_eth_data { |
| 34 | + uint8_t mac_addr[NET_ETH_ADDR_LEN]; |
| 35 | + /* more data fields */ |
| 36 | + }; |
| 37 | +
|
| 38 | + static const struct my_eth_config my_eth_config_0 = { |
| 39 | + .mac_cfg = NET_ETH_MAC_DT_INST_CONFIG_INIT(0), |
| 40 | + }; |
| 41 | + static struct my_eth_data my_eth_data_0; |
| 42 | +
|
| 43 | +During initialization, call :c:func:`net_eth_mac_load` before registering |
| 44 | +the address with the network interface. The helper copies any statically provided |
| 45 | +bytes, fills the remaining octets, and performs the necessary validation. |
| 46 | +Drivers can still fall back to SoC-specific storage when no configuration was provided: |
| 47 | + |
| 48 | +.. code-block:: c |
| 49 | +
|
| 50 | + static int my_eth_init(const struct device *dev) |
| 51 | + { |
| 52 | + const struct my_eth_config *cfg = dev->config; |
| 53 | + struct my_eth_data *data = dev->data; |
| 54 | + int ret; |
| 55 | +
|
| 56 | + ret = net_eth_mac_load(&cfg->mac_cfg, data->mac_addr); |
| 57 | + if (ret == -ENODATA) { |
| 58 | + ret = my_eth_hw_read_mac(dev, data->mac_addr); |
| 59 | + } |
| 60 | +
|
| 61 | + return ret; |
| 62 | + } |
| 63 | +
|
| 64 | + static void my_eth_iface_init(struct net_if *iface) |
| 65 | + { |
| 66 | + const struct device *dev = net_if_get_device(iface); |
| 67 | + struct my_eth_data *data = dev->data; |
| 68 | +
|
| 69 | + net_if_set_link_addr(iface, data->mac_addr, sizeof(data->mac_addr), NET_LINK_ETHERNET); |
| 70 | + } |
| 71 | +
|
| 72 | +Devicetree examples |
| 73 | +=================== |
| 74 | + |
| 75 | +The examples below show how to pick a MAC address configuration for an ethernet controller node |
| 76 | +such as ``ð0``. |
| 77 | + |
| 78 | +Static MAC address |
| 79 | +------------------ |
| 80 | + |
| 81 | +.. code-block:: devicetree |
| 82 | +
|
| 83 | + ð0 { |
| 84 | + local-mac-address = [00 11 22 33 44 55]; |
| 85 | + }; |
| 86 | +
|
| 87 | +Random MAC address with prefix |
| 88 | +------------------------------ |
| 89 | + |
| 90 | +.. code-block:: devicetree |
| 91 | +
|
| 92 | + ð0 { |
| 93 | + zephyr,mac-address-prefix = [00 04 25]; |
| 94 | + zephyr,random-mac-address; |
| 95 | + }; |
| 96 | +
|
| 97 | +NVMEM-provided MAC address with prefix |
| 98 | +-------------------------------------- |
| 99 | + |
| 100 | +.. code-block:: devicetree |
| 101 | +
|
| 102 | + ð0 { |
| 103 | + zephyr,mac-address-prefix = [00 12 34]; |
| 104 | + nvmem-cells = <&macaddr_cell>; |
| 105 | + nvmem-cell-names = "mac-address"; |
| 106 | + }; |
| 107 | +
|
| 108 | + &eeprom0 { |
| 109 | + nvmem-layout { |
| 110 | + compatible = "fixed-layout"; |
| 111 | + #address-cells = <1>; |
| 112 | + #size-cells = <1>; |
| 113 | +
|
| 114 | + macaddr_cell: cell@0 { |
| 115 | + reg = <0x0 0x6>; |
| 116 | + #nvmem-cell-cells = <0>; |
| 117 | + }; |
| 118 | + }; |
| 119 | + }; |
| 120 | +
|
| 121 | +When no MAC-related properties are present, :c:func:`net_eth_mac_load` returns ``-ENODATA`` and |
| 122 | +the driver is expected to use its existing mechanism (for example, reading the hardware registers or |
| 123 | +using a build-time constant). |
| 124 | + |
| 125 | +Changing the MAC address at runtime |
| 126 | +=================================== |
| 127 | + |
| 128 | +Applications that need to set MAC addresses dynamically (for example to adopt an address obtained |
| 129 | +from a management interface) need to enable the networking management API with |
| 130 | +:kconfig:option:`CONFIG_NET_MGMT` and use :c:macro:`net_mgmt` with |
| 131 | +:c:macro:`NET_REQUEST_ETHERNET_SET_MAC_ADDRESS`. |
| 132 | +The request internally calls the driver's :c:func:`ethernet_api.set_config` implementation. |
| 133 | + |
| 134 | +.. code-block:: c |
| 135 | +
|
| 136 | + static int app_set_mac_address(const struct device *dev) |
| 137 | + { |
| 138 | + struct net_if *iface = net_if_lookup_by_dev(dev); |
| 139 | + struct ethernet_req_params params = { |
| 140 | + .mac_address = { { 0x02, 0x00, 0x5E, 0x01, 0x02, 0x03 } }, |
| 141 | + }; |
| 142 | +
|
| 143 | + /* Make sure the iface is down */ |
| 144 | +
|
| 145 | + return net_mgmt(NET_REQUEST_ETHERNET_SET_MAC_ADDRESS, iface, |
| 146 | + ¶ms, sizeof(params)); |
| 147 | + } |
0 commit comments