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