blob: 11dbf5375b116e76843986b3b4d1e00a7664eb5f [file] [log] [blame]
wdenkb8fb6192004-08-02 21:11:11 +00001/*
2 * (C) Copyright 2004
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02005 * SPDX-License-Identifier: GPL-2.0+
wdenkb8fb6192004-08-02 21:11:11 +00006 */
7
8#include <common.h>
wdenkb8fb6192004-08-02 21:11:11 +00009#include <command.h>
Jean-Christophe PLAGNIOL-VILLARD2a7a0312009-05-16 12:14:54 +020010#include <stdio_dev.h>
wdenkb8fb6192004-08-02 21:11:11 +000011#include <net.h>
12
Joe Hershberger45f4a232012-07-31 06:09:17 +000013#ifndef CONFIG_NETCONSOLE_BUFFER_SIZE
14#define CONFIG_NETCONSOLE_BUFFER_SIZE 512
15#endif
16
17static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE];
Joe Hershberger107b3d02012-05-15 08:59:13 +000018static int input_size; /* char count in input buffer */
19static int input_offset; /* offset to valid chars in input buffer */
20static int input_recursion;
21static int output_recursion;
wdenkb8fb6192004-08-02 21:11:11 +000022static int net_timeout;
Joe Hershberger107b3d02012-05-15 08:59:13 +000023static uchar nc_ether[6]; /* server enet address */
Joe Hershberger5874dec2015-04-08 01:41:01 -050024static struct in_addr nc_ip; /* server ip */
Joe Hershbergerd0f53962012-07-31 06:06:41 +000025static short nc_out_port; /* target output port */
26static short nc_in_port; /* source input port */
Joe Hershberger107b3d02012-05-15 08:59:13 +000027static const char *output_packet; /* used by first send udp */
28static int output_packet_len;
Joe Hershberger9f374062012-08-03 10:59:08 +000029/*
30 * Start with a default last protocol.
31 * We are only interested in NETCONS or not.
32 */
33enum proto_t net_loop_last_protocol = BOOTP;
wdenkb8fb6192004-08-02 21:11:11 +000034
Luca Ceresoli428ab362011-04-18 06:19:50 +000035static void nc_wait_arp_handler(uchar *pkt, unsigned dest,
Joe Hershberger5874dec2015-04-08 01:41:01 -050036 struct in_addr sip, unsigned src,
wdenkb8fb6192004-08-02 21:11:11 +000037 unsigned len)
38{
Joe Hershbergerd4bb76a2012-05-23 07:59:14 +000039 net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */
wdenkb8fb6192004-08-02 21:11:11 +000040}
41
Joe Hershberger5874dec2015-04-08 01:41:01 -050042static void nc_handler(uchar *pkt, unsigned dest, struct in_addr sip,
43 unsigned src, unsigned len)
wdenkb8fb6192004-08-02 21:11:11 +000044{
wdenka0ebde52004-09-08 22:03:11 +000045 if (input_size)
Joe Hershbergerd4bb76a2012-05-23 07:59:14 +000046 net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */
wdenkb8fb6192004-08-02 21:11:11 +000047}
48
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -050049static void nc_timeout_handler(void)
wdenkb8fb6192004-08-02 21:11:11 +000050{
Joe Hershbergerd4bb76a2012-05-23 07:59:14 +000051 net_set_state(NETLOOP_SUCCESS);
wdenkb8fb6192004-08-02 21:11:11 +000052}
53
Joe Hershberger5874dec2015-04-08 01:41:01 -050054static int is_broadcast(struct in_addr ip)
Joe Hershberger66257802012-09-18 10:01:31 +000055{
Joe Hershberger5874dec2015-04-08 01:41:01 -050056 static struct in_addr netmask;
57 static struct in_addr our_ip;
Joe Hershberger66257802012-09-18 10:01:31 +000058 static int env_changed_id;
59 int env_id = get_env_id();
60
61 /* update only when the environment has changed */
62 if (env_changed_id != env_id) {
Simon Glassda1a1342017-08-03 12:22:15 -060063 netmask = env_get_ip("netmask");
64 our_ip = env_get_ip("ipaddr");
Joe Hershberger66257802012-09-18 10:01:31 +000065
66 env_changed_id = env_id;
67 }
68
Joe Hershberger5874dec2015-04-08 01:41:01 -050069 return (ip.s_addr == ~0 || /* 255.255.255.255 (global bcast) */
70 ((netmask.s_addr & our_ip.s_addr) ==
71 (netmask.s_addr & ip.s_addr) && /* on the same net and */
72 (netmask.s_addr | ip.s_addr) == ~0)); /* bcast to our net */
Joe Hershberger66257802012-09-18 10:01:31 +000073}
74
75static int refresh_settings_from_env(void)
76{
77 const char *p;
78 static int env_changed_id;
79 int env_id = get_env_id();
80
81 /* update only when the environment has changed */
82 if (env_changed_id != env_id) {
Simon Glass64b723f2017-08-03 12:22:12 -060083 if (env_get("ncip")) {
Simon Glassda1a1342017-08-03 12:22:15 -060084 nc_ip = env_get_ip("ncip");
Joe Hershberger5874dec2015-04-08 01:41:01 -050085 if (!nc_ip.s_addr)
Joe Hershberger66257802012-09-18 10:01:31 +000086 return -1; /* ncip is 0.0.0.0 */
Simon Glass64b723f2017-08-03 12:22:12 -060087 p = strchr(env_get("ncip"), ':');
Joe Hershberger66257802012-09-18 10:01:31 +000088 if (p != NULL) {
89 nc_out_port = simple_strtoul(p + 1, NULL, 10);
90 nc_in_port = nc_out_port;
91 }
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -050092 } else {
Joe Hershberger5874dec2015-04-08 01:41:01 -050093 nc_ip.s_addr = ~0; /* ncip is not set, so broadcast */
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -050094 }
Joe Hershberger66257802012-09-18 10:01:31 +000095
Simon Glass64b723f2017-08-03 12:22:12 -060096 p = env_get("ncoutport");
Joe Hershberger66257802012-09-18 10:01:31 +000097 if (p != NULL)
98 nc_out_port = simple_strtoul(p, NULL, 10);
Simon Glass64b723f2017-08-03 12:22:12 -060099 p = env_get("ncinport");
Joe Hershberger66257802012-09-18 10:01:31 +0000100 if (p != NULL)
101 nc_in_port = simple_strtoul(p, NULL, 10);
102
103 if (is_broadcast(nc_ip))
104 /* broadcast MAC address */
105 memset(nc_ether, 0xff, sizeof(nc_ether));
106 else
107 /* force arp request */
108 memset(nc_ether, 0, sizeof(nc_ether));
109 }
110 return 0;
111}
112
113/**
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500114 * Called from net_loop in net/net.c before each packet
Joe Hershberger66257802012-09-18 10:01:31 +0000115 */
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500116void nc_start(void)
wdenkb8fb6192004-08-02 21:11:11 +0000117{
Joe Hershberger66257802012-09-18 10:01:31 +0000118 refresh_settings_from_env();
Joe Hershberger8ecdbed2015-04-08 01:41:04 -0500119 if (!output_packet_len || memcmp(nc_ether, net_null_ethaddr, 6)) {
wdenkb8fb6192004-08-02 21:11:11 +0000120 /* going to check for input packet */
Joe Hershbergerf50357b2012-05-23 07:59:15 +0000121 net_set_udp_handler(nc_handler);
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500122 net_set_timeout_handler(net_timeout, nc_timeout_handler);
wdenkb8fb6192004-08-02 21:11:11 +0000123 } else {
124 /* send arp request */
wdenka0ebde52004-09-08 22:03:11 +0000125 uchar *pkt;
Joe Hershbergerf50357b2012-05-23 07:59:15 +0000126 net_set_arp_handler(nc_wait_arp_handler);
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500127 pkt = (uchar *)net_tx_packet + net_eth_hdr_size() +
128 IP_UDP_HDR_SIZE;
Joe Hershberger107b3d02012-05-15 08:59:13 +0000129 memcpy(pkt, output_packet, output_packet_len);
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500130 net_send_udp_packet(nc_ether, nc_ip, nc_out_port, nc_in_port,
131 output_packet_len);
wdenkb8fb6192004-08-02 21:11:11 +0000132 }
133}
134
Joe Hershberger5874dec2015-04-08 01:41:01 -0500135int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port,
Joe Hershberger58cd2182012-09-18 10:01:32 +0000136 unsigned src_port, unsigned len)
wdenkb8fb6192004-08-02 21:11:11 +0000137{
wdenka0ebde52004-09-08 22:03:11 +0000138 int end, chunk;
139
Joe Hershberger58cd2182012-09-18 10:01:32 +0000140 if (dest_port != nc_in_port || !len)
Joe Hershberger107b3d02012-05-15 08:59:13 +0000141 return 0; /* not for us */
wdenkb8fb6192004-08-02 21:11:11 +0000142
Joe Hershberger5874dec2015-04-08 01:41:01 -0500143 if (src_ip.s_addr != nc_ip.s_addr && !is_broadcast(nc_ip))
Joe Hershberger58cd2182012-09-18 10:01:32 +0000144 return 0; /* not from our client */
145
Joe Hershberger05a377b2012-05-23 08:01:04 +0000146 debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt);
147
Joe Hershberger107b3d02012-05-15 08:59:13 +0000148 if (input_size == sizeof(input_buffer))
149 return 1; /* no space */
150 if (len > sizeof(input_buffer) - input_size)
151 len = sizeof(input_buffer) - input_size;
wdenka0ebde52004-09-08 22:03:11 +0000152
153 end = input_offset + input_size;
Joe Hershberger83c48c52017-08-30 17:32:31 -0500154 if (end >= sizeof(input_buffer))
Joe Hershberger107b3d02012-05-15 08:59:13 +0000155 end -= sizeof(input_buffer);
wdenka0ebde52004-09-08 22:03:11 +0000156
157 chunk = len;
Joe Hershberger83c48c52017-08-30 17:32:31 -0500158 /* Check if packet will wrap in input_buffer */
159 if (end + len >= sizeof(input_buffer)) {
Joe Hershberger107b3d02012-05-15 08:59:13 +0000160 chunk = sizeof(input_buffer) - end;
Joe Hershberger83c48c52017-08-30 17:32:31 -0500161 /* Copy the second part of the pkt to start of input_buffer */
wdenka0ebde52004-09-08 22:03:11 +0000162 memcpy(input_buffer, pkt + chunk, len - chunk);
163 }
Joe Hershberger83c48c52017-08-30 17:32:31 -0500164 /* Copy first (or only) part of pkt after end of current valid input*/
Joe Hershberger107b3d02012-05-15 08:59:13 +0000165 memcpy(input_buffer + end, pkt, chunk);
wdenka0ebde52004-09-08 22:03:11 +0000166
167 input_size += len;
168
wdenkb8fb6192004-08-02 21:11:11 +0000169 return 1;
170}
171
Joe Hershberger107b3d02012-05-15 08:59:13 +0000172static void nc_send_packet(const char *buf, int len)
wdenkb8fb6192004-08-02 21:11:11 +0000173{
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200174#ifdef CONFIG_DM_ETH
175 struct udevice *eth;
176#else
wdenkb8fb6192004-08-02 21:11:11 +0000177 struct eth_device *eth;
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200178#endif
wdenkb8fb6192004-08-02 21:11:11 +0000179 int inited = 0;
180 uchar *pkt;
wdenka0ebde52004-09-08 22:03:11 +0000181 uchar *ether;
Joe Hershberger5874dec2015-04-08 01:41:01 -0500182 struct in_addr ip;
wdenkb8fb6192004-08-02 21:11:11 +0000183
Joe Hershberger05a377b2012-05-23 08:01:04 +0000184 debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf);
185
Joe Hershberger107b3d02012-05-15 08:59:13 +0000186 eth = eth_get_dev();
187 if (eth == NULL)
wdenkb8fb6192004-08-02 21:11:11 +0000188 return;
189
Joe Hershberger8ecdbed2015-04-08 01:41:04 -0500190 if (!memcmp(nc_ether, net_null_ethaddr, 6)) {
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200191 if (eth_is_active(eth))
wdenka0ebde52004-09-08 22:03:11 +0000192 return; /* inside net loop */
193 output_packet = buf;
194 output_packet_len = len;
Suriyan Ramasami7f37f222013-10-16 09:54:24 -0700195 input_recursion = 1;
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500196 net_loop(NETCONS); /* wait for arp reply and send packet */
Suriyan Ramasami7f37f222013-10-16 09:54:24 -0700197 input_recursion = 0;
wdenka0ebde52004-09-08 22:03:11 +0000198 output_packet_len = 0;
wdenkb8fb6192004-08-02 21:11:11 +0000199 return;
200 }
201
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200202 if (!eth_is_active(eth)) {
Joe Hershberger9f374062012-08-03 10:59:08 +0000203 if (eth_is_on_demand_init()) {
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500204 if (eth_init() < 0)
Joe Hershberger9f374062012-08-03 10:59:08 +0000205 return;
206 eth_set_last_protocol(NETCONS);
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500207 } else {
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500208 eth_init_state_only();
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500209 }
Joe Hershberger9f374062012-08-03 10:59:08 +0000210
wdenkb8fb6192004-08-02 21:11:11 +0000211 inited = 1;
212 }
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500213 pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
Joe Hershberger107b3d02012-05-15 08:59:13 +0000214 memcpy(pkt, buf, len);
wdenka0ebde52004-09-08 22:03:11 +0000215 ether = nc_ether;
216 ip = nc_ip;
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500217 net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len);
wdenkb8fb6192004-08-02 21:11:11 +0000218
Joe Hershberger9f374062012-08-03 10:59:08 +0000219 if (inited) {
220 if (eth_is_on_demand_init())
221 eth_halt();
222 else
223 eth_halt_state_only();
224 }
wdenkb8fb6192004-08-02 21:11:11 +0000225}
226
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500227static int nc_stdio_start(struct stdio_dev *dev)
wdenkb8fb6192004-08-02 21:11:11 +0000228{
Joe Hershberger66257802012-09-18 10:01:31 +0000229 int retval;
wdenka0ebde52004-09-08 22:03:11 +0000230
Joe Hershbergerd0f53962012-07-31 06:06:41 +0000231 nc_out_port = 6666; /* default port */
232 nc_in_port = nc_out_port;
wdenkb8fb6192004-08-02 21:11:11 +0000233
Joe Hershberger66257802012-09-18 10:01:31 +0000234 retval = refresh_settings_from_env();
235 if (retval != 0)
236 return retval;
wdenka0ebde52004-09-08 22:03:11 +0000237
Joe Hershberger6a9ed162012-05-23 07:59:23 +0000238 /*
239 * Initialize the static IP settings and buffer pointers
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500240 * incase we call net_send_udp_packet before net_loop
Joe Hershberger6a9ed162012-05-23 07:59:23 +0000241 */
242 net_init();
243
wdenka0ebde52004-09-08 22:03:11 +0000244 return 0;
wdenkb8fb6192004-08-02 21:11:11 +0000245}
246
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500247static void nc_stdio_putc(struct stdio_dev *dev, char c)
wdenkb8fb6192004-08-02 21:11:11 +0000248{
249 if (output_recursion)
250 return;
251 output_recursion = 1;
252
Joe Hershberger107b3d02012-05-15 08:59:13 +0000253 nc_send_packet(&c, 1);
wdenkb8fb6192004-08-02 21:11:11 +0000254
255 output_recursion = 0;
256}
257
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500258static void nc_stdio_puts(struct stdio_dev *dev, const char *s)
wdenkb8fb6192004-08-02 21:11:11 +0000259{
wdenkb21608e2005-04-20 09:28:54 +0000260 int len;
261
wdenkb8fb6192004-08-02 21:11:11 +0000262 if (output_recursion)
263 return;
264 output_recursion = 1;
265
Michael Walle9227aed2011-10-07 12:27:50 +0000266 len = strlen(s);
267 while (len) {
Masahiro Yamadadb204642014-11-07 03:03:31 +0900268 int send_len = min(len, (int)sizeof(input_buffer));
Michael Walle9227aed2011-10-07 12:27:50 +0000269 nc_send_packet(s, send_len);
270 len -= send_len;
271 s += send_len;
272 }
wdenkb8fb6192004-08-02 21:11:11 +0000273
274 output_recursion = 0;
275}
276
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500277static int nc_stdio_getc(struct stdio_dev *dev)
wdenkb8fb6192004-08-02 21:11:11 +0000278{
wdenkb21608e2005-04-20 09:28:54 +0000279 uchar c;
280
wdenkb8fb6192004-08-02 21:11:11 +0000281 input_recursion = 1;
282
283 net_timeout = 0; /* no timeout */
wdenka0ebde52004-09-08 22:03:11 +0000284 while (!input_size)
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500285 net_loop(NETCONS);
wdenkb8fb6192004-08-02 21:11:11 +0000286
287 input_recursion = 0;
288
wdenkb21608e2005-04-20 09:28:54 +0000289 c = input_buffer[input_offset++];
290
Joe Hershberger107b3d02012-05-15 08:59:13 +0000291 if (input_offset >= sizeof(input_buffer))
292 input_offset -= sizeof(input_buffer);
wdenka0ebde52004-09-08 22:03:11 +0000293 input_size--;
wdenkb8fb6192004-08-02 21:11:11 +0000294
wdenka0ebde52004-09-08 22:03:11 +0000295 return c;
wdenkb8fb6192004-08-02 21:11:11 +0000296}
297
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500298static int nc_stdio_tstc(struct stdio_dev *dev)
wdenkb8fb6192004-08-02 21:11:11 +0000299{
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200300#ifdef CONFIG_DM_ETH
301 struct udevice *eth;
302#else
wdenkb8fb6192004-08-02 21:11:11 +0000303 struct eth_device *eth;
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200304#endif
wdenkb8fb6192004-08-02 21:11:11 +0000305
306 if (input_recursion)
307 return 0;
308
wdenka0ebde52004-09-08 22:03:11 +0000309 if (input_size)
wdenkb8fb6192004-08-02 21:11:11 +0000310 return 1;
311
Joe Hershberger107b3d02012-05-15 08:59:13 +0000312 eth = eth_get_dev();
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200313 if (eth_is_active(eth))
wdenkb8fb6192004-08-02 21:11:11 +0000314 return 0; /* inside net loop */
315
316 input_recursion = 1;
317
318 net_timeout = 1;
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500319 net_loop(NETCONS); /* kind of poll */
wdenkb8fb6192004-08-02 21:11:11 +0000320
321 input_recursion = 0;
322
wdenka0ebde52004-09-08 22:03:11 +0000323 return input_size != 0;
wdenkb8fb6192004-08-02 21:11:11 +0000324}
325
Joe Hershberger107b3d02012-05-15 08:59:13 +0000326int drv_nc_init(void)
wdenkb8fb6192004-08-02 21:11:11 +0000327{
Jean-Christophe PLAGNIOL-VILLARD2a7a0312009-05-16 12:14:54 +0200328 struct stdio_dev dev;
wdenkb8fb6192004-08-02 21:11:11 +0000329 int rc;
330
Joe Hershberger107b3d02012-05-15 08:59:13 +0000331 memset(&dev, 0, sizeof(dev));
wdenkb8fb6192004-08-02 21:11:11 +0000332
Joe Hershberger107b3d02012-05-15 08:59:13 +0000333 strcpy(dev.name, "nc");
Bin Meng6abe4b62015-11-03 23:23:37 -0800334 dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500335 dev.start = nc_stdio_start;
336 dev.putc = nc_stdio_putc;
337 dev.puts = nc_stdio_puts;
338 dev.getc = nc_stdio_getc;
339 dev.tstc = nc_stdio_tstc;
wdenkb8fb6192004-08-02 21:11:11 +0000340
Joe Hershberger107b3d02012-05-15 08:59:13 +0000341 rc = stdio_register(&dev);
wdenkb8fb6192004-08-02 21:11:11 +0000342
343 return (rc == 0) ? 1 : rc;
344}