blob: ce5a15ef57cea6ae378ef2dbd0ca3fbd2122a326 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenkb8fb6192004-08-02 21:11:11 +00002/*
3 * (C) Copyright 2004
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
wdenkb8fb6192004-08-02 21:11:11 +00005 */
6
7#include <common.h>
wdenkb8fb6192004-08-02 21:11:11 +00008#include <command.h>
Jean-Christophe PLAGNIOL-VILLARD2a7a0312009-05-16 12:14:54 +02009#include <stdio_dev.h>
wdenkb8fb6192004-08-02 21:11:11 +000010#include <net.h>
11
Joe Hershberger45f4a232012-07-31 06:09:17 +000012#ifndef CONFIG_NETCONSOLE_BUFFER_SIZE
13#define CONFIG_NETCONSOLE_BUFFER_SIZE 512
14#endif
15
16static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE];
Joe Hershberger107b3d02012-05-15 08:59:13 +000017static int input_size; /* char count in input buffer */
18static int input_offset; /* offset to valid chars in input buffer */
19static int input_recursion;
20static int output_recursion;
wdenkb8fb6192004-08-02 21:11:11 +000021static int net_timeout;
Joe Hershberger107b3d02012-05-15 08:59:13 +000022static uchar nc_ether[6]; /* server enet address */
Joe Hershberger5874dec2015-04-08 01:41:01 -050023static struct in_addr nc_ip; /* server ip */
Joe Hershbergerd0f53962012-07-31 06:06:41 +000024static short nc_out_port; /* target output port */
25static short nc_in_port; /* source input port */
Joe Hershberger107b3d02012-05-15 08:59:13 +000026static const char *output_packet; /* used by first send udp */
27static int output_packet_len;
Joe Hershberger9f374062012-08-03 10:59:08 +000028/*
29 * Start with a default last protocol.
30 * We are only interested in NETCONS or not.
31 */
32enum proto_t net_loop_last_protocol = BOOTP;
wdenkb8fb6192004-08-02 21:11:11 +000033
Luca Ceresoli428ab362011-04-18 06:19:50 +000034static void nc_wait_arp_handler(uchar *pkt, unsigned dest,
Joe Hershberger5874dec2015-04-08 01:41:01 -050035 struct in_addr sip, unsigned src,
wdenkb8fb6192004-08-02 21:11:11 +000036 unsigned len)
37{
Joe Hershbergerd4bb76a2012-05-23 07:59:14 +000038 net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */
wdenkb8fb6192004-08-02 21:11:11 +000039}
40
Joe Hershberger5874dec2015-04-08 01:41:01 -050041static void nc_handler(uchar *pkt, unsigned dest, struct in_addr sip,
42 unsigned src, unsigned len)
wdenkb8fb6192004-08-02 21:11:11 +000043{
wdenka0ebde52004-09-08 22:03:11 +000044 if (input_size)
Joe Hershbergerd4bb76a2012-05-23 07:59:14 +000045 net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */
wdenkb8fb6192004-08-02 21:11:11 +000046}
47
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -050048static void nc_timeout_handler(void)
wdenkb8fb6192004-08-02 21:11:11 +000049{
Joe Hershbergerd4bb76a2012-05-23 07:59:14 +000050 net_set_state(NETLOOP_SUCCESS);
wdenkb8fb6192004-08-02 21:11:11 +000051}
52
Joe Hershberger5874dec2015-04-08 01:41:01 -050053static int is_broadcast(struct in_addr ip)
Joe Hershberger66257802012-09-18 10:01:31 +000054{
Joe Hershberger5874dec2015-04-08 01:41:01 -050055 static struct in_addr netmask;
56 static struct in_addr our_ip;
Joe Hershberger66257802012-09-18 10:01:31 +000057 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) {
Simon Glassda1a1342017-08-03 12:22:15 -060062 netmask = env_get_ip("netmask");
63 our_ip = env_get_ip("ipaddr");
Joe Hershberger66257802012-09-18 10:01:31 +000064
65 env_changed_id = env_id;
66 }
67
Joe Hershberger5874dec2015-04-08 01:41:01 -050068 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 */
Joe Hershberger66257802012-09-18 10:01:31 +000072}
73
74static 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) {
Simon Glass64b723f2017-08-03 12:22:12 -060082 if (env_get("ncip")) {
Simon Glassda1a1342017-08-03 12:22:15 -060083 nc_ip = env_get_ip("ncip");
Joe Hershberger5874dec2015-04-08 01:41:01 -050084 if (!nc_ip.s_addr)
Joe Hershberger66257802012-09-18 10:01:31 +000085 return -1; /* ncip is 0.0.0.0 */
Simon Glass64b723f2017-08-03 12:22:12 -060086 p = strchr(env_get("ncip"), ':');
Joe Hershberger66257802012-09-18 10:01:31 +000087 if (p != NULL) {
88 nc_out_port = simple_strtoul(p + 1, NULL, 10);
89 nc_in_port = nc_out_port;
90 }
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -050091 } else {
Joe Hershberger5874dec2015-04-08 01:41:01 -050092 nc_ip.s_addr = ~0; /* ncip is not set, so broadcast */
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -050093 }
Joe Hershberger66257802012-09-18 10:01:31 +000094
Simon Glass64b723f2017-08-03 12:22:12 -060095 p = env_get("ncoutport");
Joe Hershberger66257802012-09-18 10:01:31 +000096 if (p != NULL)
97 nc_out_port = simple_strtoul(p, NULL, 10);
Simon Glass64b723f2017-08-03 12:22:12 -060098 p = env_get("ncinport");
Joe Hershberger66257802012-09-18 10:01:31 +000099 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/**
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500113 * Called from net_loop in net/net.c before each packet
Joe Hershberger66257802012-09-18 10:01:31 +0000114 */
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500115void nc_start(void)
wdenkb8fb6192004-08-02 21:11:11 +0000116{
Joe Hershberger66257802012-09-18 10:01:31 +0000117 refresh_settings_from_env();
Joe Hershberger8ecdbed2015-04-08 01:41:04 -0500118 if (!output_packet_len || memcmp(nc_ether, net_null_ethaddr, 6)) {
wdenkb8fb6192004-08-02 21:11:11 +0000119 /* going to check for input packet */
Joe Hershbergerf50357b2012-05-23 07:59:15 +0000120 net_set_udp_handler(nc_handler);
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500121 net_set_timeout_handler(net_timeout, nc_timeout_handler);
wdenkb8fb6192004-08-02 21:11:11 +0000122 } else {
123 /* send arp request */
wdenka0ebde52004-09-08 22:03:11 +0000124 uchar *pkt;
Joe Hershbergerf50357b2012-05-23 07:59:15 +0000125 net_set_arp_handler(nc_wait_arp_handler);
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500126 pkt = (uchar *)net_tx_packet + net_eth_hdr_size() +
127 IP_UDP_HDR_SIZE;
Joe Hershberger107b3d02012-05-15 08:59:13 +0000128 memcpy(pkt, output_packet, output_packet_len);
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500129 net_send_udp_packet(nc_ether, nc_ip, nc_out_port, nc_in_port,
130 output_packet_len);
wdenkb8fb6192004-08-02 21:11:11 +0000131 }
132}
133
Joe Hershberger5874dec2015-04-08 01:41:01 -0500134int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port,
Joe Hershberger58cd2182012-09-18 10:01:32 +0000135 unsigned src_port, unsigned len)
wdenkb8fb6192004-08-02 21:11:11 +0000136{
wdenka0ebde52004-09-08 22:03:11 +0000137 int end, chunk;
138
Joe Hershberger58cd2182012-09-18 10:01:32 +0000139 if (dest_port != nc_in_port || !len)
Joe Hershberger107b3d02012-05-15 08:59:13 +0000140 return 0; /* not for us */
wdenkb8fb6192004-08-02 21:11:11 +0000141
Joe Hershberger5874dec2015-04-08 01:41:01 -0500142 if (src_ip.s_addr != nc_ip.s_addr && !is_broadcast(nc_ip))
Joe Hershberger58cd2182012-09-18 10:01:32 +0000143 return 0; /* not from our client */
144
Joe Hershberger05a377b2012-05-23 08:01:04 +0000145 debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt);
146
Joe Hershberger107b3d02012-05-15 08:59:13 +0000147 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;
wdenka0ebde52004-09-08 22:03:11 +0000151
152 end = input_offset + input_size;
Joe Hershberger83c48c52017-08-30 17:32:31 -0500153 if (end >= sizeof(input_buffer))
Joe Hershberger107b3d02012-05-15 08:59:13 +0000154 end -= sizeof(input_buffer);
wdenka0ebde52004-09-08 22:03:11 +0000155
156 chunk = len;
Joe Hershberger83c48c52017-08-30 17:32:31 -0500157 /* Check if packet will wrap in input_buffer */
158 if (end + len >= sizeof(input_buffer)) {
Joe Hershberger107b3d02012-05-15 08:59:13 +0000159 chunk = sizeof(input_buffer) - end;
Joe Hershberger83c48c52017-08-30 17:32:31 -0500160 /* Copy the second part of the pkt to start of input_buffer */
wdenka0ebde52004-09-08 22:03:11 +0000161 memcpy(input_buffer, pkt + chunk, len - chunk);
162 }
Joe Hershberger83c48c52017-08-30 17:32:31 -0500163 /* Copy first (or only) part of pkt after end of current valid input*/
Joe Hershberger107b3d02012-05-15 08:59:13 +0000164 memcpy(input_buffer + end, pkt, chunk);
wdenka0ebde52004-09-08 22:03:11 +0000165
166 input_size += len;
167
wdenkb8fb6192004-08-02 21:11:11 +0000168 return 1;
169}
170
Joe Hershberger107b3d02012-05-15 08:59:13 +0000171static void nc_send_packet(const char *buf, int len)
wdenkb8fb6192004-08-02 21:11:11 +0000172{
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200173#ifdef CONFIG_DM_ETH
174 struct udevice *eth;
175#else
wdenkb8fb6192004-08-02 21:11:11 +0000176 struct eth_device *eth;
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200177#endif
wdenkb8fb6192004-08-02 21:11:11 +0000178 int inited = 0;
179 uchar *pkt;
wdenka0ebde52004-09-08 22:03:11 +0000180 uchar *ether;
Joe Hershberger5874dec2015-04-08 01:41:01 -0500181 struct in_addr ip;
wdenkb8fb6192004-08-02 21:11:11 +0000182
Joe Hershberger05a377b2012-05-23 08:01:04 +0000183 debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf);
184
Joe Hershberger107b3d02012-05-15 08:59:13 +0000185 eth = eth_get_dev();
186 if (eth == NULL)
wdenkb8fb6192004-08-02 21:11:11 +0000187 return;
188
Joe Hershberger8ecdbed2015-04-08 01:41:04 -0500189 if (!memcmp(nc_ether, net_null_ethaddr, 6)) {
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200190 if (eth_is_active(eth))
wdenka0ebde52004-09-08 22:03:11 +0000191 return; /* inside net loop */
192 output_packet = buf;
193 output_packet_len = len;
Suriyan Ramasami7f37f222013-10-16 09:54:24 -0700194 input_recursion = 1;
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500195 net_loop(NETCONS); /* wait for arp reply and send packet */
Suriyan Ramasami7f37f222013-10-16 09:54:24 -0700196 input_recursion = 0;
wdenka0ebde52004-09-08 22:03:11 +0000197 output_packet_len = 0;
wdenkb8fb6192004-08-02 21:11:11 +0000198 return;
199 }
200
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200201 if (!eth_is_active(eth)) {
Joe Hershberger9f374062012-08-03 10:59:08 +0000202 if (eth_is_on_demand_init()) {
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500203 if (eth_init() < 0)
Joe Hershberger9f374062012-08-03 10:59:08 +0000204 return;
205 eth_set_last_protocol(NETCONS);
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500206 } else {
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500207 eth_init_state_only();
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500208 }
Joe Hershberger9f374062012-08-03 10:59:08 +0000209
wdenkb8fb6192004-08-02 21:11:11 +0000210 inited = 1;
211 }
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500212 pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
Joe Hershberger107b3d02012-05-15 08:59:13 +0000213 memcpy(pkt, buf, len);
wdenka0ebde52004-09-08 22:03:11 +0000214 ether = nc_ether;
215 ip = nc_ip;
Joe Hershbergera8ca4f62015-04-08 01:41:05 -0500216 net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len);
wdenkb8fb6192004-08-02 21:11:11 +0000217
Joe Hershberger9f374062012-08-03 10:59:08 +0000218 if (inited) {
219 if (eth_is_on_demand_init())
220 eth_halt();
221 else
222 eth_halt_state_only();
223 }
wdenkb8fb6192004-08-02 21:11:11 +0000224}
225
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500226static int nc_stdio_start(struct stdio_dev *dev)
wdenkb8fb6192004-08-02 21:11:11 +0000227{
Joe Hershberger66257802012-09-18 10:01:31 +0000228 int retval;
wdenka0ebde52004-09-08 22:03:11 +0000229
Joe Hershbergerd0f53962012-07-31 06:06:41 +0000230 nc_out_port = 6666; /* default port */
231 nc_in_port = nc_out_port;
wdenkb8fb6192004-08-02 21:11:11 +0000232
Joe Hershberger66257802012-09-18 10:01:31 +0000233 retval = refresh_settings_from_env();
234 if (retval != 0)
235 return retval;
wdenka0ebde52004-09-08 22:03:11 +0000236
Joe Hershberger6a9ed162012-05-23 07:59:23 +0000237 /*
238 * Initialize the static IP settings and buffer pointers
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500239 * incase we call net_send_udp_packet before net_loop
Joe Hershberger6a9ed162012-05-23 07:59:23 +0000240 */
241 net_init();
242
wdenka0ebde52004-09-08 22:03:11 +0000243 return 0;
wdenkb8fb6192004-08-02 21:11:11 +0000244}
245
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500246static void nc_stdio_putc(struct stdio_dev *dev, char c)
wdenkb8fb6192004-08-02 21:11:11 +0000247{
248 if (output_recursion)
249 return;
250 output_recursion = 1;
251
Joe Hershberger107b3d02012-05-15 08:59:13 +0000252 nc_send_packet(&c, 1);
wdenkb8fb6192004-08-02 21:11:11 +0000253
254 output_recursion = 0;
255}
256
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500257static void nc_stdio_puts(struct stdio_dev *dev, const char *s)
wdenkb8fb6192004-08-02 21:11:11 +0000258{
wdenkb21608e2005-04-20 09:28:54 +0000259 int len;
260
wdenkb8fb6192004-08-02 21:11:11 +0000261 if (output_recursion)
262 return;
263 output_recursion = 1;
264
Michael Walle9227aed2011-10-07 12:27:50 +0000265 len = strlen(s);
266 while (len) {
Masahiro Yamadadb204642014-11-07 03:03:31 +0900267 int send_len = min(len, (int)sizeof(input_buffer));
Michael Walle9227aed2011-10-07 12:27:50 +0000268 nc_send_packet(s, send_len);
269 len -= send_len;
270 s += send_len;
271 }
wdenkb8fb6192004-08-02 21:11:11 +0000272
273 output_recursion = 0;
274}
275
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500276static int nc_stdio_getc(struct stdio_dev *dev)
wdenkb8fb6192004-08-02 21:11:11 +0000277{
wdenkb21608e2005-04-20 09:28:54 +0000278 uchar c;
279
wdenkb8fb6192004-08-02 21:11:11 +0000280 input_recursion = 1;
281
282 net_timeout = 0; /* no timeout */
wdenka0ebde52004-09-08 22:03:11 +0000283 while (!input_size)
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500284 net_loop(NETCONS);
wdenkb8fb6192004-08-02 21:11:11 +0000285
286 input_recursion = 0;
287
wdenkb21608e2005-04-20 09:28:54 +0000288 c = input_buffer[input_offset++];
289
Joe Hershberger107b3d02012-05-15 08:59:13 +0000290 if (input_offset >= sizeof(input_buffer))
291 input_offset -= sizeof(input_buffer);
wdenka0ebde52004-09-08 22:03:11 +0000292 input_size--;
wdenkb8fb6192004-08-02 21:11:11 +0000293
wdenka0ebde52004-09-08 22:03:11 +0000294 return c;
wdenkb8fb6192004-08-02 21:11:11 +0000295}
296
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500297static int nc_stdio_tstc(struct stdio_dev *dev)
wdenkb8fb6192004-08-02 21:11:11 +0000298{
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200299#ifdef CONFIG_DM_ETH
300 struct udevice *eth;
301#else
wdenkb8fb6192004-08-02 21:11:11 +0000302 struct eth_device *eth;
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200303#endif
wdenkb8fb6192004-08-02 21:11:11 +0000304
305 if (input_recursion)
306 return 0;
307
wdenka0ebde52004-09-08 22:03:11 +0000308 if (input_size)
wdenkb8fb6192004-08-02 21:11:11 +0000309 return 1;
310
Joe Hershberger107b3d02012-05-15 08:59:13 +0000311 eth = eth_get_dev();
Bernhard Nortmann752abb72015-09-14 15:29:44 +0200312 if (eth_is_active(eth))
wdenkb8fb6192004-08-02 21:11:11 +0000313 return 0; /* inside net loop */
314
315 input_recursion = 1;
316
317 net_timeout = 1;
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500318 net_loop(NETCONS); /* kind of poll */
wdenkb8fb6192004-08-02 21:11:11 +0000319
320 input_recursion = 0;
321
wdenka0ebde52004-09-08 22:03:11 +0000322 return input_size != 0;
wdenkb8fb6192004-08-02 21:11:11 +0000323}
324
Joe Hershberger107b3d02012-05-15 08:59:13 +0000325int drv_nc_init(void)
wdenkb8fb6192004-08-02 21:11:11 +0000326{
Jean-Christophe PLAGNIOL-VILLARD2a7a0312009-05-16 12:14:54 +0200327 struct stdio_dev dev;
wdenkb8fb6192004-08-02 21:11:11 +0000328 int rc;
329
Joe Hershberger107b3d02012-05-15 08:59:13 +0000330 memset(&dev, 0, sizeof(dev));
wdenkb8fb6192004-08-02 21:11:11 +0000331
Joe Hershberger107b3d02012-05-15 08:59:13 +0000332 strcpy(dev.name, "nc");
Bin Meng6abe4b62015-11-03 23:23:37 -0800333 dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
Joe Hershbergerd02aa6b2015-04-08 01:41:16 -0500334 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;
wdenkb8fb6192004-08-02 21:11:11 +0000339
Joe Hershberger107b3d02012-05-15 08:59:13 +0000340 rc = stdio_register(&dev);
wdenkb8fb6192004-08-02 21:11:11 +0000341
342 return (rc == 0) ? 1 : rc;
343}