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