blob: 043d2ab6e946e15737a6c321c6d157cb9771efce [file] [log] [blame]
Jerome Forissier1ff00362024-10-16 12:04:03 +02001// SPDX-License-Identifier: GPL-2.0+
2/* Copyright (C) 2024 Linaro Ltd. */
3
4#include <command.h>
5#include <console.h>
Tom Rinic31301c2025-05-15 17:31:50 -06006#include <env.h>
Heinrich Schuchardtee9712a2024-12-05 20:17:36 +01007#include <log.h>
Jerome Forissier1ff00362024-10-16 12:04:03 +02008#include <dm/device.h>
9#include <linux/delay.h>
10#include <linux/errno.h>
11#include <lwip/dhcp.h>
12#include <lwip/dns.h>
13#include <lwip/timeouts.h>
14#include <net.h>
15#include <time.h>
16
17#define DHCP_TIMEOUT_MS 10000
18
19#ifdef CONFIG_CMD_TFTPBOOT
20/* Boot file obtained from DHCP (if present) */
21static char boot_file_name[DHCP_BOOT_FILE_LEN];
22#endif
23
24static void call_lwip_dhcp_fine_tmr(void *ctx)
25{
26 dhcp_fine_tmr();
27 sys_timeout(10, call_lwip_dhcp_fine_tmr, NULL);
28}
29
30static int dhcp_loop(struct udevice *udev)
31{
Jerome Forissier595bf7d2024-11-22 13:35:29 +010032 char ipstr[] = "ipaddr\0\0";
33 char maskstr[] = "netmask\0\0";
34 char gwstr[] = "gatewayip\0\0";
Jerome Forissier1ff00362024-10-16 12:04:03 +020035 unsigned long start;
36 struct netif *netif;
37 struct dhcp *dhcp;
38 bool bound;
39 int idx;
40
41 idx = dev_seq(udev);
42 if (idx < 0 || idx > 99) {
43 log_err("unexpected idx %d\n", idx);
44 return CMD_RET_FAILURE;
45 }
46
47 netif = net_lwip_new_netif_noip(udev);
48 if (!netif)
49 return CMD_RET_FAILURE;
50
51 start = get_timer(0);
52
53 if (dhcp_start(netif))
54 return CMD_RET_FAILURE;
55
56 call_lwip_dhcp_fine_tmr(NULL);
57
58 /* Wait for DHCP to complete */
59 do {
60 net_lwip_rx(udev, netif);
61 sys_check_timeouts();
62 bound = dhcp_supplied_address(netif);
63 if (bound)
64 break;
65 if (ctrlc()) {
66 printf("Abort\n");
67 break;
68 }
69 mdelay(1);
70 } while (get_timer(start) < DHCP_TIMEOUT_MS);
71
72 sys_untimeout(call_lwip_dhcp_fine_tmr, NULL);
73
74 if (!bound) {
75 net_lwip_remove_netif(netif);
76 return CMD_RET_FAILURE;
77 }
78
79 dhcp = netif_dhcp_data(netif);
80
81 env_set("bootfile", dhcp->boot_file_name);
82
83 if (idx > 0) {
84 sprintf(ipstr, "ipaddr%d", idx);
85 sprintf(maskstr, "netmask%d", idx);
86 sprintf(gwstr, "gatewayip%d", idx);
87 } else {
88 net_ip.s_addr = dhcp->offered_ip_addr.addr;
89 }
90
91 env_set(ipstr, ip4addr_ntoa(&dhcp->offered_ip_addr));
92 env_set(maskstr, ip4addr_ntoa(&dhcp->offered_sn_mask));
93 env_set("serverip", ip4addr_ntoa(&dhcp->server_ip_addr));
94 if (dhcp->offered_gw_addr.addr != 0)
95 env_set(gwstr, ip4addr_ntoa(&dhcp->offered_gw_addr));
96
97#ifdef CONFIG_PROT_DNS_LWIP
98 env_set("dnsip", ip4addr_ntoa(dns_getserver(0)));
99 env_set("dnsip2", ip4addr_ntoa(dns_getserver(1)));
100#endif
101#ifdef CONFIG_CMD_TFTPBOOT
102 if (dhcp->boot_file_name[0] != '\0')
103 strncpy(boot_file_name, dhcp->boot_file_name,
104 sizeof(boot_file_name));
105#endif
106
107 printf("DHCP client bound to address %pI4 (%lu ms)\n",
108 &dhcp->offered_ip_addr, get_timer(start));
109
110 net_lwip_remove_netif(netif);
111 return CMD_RET_SUCCESS;
112}
113
114int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
115{
Jerome Forissier8cc52832024-11-14 18:20:07 +0100116 int ret;
Heinrich Schuchardtee9712a2024-12-05 20:17:36 +0100117 struct udevice *dev;
Jerome Forissier8cc52832024-11-14 18:20:07 +0100118
Jerome Forissier86378a52025-04-15 23:17:36 +0200119 if (net_lwip_eth_start() < 0)
120 return CMD_RET_FAILURE;
Jerome Forissier1ff00362024-10-16 12:04:03 +0200121
Heinrich Schuchardtee9712a2024-12-05 20:17:36 +0100122 dev = eth_get_dev();
123 if (!dev) {
124 log_err("No network device\n");
125 return CMD_RET_FAILURE;
126 }
127
128 ret = dhcp_loop(dev);
Jerome Forissier8cc52832024-11-14 18:20:07 +0100129 if (ret)
130 return ret;
131
132 if (argc > 1) {
133 struct cmd_tbl cmdtp = {};
134
135 return do_tftpb(&cmdtp, 0, argc, argv);
136 }
137
138 return CMD_RET_SUCCESS;
Jerome Forissier1ff00362024-10-16 12:04:03 +0200139}
140
141int dhcp_run(ulong addr, const char *fname, bool autoload)
142{
143 char *dhcp_argv[] = {"dhcp", NULL, };
Jerome Forissier6a78e962024-10-16 12:04:05 +0200144#ifdef CONFIG_CMD_TFTPBOOT
145 char *tftp_argv[] = {"tftpboot", boot_file_name, NULL, };
146#endif
Jerome Forissier1ff00362024-10-16 12:04:03 +0200147 struct cmd_tbl cmdtp = {}; /* dummy */
148
149 if (autoload) {
Jerome Forissier6a78e962024-10-16 12:04:05 +0200150#ifdef CONFIG_CMD_TFTPBOOT
151 /* Assume DHCP was already performed */
152 if (boot_file_name[0])
153 return do_tftpb(&cmdtp, 0, 2, tftp_argv);
154 return 0;
155#else
Jerome Forissier1ff00362024-10-16 12:04:03 +0200156 return -EOPNOTSUPP;
Jerome Forissier6a78e962024-10-16 12:04:05 +0200157#endif
Jerome Forissier1ff00362024-10-16 12:04:03 +0200158 }
159
160 return do_dhcp(&cmdtp, 0, 1, dhcp_argv);
161}