]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
68ceb29e WD |
2 | /* |
3 | * (C) Copyright 2004 | |
4 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
68ceb29e WD |
5 | */ |
6 | ||
7 | #include <common.h> | |
68ceb29e | 8 | #include <command.h> |
52cb4d4f | 9 | #include <stdio_dev.h> |
68ceb29e WD |
10 | #include <net.h> |
11 | ||
2c8fe512 JH |
12 | #ifndef CONFIG_NETCONSOLE_BUFFER_SIZE |
13 | #define CONFIG_NETCONSOLE_BUFFER_SIZE 512 | |
14 | #endif | |
15 | ||
16 | static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE]; | |
e1902ac6 JH |
17 | static int input_size; /* char count in input buffer */ |
18 | static int input_offset; /* offset to valid chars in input buffer */ | |
19 | static int input_recursion; | |
20 | static int output_recursion; | |
68ceb29e | 21 | static int net_timeout; |
e1902ac6 | 22 | static uchar nc_ether[6]; /* server enet address */ |
049a95a7 | 23 | static struct in_addr nc_ip; /* server ip */ |
7f51898c JH |
24 | static short nc_out_port; /* target output port */ |
25 | static short nc_in_port; /* source input port */ | |
e1902ac6 JH |
26 | static const char *output_packet; /* used by first send udp */ |
27 | static int output_packet_len; | |
f8be7d65 JH |
28 | /* |
29 | * Start with a default last protocol. | |
30 | * We are only interested in NETCONS or not. | |
31 | */ | |
32 | enum proto_t net_loop_last_protocol = BOOTP; | |
68ceb29e | 33 | |
03eb129f | 34 | static void nc_wait_arp_handler(uchar *pkt, unsigned dest, |
049a95a7 | 35 | struct in_addr sip, unsigned src, |
68ceb29e WD |
36 | unsigned len) |
37 | { | |
22f6e99d | 38 | net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */ |
68ceb29e WD |
39 | } |
40 | ||
049a95a7 JH |
41 | static void nc_handler(uchar *pkt, unsigned dest, struct in_addr sip, |
42 | unsigned src, unsigned len) | |
68ceb29e | 43 | { |
eedcd078 | 44 | if (input_size) |
22f6e99d | 45 | net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */ |
68ceb29e WD |
46 | } |
47 | ||
6a38a5f3 | 48 | static void nc_timeout_handler(void) |
68ceb29e | 49 | { |
22f6e99d | 50 | net_set_state(NETLOOP_SUCCESS); |
68ceb29e WD |
51 | } |
52 | ||
049a95a7 | 53 | static int is_broadcast(struct in_addr ip) |
e827fec2 | 54 | { |
049a95a7 JH |
55 | static struct in_addr netmask; |
56 | static struct in_addr our_ip; | |
e827fec2 JH |
57 | static int env_changed_id; |
58 | int env_id = get_env_id(); | |
59 | ||
60 | /* update only when the environment has changed */ | |
61 | if (env_changed_id != env_id) { | |
723806cc SG |
62 | netmask = env_get_ip("netmask"); |
63 | our_ip = env_get_ip("ipaddr"); | |
e827fec2 JH |
64 | |
65 | env_changed_id = env_id; | |
66 | } | |
67 | ||
049a95a7 JH |
68 | return (ip.s_addr == ~0 || /* 255.255.255.255 (global bcast) */ |
69 | ((netmask.s_addr & our_ip.s_addr) == | |
70 | (netmask.s_addr & ip.s_addr) && /* on the same net and */ | |
71 | (netmask.s_addr | ip.s_addr) == ~0)); /* bcast to our net */ | |
e827fec2 JH |
72 | } |
73 | ||
74 | static int refresh_settings_from_env(void) | |
75 | { | |
76 | const char *p; | |
77 | static int env_changed_id; | |
78 | int env_id = get_env_id(); | |
79 | ||
80 | /* update only when the environment has changed */ | |
81 | if (env_changed_id != env_id) { | |
00caae6d | 82 | if (env_get("ncip")) { |
723806cc | 83 | nc_ip = env_get_ip("ncip"); |
049a95a7 | 84 | if (!nc_ip.s_addr) |
e827fec2 | 85 | return -1; /* ncip is 0.0.0.0 */ |
00caae6d | 86 | p = strchr(env_get("ncip"), ':'); |
e827fec2 JH |
87 | if (p != NULL) { |
88 | nc_out_port = simple_strtoul(p + 1, NULL, 10); | |
89 | nc_in_port = nc_out_port; | |
90 | } | |
6a38a5f3 | 91 | } else { |
049a95a7 | 92 | nc_ip.s_addr = ~0; /* ncip is not set, so broadcast */ |
6a38a5f3 | 93 | } |
e827fec2 | 94 | |
00caae6d | 95 | p = env_get("ncoutport"); |
e827fec2 JH |
96 | if (p != NULL) |
97 | nc_out_port = simple_strtoul(p, NULL, 10); | |
00caae6d | 98 | p = env_get("ncinport"); |
e827fec2 JH |
99 | if (p != NULL) |
100 | nc_in_port = simple_strtoul(p, NULL, 10); | |
101 | ||
102 | if (is_broadcast(nc_ip)) | |
103 | /* broadcast MAC address */ | |
104 | memset(nc_ether, 0xff, sizeof(nc_ether)); | |
105 | else | |
106 | /* force arp request */ | |
107 | memset(nc_ether, 0, sizeof(nc_ether)); | |
108 | } | |
109 | return 0; | |
110 | } | |
111 | ||
112 | /** | |
bc0571fc | 113 | * Called from net_loop in net/net.c before each packet |
e827fec2 | 114 | */ |
6a38a5f3 | 115 | void nc_start(void) |
68ceb29e | 116 | { |
e827fec2 | 117 | refresh_settings_from_env(); |
0adb5b76 | 118 | if (!output_packet_len || memcmp(nc_ether, net_null_ethaddr, 6)) { |
68ceb29e | 119 | /* going to check for input packet */ |
ece223b5 | 120 | net_set_udp_handler(nc_handler); |
bc0571fc | 121 | net_set_timeout_handler(net_timeout, nc_timeout_handler); |
68ceb29e WD |
122 | } else { |
123 | /* send arp request */ | |
eedcd078 | 124 | uchar *pkt; |
ece223b5 | 125 | net_set_arp_handler(nc_wait_arp_handler); |
1203fcce JH |
126 | pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + |
127 | IP_UDP_HDR_SIZE; | |
e1902ac6 | 128 | memcpy(pkt, output_packet, output_packet_len); |
1203fcce JH |
129 | net_send_udp_packet(nc_ether, nc_ip, nc_out_port, nc_in_port, |
130 | output_packet_len); | |
68ceb29e WD |
131 | } |
132 | } | |
133 | ||
049a95a7 | 134 | int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port, |
8a0eccb1 | 135 | unsigned src_port, unsigned len) |
68ceb29e | 136 | { |
eedcd078 WD |
137 | int end, chunk; |
138 | ||
8a0eccb1 | 139 | if (dest_port != nc_in_port || !len) |
e1902ac6 | 140 | return 0; /* not for us */ |
68ceb29e | 141 | |
049a95a7 | 142 | if (src_ip.s_addr != nc_ip.s_addr && !is_broadcast(nc_ip)) |
8a0eccb1 JH |
143 | return 0; /* not from our client */ |
144 | ||
4ef8d53c JH |
145 | debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt); |
146 | ||
e1902ac6 JH |
147 | if (input_size == sizeof(input_buffer)) |
148 | return 1; /* no space */ | |
149 | if (len > sizeof(input_buffer) - input_size) | |
150 | len = sizeof(input_buffer) - input_size; | |
eedcd078 WD |
151 | |
152 | end = input_offset + input_size; | |
3cacc6a7 | 153 | if (end >= sizeof(input_buffer)) |
e1902ac6 | 154 | end -= sizeof(input_buffer); |
eedcd078 WD |
155 | |
156 | chunk = len; | |
3cacc6a7 JH |
157 | /* Check if packet will wrap in input_buffer */ |
158 | if (end + len >= sizeof(input_buffer)) { | |
e1902ac6 | 159 | chunk = sizeof(input_buffer) - end; |
3cacc6a7 | 160 | /* Copy the second part of the pkt to start of input_buffer */ |
eedcd078 WD |
161 | memcpy(input_buffer, pkt + chunk, len - chunk); |
162 | } | |
3cacc6a7 | 163 | /* Copy first (or only) part of pkt after end of current valid input*/ |
e1902ac6 | 164 | memcpy(input_buffer + end, pkt, chunk); |
eedcd078 WD |
165 | |
166 | input_size += len; | |
167 | ||
68ceb29e WD |
168 | return 1; |
169 | } | |
170 | ||
e1902ac6 | 171 | static void nc_send_packet(const char *buf, int len) |
68ceb29e | 172 | { |
c163e436 BN |
173 | #ifdef CONFIG_DM_ETH |
174 | struct udevice *eth; | |
175 | #else | |
68ceb29e | 176 | struct eth_device *eth; |
c163e436 | 177 | #endif |
68ceb29e WD |
178 | int inited = 0; |
179 | uchar *pkt; | |
eedcd078 | 180 | uchar *ether; |
049a95a7 | 181 | struct in_addr ip; |
68ceb29e | 182 | |
4ef8d53c JH |
183 | debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf); |
184 | ||
e1902ac6 JH |
185 | eth = eth_get_dev(); |
186 | if (eth == NULL) | |
68ceb29e WD |
187 | return; |
188 | ||
0adb5b76 | 189 | if (!memcmp(nc_ether, net_null_ethaddr, 6)) { |
c163e436 | 190 | if (eth_is_active(eth)) |
eedcd078 WD |
191 | return; /* inside net loop */ |
192 | output_packet = buf; | |
193 | output_packet_len = len; | |
efd9bb9c | 194 | input_recursion = 1; |
bc0571fc | 195 | net_loop(NETCONS); /* wait for arp reply and send packet */ |
efd9bb9c | 196 | input_recursion = 0; |
eedcd078 | 197 | output_packet_len = 0; |
68ceb29e WD |
198 | return; |
199 | } | |
200 | ||
c163e436 | 201 | if (!eth_is_active(eth)) { |
f8be7d65 | 202 | if (eth_is_on_demand_init()) { |
d2eaec60 | 203 | if (eth_init() < 0) |
f8be7d65 JH |
204 | return; |
205 | eth_set_last_protocol(NETCONS); | |
6a38a5f3 | 206 | } else { |
d2eaec60 | 207 | eth_init_state_only(); |
6a38a5f3 | 208 | } |
f8be7d65 | 209 | |
68ceb29e WD |
210 | inited = 1; |
211 | } | |
1203fcce | 212 | pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
e1902ac6 | 213 | memcpy(pkt, buf, len); |
eedcd078 WD |
214 | ether = nc_ether; |
215 | ip = nc_ip; | |
1203fcce | 216 | net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len); |
68ceb29e | 217 | |
f8be7d65 JH |
218 | if (inited) { |
219 | if (eth_is_on_demand_init()) | |
220 | eth_halt(); | |
221 | else | |
222 | eth_halt_state_only(); | |
223 | } | |
68ceb29e WD |
224 | } |
225 | ||
6a38a5f3 | 226 | static int nc_stdio_start(struct stdio_dev *dev) |
68ceb29e | 227 | { |
e827fec2 | 228 | int retval; |
eedcd078 | 229 | |
7f51898c JH |
230 | nc_out_port = 6666; /* default port */ |
231 | nc_in_port = nc_out_port; | |
eedcd078 | 232 | |
e827fec2 JH |
233 | retval = refresh_settings_from_env(); |
234 | if (retval != 0) | |
235 | return retval; | |
eedcd078 | 236 | |
d7310c7e JH |
237 | /* |
238 | * Initialize the static IP settings and buffer pointers | |
bc0571fc | 239 | * incase we call net_send_udp_packet before net_loop |
d7310c7e JH |
240 | */ |
241 | net_init(); | |
242 | ||
eedcd078 | 243 | return 0; |
68ceb29e WD |
244 | } |
245 | ||
6a38a5f3 | 246 | static void nc_stdio_putc(struct stdio_dev *dev, char c) |
68ceb29e WD |
247 | { |
248 | if (output_recursion) | |
249 | return; | |
250 | output_recursion = 1; | |
251 | ||
e1902ac6 | 252 | nc_send_packet(&c, 1); |
68ceb29e WD |
253 | |
254 | output_recursion = 0; | |
255 | } | |
256 | ||
6a38a5f3 | 257 | static void nc_stdio_puts(struct stdio_dev *dev, const char *s) |
68ceb29e | 258 | { |
b2323ea6 WD |
259 | int len; |
260 | ||
68ceb29e WD |
261 | if (output_recursion) |
262 | return; | |
263 | output_recursion = 1; | |
264 | ||
1a9845b4 MW |
265 | len = strlen(s); |
266 | while (len) { | |
b4141195 | 267 | int send_len = min(len, (int)sizeof(input_buffer)); |
1a9845b4 MW |
268 | nc_send_packet(s, send_len); |
269 | len -= send_len; | |
270 | s += send_len; | |
271 | } | |
68ceb29e WD |
272 | |
273 | output_recursion = 0; | |
274 | } | |
275 | ||
6a38a5f3 | 276 | static int nc_stdio_getc(struct stdio_dev *dev) |
68ceb29e | 277 | { |
b2323ea6 WD |
278 | uchar c; |
279 | ||
68ceb29e WD |
280 | input_recursion = 1; |
281 | ||
282 | net_timeout = 0; /* no timeout */ | |
eedcd078 | 283 | while (!input_size) |
bc0571fc | 284 | net_loop(NETCONS); |
68ceb29e WD |
285 | |
286 | input_recursion = 0; | |
287 | ||
b2323ea6 WD |
288 | c = input_buffer[input_offset++]; |
289 | ||
e1902ac6 JH |
290 | if (input_offset >= sizeof(input_buffer)) |
291 | input_offset -= sizeof(input_buffer); | |
eedcd078 | 292 | input_size--; |
68ceb29e | 293 | |
eedcd078 | 294 | return c; |
68ceb29e WD |
295 | } |
296 | ||
6a38a5f3 | 297 | static int nc_stdio_tstc(struct stdio_dev *dev) |
68ceb29e | 298 | { |
c163e436 BN |
299 | #ifdef CONFIG_DM_ETH |
300 | struct udevice *eth; | |
301 | #else | |
68ceb29e | 302 | struct eth_device *eth; |
c163e436 | 303 | #endif |
68ceb29e WD |
304 | |
305 | if (input_recursion) | |
306 | return 0; | |
307 | ||
eedcd078 | 308 | if (input_size) |
68ceb29e WD |
309 | return 1; |
310 | ||
e1902ac6 | 311 | eth = eth_get_dev(); |
c163e436 | 312 | if (eth_is_active(eth)) |
68ceb29e WD |
313 | return 0; /* inside net loop */ |
314 | ||
315 | input_recursion = 1; | |
316 | ||
317 | net_timeout = 1; | |
bc0571fc | 318 | net_loop(NETCONS); /* kind of poll */ |
68ceb29e WD |
319 | |
320 | input_recursion = 0; | |
321 | ||
eedcd078 | 322 | return input_size != 0; |
68ceb29e WD |
323 | } |
324 | ||
e1902ac6 | 325 | int drv_nc_init(void) |
68ceb29e | 326 | { |
52cb4d4f | 327 | struct stdio_dev dev; |
68ceb29e WD |
328 | int rc; |
329 | ||
e1902ac6 | 330 | memset(&dev, 0, sizeof(dev)); |
68ceb29e | 331 | |
e1902ac6 | 332 | strcpy(dev.name, "nc"); |
1caf934a | 333 | dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; |
6a38a5f3 JH |
334 | dev.start = nc_stdio_start; |
335 | dev.putc = nc_stdio_putc; | |
336 | dev.puts = nc_stdio_puts; | |
337 | dev.getc = nc_stdio_getc; | |
338 | dev.tstc = nc_stdio_tstc; | |
68ceb29e | 339 | |
e1902ac6 | 340 | rc = stdio_register(&dev); |
68ceb29e WD |
341 | |
342 | return (rc == 0) ? 1 : rc; | |
343 | } |