uboot/drivers/net/netconsole.c
<<
>>
Prefs
   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
  13DECLARE_GLOBAL_DATA_PTR;
  14
  15#ifndef CONFIG_NETCONSOLE_BUFFER_SIZE
  16#define CONFIG_NETCONSOLE_BUFFER_SIZE 512
  17#endif
  18
  19static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE];
  20static int input_size; /* char count in input buffer */
  21static int input_offset; /* offset to valid chars in input buffer */
  22static int input_recursion;
  23static int output_recursion;
  24static int net_timeout;
  25static uchar nc_ether[6]; /* server enet address */
  26static struct in_addr nc_ip; /* server ip */
  27static short nc_out_port; /* target output port */
  28static short nc_in_port; /* source input port */
  29static const char *output_packet; /* used by first send udp */
  30static int output_packet_len;
  31/*
  32 * Start with a default last protocol.
  33 * We are only interested in NETCONS or not.
  34 */
  35enum proto_t net_loop_last_protocol = BOOTP;
  36
  37static 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
  44static 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
  51static void nc_timeout_handler(void)
  52{
  53        net_set_state(NETLOOP_SUCCESS);
  54}
  55
  56static 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
  77static 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
  98                p = getenv("ncoutport");
  99                if (p != NULL)
 100                        nc_out_port = simple_strtoul(p, NULL, 10);
 101                p = getenv("ncinport");
 102                if (p != NULL)
 103                        nc_in_port = simple_strtoul(p, NULL, 10);
 104
 105                if (is_broadcast(nc_ip))
 106                        /* broadcast MAC address */
 107                        memset(nc_ether, 0xff, sizeof(nc_ether));
 108                else
 109                        /* force arp request */
 110                        memset(nc_ether, 0, sizeof(nc_ether));
 111        }
 112        return 0;
 113}
 114
 115/**
 116 * Called from net_loop in net/net.c before each packet
 117 */
 118void nc_start(void)
 119{
 120        refresh_settings_from_env();
 121        if (!output_packet_len || memcmp(nc_ether, net_null_ethaddr, 6)) {
 122                /* going to check for input packet */
 123                net_set_udp_handler(nc_handler);
 124                net_set_timeout_handler(net_timeout, nc_timeout_handler);
 125        } else {
 126                /* send arp request */
 127                uchar *pkt;
 128                net_set_arp_handler(nc_wait_arp_handler);
 129                pkt = (uchar *)net_tx_packet + net_eth_hdr_size() +
 130                        IP_UDP_HDR_SIZE;
 131                memcpy(pkt, output_packet, output_packet_len);
 132                net_send_udp_packet(nc_ether, nc_ip, nc_out_port, nc_in_port,
 133                                    output_packet_len);
 134        }
 135}
 136
 137int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port,
 138        unsigned src_port, unsigned len)
 139{
 140        int end, chunk;
 141
 142        if (dest_port != nc_in_port || !len)
 143                return 0; /* not for us */
 144
 145        if (src_ip.s_addr != nc_ip.s_addr && !is_broadcast(nc_ip))
 146                return 0; /* not from our client */
 147
 148        debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt);
 149
 150        if (input_size == sizeof(input_buffer))
 151                return 1; /* no space */
 152        if (len > sizeof(input_buffer) - input_size)
 153                len = sizeof(input_buffer) - input_size;
 154
 155        end = input_offset + input_size;
 156        if (end > sizeof(input_buffer))
 157                end -= sizeof(input_buffer);
 158
 159        chunk = len;
 160        if (end + len > sizeof(input_buffer)) {
 161                chunk = sizeof(input_buffer) - end;
 162                memcpy(input_buffer, pkt + chunk, len - chunk);
 163        }
 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