u-boot/drivers/net/netconsole.c
Joe Hershberger f8be7d659c net: Improve the speed of netconsole
Previously u-boot would initialize the network interface for every
network operation and then shut it down again.  This makes sense for
most operations where the network in not known to be needed soon after
the operation is complete.  In the case of netconsole, it will use the
network for every interaction with the shell or every printf.  This
means that the network is being reinitialized very often.  On many
devices, this intialization is very slow.

This patch checks for consecutive netconsole actions and leaves the
ethernet hardware initialized between them.  It will still behave the
same old way for all other network operations and any time another
network operation happens between netconsole operations.

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
Cc: Stefano Babic <sbabic@denx.de>
Acked-by: Stefano Babic <sbabic@denx.de>
2012-09-24 13:55:43 -05:00

307 lines
6.8 KiB
C

/*
* (C) Copyright 2004
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <command.h>
#include <stdio_dev.h>
#include <net.h>
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_NETCONSOLE_BUFFER_SIZE
#define CONFIG_NETCONSOLE_BUFFER_SIZE 512
#endif
static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE];
static int input_size; /* char count in input buffer */
static int input_offset; /* offset to valid chars in input buffer */
static int input_recursion;
static int output_recursion;
static int net_timeout;
static uchar nc_ether[6]; /* server enet address */
static IPaddr_t nc_ip; /* server ip */
static short nc_out_port; /* target output port */
static short nc_in_port; /* source input port */
static const char *output_packet; /* used by first send udp */
static int output_packet_len;
/*
* Start with a default last protocol.
* We are only interested in NETCONS or not.
*/
enum proto_t net_loop_last_protocol = BOOTP;
static void nc_wait_arp_handler(uchar *pkt, unsigned dest,
IPaddr_t sip, unsigned src,
unsigned len)
{
net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */
}
static void nc_handler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
unsigned len)
{
if (input_size)
net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */
}
static void nc_timeout(void)
{
net_set_state(NETLOOP_SUCCESS);
}
void NcStart(void)
{
if (!output_packet_len || memcmp(nc_ether, NetEtherNullAddr, 6)) {
/* going to check for input packet */
net_set_udp_handler(nc_handler);
NetSetTimeout(net_timeout, nc_timeout);
} else {
/* send arp request */
uchar *pkt;
net_set_arp_handler(nc_wait_arp_handler);
pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
memcpy(pkt, output_packet, output_packet_len);
NetSendUDPPacket(nc_ether, nc_ip, nc_out_port, nc_in_port,
output_packet_len);
}
}
int nc_input_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len)
{
int end, chunk;
if (dest != nc_in_port || !len)
return 0; /* not for us */
debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt);
if (input_size == sizeof(input_buffer))
return 1; /* no space */
if (len > sizeof(input_buffer) - input_size)
len = sizeof(input_buffer) - input_size;
end = input_offset + input_size;
if (end > sizeof(input_buffer))
end -= sizeof(input_buffer);
chunk = len;
if (end + len > sizeof(input_buffer)) {
chunk = sizeof(input_buffer) - end;
memcpy(input_buffer, pkt + chunk, len - chunk);
}
memcpy(input_buffer + end, pkt, chunk);
input_size += len;
return 1;
}
static void nc_send_packet(const char *buf, int len)
{
struct eth_device *eth;
int inited = 0;
uchar *pkt;
uchar *ether;
IPaddr_t ip;
debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf);
eth = eth_get_dev();
if (eth == NULL)
return;
if (!memcmp(nc_ether, NetEtherNullAddr, 6)) {
if (eth->state == ETH_STATE_ACTIVE)
return; /* inside net loop */
output_packet = buf;
output_packet_len = len;
NetLoop(NETCONS); /* wait for arp reply and send packet */
output_packet_len = 0;
return;
}
if (eth->state != ETH_STATE_ACTIVE) {
if (eth_is_on_demand_init()) {
if (eth_init(gd->bd) < 0)
return;
eth_set_last_protocol(NETCONS);
} else
eth_init_state_only(gd->bd);
inited = 1;
}
pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
memcpy(pkt, buf, len);
ether = nc_ether;
ip = nc_ip;
NetSendUDPPacket(ether, ip, nc_out_port, nc_in_port, len);
if (inited) {
if (eth_is_on_demand_init())
eth_halt();
else
eth_halt_state_only();
}
}
static int nc_start(void)
{
int netmask, our_ip;
char *p;
nc_out_port = 6666; /* default port */
nc_in_port = nc_out_port;
if (getenv("ncip")) {
nc_ip = getenv_IPaddr("ncip");
if (!nc_ip)
return -1; /* ncip is 0.0.0.0 */
p = strchr(getenv("ncip"), ':');
if (p != NULL) {
nc_out_port = simple_strtoul(p + 1, NULL, 10);
nc_in_port = nc_out_port;
}
} else
nc_ip = ~0; /* ncip is not set, so broadcast */
p = getenv("ncoutport");
if (p != NULL)
nc_out_port = simple_strtoul(p, NULL, 10);
p = getenv("ncinport");
if (p != NULL)
nc_in_port = simple_strtoul(p, NULL, 10);
our_ip = getenv_IPaddr("ipaddr");
netmask = getenv_IPaddr("netmask");
if (nc_ip == ~0 || /* 255.255.255.255 */
((netmask & our_ip) == (netmask & nc_ip) && /* on the same net */
(netmask | nc_ip) == ~0)) /* broadcast to our net */
memset(nc_ether, 0xff, sizeof(nc_ether));
else
memset(nc_ether, 0, sizeof(nc_ether)); /* force arp request */
/*
* Initialize the static IP settings and buffer pointers
* incase we call NetSendUDPPacket before NetLoop
*/
net_init();
return 0;
}
static void nc_putc(char c)
{
if (output_recursion)
return;
output_recursion = 1;
nc_send_packet(&c, 1);
output_recursion = 0;
}
static void nc_puts(const char *s)
{
int len;
if (output_recursion)
return;
output_recursion = 1;
len = strlen(s);
while (len) {
int send_len = min(len, sizeof(input_buffer));
nc_send_packet(s, send_len);
len -= send_len;
s += send_len;
}
output_recursion = 0;
}
static int nc_getc(void)
{
uchar c;
input_recursion = 1;
net_timeout = 0; /* no timeout */
while (!input_size)
NetLoop(NETCONS);
input_recursion = 0;
c = input_buffer[input_offset++];
if (input_offset >= sizeof(input_buffer))
input_offset -= sizeof(input_buffer);
input_size--;
return c;
}
static int nc_tstc(void)
{
struct eth_device *eth;
if (input_recursion)
return 0;
if (input_size)
return 1;
eth = eth_get_dev();
if (eth && eth->state == ETH_STATE_ACTIVE)
return 0; /* inside net loop */
input_recursion = 1;
net_timeout = 1;
NetLoop(NETCONS); /* kind of poll */
input_recursion = 0;
return input_size != 0;
}
int drv_nc_init(void)
{
struct stdio_dev dev;
int rc;
memset(&dev, 0, sizeof(dev));
strcpy(dev.name, "nc");
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
dev.start = nc_start;
dev.putc = nc_putc;
dev.puts = nc_puts;
dev.getc = nc_getc;
dev.tstc = nc_tstc;
rc = stdio_register(&dev);
return (rc == 0) ? 1 : rc;
}