blob: d566f9f8e6b5be5edfde66377da56464783c8cce [file] [log] [blame]
Jerome Forissier1ff00362024-10-16 12:04:03 +02001// SPDX-License-Identifier: GPL-2.0
2
3/* Copyright (C) 2024 Linaro Ltd. */
4
5#include <command.h>
6#include <dm/device.h>
7#include <dm/uclass.h>
Jerome Forissier5b2e2732025-03-06 15:32:22 +01008#include <hexdump.h>
Jerome Forissier1ff00362024-10-16 12:04:03 +02009#include <lwip/ip4_addr.h>
10#include <lwip/err.h>
11#include <lwip/netif.h>
12#include <lwip/pbuf.h>
13#include <lwip/etharp.h>
14#include <lwip/init.h>
15#include <lwip/prot/etharp.h>
16#include <net.h>
17
18/* xx:xx:xx:xx:xx:xx\0 */
19#define MAC_ADDR_STRLEN 18
20
21#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
22void (*push_packet)(void *, int len) = 0;
23#endif
Jerome Forissierc76a9f42025-04-15 23:17:37 +020024static int net_try_count;
25static int net_restarted;
Jerome Forissier1ff00362024-10-16 12:04:03 +020026int net_restart_wrap;
27static uchar net_pkt_buf[(PKTBUFSRX) * PKTSIZE_ALIGN + PKTALIGN];
28uchar *net_rx_packets[PKTBUFSRX];
29uchar *net_rx_packet;
30const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
31char *pxelinux_configfile;
32/* Our IP addr (0 = unknown) */
33struct in_addr net_ip;
Jerome Forissier6a78e962024-10-16 12:04:05 +020034char net_boot_file_name[1024];
Jerome Forissier1ff00362024-10-16 12:04:03 +020035
Jerome Forissier9ba21312025-03-06 15:32:21 +010036static err_t net_lwip_tx(struct netif *netif, struct pbuf *p)
Jerome Forissier1ff00362024-10-16 12:04:03 +020037{
38 struct udevice *udev = netif->state;
39 void *pp = NULL;
40 int err;
41
Jerome Forissier5b2e2732025-03-06 15:32:22 +010042 if (CONFIG_IS_ENABLED(LWIP_DEBUG_RXTX)) {
43 printf("net_lwip_tx: %u bytes, udev %s\n", p->len, udev->name);
44 print_hex_dump("net_lwip_tx: ", 0, 16, 1, p->payload, p->len,
45 true);
46 }
47
Jerome Forissier1ff00362024-10-16 12:04:03 +020048 if ((unsigned long)p->payload % PKTALIGN) {
49 /*
50 * Some net drivers have strict alignment requirements and may
51 * fail or output invalid data if the packet is not aligned.
52 */
53 pp = memalign(PKTALIGN, p->len);
54 if (!pp)
55 return ERR_ABRT;
56 memcpy(pp, p->payload, p->len);
57 }
58
59 err = eth_get_ops(udev)->send(udev, pp ? pp : p->payload, p->len);
60 free(pp);
61 if (err) {
Ilias Apalodimasc99a1022025-03-26 10:28:58 +020062 debug("send error %d\n", err);
Jerome Forissier1ff00362024-10-16 12:04:03 +020063 return ERR_ABRT;
64 }
65
66 return ERR_OK;
67}
68
69static err_t net_lwip_if_init(struct netif *netif)
70{
71 netif->output = etharp_output;
Jerome Forissier9ba21312025-03-06 15:32:21 +010072 netif->linkoutput = net_lwip_tx;
Jerome Forissier1ff00362024-10-16 12:04:03 +020073 netif->mtu = 1500;
74 netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
75
76 return ERR_OK;
77}
78
79static void eth_init_rings(void)
80{
81 int i;
82
83 for (i = 0; i < PKTBUFSRX; i++)
84 net_rx_packets[i] = net_pkt_buf + i * PKTSIZE_ALIGN;
85}
86
87struct netif *net_lwip_get_netif(void)
88{
89 struct netif *netif, *found = NULL;
90
91 NETIF_FOREACH(netif) {
92 if (!found)
93 found = netif;
94 else
95 printf("Error: more than one netif in lwIP\n");
96 }
97 return found;
98}
99
100static int get_udev_ipv4_info(struct udevice *dev, ip4_addr_t *ip,
101 ip4_addr_t *mask, ip4_addr_t *gw)
102{
Jerome Forissier91596572024-11-18 15:31:25 +0100103 char ipstr[] = "ipaddr\0\0";
104 char maskstr[] = "netmask\0\0";
105 char gwstr[] = "gatewayip\0\0";
Jerome Forissier1ff00362024-10-16 12:04:03 +0200106 int idx = dev_seq(dev);
107 char *env;
108
109 if (idx < 0 || idx > 99) {
110 log_err("unexpected idx %d\n", idx);
111 return -1;
112 }
113
114 if (idx) {
115 sprintf(ipstr, "ipaddr%d", idx);
116 sprintf(maskstr, "netmask%d", idx);
117 sprintf(gwstr, "gatewayip%d", idx);
118 }
119
120 ip4_addr_set_zero(ip);
121 ip4_addr_set_zero(mask);
122 ip4_addr_set_zero(gw);
123
124 env = env_get(ipstr);
125 if (env)
126 ip4addr_aton(env, ip);
127
128 env = env_get(maskstr);
129 if (env)
130 ip4addr_aton(env, mask);
131
132 env = env_get(gwstr);
133 if (env)
134 ip4addr_aton(env, gw);
135
136 return 0;
137}
138
Jerome Forissier86378a52025-04-15 23:17:36 +0200139/*
140 * Initialize the network stack if needed and start the current device if valid
141 */
142int net_lwip_eth_start(void)
Jerome Forissier37829282025-01-30 09:22:20 +0100143{
Jerome Forissier86378a52025-04-15 23:17:36 +0200144 int ret;
Jerome Forissier37829282025-01-30 09:22:20 +0100145
Jerome Forissier86378a52025-04-15 23:17:36 +0200146 net_init();
147 if (eth_is_on_demand_init()) {
148 eth_halt();
149 eth_set_current();
150 ret = eth_init();
151 if (ret < 0) {
152 eth_halt();
153 return ret;
154 }
155 } else {
156 eth_init_state_only();
Jerome Forissier37829282025-01-30 09:22:20 +0100157 }
Jerome Forissier86378a52025-04-15 23:17:36 +0200158
159 return 0;
Jerome Forissier37829282025-01-30 09:22:20 +0100160}
161
Jerome Forissier1ff00362024-10-16 12:04:03 +0200162static struct netif *new_netif(struct udevice *udev, bool with_ip)
163{
164 unsigned char enetaddr[ARP_HLEN];
165 char hwstr[MAC_ADDR_STRLEN];
166 ip4_addr_t ip, mask, gw;
167 struct netif *netif;
168 int ret = 0;
Jerome Forissier1ff00362024-10-16 12:04:03 +0200169
170 if (!udev)
171 return NULL;
172
Jerome Forissier1ff00362024-10-16 12:04:03 +0200173 if (eth_start_udev(udev) < 0) {
174 log_err("Could not start %s\n", udev->name);
175 return NULL;
176 }
177
178 netif_remove(net_lwip_get_netif());
179
180 ip4_addr_set_zero(&ip);
181 ip4_addr_set_zero(&mask);
182 ip4_addr_set_zero(&gw);
183
184 if (with_ip)
185 if (get_udev_ipv4_info(udev, &ip, &mask, &gw) < 0)
186 return NULL;
187
188 eth_env_get_enetaddr_by_index("eth", dev_seq(udev), enetaddr);
189 ret = snprintf(hwstr, MAC_ADDR_STRLEN, "%pM", enetaddr);
190 if (ret < 0 || ret >= MAC_ADDR_STRLEN)
191 return NULL;
192
193 netif = calloc(1, sizeof(struct netif));
194 if (!netif)
195 return NULL;
196
197 netif->name[0] = 'e';
198 netif->name[1] = 't';
199
200 string_to_enetaddr(hwstr, netif->hwaddr);
201 netif->hwaddr_len = ETHARP_HWADDR_LEN;
202 debug("adding lwIP netif for %s with hwaddr:%s ip:%s ", udev->name,
203 hwstr, ip4addr_ntoa(&ip));
204 debug("mask:%s ", ip4addr_ntoa(&mask));
205 debug("gw:%s\n", ip4addr_ntoa(&gw));
206
207 if (!netif_add(netif, &ip, &mask, &gw, udev, net_lwip_if_init,
208 netif_input)) {
209 printf("error: netif_add() failed\n");
210 free(netif);
211 return NULL;
212 }
213
214 netif_set_up(netif);
215 netif_set_link_up(netif);
216 /* Routing: use this interface to reach the default gateway */
217 netif_set_default(netif);
218
219 return netif;
220}
221
222struct netif *net_lwip_new_netif(struct udevice *udev)
223{
224 return new_netif(udev, true);
225}
226
227struct netif *net_lwip_new_netif_noip(struct udevice *udev)
228{
Jerome Forissier1ff00362024-10-16 12:04:03 +0200229 return new_netif(udev, false);
230}
231
232void net_lwip_remove_netif(struct netif *netif)
233{
234 netif_remove(netif);
235 free(netif);
236}
237
Jerome Forissier86378a52025-04-15 23:17:36 +0200238/*
239 * Initialize the network buffers, an ethernet device, and the lwIP stack
240 * (once).
241 */
Jerome Forissier1ff00362024-10-16 12:04:03 +0200242int net_init(void)
243{
Jerome Forissier86378a52025-04-15 23:17:36 +0200244 static bool init_done;
Jerome Forissier1ff00362024-10-16 12:04:03 +0200245
Jerome Forissier86378a52025-04-15 23:17:36 +0200246 if (!init_done) {
247 eth_init_rings();
248 eth_init();
249 lwip_init();
250 init_done = true;
251 }
Jerome Forissier1ff00362024-10-16 12:04:03 +0200252
253 return 0;
254}
255
256static struct pbuf *alloc_pbuf_and_copy(uchar *data, int len)
257{
Jerome Forissier97083502024-11-07 12:27:57 +0100258 struct pbuf *p, *q;
Jerome Forissier1ff00362024-10-16 12:04:03 +0200259
Jerome Forissier97083502024-11-07 12:27:57 +0100260 /* We allocate a pbuf chain of pbufs from the pool. */
261 p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
262 if (!p) {
263 LINK_STATS_INC(link.memerr);
264 LINK_STATS_INC(link.drop);
265 return NULL;
266 }
Jerome Forissier1ff00362024-10-16 12:04:03 +0200267
Jerome Forissier97083502024-11-07 12:27:57 +0100268 for (q = p; q != NULL; q = q->next) {
269 memcpy(q->payload, data, q->len);
270 data += q->len;
271 }
Jerome Forissier1ff00362024-10-16 12:04:03 +0200272
Jerome Forissier97083502024-11-07 12:27:57 +0100273 LINK_STATS_INC(link.recv);
Jerome Forissier1ff00362024-10-16 12:04:03 +0200274
Jerome Forissier97083502024-11-07 12:27:57 +0100275 return p;
Jerome Forissier1ff00362024-10-16 12:04:03 +0200276}
277
278int net_lwip_rx(struct udevice *udev, struct netif *netif)
279{
280 struct pbuf *pbuf;
281 uchar *packet;
282 int flags;
283 int len;
284 int i;
285
286 if (!eth_is_active(udev))
287 return -EINVAL;
288
289 flags = ETH_RECV_CHECK_DEVICE;
290 for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) {
291 len = eth_get_ops(udev)->recv(udev, flags, &packet);
292 flags = 0;
293
294 if (len > 0) {
Jerome Forissier5b2e2732025-03-06 15:32:22 +0100295 if (CONFIG_IS_ENABLED(LWIP_DEBUG_RXTX)) {
296 printf("net_lwip_tx: %u bytes, udev %s \n", len,
297 udev->name);
298 print_hex_dump("net_lwip_rx: ", 0, 16, 1,
299 packet, len, true);
300 }
301
Jerome Forissier1ff00362024-10-16 12:04:03 +0200302 pbuf = alloc_pbuf_and_copy(packet, len);
303 if (pbuf)
304 netif->input(pbuf, netif);
305 }
306 if (len >= 0 && eth_get_ops(udev)->free_pkt)
307 eth_get_ops(udev)->free_pkt(udev, packet, len);
308 if (len <= 0)
309 break;
310 }
311 if (len == -EAGAIN)
312 len = 0;
313
314 return len;
315}
316
317void net_process_received_packet(uchar *in_packet, int len)
318{
319#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
320 if (push_packet)
321 (*push_packet)(in_packet, len);
322#endif
323}
324
Jerome Forissier6a78e962024-10-16 12:04:05 +0200325int net_loop(enum proto_t protocol)
326{
327 char *argv[1];
328
329 switch (protocol) {
330 case TFTPGET:
331 argv[0] = "tftpboot";
332 return do_tftpb(NULL, 0, 1, argv);
333 default:
334 return -EINVAL;
335 }
336
337 return -EINVAL;
338}
339
Jerome Forissier1ff00362024-10-16 12:04:03 +0200340u32_t sys_now(void)
341{
342 return get_timer(0);
343}
Jerome Forissierc76a9f42025-04-15 23:17:37 +0200344
345int net_start_again(void)
346{
347 char *nretry;
348 int retry_forever = 0;
349 unsigned long retrycnt = 0;
350
351 nretry = env_get("netretry");
352 if (nretry) {
353 if (!strcmp(nretry, "yes"))
354 retry_forever = 1;
355 else if (!strcmp(nretry, "no"))
356 retrycnt = 0;
357 else if (!strcmp(nretry, "once"))
358 retrycnt = 1;
359 else
360 retrycnt = simple_strtoul(nretry, NULL, 0);
361 } else {
362 retrycnt = 0;
363 retry_forever = 0;
364 }
365
366 if ((!retry_forever) && (net_try_count > retrycnt)) {
367 eth_halt();
368 /*
369 * We don't provide a way for the protocol to return an error,
370 * but this is almost always the reason.
371 */
372 return -ETIMEDOUT;
373 }
374
375 net_try_count++;
376
377 eth_halt();
378#if !defined(CONFIG_NET_DO_NOT_TRY_ANOTHER)
379 eth_try_another(!net_restarted);
380#endif
381 return eth_init();
382}