Restart repo
This commit is contained in:
203
esp32/qemu_ethernet.rst
Normal file
203
esp32/qemu_ethernet.rst
Normal file
@@ -0,0 +1,203 @@
|
||||
..
|
||||
Copyright (C) 2023 Jeremie Salvi.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Interface ethernet avec QEMU
|
||||
============================
|
||||
|
||||
Y a pas à dire, j'ai bien galéré pour cette partie 😅. Bon, faut dire q'il y avait des choses qui
|
||||
ne fonctionnent pas. J'y refient à la fin de cette section.
|
||||
|
||||
Après pas mal de tatonnements entre la doc et les exemples fournis par espressif, j'ai finalement
|
||||
réussi à interagir entre firefox et la machine QEMU. Il y a plusieures choses à bien comprendre
|
||||
dans la façon dont fonctionne l'IDF, notament comment sont gérées les
|
||||
`différentes couches du modèle OSI <https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI>`_.
|
||||
|
||||
Principes
|
||||
---------
|
||||
|
||||
dans l'ESP, il y à trois étapes principales pour le réseau :
|
||||
|
||||
#. La partie physique (couches physique + mac),
|
||||
#. La partie network (couches network + transport),
|
||||
#. La partie link entre physique et network.
|
||||
|
||||
La partie physique
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C'est la couche physique plus la couche mac. On la configure, on la démare, et ensuite on
|
||||
la link à la partie network. Côté wifi, comme le driver de la carte est toujours le même,
|
||||
on à la fonction ``esp_netif_create_default_wifi_sta()`` qui gère tout, elle créer tout.
|
||||
Elle gère la partie physique, la link à la partie network, et nous retourne un handle de la
|
||||
partie network.
|
||||
|
||||
Pour l'ethernet, c'est différent, comme plusieurs extensions ethernet sont disponible pour
|
||||
l'esp32, il y a plusieurs drivers disponibles, il nous faut donc choisir le bon, et configurer ça à la main.
|
||||
|
||||
Dans notre cas, pour QEMU, le driver à utiliser est le driver openeth. (`cf wiki qemu <https://github.com/espressif/qemu/wiki>`_)
|
||||
La configuration de la partie physique se fait à l'aide des fonctions suivantes :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Pysical layer configuration
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
phy_config.phy_addr = -1;
|
||||
phy_config.reset_gpio_num = -1;
|
||||
phy_config.autonego_timeout_ms = 4000;
|
||||
s_phy = esp_eth_phy_new_dp83848(&phy_config);
|
||||
// Data link layer configuration
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.rx_task_stack_size = 4096;
|
||||
mac_config.rx_task_prio = 20;
|
||||
s_mac = esp_eth_mac_new_openeth(&mac_config);
|
||||
// Install Ethernet driver
|
||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
|
||||
esp_eth_driver_install(&config, &esp_eth_handle);
|
||||
|
||||
La partie network
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Can cette partie, on gère la couche ip et la couche transport (TCP/UDP/ICMP). Espressif nous donne pour ça tout un tas
|
||||
de fonctions, qui sont un wrapper des librairies opensource `lwIP <https://fr.wikipedia.org/wiki/LwIP>`_ (lightweight IP).
|
||||
|
||||
Elle nous permet d'initialiser ces couches, de gérer l'attribution de l'adressage ip (statique ou DHCP.)
|
||||
|
||||
Cette couche s'implémente avec les fonctions suivantes :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Initialize TCP/IP layers
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_config_t esp_netif_config = ESP_NETIF_DEFAULT_ETH();
|
||||
esp_netif_t *eth_netif = esp_netif_new(&esp_netif_config);
|
||||
|
||||
// Set static ip
|
||||
// esp_netif_dhcpc_stop(eth_netif);
|
||||
// esp_netif_ip_info_t esp_netif_ip_info;
|
||||
// esp_netif_ip_info.ip.addr = ESP_IP4TOUINT32(11,2,168,192);
|
||||
// esp_netif_ip_info.netmask.addr = ESP_IP4TOUINT32(0,255,255,255);
|
||||
// esp_netif_ip_info.gw.addr = ESP_IP4TOUINT32(1,2,168,192);
|
||||
// esp_err_t ret2 = esp_netif_set_ip_info(eth_netif, &esp_netif_ip_info);
|
||||
// ESP_ERROR_CHECK(ret2);
|
||||
|
||||
Lien entre les deux premières couches
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On peut configurer les deux parties précédentes dans l'ordre de notre choix, mais c'est seulement une fois que
|
||||
ces deux étapes sont réalisées qu'on peut attacher la couche TCP/IP à la couche physique/MAC.
|
||||
|
||||
Assez simple à faire, il suffit d'utiliser le code suivant :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Bind TCP/IP to Phy+MAC
|
||||
esp_eth_netif_glue_handle = esp_eth_new_netif_glue(esp_eth_handle);
|
||||
esp_netif_attach(eth_netif, esp_eth_netif_glue_handle);
|
||||
|
||||
On peut ensuite démarrer ethernet avec la fonction suivante :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Start ethernet
|
||||
esp_eth_start(esp_eth_handle);
|
||||
|
||||
La partie applicative
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Ici, on aura l'implémentation de notre serveur web. Ça se fait à partir de la librairie ``esp_http_server``
|
||||
Rien de bien complexe ici quand on connait bien le fonctionnement de ce protocole.
|
||||
|
||||
Le code est le suivant :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
esp_err_t get_handler(httpd_req_t *req)
|
||||
{
|
||||
/* Send a simple response */
|
||||
const char resp[] = "URI GET Response\n";
|
||||
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
|
||||
ESP_LOGI(TAG, "good");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
httpd_uri_t uri_get = {
|
||||
.uri = "/uri",
|
||||
.method = HTTP_GET,
|
||||
.handler = get_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
Une fois ces trois étapes franchies, on peut démarrer QEMU avec la commande suivante :
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
/opt/qemu/build/qemu-system-xtensa -m 128 -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw \
|
||||
-net nic,model=open_eth \
|
||||
-net user,host=192.168.2.1,net=192.168.2.0/24,hostfwd=tcp::80-:80 \
|
||||
-net tap,script=no,downscript=no
|
||||
|
||||
On teste avec un simple ``curl http://127.0.0.1/uri`` et on voit que ça marche.
|
||||
|
||||
.. image:: ../images/esp32/QEMU_http_response.png
|
||||
:width: 600
|
||||
|
||||
Ce qui ne marche pas
|
||||
--------------------
|
||||
|
||||
L'IPv6
|
||||
~~~~~~
|
||||
|
||||
Ne marche pas ni en wifi avec SLAAC, en wifi avec une ip statique,
|
||||
ni avec QEMU. C'est ce qui m'a le plus fait perdre de temps.
|
||||
En m'acharnant, je suis tombé sur ça 😤 🤬:
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
esp_err_t esp_netif_add_ip6_address(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, uint8_t preference)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Il manque clairement du travail du côté d'espressif pour implémenter la couche IPv6. C'est embêtant, ça nous
|
||||
oblige à faire pleins de routes dans notre box, alors qu'on pourrait tout simplement pinger notre domotique de
|
||||
l'extérieur avec du v6
|
||||
|
||||
.. note:: Il n'y a que la librairie loobback.c qui manque d'implémentation, dans
|
||||
components/lwip/lwip/src/core/netif.c on trouve
|
||||
``netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx)``
|
||||
qui est bel et bien implémentée. J'ai perdu du temps dessus, ça sera un NAT v4 pour l'instant.
|
||||
Si à l'avenir j'arrive à faire marcher l'IPv6, je le documenterai.
|
||||
|
||||
L'interface tun0 et le bridging
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
auand je lance ma commande QEMU, j'ai deux moyens d'accéder à mon ``http_server`` :
|
||||
Soit via le ``hostfwd=tcp::80-:80``, soit via ``-net tap,script=no,downscript=no``.
|
||||
|
||||
Je fait un ``ip link set tap0 up et brctl addif br0 tap0`` pour que mon tap0 soit
|
||||
pingable depuis le bridge en 192.168.2.15.
|
||||
|
||||
Et bien, un ``curl http://127.0.0.1/uri`` marche très bien, un ``curl http://192.168.2.15/uri``
|
||||
ne parche pas, on à une erreur ``W (476492) opencores.emac: emac_opencores_isr_handler: RX frame dropped (0x14)``
|
||||
J'ai essayé de mettre au max les buffers RX, testé toutes les configurations possible et imaginables,
|
||||
impossible de passer par le bridge sans avoir un frame_drop. Grace au mod débug, j'arrive ici :
|
||||
|
||||
``components/esp_eth_src/esp_eth_mac_openeth.c``
|
||||
|
||||
.. image:: ../images/esp32/openeth_busy.png
|
||||
:width: 600
|
||||
|
||||
Reference in New Issue
Block a user