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