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