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