]>
Commit | Line | Data |
---|---|---|
68ceb29e WD |
1 | /* |
2 | * (C) Copyright 2004 | |
3 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
68ceb29e WD |
6 | */ |
7 | ||
8 | #include <common.h> | |
68ceb29e | 9 | #include <command.h> |
52cb4d4f | 10 | #include <stdio_dev.h> |
68ceb29e WD |
11 | #include <net.h> |
12 | ||
d87080b7 WD |
13 | DECLARE_GLOBAL_DATA_PTR; |
14 | ||
2c8fe512 JH |
15 | #ifndef CONFIG_NETCONSOLE_BUFFER_SIZE |
16 | #define CONFIG_NETCONSOLE_BUFFER_SIZE 512 | |
17 | #endif | |
18 | ||
19 | static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE]; | |
e1902ac6 JH |
20 | static int input_size; /* char count in input buffer */ |
21 | static int input_offset; /* offset to valid chars in input buffer */ | |
22 | static int input_recursion; | |
23 | static int output_recursion; | |
68ceb29e | 24 | static int net_timeout; |
e1902ac6 | 25 | static uchar nc_ether[6]; /* server enet address */ |
049a95a7 | 26 | static struct in_addr nc_ip; /* server ip */ |
7f51898c JH |
27 | static short nc_out_port; /* target output port */ |
28 | static short nc_in_port; /* source input port */ | |
e1902ac6 JH |
29 | static const char *output_packet; /* used by first send udp */ |
30 | static int output_packet_len; | |
f8be7d65 JH |
31 | /* |
32 | * Start with a default last protocol. | |
33 | * We are only interested in NETCONS or not. | |
34 | */ | |
35 | enum proto_t net_loop_last_protocol = BOOTP; | |
68ceb29e | 36 | |
03eb129f | 37 | static void nc_wait_arp_handler(uchar *pkt, unsigned dest, |
049a95a7 | 38 | struct in_addr sip, unsigned src, |
68ceb29e WD |
39 | unsigned len) |
40 | { | |
22f6e99d | 41 | net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */ |
68ceb29e WD |
42 | } |
43 | ||
049a95a7 JH |
44 | static void nc_handler(uchar *pkt, unsigned dest, struct in_addr sip, |
45 | unsigned src, unsigned len) | |
68ceb29e | 46 | { |
eedcd078 | 47 | if (input_size) |
22f6e99d | 48 | net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */ |
68ceb29e WD |
49 | } |
50 | ||
6a38a5f3 | 51 | static void nc_timeout_handler(void) |
68ceb29e | 52 | { |
22f6e99d | 53 | net_set_state(NETLOOP_SUCCESS); |
68ceb29e WD |
54 | } |
55 | ||
049a95a7 | 56 | static int is_broadcast(struct in_addr ip) |
e827fec2 | 57 | { |
049a95a7 JH |
58 | static struct in_addr netmask; |
59 | static struct in_addr our_ip; | |
e827fec2 JH |
60 | static int env_changed_id; |
61 | int env_id = get_env_id(); | |
62 | ||
63 | /* update only when the environment has changed */ | |
64 | if (env_changed_id != env_id) { | |
049a95a7 JH |
65 | netmask = getenv_ip("netmask"); |
66 | our_ip = getenv_ip("ipaddr"); | |
e827fec2 JH |
67 | |
68 | env_changed_id = env_id; | |
69 | } | |
70 | ||
049a95a7 JH |
71 | return (ip.s_addr == ~0 || /* 255.255.255.255 (global bcast) */ |
72 | ((netmask.s_addr & our_ip.s_addr) == | |
73 | (netmask.s_addr & ip.s_addr) && /* on the same net and */ | |
74 | (netmask.s_addr | ip.s_addr) == ~0)); /* bcast to our net */ | |
e827fec2 JH |
75 | } |
76 | ||
77 | static int refresh_settings_from_env(void) | |
78 | { | |
79 | const char *p; | |
80 | static int env_changed_id; | |
81 | int env_id = get_env_id(); | |
82 | ||
83 | /* update only when the environment has changed */ | |
84 | if (env_changed_id != env_id) { | |
85 | if (getenv("ncip")) { | |
049a95a7 JH |
86 | nc_ip = getenv_ip("ncip"); |
87 | if (!nc_ip.s_addr) | |
e827fec2 JH |
88 | return -1; /* ncip is 0.0.0.0 */ |
89 | p = strchr(getenv("ncip"), ':'); | |
90 | if (p != NULL) { | |
91 | nc_out_port = simple_strtoul(p + 1, NULL, 10); | |
92 | nc_in_port = nc_out_port; | |
93 | } | |
6a38a5f3 | 94 | } else { |
049a95a7 | 95 | nc_ip.s_addr = ~0; /* ncip is not set, so broadcast */ |
6a38a5f3 | 96 | } |
e827fec2 JH |
97 | |
98 | p = getenv("ncoutport"); | |
99 | if (p != NULL) | |
100 | nc_out_port = simple_strtoul(p, NULL, 10); | |
101 | p = getenv("ncinport"); | |
102 | if (p != NULL) | |
103 | nc_in_port = simple_strtoul(p, NULL, 10); | |
104 | ||
105 | if (is_broadcast(nc_ip)) | |
106 | /* broadcast MAC address */ | |
107 | memset(nc_ether, 0xff, sizeof(nc_ether)); | |
108 | else | |
109 | /* force arp request */ | |
110 | memset(nc_ether, 0, sizeof(nc_ether)); | |
111 | } | |
112 | return 0; | |
113 | } | |
114 | ||
115 | /** | |
bc0571fc | 116 | * Called from net_loop in net/net.c before each packet |
e827fec2 | 117 | */ |
6a38a5f3 | 118 | void nc_start(void) |
68ceb29e | 119 | { |
e827fec2 | 120 | refresh_settings_from_env(); |
0adb5b76 | 121 | if (!output_packet_len || memcmp(nc_ether, net_null_ethaddr, 6)) { |
68ceb29e | 122 | /* going to check for input packet */ |
ece223b5 | 123 | net_set_udp_handler(nc_handler); |
bc0571fc | 124 | net_set_timeout_handler(net_timeout, nc_timeout_handler); |
68ceb29e WD |
125 | } else { |
126 | /* send arp request */ | |
eedcd078 | 127 | uchar *pkt; |
ece223b5 | 128 | net_set_arp_handler(nc_wait_arp_handler); |
1203fcce JH |
129 | pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + |
130 | IP_UDP_HDR_SIZE; | |
e1902ac6 | 131 | memcpy(pkt, output_packet, output_packet_len); |
1203fcce JH |
132 | net_send_udp_packet(nc_ether, nc_ip, nc_out_port, nc_in_port, |
133 | output_packet_len); | |
68ceb29e WD |
134 | } |
135 | } | |
136 | ||
049a95a7 | 137 | int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port, |
8a0eccb1 | 138 | unsigned src_port, unsigned len) |
68ceb29e | 139 | { |
eedcd078 WD |
140 | int end, chunk; |
141 | ||
8a0eccb1 | 142 | if (dest_port != nc_in_port || !len) |
e1902ac6 | 143 | return 0; /* not for us */ |
68ceb29e | 144 | |
049a95a7 | 145 | if (src_ip.s_addr != nc_ip.s_addr && !is_broadcast(nc_ip)) |
8a0eccb1 JH |
146 | return 0; /* not from our client */ |
147 | ||
4ef8d53c JH |
148 | debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt); |
149 | ||
e1902ac6 JH |
150 | if (input_size == sizeof(input_buffer)) |
151 | return 1; /* no space */ | |
152 | if (len > sizeof(input_buffer) - input_size) | |
153 | len = sizeof(input_buffer) - input_size; | |
eedcd078 WD |
154 | |
155 | end = input_offset + input_size; | |
e1902ac6 JH |
156 | if (end > sizeof(input_buffer)) |
157 | end -= sizeof(input_buffer); | |
eedcd078 WD |
158 | |
159 | chunk = len; | |
e1902ac6 JH |
160 | if (end + len > sizeof(input_buffer)) { |
161 | chunk = sizeof(input_buffer) - end; | |
eedcd078 WD |
162 | memcpy(input_buffer, pkt + chunk, len - chunk); |
163 | } | |
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 | } |