|  | /* | 
|  | * (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> | 
|  |  | 
|  | #ifdef CONFIG_NETCONSOLE | 
|  |  | 
|  | #include <command.h> | 
|  | #include <devices.h> | 
|  | #include <net.h> | 
|  |  | 
|  | DECLARE_GLOBAL_DATA_PTR; | 
|  |  | 
|  | static char input_buffer[512]; | 
|  | static int input_size = 0;		/* char count in input buffer */ | 
|  | static int input_offset = 0;		/* offset to valid chars in input buffer */ | 
|  | static int input_recursion = 0; | 
|  | static int output_recursion = 0; | 
|  | static int net_timeout; | 
|  | static uchar nc_ether[6];		/* server enet address */ | 
|  | static IPaddr_t nc_ip;			/* server ip */ | 
|  | static short nc_port;			/* source/target port */ | 
|  | static const char *output_packet;	/* used by first send udp */ | 
|  | static int output_packet_len = 0; | 
|  |  | 
|  | static void nc_wait_arp_handler (uchar * pkt, unsigned dest, unsigned src, | 
|  | unsigned len) | 
|  | { | 
|  | NetState = NETLOOP_SUCCESS;	/* got arp reply - quit net loop */ | 
|  | } | 
|  |  | 
|  | static void nc_handler (uchar * pkt, unsigned dest, unsigned src, | 
|  | unsigned len) | 
|  | { | 
|  | if (input_size) | 
|  | NetState = NETLOOP_SUCCESS;	/* got input - quit net loop */ | 
|  | } | 
|  |  | 
|  | static void nc_timeout (void) | 
|  | { | 
|  | NetState = NETLOOP_SUCCESS; | 
|  | } | 
|  |  | 
|  | void NcStart (void) | 
|  | { | 
|  | if (!output_packet_len || memcmp (nc_ether, NetEtherNullAddr, 6)) { | 
|  | /* going to check for input packet */ | 
|  | NetSetHandler (nc_handler); | 
|  | NetSetTimeout (net_timeout, nc_timeout); | 
|  | } else { | 
|  | /* send arp request */ | 
|  | uchar *pkt; | 
|  | NetSetHandler (nc_wait_arp_handler); | 
|  | pkt = (uchar *) NetTxPacket + NetEthHdrSize () + IP_HDR_SIZE; | 
|  | memcpy (pkt, output_packet, output_packet_len); | 
|  | NetSendUDPPacket (nc_ether, nc_ip, nc_port, nc_port, output_packet_len); | 
|  | } | 
|  | } | 
|  |  | 
|  | int nc_input_packet (uchar * pkt, unsigned dest, unsigned src, unsigned len) | 
|  | { | 
|  | int end, chunk; | 
|  |  | 
|  | if (dest != nc_port || !len) | 
|  | return 0;		/* not for us */ | 
|  |  | 
|  | 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; | 
|  |  | 
|  | if ((eth = eth_get_dev ()) == 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_init (gd->bd) < 0) | 
|  | return; | 
|  | inited = 1; | 
|  | } | 
|  | pkt = (uchar *) NetTxPacket + NetEthHdrSize () + IP_HDR_SIZE; | 
|  | memcpy (pkt, buf, len); | 
|  | ether = nc_ether; | 
|  | ip = nc_ip; | 
|  | NetSendUDPPacket (ether, ip, nc_port, nc_port, len); | 
|  |  | 
|  | if (inited) | 
|  | eth_halt (); | 
|  | } | 
|  |  | 
|  | int nc_start (void) | 
|  | { | 
|  | int netmask, our_ip; | 
|  |  | 
|  | nc_port = 6666;		/* default port */ | 
|  |  | 
|  | if (getenv ("ncip")) { | 
|  | char *p; | 
|  |  | 
|  | nc_ip = getenv_IPaddr ("ncip"); | 
|  | if (!nc_ip) | 
|  | return -1;	/* ncip is 0.0.0.0 */ | 
|  | if ((p = strchr (getenv ("ncip"), ':')) != NULL) | 
|  | nc_port = simple_strtoul (p + 1, NULL, 10); | 
|  | } else | 
|  | nc_ip = ~0;		/* ncip is not set */ | 
|  |  | 
|  | 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 */ | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void nc_putc (char c) | 
|  | { | 
|  | if (output_recursion) | 
|  | return; | 
|  | output_recursion = 1; | 
|  |  | 
|  | nc_send_packet (&c, 1); | 
|  |  | 
|  | output_recursion = 0; | 
|  | } | 
|  |  | 
|  | void nc_puts (const char *s) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | if (output_recursion) | 
|  | return; | 
|  | output_recursion = 1; | 
|  |  | 
|  | if ((len = strlen (s)) > 512) | 
|  | len = 512; | 
|  |  | 
|  | nc_send_packet (s, len); | 
|  |  | 
|  | output_recursion = 0; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | device_t 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 = device_register (&dev); | 
|  |  | 
|  | return (rc == 0) ? 1 : rc; | 
|  | } | 
|  |  | 
|  | #endif	/* CONFIG_NETCONSOLE */ |