]>
Commit | Line | Data |
---|---|---|
68ceb29e WD |
1 | /* |
2 | * (C) Copyright 2004 | |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
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) { | |
723806cc SG |
65 | netmask = env_get_ip("netmask"); |
66 | our_ip = env_get_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) { | |
00caae6d | 85 | if (env_get("ncip")) { |
723806cc | 86 | nc_ip = env_get_ip("ncip"); |
049a95a7 | 87 | if (!nc_ip.s_addr) |
e827fec2 | 88 | return -1; /* ncip is 0.0.0.0 */ |
00caae6d | 89 | p = strchr(env_get("ncip"), ':'); |
e827fec2 JH |
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 | 97 | |
00caae6d | 98 | p = env_get("ncoutport"); |
e827fec2 JH |
99 | if (p != NULL) |
100 | nc_out_port = simple_strtoul(p, NULL, 10); | |
00caae6d | 101 | p = env_get("ncinport"); |
e827fec2 JH |
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; | |
3cacc6a7 | 156 | if (end >= sizeof(input_buffer)) |
e1902ac6 | 157 | end -= sizeof(input_buffer); |
eedcd078 WD |
158 | |
159 | chunk = len; | |
3cacc6a7 JH |
160 | /* Check if packet will wrap in input_buffer */ |
161 | if (end + len >= sizeof(input_buffer)) { | |
e1902ac6 | 162 | chunk = sizeof(input_buffer) - end; |
3cacc6a7 | 163 | /* Copy the second part of the pkt to start of input_buffer */ |
eedcd078 WD |
164 | memcpy(input_buffer, pkt + chunk, len - chunk); |
165 | } | |
3cacc6a7 | 166 | /* Copy first (or only) part of pkt after end of current valid input*/ |
e1902ac6 | 167 | memcpy(input_buffer + end, pkt, chunk); |
eedcd078 WD |
168 | |
169 | input_size += len; | |
170 | ||
68ceb29e WD |
171 | return 1; |
172 | } | |
173 | ||
e1902ac6 | 174 | static void nc_send_packet(const char *buf, int len) |
68ceb29e | 175 | { |
c163e436 BN |
176 | #ifdef CONFIG_DM_ETH |
177 | struct udevice *eth; | |
178 | #else | |
68ceb29e | 179 | struct eth_device *eth; |
c163e436 | 180 | #endif |
68ceb29e WD |
181 | int inited = 0; |
182 | uchar *pkt; | |
eedcd078 | 183 | uchar *ether; |
049a95a7 | 184 | struct in_addr ip; |
68ceb29e | 185 | |
4ef8d53c JH |
186 | debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf); |
187 | ||
e1902ac6 JH |
188 | eth = eth_get_dev(); |
189 | if (eth == NULL) | |
68ceb29e WD |
190 | return; |
191 | ||
0adb5b76 | 192 | if (!memcmp(nc_ether, net_null_ethaddr, 6)) { |
c163e436 | 193 | if (eth_is_active(eth)) |
eedcd078 WD |
194 | return; /* inside net loop */ |
195 | output_packet = buf; | |
196 | output_packet_len = len; | |
efd9bb9c | 197 | input_recursion = 1; |
bc0571fc | 198 | net_loop(NETCONS); /* wait for arp reply and send packet */ |
efd9bb9c | 199 | input_recursion = 0; |
eedcd078 | 200 | output_packet_len = 0; |
68ceb29e WD |
201 | return; |
202 | } | |
203 | ||
c163e436 | 204 | if (!eth_is_active(eth)) { |
f8be7d65 | 205 | if (eth_is_on_demand_init()) { |
d2eaec60 | 206 | if (eth_init() < 0) |
f8be7d65 JH |
207 | return; |
208 | eth_set_last_protocol(NETCONS); | |
6a38a5f3 | 209 | } else { |
d2eaec60 | 210 | eth_init_state_only(); |
6a38a5f3 | 211 | } |
f8be7d65 | 212 | |
68ceb29e WD |
213 | inited = 1; |
214 | } | |
1203fcce | 215 | pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
e1902ac6 | 216 | memcpy(pkt, buf, len); |
eedcd078 WD |
217 | ether = nc_ether; |
218 | ip = nc_ip; | |
1203fcce | 219 | net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len); |
68ceb29e | 220 | |
f8be7d65 JH |
221 | if (inited) { |
222 | if (eth_is_on_demand_init()) | |
223 | eth_halt(); | |
224 | else | |
225 | eth_halt_state_only(); | |
226 | } | |
68ceb29e WD |
227 | } |
228 | ||
6a38a5f3 | 229 | static int nc_stdio_start(struct stdio_dev *dev) |
68ceb29e | 230 | { |
e827fec2 | 231 | int retval; |
eedcd078 | 232 | |
7f51898c JH |
233 | nc_out_port = 6666; /* default port */ |
234 | nc_in_port = nc_out_port; | |
eedcd078 | 235 | |
e827fec2 JH |
236 | retval = refresh_settings_from_env(); |
237 | if (retval != 0) | |
238 | return retval; | |
eedcd078 | 239 | |
d7310c7e JH |
240 | /* |
241 | * Initialize the static IP settings and buffer pointers | |
bc0571fc | 242 | * incase we call net_send_udp_packet before net_loop |
d7310c7e JH |
243 | */ |
244 | net_init(); | |
245 | ||
eedcd078 | 246 | return 0; |
68ceb29e WD |
247 | } |
248 | ||
6a38a5f3 | 249 | static void nc_stdio_putc(struct stdio_dev *dev, char c) |
68ceb29e WD |
250 | { |
251 | if (output_recursion) | |
252 | return; | |
253 | output_recursion = 1; | |
254 | ||
e1902ac6 | 255 | nc_send_packet(&c, 1); |
68ceb29e WD |
256 | |
257 | output_recursion = 0; | |
258 | } | |
259 | ||
6a38a5f3 | 260 | static void nc_stdio_puts(struct stdio_dev *dev, const char *s) |
68ceb29e | 261 | { |
b2323ea6 WD |
262 | int len; |
263 | ||
68ceb29e WD |
264 | if (output_recursion) |
265 | return; | |
266 | output_recursion = 1; | |
267 | ||
1a9845b4 MW |
268 | len = strlen(s); |
269 | while (len) { | |
b4141195 | 270 | int send_len = min(len, (int)sizeof(input_buffer)); |
1a9845b4 MW |
271 | nc_send_packet(s, send_len); |
272 | len -= send_len; | |
273 | s += send_len; | |
274 | } | |
68ceb29e WD |
275 | |
276 | output_recursion = 0; | |
277 | } | |
278 | ||
6a38a5f3 | 279 | static int nc_stdio_getc(struct stdio_dev *dev) |
68ceb29e | 280 | { |
b2323ea6 WD |
281 | uchar c; |
282 | ||
68ceb29e WD |
283 | input_recursion = 1; |
284 | ||
285 | net_timeout = 0; /* no timeout */ | |
eedcd078 | 286 | while (!input_size) |
bc0571fc | 287 | net_loop(NETCONS); |
68ceb29e WD |
288 | |
289 | input_recursion = 0; | |
290 | ||
b2323ea6 WD |
291 | c = input_buffer[input_offset++]; |
292 | ||
e1902ac6 JH |
293 | if (input_offset >= sizeof(input_buffer)) |
294 | input_offset -= sizeof(input_buffer); | |
eedcd078 | 295 | input_size--; |
68ceb29e | 296 | |
eedcd078 | 297 | return c; |
68ceb29e WD |
298 | } |
299 | ||
6a38a5f3 | 300 | static int nc_stdio_tstc(struct stdio_dev *dev) |
68ceb29e | 301 | { |
c163e436 BN |
302 | #ifdef CONFIG_DM_ETH |
303 | struct udevice *eth; | |
304 | #else | |
68ceb29e | 305 | struct eth_device *eth; |
c163e436 | 306 | #endif |
68ceb29e WD |
307 | |
308 | if (input_recursion) | |
309 | return 0; | |
310 | ||
eedcd078 | 311 | if (input_size) |
68ceb29e WD |
312 | return 1; |
313 | ||
e1902ac6 | 314 | eth = eth_get_dev(); |
c163e436 | 315 | if (eth_is_active(eth)) |
68ceb29e WD |
316 | return 0; /* inside net loop */ |
317 | ||
318 | input_recursion = 1; | |
319 | ||
320 | net_timeout = 1; | |
bc0571fc | 321 | net_loop(NETCONS); /* kind of poll */ |
68ceb29e WD |
322 | |
323 | input_recursion = 0; | |
324 | ||
eedcd078 | 325 | return input_size != 0; |
68ceb29e WD |
326 | } |
327 | ||
e1902ac6 | 328 | int drv_nc_init(void) |
68ceb29e | 329 | { |
52cb4d4f | 330 | struct stdio_dev dev; |
68ceb29e WD |
331 | int rc; |
332 | ||
e1902ac6 | 333 | memset(&dev, 0, sizeof(dev)); |
68ceb29e | 334 | |
e1902ac6 | 335 | strcpy(dev.name, "nc"); |
1caf934a | 336 | dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; |
6a38a5f3 JH |
337 | dev.start = nc_stdio_start; |
338 | dev.putc = nc_stdio_putc; | |
339 | dev.puts = nc_stdio_puts; | |
340 | dev.getc = nc_stdio_getc; | |
341 | dev.tstc = nc_stdio_tstc; | |
68ceb29e | 342 | |
e1902ac6 | 343 | rc = stdio_register(&dev); |
68ceb29e WD |
344 | |
345 | return (rc == 0) ? 1 : rc; | |
346 | } |