avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
51
u-boot/net/Kconfig
Normal file
51
u-boot/net/Kconfig
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# Network configuration
|
||||
#
|
||||
|
||||
menuconfig NET
|
||||
bool "Networking support"
|
||||
|
||||
if NET
|
||||
|
||||
config NET_RANDOM_ETHADDR
|
||||
bool "Random ethaddr if unset"
|
||||
select LIB_RAND
|
||||
help
|
||||
Selecting this will allow the Ethernet interface to function
|
||||
even when the ethaddr variable for that interface is unset.
|
||||
A new MAC address will be generated on every boot and it will
|
||||
not be added to the environment.
|
||||
|
||||
config NETCONSOLE
|
||||
bool "NetConsole support"
|
||||
help
|
||||
Support the 'nc' input/output device for networked console.
|
||||
See README.NetConsole for details.
|
||||
|
||||
config NET_TFTP_VARS
|
||||
bool "Control TFTP timeout and count through environment"
|
||||
default y
|
||||
help
|
||||
If set, allows controlling the TFTP timeout through the
|
||||
environment variable tftptimeout, and the TFTP maximum
|
||||
timeout count through the variable tftptimeoutcountmax.
|
||||
If unset, timeout and maximum are hard-defined as 1 second
|
||||
and 10 timouts per TFTP transfer.
|
||||
|
||||
config BOOTP_PXE_CLIENTARCH
|
||||
hex
|
||||
default 0x16 if ARM64
|
||||
default 0x15 if ARM
|
||||
default 0 if X86
|
||||
|
||||
config BOOTP_VCI_STRING
|
||||
string
|
||||
default "U-Boot.armv7" if CPU_V7 || CPU_V7M
|
||||
default "U-Boot.armv8" if ARM64
|
||||
default "U-Boot.arm" if ARM
|
||||
default "U-Boot"
|
||||
|
||||
config SPL_NET_VCI_STRING
|
||||
string
|
||||
|
||||
endif # if NET
|
||||
27
u-boot/net/Makefile
Normal file
27
u-boot/net/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# (C) Copyright 2000-2006
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
#ccflags-y += -DDEBUG
|
||||
|
||||
obj-y += checksum.o
|
||||
obj-$(CONFIG_CMD_NET) += arp.o
|
||||
obj-$(CONFIG_CMD_NET) += bootp.o
|
||||
obj-$(CONFIG_CMD_CDP) += cdp.o
|
||||
obj-$(CONFIG_CMD_DNS) += dns.o
|
||||
ifdef CONFIG_DM_ETH
|
||||
obj-$(CONFIG_CMD_NET) += eth-uclass.o
|
||||
else
|
||||
obj-$(CONFIG_CMD_NET) += eth_legacy.o
|
||||
endif
|
||||
obj-$(CONFIG_CMD_NET) += eth_common.o
|
||||
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
|
||||
obj-$(CONFIG_CMD_NET) += net.o
|
||||
obj-$(CONFIG_CMD_NFS) += nfs.o
|
||||
obj-$(CONFIG_CMD_PING) += ping.o
|
||||
obj-$(CONFIG_CMD_RARP) += rarp.o
|
||||
obj-$(CONFIG_CMD_SNTP) += sntp.o
|
||||
obj-$(CONFIG_CMD_NET) += tftp.o
|
||||
237
u-boot/net/arp.c
Normal file
237
u-boot/net/arp.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copied from Linux Monitor (LiMon) - Networking.
|
||||
*
|
||||
* Copyright 1994 - 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Roland Borde
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include "arp.h"
|
||||
|
||||
#ifndef CONFIG_ARP_TIMEOUT
|
||||
/* Milliseconds before trying ARP again */
|
||||
# define ARP_TIMEOUT 5000UL
|
||||
#else
|
||||
# define ARP_TIMEOUT CONFIG_ARP_TIMEOUT
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef CONFIG_NET_RETRY_COUNT
|
||||
# define ARP_TIMEOUT_COUNT 5 /* # of timeouts before giving up */
|
||||
#else
|
||||
# define ARP_TIMEOUT_COUNT CONFIG_NET_RETRY_COUNT
|
||||
#endif
|
||||
|
||||
struct in_addr net_arp_wait_packet_ip;
|
||||
static struct in_addr net_arp_wait_reply_ip;
|
||||
/* MAC address of waiting packet's destination */
|
||||
uchar *arp_wait_packet_ethaddr;
|
||||
int arp_wait_tx_packet_size;
|
||||
ulong arp_wait_timer_start;
|
||||
int arp_wait_try;
|
||||
|
||||
static uchar *arp_tx_packet; /* THE ARP transmit packet */
|
||||
static uchar arp_tx_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
|
||||
|
||||
void arp_init(void)
|
||||
{
|
||||
/* XXX problem with bss workaround */
|
||||
arp_wait_packet_ethaddr = NULL;
|
||||
net_arp_wait_packet_ip.s_addr = 0;
|
||||
net_arp_wait_reply_ip.s_addr = 0;
|
||||
arp_wait_tx_packet_size = 0;
|
||||
arp_tx_packet = &arp_tx_packet_buf[0] + (PKTALIGN - 1);
|
||||
arp_tx_packet -= (ulong)arp_tx_packet % PKTALIGN;
|
||||
}
|
||||
|
||||
void arp_raw_request(struct in_addr source_ip, const uchar *target_ethaddr,
|
||||
struct in_addr target_ip)
|
||||
{
|
||||
uchar *pkt;
|
||||
struct arp_hdr *arp;
|
||||
int eth_hdr_size;
|
||||
|
||||
debug_cond(DEBUG_DEV_PKT, "ARP broadcast %d\n", arp_wait_try);
|
||||
|
||||
pkt = arp_tx_packet;
|
||||
|
||||
eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_ARP);
|
||||
pkt += eth_hdr_size;
|
||||
|
||||
arp = (struct arp_hdr *)pkt;
|
||||
|
||||
arp->ar_hrd = htons(ARP_ETHER);
|
||||
arp->ar_pro = htons(PROT_IP);
|
||||
arp->ar_hln = ARP_HLEN;
|
||||
arp->ar_pln = ARP_PLEN;
|
||||
arp->ar_op = htons(ARPOP_REQUEST);
|
||||
|
||||
memcpy(&arp->ar_sha, net_ethaddr, ARP_HLEN); /* source ET addr */
|
||||
net_write_ip(&arp->ar_spa, source_ip); /* source IP addr */
|
||||
memcpy(&arp->ar_tha, target_ethaddr, ARP_HLEN); /* target ET addr */
|
||||
net_write_ip(&arp->ar_tpa, target_ip); /* target IP addr */
|
||||
|
||||
net_send_packet(arp_tx_packet, eth_hdr_size + ARP_HDR_SIZE);
|
||||
}
|
||||
|
||||
void arp_request(void)
|
||||
{
|
||||
if ((net_arp_wait_packet_ip.s_addr & net_netmask.s_addr) !=
|
||||
(net_ip.s_addr & net_netmask.s_addr)) {
|
||||
if (net_gateway.s_addr == 0) {
|
||||
puts("## Warning: gatewayip needed but not set\n");
|
||||
net_arp_wait_reply_ip = net_arp_wait_packet_ip;
|
||||
} else {
|
||||
net_arp_wait_reply_ip = net_gateway;
|
||||
}
|
||||
} else {
|
||||
net_arp_wait_reply_ip = net_arp_wait_packet_ip;
|
||||
}
|
||||
|
||||
arp_raw_request(net_ip, net_null_ethaddr, net_arp_wait_reply_ip);
|
||||
}
|
||||
|
||||
int arp_timeout_check(void)
|
||||
{
|
||||
ulong t;
|
||||
|
||||
if (!net_arp_wait_packet_ip.s_addr)
|
||||
return 0;
|
||||
|
||||
t = get_timer(0);
|
||||
|
||||
/* check for arp timeout */
|
||||
if ((t - arp_wait_timer_start) > ARP_TIMEOUT) {
|
||||
arp_wait_try++;
|
||||
|
||||
if (arp_wait_try >= ARP_TIMEOUT_COUNT) {
|
||||
puts("\nARP Retry count exceeded; starting again\n");
|
||||
arp_wait_try = 0;
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
} else {
|
||||
arp_wait_timer_start = t;
|
||||
arp_request();
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
|
||||
{
|
||||
struct arp_hdr *arp;
|
||||
struct in_addr reply_ip_addr;
|
||||
uchar *pkt;
|
||||
int eth_hdr_size;
|
||||
|
||||
/*
|
||||
* We have to deal with two types of ARP packets:
|
||||
* - REQUEST packets will be answered by sending our
|
||||
* IP address - if we know it.
|
||||
* - REPLY packates are expected only after we asked
|
||||
* for the TFTP server's or the gateway's ethernet
|
||||
* address; so if we receive such a packet, we set
|
||||
* the server ethernet address
|
||||
*/
|
||||
debug_cond(DEBUG_NET_PKT, "Got ARP\n");
|
||||
|
||||
arp = (struct arp_hdr *)ip;
|
||||
if (len < ARP_HDR_SIZE) {
|
||||
printf("bad length %d < %d\n", len, ARP_HDR_SIZE);
|
||||
return;
|
||||
}
|
||||
if (ntohs(arp->ar_hrd) != ARP_ETHER)
|
||||
return;
|
||||
if (ntohs(arp->ar_pro) != PROT_IP)
|
||||
return;
|
||||
if (arp->ar_hln != ARP_HLEN)
|
||||
return;
|
||||
if (arp->ar_pln != ARP_PLEN)
|
||||
return;
|
||||
|
||||
if (net_ip.s_addr == 0)
|
||||
return;
|
||||
|
||||
if (net_read_ip(&arp->ar_tpa).s_addr != net_ip.s_addr)
|
||||
return;
|
||||
|
||||
switch (ntohs(arp->ar_op)) {
|
||||
case ARPOP_REQUEST:
|
||||
/* reply with our IP address */
|
||||
debug_cond(DEBUG_DEV_PKT, "Got ARP REQUEST, return our IP\n");
|
||||
pkt = (uchar *)et;
|
||||
eth_hdr_size = net_update_ether(et, et->et_src, PROT_ARP);
|
||||
pkt += eth_hdr_size;
|
||||
arp->ar_op = htons(ARPOP_REPLY);
|
||||
memcpy(&arp->ar_tha, &arp->ar_sha, ARP_HLEN);
|
||||
net_copy_ip(&arp->ar_tpa, &arp->ar_spa);
|
||||
memcpy(&arp->ar_sha, net_ethaddr, ARP_HLEN);
|
||||
net_copy_ip(&arp->ar_spa, &net_ip);
|
||||
|
||||
#ifdef CONFIG_CMD_LINK_LOCAL
|
||||
/*
|
||||
* Work-around for brain-damaged Cisco equipment with
|
||||
* arp-proxy enabled.
|
||||
*
|
||||
* If the requesting IP is not on our subnet, wait 5ms to
|
||||
* reply to ARP request so that our reply will overwrite
|
||||
* the arp-proxy's instead of the other way around.
|
||||
*/
|
||||
if ((net_read_ip(&arp->ar_tpa).s_addr & net_netmask.s_addr) !=
|
||||
(net_read_ip(&arp->ar_spa).s_addr & net_netmask.s_addr))
|
||||
udelay(5000);
|
||||
#endif
|
||||
net_send_packet((uchar *)et, eth_hdr_size + ARP_HDR_SIZE);
|
||||
return;
|
||||
|
||||
case ARPOP_REPLY: /* arp reply */
|
||||
/* are we waiting for a reply */
|
||||
if (!net_arp_wait_packet_ip.s_addr)
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_KEEP_SERVERADDR
|
||||
if (net_server_ip.s_addr == net_arp_wait_packet_ip.s_addr) {
|
||||
char buf[20];
|
||||
sprintf(buf, "%pM", &arp->ar_sha);
|
||||
setenv("serveraddr", buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
reply_ip_addr = net_read_ip(&arp->ar_spa);
|
||||
|
||||
/* matched waiting packet's address */
|
||||
if (reply_ip_addr.s_addr == net_arp_wait_reply_ip.s_addr) {
|
||||
debug_cond(DEBUG_DEV_PKT,
|
||||
"Got ARP REPLY, set eth addr (%pM)\n",
|
||||
arp->ar_data);
|
||||
|
||||
/* save address for later use */
|
||||
if (arp_wait_packet_ethaddr != NULL)
|
||||
memcpy(arp_wait_packet_ethaddr,
|
||||
&arp->ar_sha, ARP_HLEN);
|
||||
|
||||
net_get_arp_handler()((uchar *)arp, 0, reply_ip_addr,
|
||||
0, len);
|
||||
|
||||
/* set the mac address in the waiting packet's header
|
||||
and transmit it */
|
||||
memcpy(((struct ethernet_hdr *)net_tx_packet)->et_dest,
|
||||
&arp->ar_sha, ARP_HLEN);
|
||||
net_send_packet(net_tx_packet, arp_wait_tx_packet_size);
|
||||
|
||||
/* no arp request pending now */
|
||||
net_arp_wait_packet_ip.s_addr = 0;
|
||||
arp_wait_tx_packet_size = 0;
|
||||
arp_wait_packet_ethaddr = NULL;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
debug("Unexpected ARP opcode 0x%x\n",
|
||||
ntohs(arp->ar_op));
|
||||
return;
|
||||
}
|
||||
}
|
||||
31
u-boot/net/arp.h
Normal file
31
u-boot/net/arp.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copied from Linux Monitor (LiMon) - Networking.
|
||||
*
|
||||
* Copyright 1994 - 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Roland Borde
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __ARP_H__
|
||||
#define __ARP_H__
|
||||
|
||||
#include <common.h>
|
||||
|
||||
extern struct in_addr net_arp_wait_packet_ip;
|
||||
/* MAC address of waiting packet's destination */
|
||||
extern uchar *arp_wait_packet_ethaddr;
|
||||
extern int arp_wait_tx_packet_size;
|
||||
extern ulong arp_wait_timer_start;
|
||||
extern int arp_wait_try;
|
||||
|
||||
void arp_init(void);
|
||||
void arp_request(void);
|
||||
void arp_raw_request(struct in_addr source_ip, const uchar *targetEther,
|
||||
struct in_addr target_ip);
|
||||
int arp_timeout_check(void);
|
||||
void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len);
|
||||
|
||||
#endif /* __ARP_H__ */
|
||||
1099
u-boot/net/bootp.c
Normal file
1099
u-boot/net/bootp.c
Normal file
File diff suppressed because it is too large
Load Diff
93
u-boot/net/bootp.h
Normal file
93
u-boot/net/bootp.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copied from LiMon - BOOTP.
|
||||
*
|
||||
* Copyright 1994, 1995, 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
*/
|
||||
|
||||
#ifndef __BOOTP_H__
|
||||
#define __BOOTP_H__
|
||||
|
||||
#ifndef __NET_H__
|
||||
#include <net.h>
|
||||
#endif /* __NET_H__ */
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/*
|
||||
* BOOTP header.
|
||||
*/
|
||||
#if defined(CONFIG_CMD_DHCP)
|
||||
/* Minimum DHCP Options size per RFC2131 - results in 576 byte pkt */
|
||||
#define OPT_FIELD_SIZE 312
|
||||
#if defined(CONFIG_BOOTP_VENDOREX)
|
||||
extern u8 *dhcp_vendorex_prep(u8 *e); /*rtn new e after add own opts. */
|
||||
extern u8 *dhcp_vendorex_proc(u8 *e); /*rtn next e if mine,else NULL */
|
||||
#endif
|
||||
#else
|
||||
#define OPT_FIELD_SIZE 64
|
||||
#endif
|
||||
|
||||
struct bootp_hdr {
|
||||
u8 bp_op; /* Operation */
|
||||
# define OP_BOOTREQUEST 1
|
||||
# define OP_BOOTREPLY 2
|
||||
u8 bp_htype; /* Hardware type */
|
||||
# define HWT_ETHER 1
|
||||
u8 bp_hlen; /* Hardware address length */
|
||||
# define HWL_ETHER 6
|
||||
u8 bp_hops; /* Hop count (gateway thing) */
|
||||
u32 bp_id; /* Transaction ID */
|
||||
u16 bp_secs; /* Seconds since boot */
|
||||
u16 bp_spare1; /* Alignment */
|
||||
struct in_addr bp_ciaddr; /* Client IP address */
|
||||
struct in_addr bp_yiaddr; /* Your (client) IP address */
|
||||
struct in_addr bp_siaddr; /* Server IP address */
|
||||
struct in_addr bp_giaddr; /* Gateway IP address */
|
||||
u8 bp_chaddr[16]; /* Client hardware address */
|
||||
char bp_sname[64]; /* Server host name */
|
||||
char bp_file[128]; /* Boot file name */
|
||||
char bp_vend[OPT_FIELD_SIZE]; /* Vendor information */
|
||||
};
|
||||
|
||||
#define BOOTP_HDR_SIZE sizeof(struct bootp_hdr)
|
||||
|
||||
/**********************************************************************/
|
||||
/*
|
||||
* Global functions and variables.
|
||||
*/
|
||||
|
||||
/* bootp.c */
|
||||
extern u32 bootp_id; /* ID of cur BOOTP request */
|
||||
extern int bootp_try;
|
||||
|
||||
|
||||
/* Send a BOOTP request */
|
||||
void bootp_reset(void);
|
||||
void bootp_request(void);
|
||||
|
||||
/****************** DHCP Support *********************/
|
||||
void dhcp_request(void);
|
||||
|
||||
/* DHCP States */
|
||||
typedef enum { INIT,
|
||||
INIT_REBOOT,
|
||||
REBOOTING,
|
||||
SELECTING,
|
||||
REQUESTING,
|
||||
REBINDING,
|
||||
BOUND,
|
||||
RENEWING } dhcp_state_t;
|
||||
|
||||
#define DHCP_DISCOVER 1
|
||||
#define DHCP_OFFER 2
|
||||
#define DHCP_REQUEST 3
|
||||
#define DHCP_DECLINE 4
|
||||
#define DHCP_ACK 5
|
||||
#define DHCP_NAK 6
|
||||
#define DHCP_RELEASE 7
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
#endif /* __BOOTP_H__ */
|
||||
363
u-boot/net/cdp.c
Normal file
363
u-boot/net/cdp.c
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copied from Linux Monitor (LiMon) - Networking.
|
||||
*
|
||||
* Copyright 1994 - 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Roland Borde
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <net.h>
|
||||
#if defined(CONFIG_CDP_VERSION)
|
||||
#include <timestamp.h>
|
||||
#endif
|
||||
|
||||
#include "cdp.h"
|
||||
|
||||
/* Ethernet bcast address */
|
||||
const u8 net_cdp_ethaddr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc };
|
||||
|
||||
#define CDP_DEVICE_ID_TLV 0x0001
|
||||
#define CDP_ADDRESS_TLV 0x0002
|
||||
#define CDP_PORT_ID_TLV 0x0003
|
||||
#define CDP_CAPABILITIES_TLV 0x0004
|
||||
#define CDP_VERSION_TLV 0x0005
|
||||
#define CDP_PLATFORM_TLV 0x0006
|
||||
#define CDP_NATIVE_VLAN_TLV 0x000a
|
||||
#define CDP_APPLIANCE_VLAN_TLV 0x000e
|
||||
#define CDP_TRIGGER_TLV 0x000f
|
||||
#define CDP_POWER_CONSUMPTION_TLV 0x0010
|
||||
#define CDP_SYSNAME_TLV 0x0014
|
||||
#define CDP_SYSOBJECT_TLV 0x0015
|
||||
#define CDP_MANAGEMENT_ADDRESS_TLV 0x0016
|
||||
|
||||
#define CDP_TIMEOUT 250UL /* one packet every 250ms */
|
||||
|
||||
static int cdp_seq;
|
||||
static int cdp_ok;
|
||||
|
||||
ushort cdp_native_vlan;
|
||||
ushort cdp_appliance_vlan;
|
||||
|
||||
static const uchar cdp_snap_hdr[8] = {
|
||||
0xAA, 0xAA, 0x03, 0x00, 0x00, 0x0C, 0x20, 0x00 };
|
||||
|
||||
static ushort cdp_compute_csum(const uchar *buff, ushort len)
|
||||
{
|
||||
ushort csum;
|
||||
int odd;
|
||||
ulong result = 0;
|
||||
ushort leftover;
|
||||
ushort *p;
|
||||
|
||||
if (len > 0) {
|
||||
odd = 1 & (ulong)buff;
|
||||
if (odd) {
|
||||
result = *buff << 8;
|
||||
len--;
|
||||
buff++;
|
||||
}
|
||||
while (len > 1) {
|
||||
p = (ushort *)buff;
|
||||
result += *p++;
|
||||
buff = (uchar *)p;
|
||||
if (result & 0x80000000)
|
||||
result = (result & 0xFFFF) + (result >> 16);
|
||||
len -= 2;
|
||||
}
|
||||
if (len) {
|
||||
leftover = (signed short)(*(const signed char *)buff);
|
||||
/*
|
||||
* CISCO SUCKS big time! (and blows too):
|
||||
* CDP uses the IP checksum algorithm with a twist;
|
||||
* for the last byte it *sign* extends and sums.
|
||||
*/
|
||||
result = (result & 0xffff0000) |
|
||||
((result + leftover) & 0x0000ffff);
|
||||
}
|
||||
while (result >> 16)
|
||||
result = (result & 0xFFFF) + (result >> 16);
|
||||
|
||||
if (odd)
|
||||
result = ((result >> 8) & 0xff) |
|
||||
((result & 0xff) << 8);
|
||||
}
|
||||
|
||||
/* add up 16-bit and 17-bit words for 17+c bits */
|
||||
result = (result & 0xffff) + (result >> 16);
|
||||
/* add up 16-bit and 2-bit for 16+c bit */
|
||||
result = (result & 0xffff) + (result >> 16);
|
||||
/* add up carry.. */
|
||||
result = (result & 0xffff) + (result >> 16);
|
||||
|
||||
/* negate */
|
||||
csum = ~(ushort)result;
|
||||
|
||||
/* run time endian detection */
|
||||
if (csum != htons(csum)) /* little endian */
|
||||
csum = htons(csum);
|
||||
|
||||
return csum;
|
||||
}
|
||||
|
||||
static int cdp_send_trigger(void)
|
||||
{
|
||||
uchar *pkt;
|
||||
ushort *s;
|
||||
ushort *cp;
|
||||
struct ethernet_hdr *et;
|
||||
int len;
|
||||
ushort chksum;
|
||||
#if defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID) || \
|
||||
defined(CONFIG_CDP_VERSION) || defined(CONFIG_CDP_PLATFORM)
|
||||
char buf[32];
|
||||
#endif
|
||||
|
||||
pkt = net_tx_packet;
|
||||
et = (struct ethernet_hdr *)pkt;
|
||||
|
||||
/* NOTE: trigger sent not on any VLAN */
|
||||
|
||||
/* form ethernet header */
|
||||
memcpy(et->et_dest, net_cdp_ethaddr, 6);
|
||||
memcpy(et->et_src, net_ethaddr, 6);
|
||||
|
||||
pkt += ETHER_HDR_SIZE;
|
||||
|
||||
/* SNAP header */
|
||||
memcpy((uchar *)pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr));
|
||||
pkt += sizeof(cdp_snap_hdr);
|
||||
|
||||
/* CDP header */
|
||||
*pkt++ = 0x02; /* CDP version 2 */
|
||||
*pkt++ = 180; /* TTL */
|
||||
s = (ushort *)pkt;
|
||||
cp = s;
|
||||
/* checksum (0 for later calculation) */
|
||||
*s++ = htons(0);
|
||||
|
||||
/* CDP fields */
|
||||
#ifdef CONFIG_CDP_DEVICE_ID
|
||||
*s++ = htons(CDP_DEVICE_ID_TLV);
|
||||
*s++ = htons(CONFIG_CDP_DEVICE_ID);
|
||||
sprintf(buf, CONFIG_CDP_DEVICE_ID_PREFIX "%pm", net_ethaddr);
|
||||
memcpy((uchar *)s, buf, 16);
|
||||
s += 16 / 2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDP_PORT_ID
|
||||
*s++ = htons(CDP_PORT_ID_TLV);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
sprintf(buf, CONFIG_CDP_PORT_ID, eth_get_dev_index());
|
||||
len = strlen(buf);
|
||||
if (len & 1) /* make it even */
|
||||
len++;
|
||||
*s++ = htons(len + 4);
|
||||
memcpy((uchar *)s, buf, len);
|
||||
s += len / 2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDP_CAPABILITIES
|
||||
*s++ = htons(CDP_CAPABILITIES_TLV);
|
||||
*s++ = htons(8);
|
||||
*(ulong *)s = htonl(CONFIG_CDP_CAPABILITIES);
|
||||
s += 2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDP_VERSION
|
||||
*s++ = htons(CDP_VERSION_TLV);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strcpy(buf, CONFIG_CDP_VERSION);
|
||||
len = strlen(buf);
|
||||
if (len & 1) /* make it even */
|
||||
len++;
|
||||
*s++ = htons(len + 4);
|
||||
memcpy((uchar *)s, buf, len);
|
||||
s += len / 2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDP_PLATFORM
|
||||
*s++ = htons(CDP_PLATFORM_TLV);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strcpy(buf, CONFIG_CDP_PLATFORM);
|
||||
len = strlen(buf);
|
||||
if (len & 1) /* make it even */
|
||||
len++;
|
||||
*s++ = htons(len + 4);
|
||||
memcpy((uchar *)s, buf, len);
|
||||
s += len / 2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDP_TRIGGER
|
||||
*s++ = htons(CDP_TRIGGER_TLV);
|
||||
*s++ = htons(8);
|
||||
*(ulong *)s = htonl(CONFIG_CDP_TRIGGER);
|
||||
s += 2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDP_POWER_CONSUMPTION
|
||||
*s++ = htons(CDP_POWER_CONSUMPTION_TLV);
|
||||
*s++ = htons(6);
|
||||
*s++ = htons(CONFIG_CDP_POWER_CONSUMPTION);
|
||||
#endif
|
||||
|
||||
/* length of ethernet packet */
|
||||
len = (uchar *)s - ((uchar *)net_tx_packet + ETHER_HDR_SIZE);
|
||||
et->et_protlen = htons(len);
|
||||
|
||||
len = ETHER_HDR_SIZE + sizeof(cdp_snap_hdr);
|
||||
chksum = cdp_compute_csum((uchar *)net_tx_packet + len,
|
||||
(uchar *)s - (net_tx_packet + len));
|
||||
if (chksum == 0)
|
||||
chksum = 0xFFFF;
|
||||
*cp = htons(chksum);
|
||||
|
||||
net_send_packet(net_tx_packet, (uchar *)s - net_tx_packet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdp_timeout_handler(void)
|
||||
{
|
||||
cdp_seq++;
|
||||
|
||||
if (cdp_seq < 3) {
|
||||
net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
|
||||
cdp_send_trigger();
|
||||
return;
|
||||
}
|
||||
|
||||
/* if not OK try again */
|
||||
if (!cdp_ok)
|
||||
net_start_again();
|
||||
else
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
}
|
||||
|
||||
void cdp_receive(const uchar *pkt, unsigned len)
|
||||
{
|
||||
const uchar *t;
|
||||
const ushort *ss;
|
||||
ushort type, tlen;
|
||||
ushort vlan, nvlan;
|
||||
|
||||
/* minimum size? */
|
||||
if (len < sizeof(cdp_snap_hdr) + 4)
|
||||
goto pkt_short;
|
||||
|
||||
/* check for valid CDP SNAP header */
|
||||
if (memcmp(pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr)) != 0)
|
||||
return;
|
||||
|
||||
pkt += sizeof(cdp_snap_hdr);
|
||||
len -= sizeof(cdp_snap_hdr);
|
||||
|
||||
/* Version of CDP protocol must be >= 2 and TTL != 0 */
|
||||
if (pkt[0] < 0x02 || pkt[1] == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* if version is greater than 0x02 maybe we'll have a problem;
|
||||
* output a warning
|
||||
*/
|
||||
if (pkt[0] != 0x02)
|
||||
printf("**WARNING: CDP packet received with a protocol version "
|
||||
"%d > 2\n", pkt[0] & 0xff);
|
||||
|
||||
if (cdp_compute_csum(pkt, len) != 0)
|
||||
return;
|
||||
|
||||
pkt += 4;
|
||||
len -= 4;
|
||||
|
||||
vlan = htons(-1);
|
||||
nvlan = htons(-1);
|
||||
while (len > 0) {
|
||||
if (len < 4)
|
||||
goto pkt_short;
|
||||
|
||||
ss = (const ushort *)pkt;
|
||||
type = ntohs(ss[0]);
|
||||
tlen = ntohs(ss[1]);
|
||||
if (tlen > len)
|
||||
goto pkt_short;
|
||||
|
||||
pkt += tlen;
|
||||
len -= tlen;
|
||||
|
||||
ss += 2; /* point ss to the data of the TLV */
|
||||
tlen -= 4;
|
||||
|
||||
switch (type) {
|
||||
case CDP_DEVICE_ID_TLV:
|
||||
break;
|
||||
case CDP_ADDRESS_TLV:
|
||||
break;
|
||||
case CDP_PORT_ID_TLV:
|
||||
break;
|
||||
case CDP_CAPABILITIES_TLV:
|
||||
break;
|
||||
case CDP_VERSION_TLV:
|
||||
break;
|
||||
case CDP_PLATFORM_TLV:
|
||||
break;
|
||||
case CDP_NATIVE_VLAN_TLV:
|
||||
nvlan = *ss;
|
||||
break;
|
||||
case CDP_APPLIANCE_VLAN_TLV:
|
||||
t = (const uchar *)ss;
|
||||
while (tlen > 0) {
|
||||
if (tlen < 3)
|
||||
goto pkt_short;
|
||||
|
||||
ss = (const ushort *)(t + 1);
|
||||
|
||||
#ifdef CONFIG_CDP_APPLIANCE_VLAN_TYPE
|
||||
if (t[0] == CONFIG_CDP_APPLIANCE_VLAN_TYPE)
|
||||
vlan = *ss;
|
||||
#else
|
||||
/* XXX will this work; dunno */
|
||||
vlan = ntohs(*ss);
|
||||
#endif
|
||||
t += 3; tlen -= 3;
|
||||
}
|
||||
break;
|
||||
case CDP_TRIGGER_TLV:
|
||||
break;
|
||||
case CDP_POWER_CONSUMPTION_TLV:
|
||||
break;
|
||||
case CDP_SYSNAME_TLV:
|
||||
break;
|
||||
case CDP_SYSOBJECT_TLV:
|
||||
break;
|
||||
case CDP_MANAGEMENT_ADDRESS_TLV:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cdp_appliance_vlan = vlan;
|
||||
cdp_native_vlan = nvlan;
|
||||
|
||||
cdp_ok = 1;
|
||||
return;
|
||||
|
||||
pkt_short:
|
||||
printf("** CDP packet is too short\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void cdp_start(void)
|
||||
{
|
||||
printf("Using %s device\n", eth_get_name());
|
||||
cdp_seq = 0;
|
||||
cdp_ok = 0;
|
||||
|
||||
cdp_native_vlan = htons(-1);
|
||||
cdp_appliance_vlan = htons(-1);
|
||||
|
||||
net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
|
||||
|
||||
cdp_send_trigger();
|
||||
}
|
||||
22
u-boot/net/cdp.h
Normal file
22
u-boot/net/cdp.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copied from Linux Monitor (LiMon) - Networking.
|
||||
*
|
||||
* Copyright 1994 - 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Roland Borde
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_CMD_CDP)
|
||||
|
||||
#ifndef __CDP_H__
|
||||
#define __CDP_H__
|
||||
|
||||
void cdp_start(void);
|
||||
/* Process a received CDP packet */
|
||||
void cdp_receive(const uchar *pkt, unsigned len);
|
||||
|
||||
#endif /* __CDP_H__ */
|
||||
#endif
|
||||
60
u-boot/net/checksum.c
Normal file
60
u-boot/net/checksum.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file was originally taken from the FreeBSD project.
|
||||
*
|
||||
* Copyright (c) 2001 Charles Mott <cm@linktel.net>
|
||||
* Copyright (c) 2008 coresystems GmbH
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <net.h>
|
||||
|
||||
unsigned compute_ip_checksum(const void *vptr, unsigned nbytes)
|
||||
{
|
||||
int sum, oddbyte;
|
||||
const unsigned short *ptr = vptr;
|
||||
|
||||
sum = 0;
|
||||
while (nbytes > 1) {
|
||||
sum += *ptr++;
|
||||
nbytes -= 2;
|
||||
}
|
||||
if (nbytes == 1) {
|
||||
oddbyte = 0;
|
||||
((u8 *)&oddbyte)[0] = *(u8 *)ptr;
|
||||
((u8 *)&oddbyte)[1] = 0;
|
||||
sum += oddbyte;
|
||||
}
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
sum = ~sum & 0xffff;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
unsigned add_ip_checksums(unsigned offset, unsigned sum, unsigned new)
|
||||
{
|
||||
unsigned long checksum;
|
||||
|
||||
sum = ~sum & 0xffff;
|
||||
new = ~new & 0xffff;
|
||||
if (offset & 1) {
|
||||
/*
|
||||
* byte-swap the sum if it came from an odd offset; since the
|
||||
* computation is endian independant this works.
|
||||
*/
|
||||
new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
|
||||
}
|
||||
checksum = sum + new;
|
||||
if (checksum > 0xffff)
|
||||
checksum -= 0xffff;
|
||||
|
||||
return (~checksum) & 0xffff;
|
||||
}
|
||||
|
||||
int ip_checksum_ok(const void *addr, unsigned nbytes)
|
||||
{
|
||||
return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
|
||||
}
|
||||
207
u-boot/net/dns.c
Normal file
207
u-boot/net/dns.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* DNS support driver
|
||||
*
|
||||
* Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl>
|
||||
* Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org>
|
||||
*
|
||||
* This is a simple DNS implementation for U-Boot. It will use the first IP
|
||||
* in the DNS response as net_server_ip. This can then be used for any other
|
||||
* network related activities.
|
||||
*
|
||||
* The packet handling is partly based on TADNS, original copyrights
|
||||
* follow below.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
|
||||
*
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Sergey Lyubka wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <net.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "dns.h"
|
||||
|
||||
char *net_dns_resolve; /* The host to resolve */
|
||||
char *net_dns_env_var; /* The envvar to store the answer in */
|
||||
|
||||
static int dns_our_port;
|
||||
|
||||
static void dns_send(void)
|
||||
{
|
||||
struct header *header;
|
||||
int n, name_len;
|
||||
uchar *p, *pkt;
|
||||
const char *s;
|
||||
const char *name;
|
||||
enum dns_query_type qtype = DNS_A_RECORD;
|
||||
|
||||
name = net_dns_resolve;
|
||||
pkt = (uchar *)(net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE);
|
||||
p = pkt;
|
||||
|
||||
/* Prepare DNS packet header */
|
||||
header = (struct header *)pkt;
|
||||
header->tid = 1;
|
||||
header->flags = htons(0x100); /* standard query */
|
||||
header->nqueries = htons(1); /* Just one query */
|
||||
header->nanswers = 0;
|
||||
header->nauth = 0;
|
||||
header->nother = 0;
|
||||
|
||||
/* Encode DNS name */
|
||||
name_len = strlen(name);
|
||||
p = (uchar *)&header->data; /* For encoding host name into packet */
|
||||
|
||||
do {
|
||||
s = strchr(name, '.');
|
||||
if (!s)
|
||||
s = name + name_len;
|
||||
|
||||
n = s - name; /* Chunk length */
|
||||
*p++ = n; /* Copy length */
|
||||
memcpy(p, name, n); /* Copy chunk */
|
||||
p += n;
|
||||
|
||||
if (*s == '.')
|
||||
n++;
|
||||
|
||||
name += n;
|
||||
name_len -= n;
|
||||
} while (*s != '\0');
|
||||
|
||||
*p++ = 0; /* Mark end of host name */
|
||||
*p++ = 0; /* Some servers require double null */
|
||||
*p++ = (unsigned char) qtype; /* Query Type */
|
||||
|
||||
*p++ = 0;
|
||||
*p++ = 1; /* Class: inet, 0x0001 */
|
||||
|
||||
n = p - pkt; /* Total packet length */
|
||||
debug("Packet size %d\n", n);
|
||||
|
||||
dns_our_port = random_port();
|
||||
|
||||
net_send_udp_packet(net_server_ethaddr, net_dns_server,
|
||||
DNS_SERVICE_PORT, dns_our_port, n);
|
||||
debug("DNS packet sent\n");
|
||||
}
|
||||
|
||||
static void dns_timeout_handler(void)
|
||||
{
|
||||
puts("Timeout\n");
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
}
|
||||
|
||||
static void dns_handler(uchar *pkt, unsigned dest, struct in_addr sip,
|
||||
unsigned src, unsigned len)
|
||||
{
|
||||
struct header *header;
|
||||
const unsigned char *p, *e, *s;
|
||||
u16 type, i;
|
||||
int found, stop, dlen;
|
||||
char ip_str[22];
|
||||
struct in_addr ip_addr;
|
||||
|
||||
|
||||
debug("%s\n", __func__);
|
||||
if (dest != dns_our_port)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i += 4)
|
||||
debug("0x%p - 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
|
||||
pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]);
|
||||
|
||||
/* We sent one query. We want to have a single answer: */
|
||||
header = (struct header *)pkt;
|
||||
if (ntohs(header->nqueries) != 1)
|
||||
return;
|
||||
|
||||
/* Received 0 answers */
|
||||
if (header->nanswers == 0) {
|
||||
puts("DNS: host not found\n");
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip host name */
|
||||
s = &header->data[0];
|
||||
e = pkt + len;
|
||||
for (p = s; p < e && *p != '\0'; p++)
|
||||
continue;
|
||||
|
||||
/* We sent query class 1, query type 1 */
|
||||
if (&p[5] > e || get_unaligned_be16(p+1) != DNS_A_RECORD) {
|
||||
puts("DNS: response was not an A record\n");
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Go to the first answer section */
|
||||
p += 5;
|
||||
|
||||
/* Loop through the answers, we want A type answer */
|
||||
for (found = stop = 0; !stop && &p[12] < e; ) {
|
||||
/* Skip possible name in CNAME answer */
|
||||
if (*p != 0xc0) {
|
||||
while (*p && &p[12] < e)
|
||||
p++;
|
||||
p--;
|
||||
}
|
||||
debug("Name (Offset in header): %d\n", p[1]);
|
||||
|
||||
type = get_unaligned_be16(p+2);
|
||||
debug("type = %d\n", type);
|
||||
if (type == DNS_CNAME_RECORD) {
|
||||
/* CNAME answer. shift to the next section */
|
||||
debug("Found canonical name\n");
|
||||
dlen = get_unaligned_be16(p+10);
|
||||
debug("dlen = %d\n", dlen);
|
||||
p += 12 + dlen;
|
||||
} else if (type == DNS_A_RECORD) {
|
||||
debug("Found A-record\n");
|
||||
found = 1;
|
||||
stop = 1;
|
||||
} else {
|
||||
debug("Unknown type\n");
|
||||
stop = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && &p[12] < e) {
|
||||
dlen = get_unaligned_be16(p+10);
|
||||
p += 12;
|
||||
memcpy(&ip_addr, p, 4);
|
||||
|
||||
if (p + dlen <= e) {
|
||||
ip_to_string(ip_addr, ip_str);
|
||||
printf("%s\n", ip_str);
|
||||
if (net_dns_env_var)
|
||||
setenv(net_dns_env_var, ip_str);
|
||||
} else {
|
||||
puts("server responded with invalid IP number\n");
|
||||
}
|
||||
}
|
||||
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
}
|
||||
|
||||
void dns_start(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
|
||||
net_set_timeout_handler(DNS_TIMEOUT, dns_timeout_handler);
|
||||
net_set_udp_handler(dns_handler);
|
||||
|
||||
/* Clear a previous MAC address, the server IP might have changed. */
|
||||
memset(net_server_ethaddr, 0, sizeof(net_server_ethaddr));
|
||||
|
||||
dns_send();
|
||||
}
|
||||
36
u-boot/net/dns.h
Normal file
36
u-boot/net/dns.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* (C) Masami Komiya <mkomiya@sonare.it> 2005
|
||||
* Copyright 2009, Robin Getz <rgetz@blackfin.uclinux.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __DNS_H__
|
||||
#define __DNS_H__
|
||||
|
||||
#define DNS_SERVICE_PORT 53
|
||||
#define DNS_TIMEOUT 10000UL
|
||||
|
||||
/* http://en.wikipedia.org/wiki/List_of_DNS_record_types */
|
||||
enum dns_query_type {
|
||||
DNS_A_RECORD = 0x01,
|
||||
DNS_CNAME_RECORD = 0x05,
|
||||
DNS_MX_RECORD = 0x0f,
|
||||
};
|
||||
|
||||
/*
|
||||
* DNS network packet
|
||||
*/
|
||||
struct header {
|
||||
uint16_t tid; /* Transaction ID */
|
||||
uint16_t flags; /* Flags */
|
||||
uint16_t nqueries; /* Questions */
|
||||
uint16_t nanswers; /* Answers */
|
||||
uint16_t nauth; /* Authority PRs */
|
||||
uint16_t nother; /* Other PRs */
|
||||
unsigned char data[1]; /* Data, variable length */
|
||||
};
|
||||
|
||||
void dns_start(void); /* Begin DNS */
|
||||
|
||||
#endif
|
||||
551
u-boot/net/eth-uclass.c
Normal file
551
u-boot/net/eth-uclass.c
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* (C) Copyright 2001-2015
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
* Joe Hershberger, National Instruments
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <environment.h>
|
||||
#include <net.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include "eth_internal.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/**
|
||||
* struct eth_device_priv - private structure for each Ethernet device
|
||||
*
|
||||
* @state: The state of the Ethernet MAC driver (defined by enum eth_state_t)
|
||||
*/
|
||||
struct eth_device_priv {
|
||||
enum eth_state_t state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct eth_uclass_priv - The structure attached to the uclass itself
|
||||
*
|
||||
* @current: The Ethernet device that the network functions are using
|
||||
*/
|
||||
struct eth_uclass_priv {
|
||||
struct udevice *current;
|
||||
};
|
||||
|
||||
/* eth_errno - This stores the most recent failure code from DM functions */
|
||||
static int eth_errno;
|
||||
|
||||
static struct eth_uclass_priv *eth_get_uclass_priv(void)
|
||||
{
|
||||
struct uclass *uc;
|
||||
|
||||
uclass_get(UCLASS_ETH, &uc);
|
||||
assert(uc);
|
||||
return uc->priv;
|
||||
}
|
||||
|
||||
void eth_set_current_to_next(void)
|
||||
{
|
||||
struct eth_uclass_priv *uc_priv;
|
||||
|
||||
uc_priv = eth_get_uclass_priv();
|
||||
if (uc_priv->current)
|
||||
uclass_next_device(&uc_priv->current);
|
||||
if (!uc_priv->current)
|
||||
uclass_first_device(UCLASS_ETH, &uc_priv->current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Typically this will simply return the active device.
|
||||
* In the case where the most recent active device was unset, this will attempt
|
||||
* to return the first device. If that device doesn't exist or fails to probe,
|
||||
* this function will return NULL.
|
||||
*/
|
||||
struct udevice *eth_get_dev(void)
|
||||
{
|
||||
struct eth_uclass_priv *uc_priv;
|
||||
|
||||
uc_priv = eth_get_uclass_priv();
|
||||
if (!uc_priv->current)
|
||||
eth_errno = uclass_first_device(UCLASS_ETH,
|
||||
&uc_priv->current);
|
||||
return uc_priv->current;
|
||||
}
|
||||
|
||||
/*
|
||||
* Typically this will just store a device pointer.
|
||||
* In case it was not probed, we will attempt to do so.
|
||||
* dev may be NULL to unset the active device.
|
||||
*/
|
||||
void eth_set_dev(struct udevice *dev)
|
||||
{
|
||||
if (dev && !device_active(dev)) {
|
||||
eth_errno = device_probe(dev);
|
||||
if (eth_errno)
|
||||
dev = NULL;
|
||||
}
|
||||
|
||||
eth_get_uclass_priv()->current = dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the udevice that either has the name passed in as devname or has an
|
||||
* alias named devname.
|
||||
*/
|
||||
struct udevice *eth_get_dev_by_name(const char *devname)
|
||||
{
|
||||
int seq = -1;
|
||||
char *endp = NULL;
|
||||
const char *startp = NULL;
|
||||
struct udevice *it;
|
||||
struct uclass *uc;
|
||||
int len = strlen("eth");
|
||||
|
||||
/* Must be longer than 3 to be an alias */
|
||||
if (!strncmp(devname, "eth", len) && strlen(devname) > len) {
|
||||
startp = devname + len;
|
||||
seq = simple_strtoul(startp, &endp, 10);
|
||||
}
|
||||
|
||||
uclass_get(UCLASS_ETH, &uc);
|
||||
uclass_foreach_dev(it, uc) {
|
||||
/*
|
||||
* We need the seq to be valid, so try to probe it.
|
||||
* If the probe fails, the seq will not match since it will be
|
||||
* -1 instead of what we are looking for.
|
||||
* We don't care about errors from probe here. Either they won't
|
||||
* match an alias or it will match a literal name and we'll pick
|
||||
* up the error when we try to probe again in eth_set_dev().
|
||||
*/
|
||||
if (device_probe(it))
|
||||
continue;
|
||||
/* Check for the name or the sequence number to match */
|
||||
if (strcmp(it->name, devname) == 0 ||
|
||||
(endp > startp && it->seq == seq))
|
||||
return it;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char *eth_get_ethaddr(void)
|
||||
{
|
||||
struct eth_pdata *pdata;
|
||||
|
||||
if (eth_get_dev()) {
|
||||
pdata = eth_get_dev()->platdata;
|
||||
return pdata->enetaddr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set active state without calling start on the driver */
|
||||
int eth_init_state_only(void)
|
||||
{
|
||||
struct udevice *current;
|
||||
struct eth_device_priv *priv;
|
||||
|
||||
current = eth_get_dev();
|
||||
if (!current || !device_active(current))
|
||||
return -EINVAL;
|
||||
|
||||
priv = current->uclass_priv;
|
||||
priv->state = ETH_STATE_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set passive state without calling stop on the driver */
|
||||
void eth_halt_state_only(void)
|
||||
{
|
||||
struct udevice *current;
|
||||
struct eth_device_priv *priv;
|
||||
|
||||
current = eth_get_dev();
|
||||
if (!current || !device_active(current))
|
||||
return;
|
||||
|
||||
priv = current->uclass_priv;
|
||||
priv->state = ETH_STATE_PASSIVE;
|
||||
}
|
||||
|
||||
int eth_get_dev_index(void)
|
||||
{
|
||||
if (eth_get_dev())
|
||||
return eth_get_dev()->seq;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int eth_write_hwaddr(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *pdata = dev->platdata;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev || !device_active(dev))
|
||||
return -EINVAL;
|
||||
|
||||
/* seq is valid since the device is active */
|
||||
if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) {
|
||||
if (!is_valid_ethaddr(pdata->enetaddr)) {
|
||||
printf("\nError: %s address %pM illegal value\n",
|
||||
dev->name, pdata->enetaddr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drivers are allowed to decide not to implement this at
|
||||
* run-time. E.g. Some devices may use it and some may not.
|
||||
*/
|
||||
ret = eth_get_ops(dev)->write_hwaddr(dev);
|
||||
if (ret == -ENOSYS)
|
||||
ret = 0;
|
||||
if (ret)
|
||||
printf("\nWarning: %s failed to set MAC address\n",
|
||||
dev->name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int on_ethaddr(const char *name, const char *value, enum env_op op,
|
||||
int flags)
|
||||
{
|
||||
int index;
|
||||
int retval;
|
||||
struct udevice *dev;
|
||||
|
||||
/* look for an index after "eth" */
|
||||
index = simple_strtoul(name + 3, NULL, 10);
|
||||
|
||||
retval = uclass_find_device_by_seq(UCLASS_ETH, index, false, &dev);
|
||||
if (!retval) {
|
||||
struct eth_pdata *pdata = dev->platdata;
|
||||
switch (op) {
|
||||
case env_op_create:
|
||||
case env_op_overwrite:
|
||||
eth_parse_enetaddr(value, pdata->enetaddr);
|
||||
break;
|
||||
case env_op_delete:
|
||||
memset(pdata->enetaddr, 0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
|
||||
|
||||
int eth_init(void)
|
||||
{
|
||||
char *ethact = getenv("ethact");
|
||||
char *ethrotate = getenv("ethrotate");
|
||||
struct udevice *current = NULL;
|
||||
struct udevice *old_current;
|
||||
int ret = -ENODEV;
|
||||
|
||||
/*
|
||||
* When 'ethrotate' variable is set to 'no' and 'ethact' variable
|
||||
* is already set to an ethernet device, we should stick to 'ethact'.
|
||||
*/
|
||||
if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) {
|
||||
if (ethact) {
|
||||
current = eth_get_dev_by_name(ethact);
|
||||
if (!current)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!current) {
|
||||
current = eth_get_dev();
|
||||
if (!current) {
|
||||
printf("No ethernet found.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
old_current = current;
|
||||
do {
|
||||
if (current) {
|
||||
debug("Trying %s\n", current->name);
|
||||
|
||||
if (device_active(current)) {
|
||||
ret = eth_get_ops(current)->start(current);
|
||||
if (ret >= 0) {
|
||||
struct eth_device_priv *priv =
|
||||
current->uclass_priv;
|
||||
|
||||
priv->state = ETH_STATE_ACTIVE;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
ret = eth_errno;
|
||||
}
|
||||
|
||||
debug("FAIL\n");
|
||||
} else {
|
||||
debug("PROBE FAIL\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* If ethrotate is enabled, this will change "current",
|
||||
* otherwise we will drop out of this while loop immediately
|
||||
*/
|
||||
eth_try_another(0);
|
||||
/* This will ensure the new "current" attempted to probe */
|
||||
current = eth_get_dev();
|
||||
} while (old_current != current);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void eth_halt(void)
|
||||
{
|
||||
struct udevice *current;
|
||||
struct eth_device_priv *priv;
|
||||
|
||||
current = eth_get_dev();
|
||||
if (!current || !device_active(current))
|
||||
return;
|
||||
|
||||
eth_get_ops(current)->stop(current);
|
||||
priv = current->uclass_priv;
|
||||
priv->state = ETH_STATE_PASSIVE;
|
||||
}
|
||||
|
||||
int eth_is_active(struct udevice *dev)
|
||||
{
|
||||
struct eth_device_priv *priv;
|
||||
|
||||
if (!dev || !device_active(dev))
|
||||
return 0;
|
||||
|
||||
priv = dev_get_uclass_priv(dev);
|
||||
return priv->state == ETH_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
int eth_send(void *packet, int length)
|
||||
{
|
||||
struct udevice *current;
|
||||
int ret;
|
||||
|
||||
current = eth_get_dev();
|
||||
if (!current)
|
||||
return -ENODEV;
|
||||
|
||||
if (!device_active(current))
|
||||
return -EINVAL;
|
||||
|
||||
ret = eth_get_ops(current)->send(current, packet, length);
|
||||
if (ret < 0) {
|
||||
/* We cannot completely return the error at present */
|
||||
debug("%s: send() returned error %d\n", __func__, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int eth_rx(void)
|
||||
{
|
||||
struct udevice *current;
|
||||
uchar *packet;
|
||||
int flags;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
current = eth_get_dev();
|
||||
if (!current)
|
||||
return -ENODEV;
|
||||
|
||||
if (!device_active(current))
|
||||
return -EINVAL;
|
||||
|
||||
/* Process up to 32 packets at one time */
|
||||
flags = ETH_RECV_CHECK_DEVICE;
|
||||
for (i = 0; i < 32; i++) {
|
||||
ret = eth_get_ops(current)->recv(current, flags, &packet);
|
||||
flags = 0;
|
||||
if (ret > 0)
|
||||
net_process_received_packet(packet, ret);
|
||||
if (ret >= 0 && eth_get_ops(current)->free_pkt)
|
||||
eth_get_ops(current)->free_pkt(current, packet, ret);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
}
|
||||
if (ret == -EAGAIN)
|
||||
ret = 0;
|
||||
if (ret < 0) {
|
||||
/* We cannot completely return the error at present */
|
||||
debug("%s: recv() returned error %d\n", __func__, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int eth_initialize(void)
|
||||
{
|
||||
int num_devices = 0;
|
||||
struct udevice *dev;
|
||||
|
||||
eth_common_init();
|
||||
|
||||
/*
|
||||
* Devices need to write the hwaddr even if not started so that Linux
|
||||
* will have access to the hwaddr that u-boot stored for the device.
|
||||
* This is accomplished by attempting to probe each device and calling
|
||||
* their write_hwaddr() operation.
|
||||
*/
|
||||
uclass_first_device(UCLASS_ETH, &dev);
|
||||
if (!dev) {
|
||||
printf("No ethernet found.\n");
|
||||
bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
|
||||
} else {
|
||||
char *ethprime = getenv("ethprime");
|
||||
struct udevice *prime_dev = NULL;
|
||||
|
||||
if (ethprime)
|
||||
prime_dev = eth_get_dev_by_name(ethprime);
|
||||
if (prime_dev) {
|
||||
eth_set_dev(prime_dev);
|
||||
eth_current_changed();
|
||||
} else {
|
||||
eth_set_dev(NULL);
|
||||
}
|
||||
|
||||
bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
|
||||
do {
|
||||
if (num_devices)
|
||||
printf(", ");
|
||||
|
||||
printf("eth%d: %s", dev->seq, dev->name);
|
||||
|
||||
if (ethprime && dev == prime_dev)
|
||||
printf(" [PRIME]");
|
||||
|
||||
eth_write_hwaddr(dev);
|
||||
|
||||
uclass_next_device(&dev);
|
||||
num_devices++;
|
||||
} while (dev);
|
||||
|
||||
putc('\n');
|
||||
}
|
||||
|
||||
return num_devices;
|
||||
}
|
||||
|
||||
static int eth_post_bind(struct udevice *dev)
|
||||
{
|
||||
if (strchr(dev->name, ' ')) {
|
||||
printf("\nError: eth device name \"%s\" has a space!\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_pre_unbind(struct udevice *dev)
|
||||
{
|
||||
/* Don't hang onto a pointer that is going away */
|
||||
if (dev == eth_get_uclass_priv()->current)
|
||||
eth_set_dev(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_post_probe(struct udevice *dev)
|
||||
{
|
||||
struct eth_device_priv *priv = dev->uclass_priv;
|
||||
struct eth_pdata *pdata = dev->platdata;
|
||||
unsigned char env_enetaddr[6];
|
||||
|
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
|
||||
struct eth_ops *ops = eth_get_ops(dev);
|
||||
static int reloc_done;
|
||||
|
||||
if (!reloc_done) {
|
||||
if (ops->start)
|
||||
ops->start += gd->reloc_off;
|
||||
if (ops->send)
|
||||
ops->send += gd->reloc_off;
|
||||
if (ops->recv)
|
||||
ops->recv += gd->reloc_off;
|
||||
if (ops->free_pkt)
|
||||
ops->free_pkt += gd->reloc_off;
|
||||
if (ops->stop)
|
||||
ops->stop += gd->reloc_off;
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
if (ops->mcast)
|
||||
ops->mcast += gd->reloc_off;
|
||||
#endif
|
||||
if (ops->write_hwaddr)
|
||||
ops->write_hwaddr += gd->reloc_off;
|
||||
if (ops->read_rom_hwaddr)
|
||||
ops->read_rom_hwaddr += gd->reloc_off;
|
||||
|
||||
reloc_done++;
|
||||
}
|
||||
#endif
|
||||
|
||||
priv->state = ETH_STATE_INIT;
|
||||
|
||||
/* Check if the device has a MAC address in ROM */
|
||||
if (eth_get_ops(dev)->read_rom_hwaddr)
|
||||
eth_get_ops(dev)->read_rom_hwaddr(dev);
|
||||
|
||||
eth_getenv_enetaddr_by_index("eth", dev->seq, env_enetaddr);
|
||||
if (!is_zero_ethaddr(env_enetaddr)) {
|
||||
if (!is_zero_ethaddr(pdata->enetaddr) &&
|
||||
memcmp(pdata->enetaddr, env_enetaddr, 6)) {
|
||||
printf("\nWarning: %s MAC addresses don't match:\n",
|
||||
dev->name);
|
||||
printf("Address in SROM is %pM\n",
|
||||
pdata->enetaddr);
|
||||
printf("Address in environment is %pM\n",
|
||||
env_enetaddr);
|
||||
}
|
||||
|
||||
/* Override the ROM MAC address */
|
||||
memcpy(pdata->enetaddr, env_enetaddr, 6);
|
||||
} else if (is_valid_ethaddr(pdata->enetaddr)) {
|
||||
eth_setenv_enetaddr_by_index("eth", dev->seq, pdata->enetaddr);
|
||||
printf("\nWarning: %s using MAC address from ROM\n",
|
||||
dev->name);
|
||||
} else if (is_zero_ethaddr(pdata->enetaddr)) {
|
||||
#ifdef CONFIG_NET_RANDOM_ETHADDR
|
||||
net_random_ethaddr(pdata->enetaddr);
|
||||
printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
|
||||
dev->name, dev->seq, pdata->enetaddr);
|
||||
#else
|
||||
printf("\nError: %s address not set.\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_pre_remove(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *pdata = dev->platdata;
|
||||
|
||||
eth_get_ops(dev)->stop(dev);
|
||||
|
||||
/* clear the MAC address */
|
||||
memset(pdata->enetaddr, 0, 6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(eth) = {
|
||||
.name = "eth",
|
||||
.id = UCLASS_ETH,
|
||||
.post_bind = eth_post_bind,
|
||||
.pre_unbind = eth_pre_unbind,
|
||||
.post_probe = eth_post_probe,
|
||||
.pre_remove = eth_pre_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct eth_uclass_priv),
|
||||
.per_device_auto_alloc_size = sizeof(struct eth_device_priv),
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
};
|
||||
166
u-boot/net/eth_common.c
Normal file
166
u-boot/net/eth_common.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* (C) Copyright 2001-2015
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
* Joe Hershberger, National Instruments
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <miiphy.h>
|
||||
#include <net.h>
|
||||
#include "eth_internal.h"
|
||||
|
||||
void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
|
||||
{
|
||||
char *end;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; ++i) {
|
||||
enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
|
||||
if (addr)
|
||||
addr = (*end) ? end + 1 : end;
|
||||
}
|
||||
}
|
||||
|
||||
int eth_getenv_enetaddr(const char *name, uchar *enetaddr)
|
||||
{
|
||||
eth_parse_enetaddr(getenv(name), enetaddr);
|
||||
return is_valid_ethaddr(enetaddr);
|
||||
}
|
||||
|
||||
int eth_setenv_enetaddr(const char *name, const uchar *enetaddr)
|
||||
{
|
||||
char buf[20];
|
||||
|
||||
sprintf(buf, "%pM", enetaddr);
|
||||
|
||||
return setenv(name, buf);
|
||||
}
|
||||
|
||||
int eth_getenv_enetaddr_by_index(const char *base_name, int index,
|
||||
uchar *enetaddr)
|
||||
{
|
||||
char enetvar[32];
|
||||
sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
|
||||
return eth_getenv_enetaddr(enetvar, enetaddr);
|
||||
}
|
||||
|
||||
int eth_setenv_enetaddr_by_index(const char *base_name, int index,
|
||||
uchar *enetaddr)
|
||||
{
|
||||
char enetvar[32];
|
||||
sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
|
||||
return eth_setenv_enetaddr(enetvar, enetaddr);
|
||||
}
|
||||
|
||||
void eth_common_init(void)
|
||||
{
|
||||
bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
|
||||
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
|
||||
miiphy_init();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PHYLIB
|
||||
phy_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
int eth_mac_skip(int index)
|
||||
{
|
||||
char enetvar[15];
|
||||
char *skip_state;
|
||||
|
||||
sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
|
||||
skip_state = getenv(enetvar);
|
||||
return skip_state != NULL;
|
||||
}
|
||||
|
||||
void eth_current_changed(void)
|
||||
{
|
||||
char *act = getenv("ethact");
|
||||
char *ethrotate;
|
||||
|
||||
/*
|
||||
* The call to eth_get_dev() below has a side effect of rotating
|
||||
* ethernet device if uc_priv->current == NULL. This is not what
|
||||
* we want when 'ethrotate' variable is 'no'.
|
||||
*/
|
||||
ethrotate = getenv("ethrotate");
|
||||
if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
|
||||
return;
|
||||
|
||||
/* update current ethernet name */
|
||||
if (eth_get_dev()) {
|
||||
if (act == NULL || strcmp(act, eth_get_name()) != 0)
|
||||
setenv("ethact", eth_get_name());
|
||||
}
|
||||
/*
|
||||
* remove the variable completely if there is no active
|
||||
* interface
|
||||
*/
|
||||
else if (act != NULL)
|
||||
setenv("ethact", NULL);
|
||||
}
|
||||
|
||||
void eth_try_another(int first_restart)
|
||||
{
|
||||
static void *first_failed;
|
||||
char *ethrotate;
|
||||
|
||||
/*
|
||||
* Do not rotate between network interfaces when
|
||||
* 'ethrotate' variable is set to 'no'.
|
||||
*/
|
||||
ethrotate = getenv("ethrotate");
|
||||
if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
|
||||
return;
|
||||
|
||||
if (!eth_get_dev())
|
||||
return;
|
||||
|
||||
if (first_restart)
|
||||
first_failed = eth_get_dev();
|
||||
|
||||
eth_set_current_to_next();
|
||||
|
||||
eth_current_changed();
|
||||
|
||||
if (first_failed == eth_get_dev())
|
||||
net_restart_wrap = 1;
|
||||
}
|
||||
|
||||
void eth_set_current(void)
|
||||
{
|
||||
static char *act;
|
||||
static int env_changed_id;
|
||||
int env_id;
|
||||
|
||||
env_id = get_env_id();
|
||||
if ((act == NULL) || (env_changed_id != env_id)) {
|
||||
act = getenv("ethact");
|
||||
env_changed_id = env_id;
|
||||
}
|
||||
|
||||
if (act == NULL) {
|
||||
char *ethprime = getenv("ethprime");
|
||||
void *dev = NULL;
|
||||
|
||||
if (ethprime)
|
||||
dev = eth_get_dev_by_name(ethprime);
|
||||
if (dev)
|
||||
eth_set_dev(dev);
|
||||
else
|
||||
eth_set_dev(NULL);
|
||||
} else {
|
||||
eth_set_dev(eth_get_dev_by_name(act));
|
||||
}
|
||||
|
||||
eth_current_changed();
|
||||
}
|
||||
|
||||
const char *eth_get_name(void)
|
||||
{
|
||||
return eth_get_dev() ? eth_get_dev()->name : "unknown";
|
||||
}
|
||||
40
u-boot/net/eth_internal.h
Normal file
40
u-boot/net/eth_internal.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* (C) Copyright 2001-2015
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
* Joe Hershberger, National Instruments
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __ETH_INTERNAL_H
|
||||
#define __ETH_INTERNAL_H
|
||||
|
||||
/* Do init that is common to driver model and legacy networking */
|
||||
void eth_common_init(void);
|
||||
|
||||
/**
|
||||
* eth_setenv_enetaddr_by_index() - set the MAC address envrionment variable
|
||||
*
|
||||
* This sets up an environment variable with the given MAC address (@enetaddr).
|
||||
* The environment variable to be set is defined by <@base_name><@index>addr.
|
||||
* If @index is 0 it is omitted. For common Ethernet this means ethaddr,
|
||||
* eth1addr, etc.
|
||||
*
|
||||
* @base_name: Base name for variable, typically "eth"
|
||||
* @index: Index of interface being updated (>=0)
|
||||
* @enetaddr: Pointer to MAC address to put into the variable
|
||||
* @return 0 if OK, other value on error
|
||||
*/
|
||||
int eth_setenv_enetaddr_by_index(const char *base_name, int index,
|
||||
uchar *enetaddr);
|
||||
|
||||
int eth_mac_skip(int index);
|
||||
void eth_current_changed(void);
|
||||
#ifdef CONFIG_DM_ETH
|
||||
void eth_set_dev(struct udevice *dev);
|
||||
#else
|
||||
void eth_set_dev(struct eth_device *dev);
|
||||
#endif
|
||||
void eth_set_current_to_next(void);
|
||||
|
||||
#endif
|
||||
439
u-boot/net/eth_legacy.c
Normal file
439
u-boot/net/eth_legacy.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* (C) Copyright 2001-2015
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
* Joe Hershberger, National Instruments
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <environment.h>
|
||||
#include <net.h>
|
||||
#include <phy.h>
|
||||
#include <asm/errno.h>
|
||||
#include "eth_internal.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* CPU and board-specific Ethernet initializations. Aliased function
|
||||
* signals caller to move on
|
||||
*/
|
||||
static int __def_eth_init(bd_t *bis)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
|
||||
int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
|
||||
|
||||
#ifdef CONFIG_API
|
||||
static struct {
|
||||
uchar data[PKTSIZE];
|
||||
int length;
|
||||
} eth_rcv_bufs[PKTBUFSRX];
|
||||
|
||||
static unsigned int eth_rcv_current, eth_rcv_last;
|
||||
#endif
|
||||
|
||||
static struct eth_device *eth_devices;
|
||||
struct eth_device *eth_current;
|
||||
|
||||
void eth_set_current_to_next(void)
|
||||
{
|
||||
eth_current = eth_current->next;
|
||||
}
|
||||
|
||||
void eth_set_dev(struct eth_device *dev)
|
||||
{
|
||||
eth_current = dev;
|
||||
}
|
||||
|
||||
struct eth_device *eth_get_dev_by_name(const char *devname)
|
||||
{
|
||||
struct eth_device *dev, *target_dev;
|
||||
|
||||
BUG_ON(devname == NULL);
|
||||
|
||||
if (!eth_devices)
|
||||
return NULL;
|
||||
|
||||
dev = eth_devices;
|
||||
target_dev = NULL;
|
||||
do {
|
||||
if (strcmp(devname, dev->name) == 0) {
|
||||
target_dev = dev;
|
||||
break;
|
||||
}
|
||||
dev = dev->next;
|
||||
} while (dev != eth_devices);
|
||||
|
||||
return target_dev;
|
||||
}
|
||||
|
||||
struct eth_device *eth_get_dev_by_index(int index)
|
||||
{
|
||||
struct eth_device *dev, *target_dev;
|
||||
|
||||
if (!eth_devices)
|
||||
return NULL;
|
||||
|
||||
dev = eth_devices;
|
||||
target_dev = NULL;
|
||||
do {
|
||||
if (dev->index == index) {
|
||||
target_dev = dev;
|
||||
break;
|
||||
}
|
||||
dev = dev->next;
|
||||
} while (dev != eth_devices);
|
||||
|
||||
return target_dev;
|
||||
}
|
||||
|
||||
int eth_get_dev_index(void)
|
||||
{
|
||||
if (!eth_current)
|
||||
return -1;
|
||||
|
||||
return eth_current->index;
|
||||
}
|
||||
|
||||
static int on_ethaddr(const char *name, const char *value, enum env_op op,
|
||||
int flags)
|
||||
{
|
||||
int index;
|
||||
struct eth_device *dev;
|
||||
|
||||
if (!eth_devices)
|
||||
return 0;
|
||||
|
||||
/* look for an index after "eth" */
|
||||
index = simple_strtoul(name + 3, NULL, 10);
|
||||
|
||||
dev = eth_devices;
|
||||
do {
|
||||
if (dev->index == index) {
|
||||
switch (op) {
|
||||
case env_op_create:
|
||||
case env_op_overwrite:
|
||||
eth_parse_enetaddr(value, dev->enetaddr);
|
||||
break;
|
||||
case env_op_delete:
|
||||
memset(dev->enetaddr, 0, 6);
|
||||
}
|
||||
}
|
||||
dev = dev->next;
|
||||
} while (dev != eth_devices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
|
||||
|
||||
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
|
||||
int eth_number)
|
||||
{
|
||||
unsigned char env_enetaddr[6];
|
||||
int ret = 0;
|
||||
|
||||
eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
|
||||
|
||||
if (!is_zero_ethaddr(env_enetaddr)) {
|
||||
if (!is_zero_ethaddr(dev->enetaddr) &&
|
||||
memcmp(dev->enetaddr, env_enetaddr, 6)) {
|
||||
printf("\nWarning: %s MAC addresses don't match:\n",
|
||||
dev->name);
|
||||
printf("Address in SROM is %pM\n",
|
||||
dev->enetaddr);
|
||||
printf("Address in environment is %pM\n",
|
||||
env_enetaddr);
|
||||
}
|
||||
|
||||
memcpy(dev->enetaddr, env_enetaddr, 6);
|
||||
} else if (is_valid_ethaddr(dev->enetaddr)) {
|
||||
eth_setenv_enetaddr_by_index(base_name, eth_number,
|
||||
dev->enetaddr);
|
||||
} else if (is_zero_ethaddr(dev->enetaddr)) {
|
||||
#ifdef CONFIG_NET_RANDOM_ETHADDR
|
||||
net_random_ethaddr(dev->enetaddr);
|
||||
printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
|
||||
dev->name, eth_number, dev->enetaddr);
|
||||
#else
|
||||
printf("\nError: %s address not set.\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
|
||||
if (!is_valid_ethaddr(dev->enetaddr)) {
|
||||
printf("\nError: %s address %pM illegal value\n",
|
||||
dev->name, dev->enetaddr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dev->write_hwaddr(dev);
|
||||
if (ret)
|
||||
printf("\nWarning: %s failed to set MAC address\n",
|
||||
dev->name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int eth_register(struct eth_device *dev)
|
||||
{
|
||||
struct eth_device *d;
|
||||
static int index;
|
||||
|
||||
assert(strlen(dev->name) < sizeof(dev->name));
|
||||
|
||||
if (!eth_devices) {
|
||||
eth_devices = dev;
|
||||
eth_current = dev;
|
||||
eth_current_changed();
|
||||
} else {
|
||||
for (d = eth_devices; d->next != eth_devices; d = d->next)
|
||||
;
|
||||
d->next = dev;
|
||||
}
|
||||
|
||||
dev->state = ETH_STATE_INIT;
|
||||
dev->next = eth_devices;
|
||||
dev->index = index++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eth_unregister(struct eth_device *dev)
|
||||
{
|
||||
struct eth_device *cur;
|
||||
|
||||
/* No device */
|
||||
if (!eth_devices)
|
||||
return -ENODEV;
|
||||
|
||||
for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
|
||||
cur = cur->next)
|
||||
;
|
||||
|
||||
/* Device not found */
|
||||
if (cur->next != dev)
|
||||
return -ENODEV;
|
||||
|
||||
cur->next = dev->next;
|
||||
|
||||
if (eth_devices == dev)
|
||||
eth_devices = dev->next == eth_devices ? NULL : dev->next;
|
||||
|
||||
if (eth_current == dev) {
|
||||
eth_current = eth_devices;
|
||||
eth_current_changed();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eth_initialize(void)
|
||||
{
|
||||
int num_devices = 0;
|
||||
|
||||
eth_devices = NULL;
|
||||
eth_current = NULL;
|
||||
eth_common_init();
|
||||
/*
|
||||
* If board-specific initialization exists, call it.
|
||||
* If not, call a CPU-specific one
|
||||
*/
|
||||
if (board_eth_init != __def_eth_init) {
|
||||
if (board_eth_init(gd->bd) < 0)
|
||||
printf("Board Net Initialization Failed\n");
|
||||
} else if (cpu_eth_init != __def_eth_init) {
|
||||
if (cpu_eth_init(gd->bd) < 0)
|
||||
printf("CPU Net Initialization Failed\n");
|
||||
} else {
|
||||
printf("Net Initialization Skipped\n");
|
||||
}
|
||||
|
||||
if (!eth_devices) {
|
||||
puts("No ethernet found.\n");
|
||||
bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
|
||||
} else {
|
||||
struct eth_device *dev = eth_devices;
|
||||
char *ethprime = getenv("ethprime");
|
||||
|
||||
bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
|
||||
do {
|
||||
if (dev->index)
|
||||
puts(", ");
|
||||
|
||||
printf("%s", dev->name);
|
||||
|
||||
if (ethprime && strcmp(dev->name, ethprime) == 0) {
|
||||
eth_current = dev;
|
||||
puts(" [PRIME]");
|
||||
}
|
||||
|
||||
if (strchr(dev->name, ' '))
|
||||
puts("\nWarning: eth device name has a space!"
|
||||
"\n");
|
||||
|
||||
eth_write_hwaddr(dev, "eth", dev->index);
|
||||
|
||||
dev = dev->next;
|
||||
num_devices++;
|
||||
} while (dev != eth_devices);
|
||||
|
||||
eth_current_changed();
|
||||
putc('\n');
|
||||
}
|
||||
|
||||
return num_devices;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
/* Multicast.
|
||||
* mcast_addr: multicast ipaddr from which multicast Mac is made
|
||||
* join: 1=join, 0=leave.
|
||||
*/
|
||||
int eth_mcast_join(struct in_addr mcast_ip, int join)
|
||||
{
|
||||
u8 mcast_mac[6];
|
||||
if (!eth_current || !eth_current->mcast)
|
||||
return -1;
|
||||
mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff;
|
||||
mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff;
|
||||
mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f;
|
||||
mcast_mac[2] = 0x5e;
|
||||
mcast_mac[1] = 0x0;
|
||||
mcast_mac[0] = 0x1;
|
||||
return eth_current->mcast(eth_current, mcast_mac, join);
|
||||
}
|
||||
|
||||
/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
|
||||
* and this is the ethernet-crc method needed for TSEC -- and perhaps
|
||||
* some other adapter -- hash tables
|
||||
*/
|
||||
#define CRCPOLY_LE 0xedb88320
|
||||
u32 ether_crc(size_t len, unsigned char const *p)
|
||||
{
|
||||
int i;
|
||||
u32 crc;
|
||||
crc = ~0;
|
||||
while (len--) {
|
||||
crc ^= *p++;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
|
||||
}
|
||||
/* an reverse the bits, cuz of way they arrive -- last-first */
|
||||
crc = (crc >> 16) | (crc << 16);
|
||||
crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
|
||||
crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
|
||||
crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
|
||||
crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
int eth_init(void)
|
||||
{
|
||||
struct eth_device *old_current;
|
||||
|
||||
if (!eth_current) {
|
||||
puts("No ethernet found.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
old_current = eth_current;
|
||||
do {
|
||||
debug("Trying %s\n", eth_current->name);
|
||||
|
||||
if (eth_current->init(eth_current, gd->bd) >= 0) {
|
||||
eth_current->state = ETH_STATE_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
debug("FAIL\n");
|
||||
|
||||
eth_try_another(0);
|
||||
} while (old_current != eth_current);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
void eth_halt(void)
|
||||
{
|
||||
if (!eth_current)
|
||||
return;
|
||||
|
||||
eth_current->halt(eth_current);
|
||||
|
||||
eth_current->state = ETH_STATE_PASSIVE;
|
||||
}
|
||||
|
||||
int eth_is_active(struct eth_device *dev)
|
||||
{
|
||||
return dev && dev->state == ETH_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
int eth_send(void *packet, int length)
|
||||
{
|
||||
if (!eth_current)
|
||||
return -ENODEV;
|
||||
|
||||
return eth_current->send(eth_current, packet, length);
|
||||
}
|
||||
|
||||
int eth_rx(void)
|
||||
{
|
||||
if (!eth_current)
|
||||
return -ENODEV;
|
||||
|
||||
return eth_current->recv(eth_current);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_API
|
||||
static void eth_save_packet(void *packet, int length)
|
||||
{
|
||||
char *p = packet;
|
||||
int i;
|
||||
|
||||
if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
|
||||
return;
|
||||
|
||||
if (PKTSIZE < length)
|
||||
return;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
eth_rcv_bufs[eth_rcv_last].data[i] = p[i];
|
||||
|
||||
eth_rcv_bufs[eth_rcv_last].length = length;
|
||||
eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
|
||||
}
|
||||
|
||||
int eth_receive(void *packet, int length)
|
||||
{
|
||||
char *p = packet;
|
||||
void *pp = push_packet;
|
||||
int i;
|
||||
|
||||
if (eth_rcv_current == eth_rcv_last) {
|
||||
push_packet = eth_save_packet;
|
||||
eth_rx();
|
||||
push_packet = pp;
|
||||
|
||||
if (eth_rcv_current == eth_rcv_last)
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = min(eth_rcv_bufs[eth_rcv_current].length, length);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
p[i] = eth_rcv_bufs[eth_rcv_current].data[i];
|
||||
|
||||
eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
|
||||
return length;
|
||||
}
|
||||
#endif /* CONFIG_API */
|
||||
346
u-boot/net/link_local.c
Normal file
346
u-boot/net/link_local.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* RFC3927 ZeroConf IPv4 Link-Local addressing
|
||||
* (see <http://www.zeroconf.org/>)
|
||||
*
|
||||
* Copied from BusyBox - networking/zcip.c
|
||||
*
|
||||
* Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
|
||||
* Copyright (C) 2004 by David Brownell
|
||||
* Copyright (C) 2010 by Joe Hershberger
|
||||
*
|
||||
* Licensed under the GPL v2 or later
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <net.h>
|
||||
#include "arp.h"
|
||||
#include "net_rand.h"
|
||||
|
||||
/* We don't need more than 32 bits of the counter */
|
||||
#define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ))
|
||||
|
||||
enum {
|
||||
/* 169.254.0.0 */
|
||||
LINKLOCAL_ADDR = 0xa9fe0000,
|
||||
|
||||
IN_CLASSB_NET = 0xffff0000,
|
||||
IN_CLASSB_HOST = 0x0000ffff,
|
||||
|
||||
/* protocol timeout parameters, specified in seconds */
|
||||
PROBE_WAIT = 1,
|
||||
PROBE_MIN = 1,
|
||||
PROBE_MAX = 2,
|
||||
PROBE_NUM = 3,
|
||||
MAX_CONFLICTS = 10,
|
||||
RATE_LIMIT_INTERVAL = 60,
|
||||
ANNOUNCE_WAIT = 2,
|
||||
ANNOUNCE_NUM = 2,
|
||||
ANNOUNCE_INTERVAL = 2,
|
||||
DEFEND_INTERVAL = 10
|
||||
};
|
||||
|
||||
/* States during the configuration process. */
|
||||
static enum ll_state_t {
|
||||
PROBE = 0,
|
||||
RATE_LIMIT_PROBE,
|
||||
ANNOUNCE,
|
||||
MONITOR,
|
||||
DEFEND,
|
||||
DISABLED
|
||||
} state = DISABLED;
|
||||
|
||||
static struct in_addr ip;
|
||||
static int timeout_ms = -1;
|
||||
static unsigned deadline_ms;
|
||||
static unsigned conflicts;
|
||||
static unsigned nprobes;
|
||||
static unsigned nclaims;
|
||||
static int ready;
|
||||
static unsigned int seed;
|
||||
|
||||
static void link_local_timeout(void);
|
||||
|
||||
/**
|
||||
* Pick a random link local IP address on 169.254/16, except that
|
||||
* the first and last 256 addresses are reserved.
|
||||
*/
|
||||
static struct in_addr pick(void)
|
||||
{
|
||||
unsigned tmp;
|
||||
struct in_addr ip;
|
||||
|
||||
do {
|
||||
tmp = rand_r(&seed) & IN_CLASSB_HOST;
|
||||
} while (tmp > (IN_CLASSB_HOST - 0x0200));
|
||||
ip.s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return milliseconds of random delay, up to "secs" seconds.
|
||||
*/
|
||||
static inline unsigned random_delay_ms(unsigned secs)
|
||||
{
|
||||
return rand_r(&seed) % (secs * 1000);
|
||||
}
|
||||
|
||||
static void configure_wait(void)
|
||||
{
|
||||
if (timeout_ms == -1)
|
||||
return;
|
||||
|
||||
/* poll, being ready to adjust current timeout */
|
||||
if (!timeout_ms)
|
||||
timeout_ms = random_delay_ms(PROBE_WAIT);
|
||||
|
||||
/* set deadline_ms to the point in time when we timeout */
|
||||
deadline_ms = MONOTONIC_MS() + timeout_ms;
|
||||
|
||||
debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u\n",
|
||||
timeout_ms, eth_get_name(), nprobes, nclaims);
|
||||
|
||||
net_set_timeout_handler(timeout_ms, link_local_timeout);
|
||||
}
|
||||
|
||||
void link_local_start(void)
|
||||
{
|
||||
ip = getenv_ip("llipaddr");
|
||||
if (ip.s_addr != 0 &&
|
||||
(ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR) {
|
||||
puts("invalid link address");
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
return;
|
||||
}
|
||||
net_netmask.s_addr = IN_CLASSB_NET;
|
||||
|
||||
seed = seed_mac();
|
||||
if (ip.s_addr == 0)
|
||||
ip = pick();
|
||||
|
||||
state = PROBE;
|
||||
timeout_ms = 0;
|
||||
conflicts = 0;
|
||||
nprobes = 0;
|
||||
nclaims = 0;
|
||||
ready = 0;
|
||||
|
||||
configure_wait();
|
||||
}
|
||||
|
||||
static void link_local_timeout(void)
|
||||
{
|
||||
switch (state) {
|
||||
case PROBE:
|
||||
/* timeouts in the PROBE state mean no conflicting ARP packets
|
||||
have been received, so we can progress through the states */
|
||||
if (nprobes < PROBE_NUM) {
|
||||
struct in_addr zero_ip = {.s_addr = 0};
|
||||
|
||||
nprobes++;
|
||||
debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4\n",
|
||||
nprobes, eth_get_name(), &ip);
|
||||
arp_raw_request(zero_ip, net_null_ethaddr, ip);
|
||||
timeout_ms = PROBE_MIN * 1000;
|
||||
timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
|
||||
} else {
|
||||
/* Switch to announce state */
|
||||
state = ANNOUNCE;
|
||||
nclaims = 0;
|
||||
debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
|
||||
nclaims, eth_get_name(), &ip);
|
||||
arp_raw_request(ip, net_ethaddr, ip);
|
||||
timeout_ms = ANNOUNCE_INTERVAL * 1000;
|
||||
}
|
||||
break;
|
||||
case RATE_LIMIT_PROBE:
|
||||
/* timeouts in the RATE_LIMIT_PROBE state mean no conflicting
|
||||
ARP packets have been received, so we can move immediately
|
||||
to the announce state */
|
||||
state = ANNOUNCE;
|
||||
nclaims = 0;
|
||||
debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
|
||||
nclaims, eth_get_name(), &ip);
|
||||
arp_raw_request(ip, net_ethaddr, ip);
|
||||
timeout_ms = ANNOUNCE_INTERVAL * 1000;
|
||||
break;
|
||||
case ANNOUNCE:
|
||||
/* timeouts in the ANNOUNCE state mean no conflicting ARP
|
||||
packets have been received, so we can progress through
|
||||
the states */
|
||||
if (nclaims < ANNOUNCE_NUM) {
|
||||
nclaims++;
|
||||
debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
|
||||
nclaims, eth_get_name(), &ip);
|
||||
arp_raw_request(ip, net_ethaddr, ip);
|
||||
timeout_ms = ANNOUNCE_INTERVAL * 1000;
|
||||
} else {
|
||||
/* Switch to monitor state */
|
||||
state = MONITOR;
|
||||
printf("Successfully assigned %pI4\n", &ip);
|
||||
net_copy_ip(&net_ip, &ip);
|
||||
ready = 1;
|
||||
conflicts = 0;
|
||||
timeout_ms = -1;
|
||||
/* Never timeout in the monitor state */
|
||||
net_set_timeout_handler(0, NULL);
|
||||
|
||||
/* NOTE: all other exit paths should deconfig ... */
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case DEFEND:
|
||||
/* We won! No ARP replies, so just go back to monitor */
|
||||
state = MONITOR;
|
||||
timeout_ms = -1;
|
||||
conflicts = 0;
|
||||
break;
|
||||
default:
|
||||
/* Invalid, should never happen. Restart the whole protocol */
|
||||
state = PROBE;
|
||||
ip = pick();
|
||||
timeout_ms = 0;
|
||||
nprobes = 0;
|
||||
nclaims = 0;
|
||||
break;
|
||||
}
|
||||
configure_wait();
|
||||
}
|
||||
|
||||
void link_local_receive_arp(struct arp_hdr *arp, int len)
|
||||
{
|
||||
int source_ip_conflict;
|
||||
int target_ip_conflict;
|
||||
struct in_addr null_ip = {.s_addr = 0};
|
||||
|
||||
if (state == DISABLED)
|
||||
return;
|
||||
|
||||
/* We need to adjust the timeout in case we didn't receive a
|
||||
conflicting packet. */
|
||||
if (timeout_ms > 0) {
|
||||
unsigned diff = deadline_ms - MONOTONIC_MS();
|
||||
if ((int)(diff) < 0) {
|
||||
/* Current time is greater than the expected timeout
|
||||
time. This should never happen */
|
||||
debug_cond(DEBUG_LL_STATE,
|
||||
"missed an expected timeout\n");
|
||||
timeout_ms = 0;
|
||||
} else {
|
||||
debug_cond(DEBUG_INT_STATE, "adjusting timeout\n");
|
||||
timeout_ms = diff | 1; /* never 0 */
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
/* XXX Don't bother with ethernet link just yet */
|
||||
if ((fds[0].revents & POLLIN) == 0) {
|
||||
if (fds[0].revents & POLLERR) {
|
||||
/*
|
||||
* FIXME: links routinely go down;
|
||||
*/
|
||||
bb_error_msg("iface %s is down", eth_get_name());
|
||||
if (ready)
|
||||
run(argv, "deconfig", &ip);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d,\n",
|
||||
eth_get_name(), ntohs(arp->ar_pro),
|
||||
ntohs(arp->ar_op));
|
||||
debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4\n",
|
||||
&arp->ar_sha,
|
||||
&arp->ar_spa);
|
||||
debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4\n",
|
||||
&arp->ar_tha,
|
||||
&arp->ar_tpa);
|
||||
|
||||
if (arp->ar_op != htons(ARPOP_REQUEST) &&
|
||||
arp->ar_op != htons(ARPOP_REPLY)) {
|
||||
configure_wait();
|
||||
return;
|
||||
}
|
||||
|
||||
source_ip_conflict = 0;
|
||||
target_ip_conflict = 0;
|
||||
|
||||
if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 &&
|
||||
memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0)
|
||||
source_ip_conflict = 1;
|
||||
|
||||
/*
|
||||
* According to RFC 3927, section 2.2.1:
|
||||
* Check if packet is an ARP probe by checking for a null source IP
|
||||
* then check that target IP is equal to ours and source hw addr
|
||||
* is not equal to ours. This condition should cause a conflict only
|
||||
* during probe.
|
||||
*/
|
||||
if (arp->ar_op == htons(ARPOP_REQUEST) &&
|
||||
memcmp(&arp->ar_spa, &null_ip, ARP_PLEN) == 0 &&
|
||||
memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 &&
|
||||
memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0) {
|
||||
target_ip_conflict = 1;
|
||||
}
|
||||
|
||||
debug_cond(DEBUG_NET_PKT,
|
||||
"state = %d, source ip conflict = %d, target ip conflict = "
|
||||
"%d\n", state, source_ip_conflict, target_ip_conflict);
|
||||
switch (state) {
|
||||
case PROBE:
|
||||
case ANNOUNCE:
|
||||
/* When probing or announcing, check for source IP conflicts
|
||||
and other hosts doing ARP probes (target IP conflicts). */
|
||||
if (source_ip_conflict || target_ip_conflict) {
|
||||
conflicts++;
|
||||
state = PROBE;
|
||||
if (conflicts >= MAX_CONFLICTS) {
|
||||
debug("%s ratelimit\n", eth_get_name());
|
||||
timeout_ms = RATE_LIMIT_INTERVAL * 1000;
|
||||
state = RATE_LIMIT_PROBE;
|
||||
}
|
||||
|
||||
/* restart the whole protocol */
|
||||
ip = pick();
|
||||
timeout_ms = 0;
|
||||
nprobes = 0;
|
||||
nclaims = 0;
|
||||
}
|
||||
break;
|
||||
case MONITOR:
|
||||
/* If a conflict, we try to defend with a single ARP probe */
|
||||
if (source_ip_conflict) {
|
||||
debug("monitor conflict -- defending\n");
|
||||
state = DEFEND;
|
||||
timeout_ms = DEFEND_INTERVAL * 1000;
|
||||
arp_raw_request(ip, net_ethaddr, ip);
|
||||
}
|
||||
break;
|
||||
case DEFEND:
|
||||
/* Well, we tried. Start over (on conflict) */
|
||||
if (source_ip_conflict) {
|
||||
state = PROBE;
|
||||
debug("defend conflict -- starting over\n");
|
||||
ready = 0;
|
||||
net_ip.s_addr = 0;
|
||||
|
||||
/* restart the whole protocol */
|
||||
ip = pick();
|
||||
timeout_ms = 0;
|
||||
nprobes = 0;
|
||||
nclaims = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Invalid, should never happen. Restart the whole protocol */
|
||||
debug("invalid state -- starting over\n");
|
||||
state = PROBE;
|
||||
ip = pick();
|
||||
timeout_ms = 0;
|
||||
nprobes = 0;
|
||||
nclaims = 0;
|
||||
break;
|
||||
}
|
||||
configure_wait();
|
||||
}
|
||||
24
u-boot/net/link_local.h
Normal file
24
u-boot/net/link_local.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* RFC3927 ZeroConf IPv4 Link-Local addressing
|
||||
* (see <http://www.zeroconf.org/>)
|
||||
*
|
||||
* Copied from BusyBox - networking/zcip.c
|
||||
*
|
||||
* Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
|
||||
* Copyright (C) 2004 by David Brownell
|
||||
*
|
||||
* Licensed under the GPL v2 or later
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_CMD_LINK_LOCAL)
|
||||
|
||||
#ifndef __LINK_LOCAL_H__
|
||||
#define __LINK_LOCAL_H__
|
||||
|
||||
#include <common.h>
|
||||
|
||||
void link_local_receive_arp(struct arp_hdr *arp, int len);
|
||||
void link_local_start(void);
|
||||
|
||||
#endif /* __LINK_LOCAL_H__ */
|
||||
#endif
|
||||
1548
u-boot/net/net.c
Normal file
1548
u-boot/net/net.c
Normal file
File diff suppressed because it is too large
Load Diff
43
u-boot/net/net_rand.h
Normal file
43
u-boot/net/net_rand.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copied from LiMon - BOOTP.
|
||||
*
|
||||
* Copyright 1994, 1995, 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
*/
|
||||
|
||||
#ifndef __NET_RAND_H__
|
||||
#define __NET_RAND_H__
|
||||
|
||||
#include <common.h>
|
||||
|
||||
/*
|
||||
* Return a seed for the PRNG derived from the eth0 MAC address.
|
||||
*/
|
||||
static inline unsigned int seed_mac(void)
|
||||
{
|
||||
unsigned char enetaddr[6];
|
||||
unsigned int seed;
|
||||
|
||||
/* get our mac */
|
||||
eth_getenv_enetaddr("ethaddr", enetaddr);
|
||||
|
||||
seed = enetaddr[5];
|
||||
seed ^= enetaddr[4] << 8;
|
||||
seed ^= enetaddr[3] << 16;
|
||||
seed ^= enetaddr[2] << 24;
|
||||
seed ^= enetaddr[1];
|
||||
seed ^= enetaddr[0] << 8;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seed the random number generator using the eth0 MAC address.
|
||||
*/
|
||||
static inline void srand_mac(void)
|
||||
{
|
||||
srand(seed_mac());
|
||||
}
|
||||
|
||||
#endif /* __NET_RAND_H__ */
|
||||
789
u-boot/net/nfs.c
Normal file
789
u-boot/net/nfs.c
Normal file
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
* NFS support driver - based on etherboot and U-BOOT's tftp.c
|
||||
*
|
||||
* Masami Komiya <mkomiya@sonare.it> 2004
|
||||
*
|
||||
*/
|
||||
|
||||
/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
|
||||
* large portions are copied verbatim) as distributed in OSKit 0.97. A few
|
||||
* changes were necessary to adapt the code to Etherboot and to fix several
|
||||
* inconsistencies. Also the RPC message preparation is done "by hand" to
|
||||
* avoid adding netsprintf() which I find hard to understand and use. */
|
||||
|
||||
/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
|
||||
* it loads the kernel image off the boot server (ARP_SERVER) and does not
|
||||
* access the client root disk (root-path in dhcpd.conf), which would use
|
||||
* ARP_ROOTSERVER. The root disk is something the operating system we are
|
||||
* about to load needs to use. This is different from the OSKit 0.97 logic. */
|
||||
|
||||
/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
|
||||
* If a symlink is encountered, it is followed as far as possible (recursion
|
||||
* possible, maximum 16 steps). There is no clearing of ".."'s inside the
|
||||
* path, so please DON'T DO THAT. thx. */
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <net.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include "nfs.h"
|
||||
#include "bootp.h"
|
||||
|
||||
#define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */
|
||||
#define NFS_RETRY_COUNT 30
|
||||
#ifndef CONFIG_NFS_TIMEOUT
|
||||
# define NFS_TIMEOUT 2000UL
|
||||
#else
|
||||
# define NFS_TIMEOUT CONFIG_NFS_TIMEOUT
|
||||
#endif
|
||||
|
||||
#define NFS_RPC_ERR 1
|
||||
#define NFS_RPC_DROP 124
|
||||
|
||||
static int fs_mounted;
|
||||
static unsigned long rpc_id;
|
||||
static int nfs_offset = -1;
|
||||
static int nfs_len;
|
||||
static ulong nfs_timeout = NFS_TIMEOUT;
|
||||
|
||||
static char dirfh[NFS_FHSIZE]; /* file handle of directory */
|
||||
static char filefh[NFS_FHSIZE]; /* file handle of kernel image */
|
||||
|
||||
static enum net_loop_state nfs_download_state;
|
||||
static struct in_addr nfs_server_ip;
|
||||
static int nfs_server_mount_port;
|
||||
static int nfs_server_port;
|
||||
static int nfs_our_port;
|
||||
static int nfs_timeout_count;
|
||||
static int nfs_state;
|
||||
#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
|
||||
#define STATE_PRCLOOKUP_PROG_NFS_REQ 2
|
||||
#define STATE_MOUNT_REQ 3
|
||||
#define STATE_UMOUNT_REQ 4
|
||||
#define STATE_LOOKUP_REQ 5
|
||||
#define STATE_READ_REQ 6
|
||||
#define STATE_READLINK_REQ 7
|
||||
|
||||
static char default_filename[64];
|
||||
static char *nfs_filename;
|
||||
static char *nfs_path;
|
||||
static char nfs_path_buff[2048];
|
||||
|
||||
static inline int store_block(uchar *src, unsigned offset, unsigned len)
|
||||
{
|
||||
ulong newsize = offset + len;
|
||||
#ifdef CONFIG_SYS_DIRECT_FLASH_NFS
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
|
||||
/* start address in flash? */
|
||||
if (load_addr + offset >= flash_info[i].start[0]) {
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc) { /* Flash is destination for this packet */
|
||||
rc = flash_write((uchar *)src, (ulong)(load_addr+offset), len);
|
||||
if (rc) {
|
||||
flash_perror(rc);
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
|
||||
{
|
||||
void *ptr = map_sysmem(load_addr + offset, len);
|
||||
|
||||
memcpy(ptr, src, len);
|
||||
unmap_sysmem(ptr);
|
||||
}
|
||||
|
||||
if (net_boot_file_size < (offset + len))
|
||||
net_boot_file_size = newsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *basename(char *path)
|
||||
{
|
||||
char *fname;
|
||||
|
||||
fname = path + strlen(path) - 1;
|
||||
while (fname >= path) {
|
||||
if (*fname == '/') {
|
||||
fname++;
|
||||
break;
|
||||
}
|
||||
fname--;
|
||||
}
|
||||
return fname;
|
||||
}
|
||||
|
||||
static char *dirname(char *path)
|
||||
{
|
||||
char *fname;
|
||||
|
||||
fname = basename(path);
|
||||
--fname;
|
||||
*fname = '\0';
|
||||
return path;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
|
||||
**************************************************************************/
|
||||
static uint32_t *rpc_add_credentials(uint32_t *p)
|
||||
{
|
||||
int hl;
|
||||
int hostnamelen;
|
||||
char hostname[256];
|
||||
|
||||
strcpy(hostname, "");
|
||||
hostnamelen = strlen(hostname);
|
||||
|
||||
/* Here's the executive summary on authentication requirements of the
|
||||
* various NFS server implementations: Linux accepts both AUTH_NONE
|
||||
* and AUTH_UNIX authentication (also accepts an empty hostname field
|
||||
* in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
|
||||
* AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
|
||||
* scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
|
||||
* it (if the BOOTP/DHCP reply didn't give one, just use an empty
|
||||
* hostname). */
|
||||
|
||||
hl = (hostnamelen + 3) & ~3;
|
||||
|
||||
/* Provide an AUTH_UNIX credential. */
|
||||
*p++ = htonl(1); /* AUTH_UNIX */
|
||||
*p++ = htonl(hl+20); /* auth length */
|
||||
*p++ = htonl(0); /* stamp */
|
||||
*p++ = htonl(hostnamelen); /* hostname string */
|
||||
if (hostnamelen & 3)
|
||||
*(p + hostnamelen / 4) = 0; /* add zero padding */
|
||||
memcpy(p, hostname, hostnamelen);
|
||||
p += hl / 4;
|
||||
*p++ = 0; /* uid */
|
||||
*p++ = 0; /* gid */
|
||||
*p++ = 0; /* auxiliary gid list */
|
||||
|
||||
/* Provide an AUTH_NONE verifier. */
|
||||
*p++ = 0; /* AUTH_NONE */
|
||||
*p++ = 0; /* auth length */
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_LOOKUP - Lookup RPC Port numbers
|
||||
**************************************************************************/
|
||||
static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
|
||||
{
|
||||
struct rpc_t pkt;
|
||||
unsigned long id;
|
||||
uint32_t *p;
|
||||
int pktlen;
|
||||
int sport;
|
||||
|
||||
id = ++rpc_id;
|
||||
pkt.u.call.id = htonl(id);
|
||||
pkt.u.call.type = htonl(MSG_CALL);
|
||||
pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
pkt.u.call.prog = htonl(rpc_prog);
|
||||
pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
|
||||
pkt.u.call.proc = htonl(rpc_proc);
|
||||
p = (uint32_t *)&(pkt.u.call.data);
|
||||
|
||||
if (datalen)
|
||||
memcpy((char *)p, (char *)data, datalen*sizeof(uint32_t));
|
||||
|
||||
pktlen = (char *)p + datalen*sizeof(uint32_t) - (char *)&pkt;
|
||||
|
||||
memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
|
||||
(char *)&pkt, pktlen);
|
||||
|
||||
if (rpc_prog == PROG_PORTMAP)
|
||||
sport = SUNRPC_PORT;
|
||||
else if (rpc_prog == PROG_MOUNT)
|
||||
sport = nfs_server_mount_port;
|
||||
else
|
||||
sport = nfs_server_port;
|
||||
|
||||
net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport,
|
||||
nfs_our_port, pktlen);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_LOOKUP - Lookup RPC Port numbers
|
||||
**************************************************************************/
|
||||
static void rpc_lookup_req(int prog, int ver)
|
||||
{
|
||||
uint32_t data[16];
|
||||
|
||||
data[0] = 0; data[1] = 0; /* auth credential */
|
||||
data[2] = 0; data[3] = 0; /* auth verifier */
|
||||
data[4] = htonl(prog);
|
||||
data[5] = htonl(ver);
|
||||
data[6] = htonl(17); /* IP_UDP */
|
||||
data[7] = 0;
|
||||
|
||||
rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_MOUNT - Mount an NFS Filesystem
|
||||
**************************************************************************/
|
||||
static void nfs_mount_req(char *path)
|
||||
{
|
||||
uint32_t data[1024];
|
||||
uint32_t *p;
|
||||
int len;
|
||||
int pathlen;
|
||||
|
||||
pathlen = strlen(path);
|
||||
|
||||
p = &(data[0]);
|
||||
p = rpc_add_credentials(p);
|
||||
|
||||
*p++ = htonl(pathlen);
|
||||
if (pathlen & 3)
|
||||
*(p + pathlen / 4) = 0;
|
||||
memcpy(p, path, pathlen);
|
||||
p += (pathlen + 3) / 4;
|
||||
|
||||
len = (uint32_t *)p - (uint32_t *)&(data[0]);
|
||||
|
||||
rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
|
||||
**************************************************************************/
|
||||
static void nfs_umountall_req(void)
|
||||
{
|
||||
uint32_t data[1024];
|
||||
uint32_t *p;
|
||||
int len;
|
||||
|
||||
if ((nfs_server_mount_port == -1) || (!fs_mounted))
|
||||
/* Nothing mounted, nothing to umount */
|
||||
return;
|
||||
|
||||
p = &(data[0]);
|
||||
p = rpc_add_credentials(p);
|
||||
|
||||
len = (uint32_t *)p - (uint32_t *)&(data[0]);
|
||||
|
||||
rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* NFS_READLINK (AH 2003-07-14)
|
||||
* This procedure is called when read of the first block fails -
|
||||
* this probably happens when it's a directory or a symlink
|
||||
* In case of successful readlink(), the dirname is manipulated,
|
||||
* so that inside the nfs() function a recursion can be done.
|
||||
**************************************************************************/
|
||||
static void nfs_readlink_req(void)
|
||||
{
|
||||
uint32_t data[1024];
|
||||
uint32_t *p;
|
||||
int len;
|
||||
|
||||
p = &(data[0]);
|
||||
p = rpc_add_credentials(p);
|
||||
|
||||
memcpy(p, filefh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
|
||||
len = (uint32_t *)p - (uint32_t *)&(data[0]);
|
||||
|
||||
rpc_req(PROG_NFS, NFS_READLINK, data, len);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_LOOKUP - Lookup Pathname
|
||||
**************************************************************************/
|
||||
static void nfs_lookup_req(char *fname)
|
||||
{
|
||||
uint32_t data[1024];
|
||||
uint32_t *p;
|
||||
int len;
|
||||
int fnamelen;
|
||||
|
||||
fnamelen = strlen(fname);
|
||||
|
||||
p = &(data[0]);
|
||||
p = rpc_add_credentials(p);
|
||||
|
||||
memcpy(p, dirfh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
*p++ = htonl(fnamelen);
|
||||
if (fnamelen & 3)
|
||||
*(p + fnamelen / 4) = 0;
|
||||
memcpy(p, fname, fnamelen);
|
||||
p += (fnamelen + 3) / 4;
|
||||
|
||||
len = (uint32_t *)p - (uint32_t *)&(data[0]);
|
||||
|
||||
rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_READ - Read File on NFS Server
|
||||
**************************************************************************/
|
||||
static void nfs_read_req(int offset, int readlen)
|
||||
{
|
||||
uint32_t data[1024];
|
||||
uint32_t *p;
|
||||
int len;
|
||||
|
||||
p = &(data[0]);
|
||||
p = rpc_add_credentials(p);
|
||||
|
||||
memcpy(p, filefh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
*p++ = htonl(offset);
|
||||
*p++ = htonl(readlen);
|
||||
*p++ = 0;
|
||||
|
||||
len = (uint32_t *)p - (uint32_t *)&(data[0]);
|
||||
|
||||
rpc_req(PROG_NFS, NFS_READ, data, len);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC request dispatcher
|
||||
**************************************************************************/
|
||||
static void nfs_send(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
|
||||
switch (nfs_state) {
|
||||
case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
|
||||
rpc_lookup_req(PROG_MOUNT, 1);
|
||||
break;
|
||||
case STATE_PRCLOOKUP_PROG_NFS_REQ:
|
||||
rpc_lookup_req(PROG_NFS, 2);
|
||||
break;
|
||||
case STATE_MOUNT_REQ:
|
||||
nfs_mount_req(nfs_path);
|
||||
break;
|
||||
case STATE_UMOUNT_REQ:
|
||||
nfs_umountall_req();
|
||||
break;
|
||||
case STATE_LOOKUP_REQ:
|
||||
nfs_lookup_req(nfs_filename);
|
||||
break;
|
||||
case STATE_READ_REQ:
|
||||
nfs_read_req(nfs_offset, nfs_len);
|
||||
break;
|
||||
case STATE_READLINK_REQ:
|
||||
nfs_readlink_req();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Handlers for the reply from server
|
||||
**************************************************************************/
|
||||
|
||||
static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len)
|
||||
{
|
||||
struct rpc_t rpc_pkt;
|
||||
|
||||
memcpy((unsigned char *)&rpc_pkt, pkt, len);
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
|
||||
return -NFS_RPC_ERR;
|
||||
else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
|
||||
return -NFS_RPC_DROP;
|
||||
|
||||
if (rpc_pkt.u.reply.rstatus ||
|
||||
rpc_pkt.u.reply.verifier ||
|
||||
rpc_pkt.u.reply.astatus)
|
||||
return -1;
|
||||
|
||||
switch (prog) {
|
||||
case PROG_MOUNT:
|
||||
nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
|
||||
break;
|
||||
case PROG_NFS:
|
||||
nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_mount_reply(uchar *pkt, unsigned len)
|
||||
{
|
||||
struct rpc_t rpc_pkt;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
memcpy((unsigned char *)&rpc_pkt, pkt, len);
|
||||
|
||||
if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
|
||||
return -NFS_RPC_ERR;
|
||||
else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
|
||||
return -NFS_RPC_DROP;
|
||||
|
||||
if (rpc_pkt.u.reply.rstatus ||
|
||||
rpc_pkt.u.reply.verifier ||
|
||||
rpc_pkt.u.reply.astatus ||
|
||||
rpc_pkt.u.reply.data[0])
|
||||
return -1;
|
||||
|
||||
fs_mounted = 1;
|
||||
memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_umountall_reply(uchar *pkt, unsigned len)
|
||||
{
|
||||
struct rpc_t rpc_pkt;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
memcpy((unsigned char *)&rpc_pkt, pkt, len);
|
||||
|
||||
if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
|
||||
return -NFS_RPC_ERR;
|
||||
else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
|
||||
return -NFS_RPC_DROP;
|
||||
|
||||
if (rpc_pkt.u.reply.rstatus ||
|
||||
rpc_pkt.u.reply.verifier ||
|
||||
rpc_pkt.u.reply.astatus)
|
||||
return -1;
|
||||
|
||||
fs_mounted = 0;
|
||||
memset(dirfh, 0, sizeof(dirfh));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_lookup_reply(uchar *pkt, unsigned len)
|
||||
{
|
||||
struct rpc_t rpc_pkt;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
memcpy((unsigned char *)&rpc_pkt, pkt, len);
|
||||
|
||||
if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
|
||||
return -NFS_RPC_ERR;
|
||||
else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
|
||||
return -NFS_RPC_DROP;
|
||||
|
||||
if (rpc_pkt.u.reply.rstatus ||
|
||||
rpc_pkt.u.reply.verifier ||
|
||||
rpc_pkt.u.reply.astatus ||
|
||||
rpc_pkt.u.reply.data[0]) {
|
||||
switch (ntohl(rpc_pkt.u.reply.astatus)) {
|
||||
case 0: /* Not an error */
|
||||
break;
|
||||
case 2: /* Remote can't support NFS version */
|
||||
printf("*** ERROR: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
|
||||
2,
|
||||
ntohl(rpc_pkt.u.reply.data[0]),
|
||||
ntohl(rpc_pkt.u.reply.data[1]));
|
||||
break;
|
||||
default: /* Unknown error on 'accept state' flag */
|
||||
printf("*** ERROR: accept state error (%d)\n",
|
||||
ntohl(rpc_pkt.u.reply.astatus));
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_readlink_reply(uchar *pkt, unsigned len)
|
||||
{
|
||||
struct rpc_t rpc_pkt;
|
||||
int rlen;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
memcpy((unsigned char *)&rpc_pkt, pkt, len);
|
||||
|
||||
if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
|
||||
return -NFS_RPC_ERR;
|
||||
else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
|
||||
return -NFS_RPC_DROP;
|
||||
|
||||
if (rpc_pkt.u.reply.rstatus ||
|
||||
rpc_pkt.u.reply.verifier ||
|
||||
rpc_pkt.u.reply.astatus ||
|
||||
rpc_pkt.u.reply.data[0])
|
||||
return -1;
|
||||
|
||||
rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */
|
||||
|
||||
if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
|
||||
int pathlen;
|
||||
strcat(nfs_path, "/");
|
||||
pathlen = strlen(nfs_path);
|
||||
memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]),
|
||||
rlen);
|
||||
nfs_path[pathlen + rlen] = 0;
|
||||
} else {
|
||||
memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen);
|
||||
nfs_path[rlen] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_read_reply(uchar *pkt, unsigned len)
|
||||
{
|
||||
struct rpc_t rpc_pkt;
|
||||
int rlen;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
memcpy((uchar *)&rpc_pkt, pkt, sizeof(rpc_pkt.u.reply));
|
||||
|
||||
if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
|
||||
return -NFS_RPC_ERR;
|
||||
else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
|
||||
return -NFS_RPC_DROP;
|
||||
|
||||
if (rpc_pkt.u.reply.rstatus ||
|
||||
rpc_pkt.u.reply.verifier ||
|
||||
rpc_pkt.u.reply.astatus ||
|
||||
rpc_pkt.u.reply.data[0]) {
|
||||
if (rpc_pkt.u.reply.rstatus)
|
||||
return -9999;
|
||||
if (rpc_pkt.u.reply.astatus)
|
||||
return -9999;
|
||||
return -ntohl(rpc_pkt.u.reply.data[0]);
|
||||
}
|
||||
|
||||
if ((nfs_offset != 0) && !((nfs_offset) %
|
||||
(NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
|
||||
puts("\n\t ");
|
||||
if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
|
||||
putc('#');
|
||||
|
||||
rlen = ntohl(rpc_pkt.u.reply.data[18]);
|
||||
if (store_block((uchar *)pkt + sizeof(rpc_pkt.u.reply),
|
||||
nfs_offset, rlen))
|
||||
return -9999;
|
||||
|
||||
return rlen;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Interfaces of U-BOOT
|
||||
**************************************************************************/
|
||||
static void nfs_timeout_handler(void)
|
||||
{
|
||||
if (++nfs_timeout_count > NFS_RETRY_COUNT) {
|
||||
puts("\nRetry count exceeded; starting again\n");
|
||||
net_start_again();
|
||||
} else {
|
||||
puts("T ");
|
||||
net_set_timeout_handler(nfs_timeout +
|
||||
NFS_TIMEOUT * nfs_timeout_count,
|
||||
nfs_timeout_handler);
|
||||
nfs_send();
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
|
||||
unsigned src, unsigned len)
|
||||
{
|
||||
int rlen;
|
||||
int reply;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
if (dest != nfs_our_port)
|
||||
return;
|
||||
|
||||
switch (nfs_state) {
|
||||
case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
|
||||
if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
|
||||
break;
|
||||
nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
|
||||
nfs_send();
|
||||
break;
|
||||
|
||||
case STATE_PRCLOOKUP_PROG_NFS_REQ:
|
||||
if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
|
||||
break;
|
||||
nfs_state = STATE_MOUNT_REQ;
|
||||
nfs_send();
|
||||
break;
|
||||
|
||||
case STATE_MOUNT_REQ:
|
||||
reply = nfs_mount_reply(pkt, len);
|
||||
if (reply == -NFS_RPC_DROP) {
|
||||
break;
|
||||
} else if (reply == -NFS_RPC_ERR) {
|
||||
puts("*** ERROR: Cannot mount\n");
|
||||
/* just to be sure... */
|
||||
nfs_state = STATE_UMOUNT_REQ;
|
||||
nfs_send();
|
||||
} else {
|
||||
nfs_state = STATE_LOOKUP_REQ;
|
||||
nfs_send();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_UMOUNT_REQ:
|
||||
reply = nfs_umountall_reply(pkt, len);
|
||||
if (reply == -NFS_RPC_DROP) {
|
||||
break;
|
||||
} else if (reply == -NFS_RPC_ERR) {
|
||||
puts("*** ERROR: Cannot umount\n");
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
} else {
|
||||
puts("\ndone\n");
|
||||
net_set_state(nfs_download_state);
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_LOOKUP_REQ:
|
||||
reply = nfs_lookup_reply(pkt, len);
|
||||
if (reply == -NFS_RPC_DROP) {
|
||||
break;
|
||||
} else if (reply == -NFS_RPC_ERR) {
|
||||
puts("*** ERROR: File lookup fail\n");
|
||||
nfs_state = STATE_UMOUNT_REQ;
|
||||
nfs_send();
|
||||
} else {
|
||||
nfs_state = STATE_READ_REQ;
|
||||
nfs_offset = 0;
|
||||
nfs_len = NFS_READ_SIZE;
|
||||
nfs_send();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READLINK_REQ:
|
||||
reply = nfs_readlink_reply(pkt, len);
|
||||
if (reply == -NFS_RPC_DROP) {
|
||||
break;
|
||||
} else if (reply == -NFS_RPC_ERR) {
|
||||
puts("*** ERROR: Symlink fail\n");
|
||||
nfs_state = STATE_UMOUNT_REQ;
|
||||
nfs_send();
|
||||
} else {
|
||||
debug("Symlink --> %s\n", nfs_path);
|
||||
nfs_filename = basename(nfs_path);
|
||||
nfs_path = dirname(nfs_path);
|
||||
|
||||
nfs_state = STATE_MOUNT_REQ;
|
||||
nfs_send();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READ_REQ:
|
||||
rlen = nfs_read_reply(pkt, len);
|
||||
net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
|
||||
if (rlen > 0) {
|
||||
nfs_offset += rlen;
|
||||
nfs_send();
|
||||
} else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
|
||||
/* symbolic link */
|
||||
nfs_state = STATE_READLINK_REQ;
|
||||
nfs_send();
|
||||
} else {
|
||||
if (!rlen)
|
||||
nfs_download_state = NETLOOP_SUCCESS;
|
||||
nfs_state = STATE_UMOUNT_REQ;
|
||||
nfs_send();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nfs_start(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
nfs_download_state = NETLOOP_FAIL;
|
||||
|
||||
nfs_server_ip = net_server_ip;
|
||||
nfs_path = (char *)nfs_path_buff;
|
||||
|
||||
if (nfs_path == NULL) {
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
puts("*** ERROR: Fail allocate memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (net_boot_file_name[0] == '\0') {
|
||||
sprintf(default_filename, "/nfsroot/%02X%02X%02X%02X.img",
|
||||
net_ip.s_addr & 0xFF,
|
||||
(net_ip.s_addr >> 8) & 0xFF,
|
||||
(net_ip.s_addr >> 16) & 0xFF,
|
||||
(net_ip.s_addr >> 24) & 0xFF);
|
||||
strcpy(nfs_path, default_filename);
|
||||
|
||||
printf("*** Warning: no boot file name; using '%s'\n",
|
||||
nfs_path);
|
||||
} else {
|
||||
char *p = net_boot_file_name;
|
||||
|
||||
p = strchr(p, ':');
|
||||
|
||||
if (p != NULL) {
|
||||
nfs_server_ip = string_to_ip(net_boot_file_name);
|
||||
++p;
|
||||
strcpy(nfs_path, p);
|
||||
} else {
|
||||
strcpy(nfs_path, net_boot_file_name);
|
||||
}
|
||||
}
|
||||
|
||||
nfs_filename = basename(nfs_path);
|
||||
nfs_path = dirname(nfs_path);
|
||||
|
||||
printf("Using %s device\n", eth_get_name());
|
||||
|
||||
printf("File transfer via NFS from server %pI4; our IP address is %pI4",
|
||||
&nfs_server_ip, &net_ip);
|
||||
|
||||
/* Check if we need to send across this subnet */
|
||||
if (net_gateway.s_addr && net_netmask.s_addr) {
|
||||
struct in_addr our_net;
|
||||
struct in_addr server_net;
|
||||
|
||||
our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
|
||||
server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
|
||||
if (our_net.s_addr != server_net.s_addr)
|
||||
printf("; sending through gateway %pI4",
|
||||
&net_gateway);
|
||||
}
|
||||
printf("\nFilename '%s/%s'.", nfs_path, nfs_filename);
|
||||
|
||||
if (net_boot_file_expected_size_in_blocks) {
|
||||
printf(" Size is 0x%x Bytes = ",
|
||||
net_boot_file_expected_size_in_blocks << 9);
|
||||
print_size(net_boot_file_expected_size_in_blocks << 9, "");
|
||||
}
|
||||
printf("\nLoad address: 0x%lx\n"
|
||||
"Loading: *\b", load_addr);
|
||||
|
||||
net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
|
||||
net_set_udp_handler(nfs_handler);
|
||||
|
||||
nfs_timeout_count = 0;
|
||||
nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
|
||||
|
||||
/*nfs_our_port = 4096 + (get_ticks() % 3072);*/
|
||||
/*FIX ME !!!*/
|
||||
nfs_our_port = 1000;
|
||||
|
||||
/* zero out server ether in case the server ip has changed */
|
||||
memset(net_server_ethaddr, 0, 6);
|
||||
|
||||
nfs_send();
|
||||
}
|
||||
77
u-boot/net/nfs.h
Normal file
77
u-boot/net/nfs.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* (C) Masami Komiya <mkomiya@sonare.it> 2004
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __NFS_H__
|
||||
#define __NFS_H__
|
||||
|
||||
#define SUNRPC_PORT 111
|
||||
|
||||
#define PROG_PORTMAP 100000
|
||||
#define PROG_NFS 100003
|
||||
#define PROG_MOUNT 100005
|
||||
|
||||
#define MSG_CALL 0
|
||||
#define MSG_REPLY 1
|
||||
|
||||
#define PORTMAP_GETPORT 3
|
||||
|
||||
#define MOUNT_ADDENTRY 1
|
||||
#define MOUNT_UMOUNTALL 4
|
||||
|
||||
#define NFS_LOOKUP 4
|
||||
#define NFS_READLINK 5
|
||||
#define NFS_READ 6
|
||||
|
||||
#define NFS_FHSIZE 32
|
||||
|
||||
#define NFSERR_PERM 1
|
||||
#define NFSERR_NOENT 2
|
||||
#define NFSERR_ACCES 13
|
||||
#define NFSERR_ISDIR 21
|
||||
#define NFSERR_INVAL 22
|
||||
|
||||
/* Block size used for NFS read accesses. A RPC reply packet (including all
|
||||
* headers) must fit within a single Ethernet frame to avoid fragmentation.
|
||||
* However, if CONFIG_IP_DEFRAG is set, the config file may want to use a
|
||||
* bigger value. In any case, most NFS servers are optimized for a power of 2.
|
||||
*/
|
||||
#ifdef CONFIG_NFS_READ_SIZE
|
||||
#define NFS_READ_SIZE CONFIG_NFS_READ_SIZE
|
||||
#else
|
||||
#define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */
|
||||
#endif
|
||||
|
||||
#define NFS_MAXLINKDEPTH 16
|
||||
|
||||
struct rpc_t {
|
||||
union {
|
||||
uint8_t data[2048];
|
||||
struct {
|
||||
uint32_t id;
|
||||
uint32_t type;
|
||||
uint32_t rpcvers;
|
||||
uint32_t prog;
|
||||
uint32_t vers;
|
||||
uint32_t proc;
|
||||
uint32_t data[1];
|
||||
} call;
|
||||
struct {
|
||||
uint32_t id;
|
||||
uint32_t type;
|
||||
uint32_t rstatus;
|
||||
uint32_t verifier;
|
||||
uint32_t v2;
|
||||
uint32_t astatus;
|
||||
uint32_t data[19];
|
||||
} reply;
|
||||
} u;
|
||||
};
|
||||
void nfs_start(void); /* Begin NFS */
|
||||
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
#endif /* __NFS_H__ */
|
||||
115
u-boot/net/ping.c
Normal file
115
u-boot/net/ping.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copied from Linux Monitor (LiMon) - Networking.
|
||||
*
|
||||
* Copyright 1994 - 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Roland Borde
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include "ping.h"
|
||||
#include "arp.h"
|
||||
|
||||
static ushort ping_seq_number;
|
||||
|
||||
/* The ip address to ping */
|
||||
struct in_addr net_ping_ip;
|
||||
|
||||
static void set_icmp_header(uchar *pkt, struct in_addr dest)
|
||||
{
|
||||
/*
|
||||
* Construct an IP and ICMP header.
|
||||
*/
|
||||
struct ip_hdr *ip = (struct ip_hdr *)pkt;
|
||||
struct icmp_hdr *icmp = (struct icmp_hdr *)(pkt + IP_HDR_SIZE);
|
||||
|
||||
net_set_ip_header(pkt, dest, net_ip);
|
||||
|
||||
ip->ip_len = htons(IP_ICMP_HDR_SIZE);
|
||||
ip->ip_p = IPPROTO_ICMP;
|
||||
ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
|
||||
|
||||
icmp->type = ICMP_ECHO_REQUEST;
|
||||
icmp->code = 0;
|
||||
icmp->checksum = 0;
|
||||
icmp->un.echo.id = 0;
|
||||
icmp->un.echo.sequence = htons(ping_seq_number++);
|
||||
icmp->checksum = compute_ip_checksum(icmp, ICMP_HDR_SIZE);
|
||||
}
|
||||
|
||||
static int ping_send(void)
|
||||
{
|
||||
uchar *pkt;
|
||||
int eth_hdr_size;
|
||||
|
||||
/* XXX always send arp request */
|
||||
|
||||
debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &net_ping_ip);
|
||||
|
||||
net_arp_wait_packet_ip = net_ping_ip;
|
||||
|
||||
eth_hdr_size = net_set_ether(net_tx_packet, net_null_ethaddr, PROT_IP);
|
||||
pkt = (uchar *)net_tx_packet + eth_hdr_size;
|
||||
|
||||
set_icmp_header(pkt, net_ping_ip);
|
||||
|
||||
/* size of the waiting packet */
|
||||
arp_wait_tx_packet_size = eth_hdr_size + IP_ICMP_HDR_SIZE;
|
||||
|
||||
/* and do the ARP request */
|
||||
arp_wait_try = 1;
|
||||
arp_wait_timer_start = get_timer(0);
|
||||
arp_request();
|
||||
return 1; /* waiting */
|
||||
}
|
||||
|
||||
static void ping_timeout_handler(void)
|
||||
{
|
||||
eth_halt();
|
||||
net_set_state(NETLOOP_FAIL); /* we did not get the reply */
|
||||
}
|
||||
|
||||
void ping_start(void)
|
||||
{
|
||||
printf("Using %s device\n", eth_get_name());
|
||||
net_set_timeout_handler(10000UL, ping_timeout_handler);
|
||||
|
||||
ping_send();
|
||||
}
|
||||
|
||||
void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
|
||||
{
|
||||
struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;
|
||||
struct in_addr src_ip;
|
||||
int eth_hdr_size;
|
||||
|
||||
switch (icmph->type) {
|
||||
case ICMP_ECHO_REPLY:
|
||||
src_ip = net_read_ip((void *)&ip->ip_src);
|
||||
if (src_ip.s_addr == net_ping_ip.s_addr)
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
return;
|
||||
case ICMP_ECHO_REQUEST:
|
||||
eth_hdr_size = net_update_ether(et, et->et_src, PROT_IP);
|
||||
|
||||
debug_cond(DEBUG_DEV_PKT,
|
||||
"Got ICMP ECHO REQUEST, return %d bytes\n",
|
||||
eth_hdr_size + len);
|
||||
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_off = 0;
|
||||
net_copy_ip((void *)&ip->ip_dst, &ip->ip_src);
|
||||
net_copy_ip((void *)&ip->ip_src, &net_ip);
|
||||
ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
|
||||
|
||||
icmph->type = ICMP_ECHO_REPLY;
|
||||
icmph->checksum = 0;
|
||||
icmph->checksum = compute_ip_checksum(icmph, len - IP_HDR_SIZE);
|
||||
net_send_packet((uchar *)et, eth_hdr_size + len);
|
||||
return;
|
||||
/* default:
|
||||
return;*/
|
||||
}
|
||||
}
|
||||
32
u-boot/net/ping.h
Normal file
32
u-boot/net/ping.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copied from Linux Monitor (LiMon) - Networking.
|
||||
*
|
||||
* Copyright 1994 - 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000 Roland Borde
|
||||
* Copyright 2000 Paolo Scaffardi
|
||||
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __PING_H__
|
||||
#define __PING_H__
|
||||
|
||||
#include <common.h>
|
||||
#include <net.h>
|
||||
|
||||
/*
|
||||
* Initialize ping (beginning of netloop)
|
||||
*/
|
||||
void ping_start(void);
|
||||
|
||||
/*
|
||||
* Deal with the receipt of a ping packet
|
||||
*
|
||||
* @param et Ethernet header in packet
|
||||
* @param ip IP header in the same packet
|
||||
* @param len Packet length
|
||||
*/
|
||||
void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len);
|
||||
|
||||
#endif /* __PING_H__ */
|
||||
99
u-boot/net/rarp.c
Normal file
99
u-boot/net/rarp.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* (C) Copyright 2000-2002
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <net.h>
|
||||
#include <net/tftp.h>
|
||||
#include "nfs.h"
|
||||
#include "bootp.h"
|
||||
#include "rarp.h"
|
||||
|
||||
#define TIMEOUT 5000UL /* Milliseconds before trying BOOTP again */
|
||||
#ifndef CONFIG_NET_RETRY_COUNT
|
||||
#define TIMEOUT_COUNT 5 /* # of timeouts before giving up */
|
||||
#else
|
||||
#define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT)
|
||||
#endif
|
||||
|
||||
int rarp_try;
|
||||
|
||||
/*
|
||||
* Handle a RARP received packet.
|
||||
*/
|
||||
void rarp_receive(struct ip_udp_hdr *ip, unsigned len)
|
||||
{
|
||||
struct arp_hdr *arp;
|
||||
|
||||
debug_cond(DEBUG_NET_PKT, "Got RARP\n");
|
||||
arp = (struct arp_hdr *)ip;
|
||||
if (len < ARP_HDR_SIZE) {
|
||||
printf("bad length %d < %d\n", len, ARP_HDR_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ntohs(arp->ar_op) != RARPOP_REPLY) ||
|
||||
(ntohs(arp->ar_hrd) != ARP_ETHER) ||
|
||||
(ntohs(arp->ar_pro) != PROT_IP) ||
|
||||
(arp->ar_hln != 6) || (arp->ar_pln != 4)) {
|
||||
puts("invalid RARP header\n");
|
||||
} else {
|
||||
net_copy_ip(&net_ip, &arp->ar_data[16]);
|
||||
if (net_server_ip.s_addr == 0)
|
||||
net_copy_ip(&net_server_ip, &arp->ar_data[6]);
|
||||
memcpy(net_server_ethaddr, &arp->ar_data[0], 6);
|
||||
debug_cond(DEBUG_DEV_PKT, "Got good RARP\n");
|
||||
net_auto_load();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Timeout on BOOTP request.
|
||||
*/
|
||||
static void rarp_timeout_handler(void)
|
||||
{
|
||||
if (rarp_try >= TIMEOUT_COUNT) {
|
||||
puts("\nRetry count exceeded; starting again\n");
|
||||
net_start_again();
|
||||
} else {
|
||||
net_set_timeout_handler(TIMEOUT, rarp_timeout_handler);
|
||||
rarp_request();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rarp_request(void)
|
||||
{
|
||||
uchar *pkt;
|
||||
struct arp_hdr *rarp;
|
||||
int eth_hdr_size;
|
||||
|
||||
printf("RARP broadcast %d\n", ++rarp_try);
|
||||
pkt = net_tx_packet;
|
||||
|
||||
eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_RARP);
|
||||
pkt += eth_hdr_size;
|
||||
|
||||
rarp = (struct arp_hdr *)pkt;
|
||||
|
||||
rarp->ar_hrd = htons(ARP_ETHER);
|
||||
rarp->ar_pro = htons(PROT_IP);
|
||||
rarp->ar_hln = 6;
|
||||
rarp->ar_pln = 4;
|
||||
rarp->ar_op = htons(RARPOP_REQUEST);
|
||||
memcpy(&rarp->ar_data[0], net_ethaddr, 6); /* source ET addr */
|
||||
memcpy(&rarp->ar_data[6], &net_ip, 4); /* source IP addr */
|
||||
/* dest ET addr = source ET addr ??*/
|
||||
memcpy(&rarp->ar_data[10], net_ethaddr, 6);
|
||||
/* dest IP addr set to broadcast */
|
||||
memset(&rarp->ar_data[16], 0xff, 4);
|
||||
|
||||
net_send_packet(net_tx_packet, eth_hdr_size + ARP_HDR_SIZE);
|
||||
|
||||
net_set_timeout_handler(TIMEOUT, rarp_timeout_handler);
|
||||
}
|
||||
29
u-boot/net/rarp.h
Normal file
29
u-boot/net/rarp.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* (C) Copyright 2000
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_CMD_RARP)
|
||||
|
||||
#ifndef __RARP_H__
|
||||
#define __RARP_H__
|
||||
|
||||
#include <net.h>
|
||||
|
||||
/**********************************************************************/
|
||||
/*
|
||||
* Global functions and variables.
|
||||
*/
|
||||
|
||||
extern int rarp_try;
|
||||
|
||||
/* Process the receipt of a RARP packet */
|
||||
void rarp_receive(struct ip_udp_hdr *ip, unsigned len);
|
||||
void rarp_request(void); /* Send a RARP request */
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
#endif /* __RARP_H__ */
|
||||
#endif
|
||||
104
u-boot/net/sntp.c
Normal file
104
u-boot/net/sntp.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SNTP support driver
|
||||
*
|
||||
* Masami Komiya <mkomiya@sonare.it> 2005
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <dm.h>
|
||||
#include <net.h>
|
||||
#include <rtc.h>
|
||||
|
||||
#include "sntp.h"
|
||||
|
||||
#define SNTP_TIMEOUT 10000UL
|
||||
|
||||
static int sntp_our_port;
|
||||
|
||||
static void sntp_send(void)
|
||||
{
|
||||
struct sntp_pkt_t pkt;
|
||||
int pktlen = SNTP_PACKET_LEN;
|
||||
int sport;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
|
||||
pkt.li = NTP_LI_NOLEAP;
|
||||
pkt.vn = NTP_VERSION;
|
||||
pkt.mode = NTP_MODE_CLIENT;
|
||||
|
||||
memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
|
||||
(char *)&pkt, pktlen);
|
||||
|
||||
sntp_our_port = 10000 + (get_timer(0) % 4096);
|
||||
sport = NTP_SERVICE_PORT;
|
||||
|
||||
net_send_udp_packet(net_server_ethaddr, net_ntp_server, sport,
|
||||
sntp_our_port, pktlen);
|
||||
}
|
||||
|
||||
static void sntp_timeout_handler(void)
|
||||
{
|
||||
puts("Timeout\n");
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
static void sntp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
|
||||
unsigned src, unsigned len)
|
||||
{
|
||||
#ifdef CONFIG_TIMESTAMP
|
||||
struct sntp_pkt_t *rpktp = (struct sntp_pkt_t *)pkt;
|
||||
struct rtc_time tm;
|
||||
ulong seconds;
|
||||
#endif
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
if (dest != sntp_our_port)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_TIMESTAMP
|
||||
/*
|
||||
* As the RTC's used in U-Boot support second resolution only
|
||||
* we simply ignore the sub-second field.
|
||||
*/
|
||||
memcpy(&seconds, &rpktp->transmit_timestamp, sizeof(ulong));
|
||||
|
||||
rtc_to_tm(ntohl(seconds) - 2208988800UL + net_ntp_time_offset, &tm);
|
||||
#if defined(CONFIG_CMD_DATE)
|
||||
# ifdef CONFIG_DM_RTC
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device(UCLASS_RTC, 0, &dev);
|
||||
if (ret)
|
||||
printf("SNTP: cannot find RTC: err=%d\n", ret);
|
||||
else
|
||||
dm_rtc_set(dev, &tm);
|
||||
# else
|
||||
rtc_set(&tm);
|
||||
# endif
|
||||
#endif
|
||||
printf("Date: %4d-%02d-%02d Time: %2d:%02d:%02d\n",
|
||||
tm.tm_year, tm.tm_mon, tm.tm_mday,
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
#endif
|
||||
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
}
|
||||
|
||||
void sntp_start(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
|
||||
net_set_timeout_handler(SNTP_TIMEOUT, sntp_timeout_handler);
|
||||
net_set_udp_handler(sntp_handler);
|
||||
memset(net_server_ethaddr, 0, sizeof(net_server_ethaddr));
|
||||
|
||||
sntp_send();
|
||||
}
|
||||
58
u-boot/net/sntp.h
Normal file
58
u-boot/net/sntp.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* (C) Masami Komiya <mkomiya@sonare.it> 2005
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __SNTP_H__
|
||||
#define __SNTP_H__
|
||||
|
||||
#define NTP_SERVICE_PORT 123
|
||||
#define SNTP_PACKET_LEN 48
|
||||
|
||||
|
||||
/* Leap Indicator */
|
||||
#define NTP_LI_NOLEAP 0x0
|
||||
#define NTP_LI_61SECS 0x1
|
||||
#define NTP_LI_59SECS 0x2
|
||||
#define NTP_LI_ALARM 0x3
|
||||
|
||||
/* Version */
|
||||
|
||||
#define NTP_VERSION 4
|
||||
|
||||
/* Mode */
|
||||
#define NTP_MODE_RESERVED 0
|
||||
#define NTP_MODE_SYMACTIVE 1 /* Symmetric Active */
|
||||
#define NTP_MODE_SYMPASSIVE 2 /* Symmetric Passive */
|
||||
#define NTP_MODE_CLIENT 3
|
||||
#define NTP_MODE_SERVER 4
|
||||
#define NTP_MODE_BROADCAST 5
|
||||
#define NTP_MODE_NTPCTRL 6 /* Reserved for NTP control message */
|
||||
#define NTP_MODE_PRIVATE 7 /* Reserved for private use */
|
||||
|
||||
struct sntp_pkt_t {
|
||||
#if __LITTLE_ENDIAN
|
||||
uchar mode:3;
|
||||
uchar vn:3;
|
||||
uchar li:2;
|
||||
#else
|
||||
uchar li:2;
|
||||
uchar vn:3;
|
||||
uchar mode:3;
|
||||
#endif
|
||||
uchar stratum;
|
||||
uchar poll;
|
||||
uchar precision;
|
||||
uint root_delay;
|
||||
uint root_dispersion;
|
||||
uint reference_id;
|
||||
unsigned long long reference_timestamp;
|
||||
unsigned long long originate_timestamp;
|
||||
unsigned long long receive_timestamp;
|
||||
unsigned long long transmit_timestamp;
|
||||
};
|
||||
|
||||
void sntp_start(void); /* Begin SNTP */
|
||||
|
||||
#endif /* __SNTP_H__ */
|
||||
981
u-boot/net/tftp.c
Normal file
981
u-boot/net/tftp.c
Normal file
@@ -0,0 +1,981 @@
|
||||
/*
|
||||
* Copyright 1994, 1995, 2000 Neil Russell.
|
||||
* (See License)
|
||||
* Copyright 2000, 2001 DENX Software Engineering, Wolfgang Denk, wd@denx.de
|
||||
* Copyright 2011 Comelit Group SpA,
|
||||
* Luca Ceresoli <luca.ceresoli@comelit.it>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <efi_loader.h>
|
||||
#include <mapmem.h>
|
||||
#include <net.h>
|
||||
#include <net/tftp.h>
|
||||
#include "bootp.h"
|
||||
#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
|
||||
#include <flash.h>
|
||||
#endif
|
||||
|
||||
/* Well known TFTP port # */
|
||||
#define WELL_KNOWN_PORT 69
|
||||
/* Millisecs to timeout for lost pkt */
|
||||
#define TIMEOUT 5000UL
|
||||
#ifndef CONFIG_NET_RETRY_COUNT
|
||||
/* # of timeouts before giving up */
|
||||
# define TIMEOUT_COUNT 10
|
||||
#else
|
||||
# define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT * 2)
|
||||
#endif
|
||||
/* Number of "loading" hashes per line (for checking the image size) */
|
||||
#define HASHES_PER_LINE 65
|
||||
|
||||
/*
|
||||
* TFTP operations.
|
||||
*/
|
||||
#define TFTP_RRQ 1
|
||||
#define TFTP_WRQ 2
|
||||
#define TFTP_DATA 3
|
||||
#define TFTP_ACK 4
|
||||
#define TFTP_ERROR 5
|
||||
#define TFTP_OACK 6
|
||||
|
||||
static ulong timeout_ms = TIMEOUT;
|
||||
static int timeout_count_max = TIMEOUT_COUNT;
|
||||
static ulong time_start; /* Record time we started tftp */
|
||||
|
||||
/*
|
||||
* These globals govern the timeout behavior when attempting a connection to a
|
||||
* TFTP server. tftp_timeout_ms specifies the number of milliseconds to
|
||||
* wait for the server to respond to initial connection. Second global,
|
||||
* tftp_timeout_count_max, gives the number of such connection retries.
|
||||
* tftp_timeout_count_max must be non-negative and tftp_timeout_ms must be
|
||||
* positive. The globals are meant to be set (and restored) by code needing
|
||||
* non-standard timeout behavior when initiating a TFTP transfer.
|
||||
*/
|
||||
ulong tftp_timeout_ms = TIMEOUT;
|
||||
int tftp_timeout_count_max = TIMEOUT_COUNT;
|
||||
|
||||
enum {
|
||||
TFTP_ERR_UNDEFINED = 0,
|
||||
TFTP_ERR_FILE_NOT_FOUND = 1,
|
||||
TFTP_ERR_ACCESS_DENIED = 2,
|
||||
TFTP_ERR_DISK_FULL = 3,
|
||||
TFTP_ERR_UNEXPECTED_OPCODE = 4,
|
||||
TFTP_ERR_UNKNOWN_TRANSFER_ID = 5,
|
||||
TFTP_ERR_FILE_ALREADY_EXISTS = 6,
|
||||
};
|
||||
|
||||
static struct in_addr tftp_remote_ip;
|
||||
/* The UDP port at their end */
|
||||
static int tftp_remote_port;
|
||||
/* The UDP port at our end */
|
||||
static int tftp_our_port;
|
||||
static int timeout_count;
|
||||
/* packet sequence number */
|
||||
static ulong tftp_cur_block;
|
||||
/* last packet sequence number received */
|
||||
static ulong tftp_prev_block;
|
||||
/* count of sequence number wraparounds */
|
||||
static ulong tftp_block_wrap;
|
||||
/* memory offset due to wrapping */
|
||||
static ulong tftp_block_wrap_offset;
|
||||
static int tftp_state;
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
/* The file size reported by the server */
|
||||
static int tftp_tsize;
|
||||
/* The number of hashes we printed */
|
||||
static short tftp_tsize_num_hash;
|
||||
#endif
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
/* 1 if writing, else 0 */
|
||||
static int tftp_put_active;
|
||||
/* 1 if we have sent the last block */
|
||||
static int tftp_put_final_block_sent;
|
||||
#else
|
||||
#define tftp_put_active 0
|
||||
#endif
|
||||
|
||||
#define STATE_SEND_RRQ 1
|
||||
#define STATE_DATA 2
|
||||
#define STATE_TOO_LARGE 3
|
||||
#define STATE_BAD_MAGIC 4
|
||||
#define STATE_OACK 5
|
||||
#define STATE_RECV_WRQ 6
|
||||
#define STATE_SEND_WRQ 7
|
||||
|
||||
/* default TFTP block size */
|
||||
#define TFTP_BLOCK_SIZE 512
|
||||
/* sequence number is 16 bit */
|
||||
#define TFTP_SEQUENCE_SIZE ((ulong)(1<<16))
|
||||
|
||||
#define DEFAULT_NAME_LEN (8 + 4 + 1)
|
||||
static char default_filename[DEFAULT_NAME_LEN];
|
||||
|
||||
#ifndef CONFIG_TFTP_FILE_NAME_MAX_LEN
|
||||
#define MAX_LEN 128
|
||||
#else
|
||||
#define MAX_LEN CONFIG_TFTP_FILE_NAME_MAX_LEN
|
||||
#endif
|
||||
|
||||
static char tftp_filename[MAX_LEN];
|
||||
|
||||
/* 512 is poor choice for ethernet, MTU is typically 1500.
|
||||
* Minus eth.hdrs thats 1468. Can get 2x better throughput with
|
||||
* almost-MTU block sizes. At least try... fall back to 512 if need be.
|
||||
* (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file)
|
||||
*/
|
||||
#ifdef CONFIG_TFTP_BLOCKSIZE
|
||||
#define TFTP_MTU_BLOCKSIZE CONFIG_TFTP_BLOCKSIZE
|
||||
#else
|
||||
#define TFTP_MTU_BLOCKSIZE 1468
|
||||
#endif
|
||||
|
||||
static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
|
||||
static unsigned short tftp_block_size_option = TFTP_MTU_BLOCKSIZE;
|
||||
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
#include <malloc.h>
|
||||
#define MTFTP_BITMAPSIZE 0x1000
|
||||
static unsigned *tftp_mcast_bitmap;
|
||||
static int tftp_mcast_prev_hole;
|
||||
static int tftp_mcast_bitmap_size = MTFTP_BITMAPSIZE;
|
||||
static int tftp_mcast_disabled;
|
||||
static int tftp_mcast_master_client;
|
||||
static int tftp_mcast_active;
|
||||
static int tftp_mcast_port;
|
||||
/* can get 'last' block before done..*/
|
||||
static ulong tftp_mcast_ending_block;
|
||||
|
||||
static void parse_multicast_oack(char *pkt, int len);
|
||||
|
||||
static void mcast_cleanup(void)
|
||||
{
|
||||
if (net_mcast_addr)
|
||||
eth_mcast_join(net_mcast_addr, 0);
|
||||
if (tftp_mcast_bitmap)
|
||||
free(tftp_mcast_bitmap);
|
||||
tftp_mcast_bitmap = NULL;
|
||||
net_mcast_addr.s_addr = 0;
|
||||
tftp_mcast_active = 0;
|
||||
tftp_mcast_port = 0;
|
||||
tftp_mcast_ending_block = -1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MCAST_TFTP */
|
||||
|
||||
static inline void store_block(int block, uchar *src, unsigned len)
|
||||
{
|
||||
ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
|
||||
ulong newsize = offset + len;
|
||||
#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
|
||||
/* start address in flash? */
|
||||
if (flash_info[i].flash_id == FLASH_UNKNOWN)
|
||||
continue;
|
||||
if (load_addr + offset >= flash_info[i].start[0]) {
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc) { /* Flash is destination for this packet */
|
||||
rc = flash_write((char *)src, (ulong)(load_addr+offset), len);
|
||||
if (rc) {
|
||||
flash_perror(rc);
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
#endif /* CONFIG_SYS_DIRECT_FLASH_TFTP */
|
||||
{
|
||||
void *ptr = map_sysmem(load_addr + offset, len);
|
||||
|
||||
memcpy(ptr, src, len);
|
||||
unmap_sysmem(ptr);
|
||||
}
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
if (tftp_mcast_active)
|
||||
ext2_set_bit(block, tftp_mcast_bitmap);
|
||||
#endif
|
||||
|
||||
if (net_boot_file_size < newsize)
|
||||
net_boot_file_size = newsize;
|
||||
}
|
||||
|
||||
/* Clear our state ready for a new transfer */
|
||||
static void new_transfer(void)
|
||||
{
|
||||
tftp_prev_block = 0;
|
||||
tftp_block_wrap = 0;
|
||||
tftp_block_wrap_offset = 0;
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
tftp_put_final_block_sent = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
/**
|
||||
* Load the next block from memory to be sent over tftp.
|
||||
*
|
||||
* @param block Block number to send
|
||||
* @param dst Destination buffer for data
|
||||
* @param len Number of bytes in block (this one and every other)
|
||||
* @return number of bytes loaded
|
||||
*/
|
||||
static int load_block(unsigned block, uchar *dst, unsigned len)
|
||||
{
|
||||
/* We may want to get the final block from the previous set */
|
||||
ulong offset = ((int)block - 1) * len + tftp_block_wrap_offset;
|
||||
ulong tosend = len;
|
||||
|
||||
tosend = min(net_boot_file_size - offset, tosend);
|
||||
(void)memcpy(dst, (void *)(save_addr + offset), tosend);
|
||||
debug("%s: block=%d, offset=%ld, len=%d, tosend=%ld\n", __func__,
|
||||
block, offset, len, tosend);
|
||||
return tosend;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void tftp_send(void);
|
||||
static void tftp_timeout_handler(void);
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static void show_block_marker(void)
|
||||
{
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
if (tftp_tsize) {
|
||||
ulong pos = tftp_cur_block * tftp_block_size +
|
||||
tftp_block_wrap_offset;
|
||||
if (pos > tftp_tsize)
|
||||
pos = tftp_tsize;
|
||||
|
||||
while (tftp_tsize_num_hash < pos * 50 / tftp_tsize) {
|
||||
putc('#');
|
||||
tftp_tsize_num_hash++;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (((tftp_cur_block - 1) % 10) == 0)
|
||||
putc('#');
|
||||
else if ((tftp_cur_block % (10 * HASHES_PER_LINE)) == 0)
|
||||
puts("\n\t ");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* restart the current transfer due to an error
|
||||
*
|
||||
* @param msg Message to print for user
|
||||
*/
|
||||
static void restart(const char *msg)
|
||||
{
|
||||
printf("\n%s; starting again\n", msg);
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
mcast_cleanup();
|
||||
#endif
|
||||
net_start_again();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the block number has wrapped, and update progress
|
||||
*
|
||||
* TODO: The egregious use of global variables in this file should be tidied.
|
||||
*/
|
||||
static void update_block_number(void)
|
||||
{
|
||||
/*
|
||||
* RFC1350 specifies that the first data packet will
|
||||
* have sequence number 1. If we receive a sequence
|
||||
* number of 0 this means that there was a wrap
|
||||
* around of the (16 bit) counter.
|
||||
*/
|
||||
if (tftp_cur_block == 0 && tftp_prev_block != 0) {
|
||||
tftp_block_wrap++;
|
||||
tftp_block_wrap_offset += tftp_block_size * TFTP_SEQUENCE_SIZE;
|
||||
timeout_count = 0; /* we've done well, reset the timeout */
|
||||
} else {
|
||||
show_block_marker();
|
||||
}
|
||||
}
|
||||
|
||||
/* The TFTP get or put is complete */
|
||||
static void tftp_complete(void)
|
||||
{
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
/* Print hash marks for the last packet received */
|
||||
while (tftp_tsize && tftp_tsize_num_hash < 49) {
|
||||
putc('#');
|
||||
tftp_tsize_num_hash++;
|
||||
}
|
||||
puts(" ");
|
||||
print_size(tftp_tsize, "");
|
||||
#endif
|
||||
time_start = get_timer(time_start);
|
||||
if (time_start > 0) {
|
||||
puts("\n\t "); /* Line up with "Loading: " */
|
||||
print_size(net_boot_file_size /
|
||||
time_start * 1000, "/s");
|
||||
}
|
||||
puts("\ndone\n");
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
}
|
||||
|
||||
static void tftp_send(void)
|
||||
{
|
||||
uchar *pkt;
|
||||
uchar *xp;
|
||||
int len = 0;
|
||||
ushort *s;
|
||||
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
/* Multicast TFTP.. non-MasterClients do not ACK data. */
|
||||
if (tftp_mcast_active && tftp_state == STATE_DATA &&
|
||||
tftp_mcast_master_client == 0)
|
||||
return;
|
||||
#endif
|
||||
/*
|
||||
* We will always be sending some sort of packet, so
|
||||
* cobble together the packet headers now.
|
||||
*/
|
||||
pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
|
||||
|
||||
switch (tftp_state) {
|
||||
case STATE_SEND_RRQ:
|
||||
case STATE_SEND_WRQ:
|
||||
xp = pkt;
|
||||
s = (ushort *)pkt;
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
*s++ = htons(tftp_state == STATE_SEND_RRQ ? TFTP_RRQ :
|
||||
TFTP_WRQ);
|
||||
#else
|
||||
*s++ = htons(TFTP_RRQ);
|
||||
#endif
|
||||
pkt = (uchar *)s;
|
||||
strcpy((char *)pkt, tftp_filename);
|
||||
pkt += strlen(tftp_filename) + 1;
|
||||
strcpy((char *)pkt, "octet");
|
||||
pkt += 5 /*strlen("octet")*/ + 1;
|
||||
strcpy((char *)pkt, "timeout");
|
||||
pkt += 7 /*strlen("timeout")*/ + 1;
|
||||
sprintf((char *)pkt, "%lu", timeout_ms / 1000);
|
||||
debug("send option \"timeout %s\"\n", (char *)pkt);
|
||||
pkt += strlen((char *)pkt) + 1;
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
pkt += sprintf((char *)pkt, "tsize%c%u%c",
|
||||
0, net_boot_file_size, 0);
|
||||
#endif
|
||||
/* try for more effic. blk size */
|
||||
pkt += sprintf((char *)pkt, "blksize%c%d%c",
|
||||
0, tftp_block_size_option, 0);
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
/* Check all preconditions before even trying the option */
|
||||
if (!tftp_mcast_disabled) {
|
||||
tftp_mcast_bitmap = malloc(tftp_mcast_bitmap_size);
|
||||
if (tftp_mcast_bitmap && eth_get_dev()->mcast) {
|
||||
free(tftp_mcast_bitmap);
|
||||
tftp_mcast_bitmap = NULL;
|
||||
pkt += sprintf((char *)pkt, "multicast%c%c",
|
||||
0, 0);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_MCAST_TFTP */
|
||||
len = pkt - xp;
|
||||
break;
|
||||
|
||||
case STATE_OACK:
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
/* My turn! Start at where I need blocks I missed. */
|
||||
if (tftp_mcast_active)
|
||||
tftp_cur_block = ext2_find_next_zero_bit(
|
||||
tftp_mcast_bitmap,
|
||||
tftp_mcast_bitmap_size * 8, 0);
|
||||
/* fall through */
|
||||
#endif
|
||||
|
||||
case STATE_RECV_WRQ:
|
||||
case STATE_DATA:
|
||||
xp = pkt;
|
||||
s = (ushort *)pkt;
|
||||
s[0] = htons(TFTP_ACK);
|
||||
s[1] = htons(tftp_cur_block);
|
||||
pkt = (uchar *)(s + 2);
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
if (tftp_put_active) {
|
||||
int toload = tftp_block_size;
|
||||
int loaded = load_block(tftp_cur_block, pkt, toload);
|
||||
|
||||
s[0] = htons(TFTP_DATA);
|
||||
pkt += loaded;
|
||||
tftp_put_final_block_sent = (loaded < toload);
|
||||
}
|
||||
#endif
|
||||
len = pkt - xp;
|
||||
break;
|
||||
|
||||
case STATE_TOO_LARGE:
|
||||
xp = pkt;
|
||||
s = (ushort *)pkt;
|
||||
*s++ = htons(TFTP_ERROR);
|
||||
*s++ = htons(3);
|
||||
|
||||
pkt = (uchar *)s;
|
||||
strcpy((char *)pkt, "File too large");
|
||||
pkt += 14 /*strlen("File too large")*/ + 1;
|
||||
len = pkt - xp;
|
||||
break;
|
||||
|
||||
case STATE_BAD_MAGIC:
|
||||
xp = pkt;
|
||||
s = (ushort *)pkt;
|
||||
*s++ = htons(TFTP_ERROR);
|
||||
*s++ = htons(2);
|
||||
pkt = (uchar *)s;
|
||||
strcpy((char *)pkt, "File has bad magic");
|
||||
pkt += 18 /*strlen("File has bad magic")*/ + 1;
|
||||
len = pkt - xp;
|
||||
break;
|
||||
}
|
||||
|
||||
net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
|
||||
tftp_remote_port, tftp_our_port, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
static void icmp_handler(unsigned type, unsigned code, unsigned dest,
|
||||
struct in_addr sip, unsigned src, uchar *pkt,
|
||||
unsigned len)
|
||||
{
|
||||
if (type == ICMP_NOT_REACH && code == ICMP_NOT_REACH_PORT) {
|
||||
/* Oh dear the other end has gone away */
|
||||
restart("TFTP server died");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
|
||||
unsigned src, unsigned len)
|
||||
{
|
||||
__be16 proto;
|
||||
__be16 *s;
|
||||
int i;
|
||||
|
||||
if (dest != tftp_our_port) {
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
if (tftp_mcast_active &&
|
||||
(!tftp_mcast_port || dest != tftp_mcast_port))
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (tftp_state != STATE_SEND_RRQ && src != tftp_remote_port &&
|
||||
tftp_state != STATE_RECV_WRQ && tftp_state != STATE_SEND_WRQ)
|
||||
return;
|
||||
|
||||
if (len < 2)
|
||||
return;
|
||||
len -= 2;
|
||||
/* warning: don't use increment (++) in ntohs() macros!! */
|
||||
s = (__be16 *)pkt;
|
||||
proto = *s++;
|
||||
pkt = (uchar *)s;
|
||||
switch (ntohs(proto)) {
|
||||
case TFTP_RRQ:
|
||||
break;
|
||||
|
||||
case TFTP_ACK:
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
if (tftp_put_active) {
|
||||
if (tftp_put_final_block_sent) {
|
||||
tftp_complete();
|
||||
} else {
|
||||
/*
|
||||
* Move to the next block. We want our block
|
||||
* count to wrap just like the other end!
|
||||
*/
|
||||
int block = ntohs(*s);
|
||||
int ack_ok = (tftp_cur_block == block);
|
||||
|
||||
tftp_cur_block = (unsigned short)(block + 1);
|
||||
update_block_number();
|
||||
if (ack_ok)
|
||||
tftp_send(); /* Send next data block */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_CMD_TFTPSRV
|
||||
case TFTP_WRQ:
|
||||
debug("Got WRQ\n");
|
||||
tftp_remote_ip = sip;
|
||||
tftp_remote_port = src;
|
||||
tftp_our_port = 1024 + (get_timer(0) % 3072);
|
||||
new_transfer();
|
||||
tftp_send(); /* Send ACK(0) */
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TFTP_OACK:
|
||||
debug("Got OACK: %s %s\n",
|
||||
pkt, pkt + strlen((char *)pkt) + 1);
|
||||
tftp_state = STATE_OACK;
|
||||
tftp_remote_port = src;
|
||||
/*
|
||||
* Check for 'blksize' option.
|
||||
* Careful: "i" is signed, "len" is unsigned, thus
|
||||
* something like "len-8" may give a *huge* number
|
||||
*/
|
||||
for (i = 0; i+8 < len; i++) {
|
||||
if (strcmp((char *)pkt + i, "blksize") == 0) {
|
||||
tftp_block_size = (unsigned short)
|
||||
simple_strtoul((char *)pkt + i + 8,
|
||||
NULL, 10);
|
||||
debug("Blocksize ack: %s, %d\n",
|
||||
(char *)pkt + i + 8, tftp_block_size);
|
||||
}
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
if (strcmp((char *)pkt+i, "tsize") == 0) {
|
||||
tftp_tsize = simple_strtoul((char *)pkt + i + 6,
|
||||
NULL, 10);
|
||||
debug("size = %s, %d\n",
|
||||
(char *)pkt + i + 6, tftp_tsize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
parse_multicast_oack((char *)pkt, len - 1);
|
||||
if ((tftp_mcast_active) && (!tftp_mcast_master_client))
|
||||
tftp_state = STATE_DATA; /* passive.. */
|
||||
else
|
||||
#endif
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
if (tftp_put_active) {
|
||||
/* Get ready to send the first block */
|
||||
tftp_state = STATE_DATA;
|
||||
tftp_cur_block++;
|
||||
}
|
||||
#endif
|
||||
tftp_send(); /* Send ACK or first data block */
|
||||
break;
|
||||
case TFTP_DATA:
|
||||
if (len < 2)
|
||||
return;
|
||||
len -= 2;
|
||||
tftp_cur_block = ntohs(*(__be16 *)pkt);
|
||||
|
||||
update_block_number();
|
||||
|
||||
if (tftp_state == STATE_SEND_RRQ)
|
||||
debug("Server did not acknowledge timeout option!\n");
|
||||
|
||||
if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
|
||||
tftp_state == STATE_RECV_WRQ) {
|
||||
/* first block received */
|
||||
tftp_state = STATE_DATA;
|
||||
tftp_remote_port = src;
|
||||
new_transfer();
|
||||
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
if (tftp_mcast_active) { /* start!=1 common if mcast */
|
||||
tftp_prev_block = tftp_cur_block - 1;
|
||||
} else
|
||||
#endif
|
||||
if (tftp_cur_block != 1) { /* Assertion */
|
||||
puts("\nTFTP error: ");
|
||||
printf("First block is not block 1 (%ld)\n",
|
||||
tftp_cur_block);
|
||||
puts("Starting again\n\n");
|
||||
net_start_again();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tftp_cur_block == tftp_prev_block) {
|
||||
/* Same block again; ignore it. */
|
||||
break;
|
||||
}
|
||||
|
||||
tftp_prev_block = tftp_cur_block;
|
||||
timeout_count_max = tftp_timeout_count_max;
|
||||
net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
|
||||
|
||||
store_block(tftp_cur_block - 1, pkt + 2, len);
|
||||
|
||||
/*
|
||||
* Acknowledge the block just received, which will prompt
|
||||
* the remote for the next one.
|
||||
*/
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
/* if I am the MasterClient, actively calculate what my next
|
||||
* needed block is; else I'm passive; not ACKING
|
||||
*/
|
||||
if (tftp_mcast_active) {
|
||||
if (len < tftp_block_size) {
|
||||
tftp_mcast_ending_block = tftp_cur_block;
|
||||
} else if (tftp_mcast_master_client) {
|
||||
tftp_mcast_prev_hole = ext2_find_next_zero_bit(
|
||||
tftp_mcast_bitmap,
|
||||
tftp_mcast_bitmap_size * 8,
|
||||
tftp_mcast_prev_hole);
|
||||
tftp_cur_block = tftp_mcast_prev_hole;
|
||||
if (tftp_cur_block >
|
||||
((tftp_mcast_bitmap_size * 8) - 1)) {
|
||||
debug("tftpfile too big\n");
|
||||
/* try to double it and retry */
|
||||
tftp_mcast_bitmap_size <<= 1;
|
||||
mcast_cleanup();
|
||||
net_start_again();
|
||||
return;
|
||||
}
|
||||
tftp_prev_block = tftp_cur_block;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tftp_send();
|
||||
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
if (tftp_mcast_active) {
|
||||
if (tftp_mcast_master_client &&
|
||||
(tftp_cur_block >= tftp_mcast_ending_block)) {
|
||||
puts("\nMulticast tftp done\n");
|
||||
mcast_cleanup();
|
||||
net_set_state(NETLOOP_SUCCESS);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (len < tftp_block_size)
|
||||
tftp_complete();
|
||||
break;
|
||||
|
||||
case TFTP_ERROR:
|
||||
printf("\nTFTP error: '%s' (%d)\n",
|
||||
pkt + 2, ntohs(*(__be16 *)pkt));
|
||||
|
||||
switch (ntohs(*(__be16 *)pkt)) {
|
||||
case TFTP_ERR_FILE_NOT_FOUND:
|
||||
case TFTP_ERR_ACCESS_DENIED:
|
||||
puts("Not retrying...\n");
|
||||
eth_halt();
|
||||
net_set_state(NETLOOP_FAIL);
|
||||
break;
|
||||
case TFTP_ERR_UNDEFINED:
|
||||
case TFTP_ERR_DISK_FULL:
|
||||
case TFTP_ERR_UNEXPECTED_OPCODE:
|
||||
case TFTP_ERR_UNKNOWN_TRANSFER_ID:
|
||||
case TFTP_ERR_FILE_ALREADY_EXISTS:
|
||||
default:
|
||||
puts("Starting again\n\n");
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
mcast_cleanup();
|
||||
#endif
|
||||
net_start_again();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void tftp_timeout_handler(void)
|
||||
{
|
||||
if (++timeout_count > timeout_count_max) {
|
||||
restart("Retry count exceeded");
|
||||
} else {
|
||||
puts("T ");
|
||||
net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
|
||||
if (tftp_state != STATE_RECV_WRQ)
|
||||
tftp_send();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tftp_start(enum proto_t protocol)
|
||||
{
|
||||
#if CONFIG_NET_TFTP_VARS
|
||||
char *ep; /* Environment pointer */
|
||||
|
||||
/*
|
||||
* Allow the user to choose TFTP blocksize and timeout.
|
||||
* TFTP protocol has a minimal timeout of 1 second.
|
||||
*/
|
||||
|
||||
ep = getenv("tftpblocksize");
|
||||
if (ep != NULL)
|
||||
tftp_block_size_option = simple_strtol(ep, NULL, 10);
|
||||
|
||||
ep = getenv("tftptimeout");
|
||||
if (ep != NULL)
|
||||
timeout_ms = simple_strtol(ep, NULL, 10);
|
||||
|
||||
if (timeout_ms < 1000) {
|
||||
printf("TFTP timeout (%ld ms) too low, set min = 1000 ms\n",
|
||||
timeout_ms);
|
||||
timeout_ms = 1000;
|
||||
}
|
||||
|
||||
ep = getenv("tftptimeoutcountmax");
|
||||
if (ep != NULL)
|
||||
tftp_timeout_count_max = simple_strtol(ep, NULL, 10);
|
||||
|
||||
if (tftp_timeout_count_max < 0) {
|
||||
printf("TFTP timeout count max (%d ms) negative, set to 0\n",
|
||||
tftp_timeout_count_max);
|
||||
tftp_timeout_count_max = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
debug("TFTP blocksize = %i, timeout = %ld ms\n",
|
||||
tftp_block_size_option, timeout_ms);
|
||||
|
||||
tftp_remote_ip = net_server_ip;
|
||||
if (net_boot_file_name[0] == '\0') {
|
||||
sprintf(default_filename, "%02X%02X%02X%02X.img",
|
||||
net_ip.s_addr & 0xFF,
|
||||
(net_ip.s_addr >> 8) & 0xFF,
|
||||
(net_ip.s_addr >> 16) & 0xFF,
|
||||
(net_ip.s_addr >> 24) & 0xFF);
|
||||
|
||||
strncpy(tftp_filename, default_filename, MAX_LEN);
|
||||
tftp_filename[MAX_LEN - 1] = 0;
|
||||
|
||||
printf("*** Warning: no boot file name; using '%s'\n",
|
||||
tftp_filename);
|
||||
} else {
|
||||
char *p = strchr(net_boot_file_name, ':');
|
||||
|
||||
if (p == NULL) {
|
||||
strncpy(tftp_filename, net_boot_file_name, MAX_LEN);
|
||||
tftp_filename[MAX_LEN - 1] = 0;
|
||||
} else {
|
||||
tftp_remote_ip = string_to_ip(net_boot_file_name);
|
||||
strncpy(tftp_filename, p + 1, MAX_LEN);
|
||||
tftp_filename[MAX_LEN - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Using %s device\n", eth_get_name());
|
||||
printf("TFTP %s server %pI4; our IP address is %pI4",
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
protocol == TFTPPUT ? "to" : "from",
|
||||
#else
|
||||
"from",
|
||||
#endif
|
||||
&tftp_remote_ip, &net_ip);
|
||||
|
||||
/* Check if we need to send across this subnet */
|
||||
if (net_gateway.s_addr && net_netmask.s_addr) {
|
||||
struct in_addr our_net;
|
||||
struct in_addr remote_net;
|
||||
|
||||
our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
|
||||
remote_net.s_addr = tftp_remote_ip.s_addr & net_netmask.s_addr;
|
||||
if (our_net.s_addr != remote_net.s_addr)
|
||||
printf("; sending through gateway %pI4", &net_gateway);
|
||||
}
|
||||
putc('\n');
|
||||
|
||||
printf("Filename '%s'.", tftp_filename);
|
||||
|
||||
if (net_boot_file_expected_size_in_blocks) {
|
||||
printf(" Size is 0x%x Bytes = ",
|
||||
net_boot_file_expected_size_in_blocks << 9);
|
||||
print_size(net_boot_file_expected_size_in_blocks << 9, "");
|
||||
}
|
||||
|
||||
putc('\n');
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
tftp_put_active = (protocol == TFTPPUT);
|
||||
if (tftp_put_active) {
|
||||
printf("Save address: 0x%lx\n", save_addr);
|
||||
printf("Save size: 0x%lx\n", save_size);
|
||||
net_boot_file_size = save_size;
|
||||
puts("Saving: *\b");
|
||||
tftp_state = STATE_SEND_WRQ;
|
||||
new_transfer();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
printf("Load address: 0x%lx\n", load_addr);
|
||||
puts("Loading: *\b");
|
||||
tftp_state = STATE_SEND_RRQ;
|
||||
efi_set_bootdev("Net", "", tftp_filename);
|
||||
}
|
||||
|
||||
time_start = get_timer(0);
|
||||
timeout_count_max = tftp_timeout_count_max;
|
||||
|
||||
net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
|
||||
net_set_udp_handler(tftp_handler);
|
||||
#ifdef CONFIG_CMD_TFTPPUT
|
||||
net_set_icmp_handler(icmp_handler);
|
||||
#endif
|
||||
tftp_remote_port = WELL_KNOWN_PORT;
|
||||
timeout_count = 0;
|
||||
/* Use a pseudo-random port unless a specific port is set */
|
||||
tftp_our_port = 1024 + (get_timer(0) % 3072);
|
||||
|
||||
#ifdef CONFIG_TFTP_PORT
|
||||
ep = getenv("tftpdstp");
|
||||
if (ep != NULL)
|
||||
tftp_remote_port = simple_strtol(ep, NULL, 10);
|
||||
ep = getenv("tftpsrcp");
|
||||
if (ep != NULL)
|
||||
tftp_our_port = simple_strtol(ep, NULL, 10);
|
||||
#endif
|
||||
tftp_cur_block = 0;
|
||||
|
||||
/* zero out server ether in case the server ip has changed */
|
||||
memset(net_server_ethaddr, 0, 6);
|
||||
/* Revert tftp_block_size to dflt */
|
||||
tftp_block_size = TFTP_BLOCK_SIZE;
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
mcast_cleanup();
|
||||
#endif
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
tftp_tsize = 0;
|
||||
tftp_tsize_num_hash = 0;
|
||||
#endif
|
||||
|
||||
tftp_send();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_TFTPSRV
|
||||
void tftp_start_server(void)
|
||||
{
|
||||
tftp_filename[0] = 0;
|
||||
|
||||
printf("Using %s device\n", eth_get_name());
|
||||
printf("Listening for TFTP transfer on %pI4\n", &net_ip);
|
||||
printf("Load address: 0x%lx\n", load_addr);
|
||||
|
||||
puts("Loading: *\b");
|
||||
|
||||
timeout_count_max = tftp_timeout_count_max;
|
||||
timeout_count = 0;
|
||||
timeout_ms = TIMEOUT;
|
||||
net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
|
||||
|
||||
/* Revert tftp_block_size to dflt */
|
||||
tftp_block_size = TFTP_BLOCK_SIZE;
|
||||
tftp_cur_block = 0;
|
||||
tftp_our_port = WELL_KNOWN_PORT;
|
||||
|
||||
#ifdef CONFIG_TFTP_TSIZE
|
||||
tftp_tsize = 0;
|
||||
tftp_tsize_num_hash = 0;
|
||||
#endif
|
||||
|
||||
tftp_state = STATE_RECV_WRQ;
|
||||
net_set_udp_handler(tftp_handler);
|
||||
|
||||
/* zero out server ether in case the server ip has changed */
|
||||
memset(net_server_ethaddr, 0, 6);
|
||||
}
|
||||
#endif /* CONFIG_CMD_TFTPSRV */
|
||||
|
||||
#ifdef CONFIG_MCAST_TFTP
|
||||
/*
|
||||
* Credits: atftp project.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Pick up BcastAddr, Port, and whether I am [now] the master-client.
|
||||
* Frame:
|
||||
* +-------+-----------+---+-------~~-------+---+
|
||||
* | opc | multicast | 0 | addr, port, mc | 0 |
|
||||
* +-------+-----------+---+-------~~-------+---+
|
||||
* The multicast addr/port becomes what I listen to, and if 'mc' is '1' then
|
||||
* I am the new master-client so must send ACKs to DataBlocks. If I am not
|
||||
* master-client, I'm a passive client, gathering what DataBlocks I may and
|
||||
* making note of which ones I got in my bitmask.
|
||||
* In theory, I never go from master->passive..
|
||||
* .. this comes in with pkt already pointing just past opc
|
||||
*/
|
||||
static void parse_multicast_oack(char *pkt, int len)
|
||||
{
|
||||
int i;
|
||||
struct in_addr addr;
|
||||
char *mc_adr;
|
||||
char *port;
|
||||
char *mc;
|
||||
|
||||
mc_adr = NULL;
|
||||
port = NULL;
|
||||
mc = NULL;
|
||||
/* march along looking for 'multicast\0', which has to start at least
|
||||
* 14 bytes back from the end.
|
||||
*/
|
||||
for (i = 0; i < len - 14; i++)
|
||||
if (strcmp(pkt + i, "multicast") == 0)
|
||||
break;
|
||||
if (i >= (len - 14)) /* non-Multicast OACK, ign. */
|
||||
return;
|
||||
|
||||
i += 10; /* strlen multicast */
|
||||
mc_adr = pkt + i;
|
||||
for (; i < len; i++) {
|
||||
if (*(pkt + i) == ',') {
|
||||
*(pkt + i) = '\0';
|
||||
if (port) {
|
||||
mc = pkt + i + 1;
|
||||
break;
|
||||
} else {
|
||||
port = pkt + i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!port || !mc_adr || !mc)
|
||||
return;
|
||||
if (tftp_mcast_active && tftp_mcast_master_client) {
|
||||
printf("I got a OACK as master Client, WRONG!\n");
|
||||
return;
|
||||
}
|
||||
/* ..I now accept packets destined for this MCAST addr, port */
|
||||
if (!tftp_mcast_active) {
|
||||
if (tftp_mcast_bitmap) {
|
||||
printf("Internal failure! no mcast.\n");
|
||||
free(tftp_mcast_bitmap);
|
||||
tftp_mcast_bitmap = NULL;
|
||||
tftp_mcast_disabled = 1;
|
||||
return;
|
||||
}
|
||||
/* I malloc instead of pre-declare; so that if the file ends
|
||||
* up being too big for this bitmap I can retry
|
||||
*/
|
||||
tftp_mcast_bitmap = malloc(tftp_mcast_bitmap_size);
|
||||
if (!tftp_mcast_bitmap) {
|
||||
printf("No bitmap, no multicast. Sorry.\n");
|
||||
tftp_mcast_disabled = 1;
|
||||
return;
|
||||
}
|
||||
memset(tftp_mcast_bitmap, 0, tftp_mcast_bitmap_size);
|
||||
tftp_mcast_prev_hole = 0;
|
||||
tftp_mcast_active = 1;
|
||||
}
|
||||
addr = string_to_ip(mc_adr);
|
||||
if (net_mcast_addr.s_addr != addr.s_addr) {
|
||||
if (net_mcast_addr.s_addr)
|
||||
eth_mcast_join(net_mcast_addr, 0);
|
||||
net_mcast_addr = addr;
|
||||
if (eth_mcast_join(net_mcast_addr, 1)) {
|
||||
printf("Fail to set mcast, revert to TFTP\n");
|
||||
tftp_mcast_disabled = 1;
|
||||
mcast_cleanup();
|
||||
net_start_again();
|
||||
}
|
||||
}
|
||||
tftp_mcast_master_client = simple_strtoul((char *)mc, NULL, 10);
|
||||
tftp_mcast_port = (unsigned short)simple_strtoul(port, NULL, 10);
|
||||
printf("Multicast: %s:%d [%d]\n", mc_adr, tftp_mcast_port,
|
||||
tftp_mcast_master_client);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* Multicast TFTP */
|
||||
Reference in New Issue
Block a user