📜 ⬆️ ⬇️

Ethernet over USB to STM32F4



Recently, the idea arose of making the board based on the MK STM32F4 work on a network. Since there was no Ethernet PHY controller onboard, the only option was to use the USB FullSpeed ​​interface to emulate an Ethernet device. A common USB class standard that implements this function is called RNDIS.
To his chagrin, the search for RNDIS driver for the STM32 was not successful. However, this was not surprising, since Open examples of using the USB port of the STM32 are limited only to those provided by the manufacturer.
I wanted to correct this injustice. And at the same time to fuck the necessary source, good in the future they will be useful.
Now, when the demo version of the library is ready, I post it to the public as a MIT license. Therefore, all who are interested in the library - use the "health." The library has the name LRNDIS, the first letter of which means using the network stack for embedded systems "LwIP".
To demonstrate the capabilities of the library, an example was created on the stm32f4discovery board. His job is to support basic services (DHCP and DNS servers) and transfer usb host to the requested WEB pages. Thus, our discovery has turned into an almost full-fledged WEB-server that works on the USB port!
A few words on where applicable.
In everyday life, RNDIS devices are usually USB modems for Internet access. Perhaps such an application would indeed be useful if the developer chooses the STM32 as a connecting chain between the PC and the radio frequency (or other) transceiver. Or, maybe, it will want to expand own network on an Ethernet segment?
Another application in which I find the main benefit for myself is the interface for managing complex devices. A typical solution in this area is the creation of terminal software. In this case, you have to deal with its support along with the support of the device, which can be inconvenient. Actually, in the rejection of such a scheme in favor of the managing Web-interface is the meaning of the possible use of the library. Recall the web interfaces for setting up routers. Conveniently. Handsomely. Without too much software.

So, if you are interested, read on ...

1. RNDIS driver


At the writing stage, two tasks were solved: sign our device and maintain the RNDIS standard.
Signature of the device is reduced to the compilation of correct USB descriptors. VID value is 0x0483 (STMicroelectronics), PID value is 0x0123 (arbitrary). Needless to say, in a commercial application should not do so.
View Handles
Device descriptor
OffsetFieldSizeValueDescription
0bLengthone12h
onebDescriptorTypeone01hDevice
2bcdUSB20200hUSB Spec 2.0
fourbDeviceClassone02hCDC Control
fivebDeviceSubClassone00h
6bDeviceProtocolone00h
7bMaxPacketSize0one40h64 bytes
eightidVendor20483hSGS Thomson Microelectronics
tenidProduct20123h
12bcdDevice20001h0.01
14iManufacturerone01h"Fetisov Sergey"
15iProductone02h"STM32F4 RNDIS"
sixteeniSerialNumberone03h"00000000123C"
17bNumConfigurationsone01h
Configuration Descriptor 1
OffsetFieldSizeValueDescription
0bLengthone09h
onebDescriptorTypeone02hConfiguration
2wTotalLength20043h
fourbNumInterfacesone02h
fivebConfigurationValueone01h
6iConfigurationone00h
7bmAttributesone40hSelf powered
eightbMaxPowerone01h2 mA
Interface Descriptor 0/0 CDC Control, 1 Endpoint
OffsetFieldSizeValueDescription
0bLengthone09h
onebDescriptorTypeone04hInterface
2bInterfaceNumberone00h
3bAlternateSettingone00h
fourbNumEndpointsone01h
fivebInterfaceClassone02hCDC Control
6bInterfaceSubClassone02hAbstract Control Model
7bInterfaceProtocoloneFFhVendor-specific
eightiInterfaceone00h
Header Functional Descriptor
OffsetFieldSizeValueDescription
0bFunctionLengthone05h
onebDescriptorTypeone24hCS Interface
2bDescriptorSubtypeone00hHeader
3bcdCDC20110h1.10
Call Management Functional Descriptor
OffsetFieldSizeValueDescription
0bFunctionLengthone05h
onebDescriptorTypeone24hCS Interface
2bDescriptorSubtypeone01hCall management
3bmCapabilitiesone00h
fourbDataInterfaceone01h
Abstract Control Management Functional Descriptor
OffsetFieldSizeValueDescription
0bFunctionLengthone04h
onebDescriptorTypeone24hCS Interface
2bDescriptorSubtypeone02hAbstract Control Management
3bmCapabilitiesone00hRequests / notifications not supported
Union Functional Descriptor
OffsetFieldSizeValueDescription
0bFunctionLengthone05h
onebDescriptorTypeone24hCS Interface
2bDescriptorSubtypeone06hUnion
3bControlInterfaceone00h
fourbSubordinateInterface0one01hCDC Data
Endpoint Descriptor 81 1 In, Interrupt, 80 ms
OffsetFieldSizeValueDescription
0bLengthone07h
onebDescriptorTypeone05hEndpoint
2bEndpointAddressone81h1 In
3bmAttributesone03hInterrupt
fourwMaxPacketSize20008h8 bytes
6bIntervalone50h80 ms
Interface Descriptor 1/0 CDC Data, 2 Endpoints
OffsetFieldSizeValueDescription
0bLengthone09h
onebDescriptorTypeone04hInterface
2bInterfaceNumberone01h
3bAlternateSettingone00h
fourbNumEndpointsone02h
fivebInterfaceClassone0AhCDC Data
6bInterfaceSubClassone00h
7bInterfaceProtocolone00h
eightiInterfaceone00h
Endpoint Descriptor 82 2 In, Bulk, 64 bytes
OffsetFieldSizeValueDescription
0bLengthone07h
onebDescriptorTypeone05hEndpoint
2bEndpointAddressone82h2 In
3bmAttributesone02hBulk
fourwMaxPacketSize20040h64 bytes
6bIntervalone00h
Endpoint Descriptor 03 3 Out, Bulk, 64 bytes
OffsetFieldSizeValueDescription
0bLengthone07h
onebDescriptorTypeone05hEndpoint
2bEndpointAddressone03h3 out
3bmAttributesone02hBulk
fourwMaxPacketSize20040h64 bytes
6bIntervalone00h


Support standard implemented in accordance with the documentation .
')
Also on the network there are many RNDIS-drivers for other platforms ( 1 , 2 , 3 ), which greatly simplified development.
In the exchange part, the driver repeats the feature of the CDC class with the exception that the transmitted packets are wrapped with service information. There is also a special interface for sending requests from a usb host in order to configure the device or obtain its status. Detailed driver code can be studied in the usbd_rndis_core.c module.
The minimum driver setting for embedding is to change the definitions in the usbd_rndis_core.h file.
- MAC address of the device (PERMANENT_HWADDR, STATION_HWADDR)
- Manufacturer ID (RNDIS_VENDOR)
- MTU value (ETH_MTU):

#define ETH_MTU 1500 // MTU value #define ETH_LINK_SPEED 250000 // bits per sec #define RNDIS_VENDOR "fetisov" // NIC vendor name #define STATION_HWADDR 0x20,0x89,0x84,0x6A,0x96,0xAA // station MAC #define PERMANENT_HWADDR 0x20,0x89,0x84,0x6A,0x96,0xAA // permanent MAC 

Also in the usbd_desc.h file you should change the product name (USBD_PRODUCT_STRING) and manufacturer (USBD_MANUFACTURER_STRING).

The process of installing the driver in Windows:



1. The menu item "update driver";
2. Run a driver search;
3. Select a driver from the list;
4. In the list of device classes, select "Network adapters";
5. In the list of manufacturers, select "Microsoft Corporation";
6. And the product "Remote NDIS based Internet Sharing Device".

In more detail the process of installing a standard RNDIS device is described here .

2. We fasten LwIP




The RNDIS driver provides only the transport function for 802.3 (Ethernet) frames. Obviously, support for the full diversity of packages and standards is required to be placed on the network stack. In his role, it was decided to use the popular stack for embedded systems lwip latest (currently) version 1.4.1. We must pay tribute to the authors of the stack, which turned out to be very easy to integrate. With all this, the stack code is rich in useful comments and instructions.

The TCP / IP protocol suite has been continued developed.
While IP TCP TCP TCP TCP / IP It also makes it possible to use it.
Main features include:
- Protocols: IP, ICMP, UDP, TCP, IGMP, ARP, PPPoS, PPPoE
- DHCP client, DNS client, AutoIP / APIPA (Zeroconf), SNMP agent (private MIB support)
- APIs: specialized APIs for enhanced performance, optional Berkeley-alike socket API
- Extended features: IP forwarding and recovery / retransmit
- Addon applications: HTTP server, SNTP client, SMTP client, ping, NetBIOS nameserver

Running the stack under stm32 limited itself to including a specific set of source files in the build process and entering definitions in the lwipopts.h file:

 #define NO_SYS 1 #define LWIP_RAW 1 #define LWIP_NETCONN 0 #define LWIP_SOCKET 0 #define LWIP_DHCP 0 #define LWIP_ICMP 1 #define LWIP_UDP 1 #define LWIP_TCP 1 #define ETH_PAD_SIZE 0 #define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) #define MEM_SIZE 10000 #define TCP_MSS (1500 /*mtu*/ - 14 /*ethhdr*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) #define ETHARP_SUPPORT_STATIC_ENTRIES 1 

It should be noted that the work on porting the stack to the STM32F4 was done before by STMicroelectronics (application STSW-STM32070).

We start the DHCP server




Adding a DHCP server to the library is associated with the need to initialize the network interface on the host side. By default, the interface created when the device is connected is configured to automatically receive an ip-address. The library also works successfully with static addressing, but this is not very convenient.

Unfortunately, among the tools supplied by lwip, there is no DHCP server.

However, this is not a significant problem, because In its minimal implementation, the DHCP server is very minimalistic.

The network is, perhaps, the only example of a DHCP server that works with the lwip stack. This source turned out to be very useful for studying, although not suitable for embedding on the “as-is” principle due to the lack of configuration and use of socket-api.

Therefore, it was decided to write a DHCP server.

And here are his modest features:
- issuing addresses at any time
- reservation of addresses by MAC address
- DNS server setup

Server connection in the test project:

 #define NUM_DHCP_ENTRY 3 static dhcp_entry_t entries[NUM_DHCP_ENTRY] = { // mac ip address subnet mask lease time { {0}, {192, 168, 7, 2}, {255, 255, 255, 0}, 24 * 60 * 60 }, { {0}, {192, 168, 7, 3}, {255, 255, 255, 0}, 24 * 60 * 60 }, { {0}, {192, 168, 7, 4}, {255, 255, 255, 0}, 24 * 60 * 60 } }; static dhcp_config_t dhcp_config = { {192, 168, 7, 1}, 67, // server address, port {192, 168, 7, 1}, // dns server "stm", // dns suffix NUM_DHCP_ENTRY, // num entry entries // entries }; int main(void) { ... while (dhserv_init(&dhcp_config) != ERR_OK) ; ... } 


We start the DNS server




The desired result of our work is the display of a web page when you enter some resource name in the browser. However, this is only possible if there is a DNS server that will “know” about our host. Of course, this result is available if you directly enter the ip-address in the address bar: 192.168.7.1 . This address is the default of our device. However, we will be more adept and run the DNS server.

Unlike DHCP, the current implementation of the DNS server is even "thinner." At the moment, it allows you to process only standard DNS requests for one record.

Running the server in the project:

 bool dns_query_proc(const char *name, ip_addr_t *addr) { if (strcmp(name, "run.stm") == 0 || strcmp(name, "www.run.stm") == 0) { addr->addr = *(uint32_t *)ipaddr; return true; } return false; } int main(void) { ... while (dnserv_init(PADDR(ipaddr), 53, dns_query_proc) != ERR_OK) ; ... } 


We start the HTTP server


I had to struggle for a long time and to no avail with the launch of the famous server from the lwip package “contrib-1.4.1”. Until now, the mysterious HardFault that appears on a seemingly level spot remains a mystery to me. All settings, addresses of reading and writing, stack depth were checked ... But, alas.

Later, I started writing a working http server and HardFault repeated when I was accessing the memory area allocated by the mem_malloc function. In this case, the addresses were valid and belonged to the internal RAM. In general, I happily said goodbye to dynamic allocation and began to use static in the server. However, the question of the reason for HardFault remains open, and therefore it must be borne in mind that using the lwip functions of mem_ * in the current version of the library is not safe.

So, the result was still obtained.

HTTP server enable code:

 static const my_page_t my_pages[] = { { "/", 200, MIME_TEXT_HTML, page1_html, page1_html_size }, { "/page2.htm", 200, MIME_TEXT_HTML, page2_html, page2_html_size }, { "/page3.htm", 200, MIME_TEXT_HTML, page3_html, page3_html_size }, { "/check.gif", 200, MIME_IMAGE_GIF, check_png, check_png_size }, { NULL, 404, MIME_TEXT_HTML, page_not_found, page_not_found_size } }; bool on_http_req(const http_req_t *req, http_resp_t *resp, void **arg) { const my_page_t *page; for (page = my_pages; page->uri != NULL; page++) if (strcmp(page->uri, req->uri) == 0) break; resp->code = page->code; resp->cont_len = page->size; resp->mime = page->mime; resp->conn_type = CT_CLOSE; *arg = (void *)page; return true; } void http_write_data() { for (int i = 0; i < HTTP_SERVER_MAX_CON; i++) { int n; const htcon_t *con; my_page_t *page; con = htcon(i); if (con == NULL) continue; page = (my_page_t *)con->arg; if (con->state == CON_CLOSED) { htcon_free(i); continue; } if (con->state != CON_ACTIVE) continue; n = page->size - con->writed; htcon_write(i, (char *)page->data + con->writed, n); } } int main(void) { ... htserv_on_req = on_http_req; while (htserv_init(80) != ERR_OK) ; while (1) { stmr(); // call software timers usb_polling(); // usb device polling http_write_data(); // writes http response } } 


Unsolved Issues


1. Newns relicensing stack lwip, which may have its own conditions for inclusion in the other software;
2. The problem with working under Linux;
3. Adding POST request processing;
4. Revision of the DNS server to handle "multi-query" packets;
5. The problem with mem_malloc.

Source: https://habr.com/ru/post/248097/


All Articles