Merge branch '2022-11-28-networking-updates-and-improvements'

- LiteX Ethernet support, dwc_eth_qos fixes, re-work fixing
  CVE-2022-{30790,30552}, macb race fix, Intel XWAY PHY support
  and add wget command and TCP support.
This commit is contained in:
Tom Rini 2022-11-28 13:12:40 -05:00
commit 39b81955d3
28 changed files with 2425 additions and 98 deletions

View File

@ -1812,6 +1812,13 @@ config SYS_DISABLE_AUTOLOAD
is complete. Enable this option to disable this behavior and instead
require files to be loaded over the network by subsequent commands.
config CMD_WGET
bool "wget"
select TCP
help
wget is a simple command to download kernel, or other files,
from a http server over TCP.
config CMD_MII
bool "mii"
imply CMD_MDIO

View File

@ -125,6 +125,19 @@ U_BOOT_CMD(
);
#endif
#if defined(CONFIG_CMD_WGET)
static int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
{
return netboot_common(WGET, cmdtp, argc, argv);
}
U_BOOT_CMD(
wget, 3, 1, do_wget,
"boot image via network using HTTP protocol",
"[loadAddress] [[hostIPaddr:]path and image name]"
);
#endif
static void netboot_update_env(void)
{
char tmp[22];

61
doc/usage/cmd/wget.rst Normal file
View File

@ -0,0 +1,61 @@
.. SPDX-License-Identifier: GPL-2.0+:
wget command
============
Synopsis
--------
::
wget address [[hostIPaddr:]path]
Description
-----------
The wget command is used to download a file from an HTTP server.
wget command will use HTTP over TCP to download files from an HTTP server.
Currently it can only download image from an HTTP server hosted on port 80.
address
memory address for the data downloaded
hostIPaddr
IP address of the HTTP server, defaults to the value of environment
variable *serverip*
path
path of the file to be downloaded.
Example
-------
In the example the following steps are executed:
* setup client network address
* download a file from the HTTP server
::
=> setenv autoload no
=> dhcp
BOOTP broadcast 1
*** Unhandled DHCP Option in OFFER/ACK: 23
*** Unhandled DHCP Option in OFFER/ACK: 23
DHCP client bound to address 192.168.1.105 (210 ms)
=> wget ${loadaddr} 192.168.1.254:/index.html
HTTP/1.0 302 Found
Packets received 4, Transfer Successful
Configuration
-------------
The command is only available if CONFIG_CMD_WGET=y.
CONFIG_PROT_TCP_SACK can be turned on for the TCP SACK options. This will
help increasing the downloading speed.
Return value
------------
The return value $? is 0 (true) on success and 1 (false) otherwise.

View File

@ -79,6 +79,7 @@ Shell commands
cmd/ums
cmd/ut
cmd/wdt
cmd/wget
cmd/xxd
Booting OS

View File

@ -438,6 +438,11 @@ config KSZ9477
This driver implements a DSA switch driver for the KSZ9477 family
of GbE switches using the I2C interface.
config LITEETH
bool "LiteX LiteEth Ethernet MAC"
help
Driver for the LiteEth Ethernet MAC from LiteX.
config MVGBE
bool "Marvell Orion5x/Kirkwood network interface support"
depends on ARCH_KIRKWOOD || ARCH_ORION5X

View File

@ -47,6 +47,7 @@ obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o
obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
obj-$(CONFIG_KSZ9477) += ksz9477.o
obj-$(CONFIG_LITEETH) += liteeth.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o

View File

@ -75,10 +75,7 @@
*/
static void *eqos_alloc_descs(struct eqos_priv *eqos, unsigned int num)
{
eqos->desc_size = ALIGN(sizeof(struct eqos_desc),
(unsigned int)ARCH_DMA_MINALIGN);
return memalign(eqos->desc_size, num * eqos->desc_size);
return memalign(ARCH_DMA_MINALIGN, num * eqos->desc_size);
}
static void eqos_free_descs(void *descs)
@ -89,13 +86,13 @@ static void eqos_free_descs(void *descs)
static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos,
unsigned int num, bool rx)
{
return eqos->descs +
((rx ? EQOS_DESCRIPTORS_TX : 0) + num) * eqos->desc_size;
return (rx ? eqos->rx_descs : eqos->tx_descs) +
(num * eqos->desc_size);
}
void eqos_inval_desc_generic(void *desc)
{
unsigned long start = (unsigned long)desc;
unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
unsigned long end = ALIGN(start + sizeof(struct eqos_desc),
ARCH_DMA_MINALIGN);
@ -104,7 +101,7 @@ void eqos_inval_desc_generic(void *desc)
void eqos_flush_desc_generic(void *desc)
{
unsigned long start = (unsigned long)desc;
unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
unsigned long end = ALIGN(start + sizeof(struct eqos_desc),
ARCH_DMA_MINALIGN);
@ -1001,7 +998,8 @@ static int eqos_start(struct udevice *dev)
/* Set up descriptors */
memset(eqos->descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_NUM);
memset(eqos->tx_descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_TX);
memset(eqos->rx_descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_RX);
for (i = 0; i < EQOS_DESCRIPTORS_TX; i++) {
struct eqos_desc *tx_desc = eqos_get_desc(eqos, i, false);
@ -1187,6 +1185,7 @@ static int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
{
struct eqos_priv *eqos = dev_get_priv(dev);
u32 idx, idx_mask = eqos->desc_per_cacheline - 1;
uchar *packet_expected;
struct eqos_desc *rx_desc;
@ -1202,24 +1201,30 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
eqos->config->ops->eqos_inval_buffer(packet, length);
rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true);
if ((eqos->rx_desc_idx & idx_mask) == idx_mask) {
for (idx = eqos->rx_desc_idx - idx_mask;
idx <= eqos->rx_desc_idx;
idx++) {
rx_desc = eqos_get_desc(eqos, idx, true);
rx_desc->des0 = 0;
mb();
eqos->config->ops->eqos_flush_desc(rx_desc);
eqos->config->ops->eqos_inval_buffer(packet, length);
rx_desc->des0 = (u32)(ulong)packet;
rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf +
(idx * EQOS_MAX_PACKET_SIZE));
rx_desc->des1 = 0;
rx_desc->des2 = 0;
/*
* Make sure that if HW sees the _OWN write below, it will see all the
* writes to the rest of the descriptor too.
* Make sure that if HW sees the _OWN write below,
* it will see all the writes to the rest of the
* descriptor too.
*/
mb();
rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
eqos->config->ops->eqos_flush_desc(rx_desc);
}
writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
}
eqos->rx_desc_idx++;
eqos->rx_desc_idx %= EQOS_DESCRIPTORS_RX;
@ -1230,17 +1235,41 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
static int eqos_probe_resources_core(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
unsigned int desc_step;
int ret;
debug("%s(dev=%p):\n", __func__, dev);
eqos->descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_NUM);
if (!eqos->descs) {
debug("%s: eqos_alloc_descs() failed\n", __func__);
/* Maximum distance between neighboring descriptors, in Bytes. */
desc_step = sizeof(struct eqos_desc) +
EQOS_DMA_CH0_CONTROL_DSL_MASK * eqos->config->axi_bus_width;
if (desc_step < ARCH_DMA_MINALIGN) {
/*
* The EQoS hardware implementation cannot place one descriptor
* per cacheline, it is necessary to place multiple descriptors
* per cacheline in memory and do cache management carefully.
*/
eqos->desc_size = BIT(fls(desc_step) - 1);
} else {
eqos->desc_size = ALIGN(sizeof(struct eqos_desc),
(unsigned int)ARCH_DMA_MINALIGN);
}
eqos->desc_per_cacheline = ARCH_DMA_MINALIGN / eqos->desc_size;
eqos->tx_descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_TX);
if (!eqos->tx_descs) {
debug("%s: eqos_alloc_descs(tx) failed\n", __func__);
ret = -ENOMEM;
goto err;
}
eqos->rx_descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_RX);
if (!eqos->rx_descs) {
debug("%s: eqos_alloc_descs(rx) failed\n", __func__);
ret = -ENOMEM;
goto err_free_tx_descs;
}
eqos->tx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_MAX_PACKET_SIZE);
if (!eqos->tx_dma_buf) {
debug("%s: memalign(tx_dma_buf) failed\n", __func__);
@ -1276,7 +1305,9 @@ err_free_rx_dma_buf:
err_free_tx_dma_buf:
free(eqos->tx_dma_buf);
err_free_descs:
eqos_free_descs(eqos->descs);
eqos_free_descs(eqos->rx_descs);
err_free_tx_descs:
eqos_free_descs(eqos->tx_descs);
err:
debug("%s: returns %d\n", __func__, ret);
@ -1292,7 +1323,8 @@ static int eqos_remove_resources_core(struct udevice *dev)
free(eqos->rx_pkt);
free(eqos->rx_dma_buf);
free(eqos->tx_dma_buf);
eqos_free_descs(eqos->descs);
eqos_free_descs(eqos->rx_descs);
eqos_free_descs(eqos->tx_descs);
debug("%s: OK\n", __func__);
return 0;

View File

@ -162,6 +162,7 @@ struct eqos_dma_regs {
#define EQOS_DMA_SYSBUS_MODE_BLEN4 BIT(1)
#define EQOS_DMA_CH0_CONTROL_DSL_SHIFT 18
#define EQOS_DMA_CH0_CONTROL_DSL_MASK 0x7
#define EQOS_DMA_CH0_CONTROL_PBLX8 BIT(16)
#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16
@ -264,9 +265,11 @@ struct eqos_priv {
struct phy_device *phy;
ofnode phy_of_node;
u32 max_speed;
void *descs;
void *tx_descs;
void *rx_descs;
int tx_desc_idx, rx_desc_idx;
unsigned int desc_size;
unsigned int desc_per_cacheline;
void *tx_dma_buf;
void *rx_dma_buf;
void *rx_pkt;

214
drivers/net/liteeth.c Normal file
View File

@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* LiteX Liteeth Ethernet
*
* Copyright 2021 Joel Stanley <joel@jms.id.au>, IBM Corp.
*/
#include <linux/litex.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <net.h>
#define LITEETH_WRITER_SLOT 0x00
#define LITEETH_WRITER_LENGTH 0x04
#define LITEETH_WRITER_ERRORS 0x08
#define LITEETH_WRITER_EV_STATUS 0x0C
#define LITEETH_WRITER_EV_PENDING 0x10
#define LITEETH_WRITER_EV_ENABLE 0x14
#define LITEETH_READER_START 0x18
#define LITEETH_READER_READY 0x1C
#define LITEETH_READER_LEVEL 0x20
#define LITEETH_READER_SLOT 0x24
#define LITEETH_READER_LENGTH 0x28
#define LITEETH_READER_EV_STATUS 0x2C
#define LITEETH_READER_EV_PENDING 0x30
#define LITEETH_READER_EV_ENABLE 0x34
#define LITEETH_PREAMBLE_CRC 0x38
#define LITEETH_PREAMBLE_ERRORS 0x3C
#define LITEETH_CRC_ERRORS 0x40
struct liteeth {
struct udevice *dev;
void __iomem *base;
u32 slot_size;
/* Tx */
u32 tx_slot;
u32 num_tx_slots;
void __iomem *tx_base;
/* Rx */
u32 rx_slot;
u32 num_rx_slots;
void __iomem *rx_base;
};
static int liteeth_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct liteeth *priv = dev_get_priv(dev);
u8 rx_slot;
int len;
if (!litex_read8(priv->base + LITEETH_WRITER_EV_PENDING)) {
debug("liteeth: No packet ready\n");
return -EAGAIN;
}
rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT);
len = litex_read32(priv->base + LITEETH_WRITER_LENGTH);
debug("%s: slot %d len 0x%x\n", __func__, rx_slot, len);
*packetp = priv->rx_base + rx_slot * priv->slot_size;
return len;
}
static int liteeth_free_pkt(struct udevice *dev, uchar *packet, int length)
{
struct liteeth *priv = dev_get_priv(dev);
litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
return 0;
}
static int liteeth_start(struct udevice *dev)
{
struct liteeth *priv = dev_get_priv(dev);
/* Clear pending events */
litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1);
/* Enable events */
litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 1);
litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 1);
return 0;
}
static void liteeth_stop(struct udevice *dev)
{
struct liteeth *priv = dev_get_priv(dev);
litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0);
litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0);
}
static int liteeth_send(struct udevice *dev, void *packet, int len)
{
struct liteeth *priv = dev_get_priv(dev);
void __iomem *txbuffer;
if (!litex_read8(priv->base + LITEETH_READER_READY)) {
printf("liteeth: reader not ready\n");
return -EAGAIN;
}
/* Reject oversize packets */
if (unlikely(len > priv->slot_size))
return -EMSGSIZE;
txbuffer = priv->tx_base + priv->tx_slot * priv->slot_size;
memcpy_toio(txbuffer, packet, len);
litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot);
litex_write16(priv->base + LITEETH_READER_LENGTH, len);
litex_write8(priv->base + LITEETH_READER_START, 1);
priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots;
return 0;
}
static void liteeth_setup_slots(struct liteeth *priv)
{
int err;
err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,rx-slots", &priv->num_rx_slots);
if (err) {
dev_dbg(priv->dev, "unable to get litex,rx-slots, using 2\n");
priv->num_rx_slots = 2;
}
err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,tx-slots", &priv->num_tx_slots);
if (err) {
dev_dbg(priv->dev, "unable to get litex,tx-slots, using 2\n");
priv->num_tx_slots = 2;
}
err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,slot-size", &priv->slot_size);
if (err) {
dev_dbg(priv->dev, "unable to get litex,slot-size, using 0x800\n");
priv->slot_size = 0x800;
}
}
static int liteeth_remove(struct udevice *dev)
{
liteeth_stop(dev);
return 0;
}
static const struct eth_ops liteeth_ops = {
.start = liteeth_start,
.stop = liteeth_stop,
.send = liteeth_send,
.recv = liteeth_recv,
.free_pkt = liteeth_free_pkt,
};
static int liteeth_of_to_plat(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
struct liteeth *priv = dev_get_priv(dev);
void __iomem *buf_base;
pdata->iobase = dev_read_addr(dev);
priv->dev = dev;
priv->base = dev_remap_addr_name(dev, "mac");
if (!priv->base) {
dev_err(dev, "failed to map registers\n");
return -EINVAL;
}
buf_base = dev_remap_addr_name(dev, "buffer");
if (!buf_base) {
dev_err(dev, "failed to map buffer\n");
return -EINVAL;
}
liteeth_setup_slots(priv);
/* Rx slots */
priv->rx_base = buf_base;
priv->rx_slot = 0;
/* Tx slots come after Rx slots */
priv->tx_base = buf_base + priv->num_rx_slots * priv->slot_size;
priv->tx_slot = 0;
return 0;
}
static const struct udevice_id liteeth_ids[] = {
{ .compatible = "litex,liteeth" },
{}
};
U_BOOT_DRIVER(liteeth) = {
.name = "liteeth",
.id = UCLASS_ETH,
.of_match = liteeth_ids,
.of_to_plat = liteeth_of_to_plat,
.plat_auto = sizeof(struct eth_pdata),
.remove = liteeth_remove,
.ops = &liteeth_ops,
.priv_auto = sizeof(struct liteeth),
};

View File

@ -98,6 +98,9 @@ struct macb_dma_desc_64 {
#define MACB_RX_DMA_DESC_SIZE (DMA_DESC_BYTES(MACB_RX_RING_SIZE))
#define MACB_TX_DUMMY_DMA_DESC_SIZE (DMA_DESC_BYTES(1))
#define DESC_PER_CACHELINE_32 (ARCH_DMA_MINALIGN/sizeof(struct macb_dma_desc))
#define DESC_PER_CACHELINE_64 (ARCH_DMA_MINALIGN/DMA_DESC_SIZE)
#define RXBUF_FRMLEN_MASK 0x00000fff
#define TXBUF_FRMLEN_MASK 0x000007ff
@ -401,32 +404,56 @@ static int _macb_send(struct macb_device *macb, const char *name, void *packet,
return 0;
}
static void reclaim_rx_buffer(struct macb_device *macb,
unsigned int idx)
{
unsigned int mask;
unsigned int shift;
unsigned int i;
/*
* There may be multiple descriptors per CPU cacheline,
* so a cache flush would flush the whole line, meaning the content of other descriptors
* in the cacheline would also flush. If one of the other descriptors had been
* written to by the controller, the flush would cause those changes to be lost.
*
* To circumvent this issue, we do the actual freeing only when we need to free
* the last descriptor in the current cacheline. When the current descriptor is the
* last in the cacheline, we free all the descriptors that belong to that cacheline.
*/
if (macb->config->hw_dma_cap & HW_DMA_CAP_64B) {
mask = DESC_PER_CACHELINE_64 - 1;
shift = 1;
} else {
mask = DESC_PER_CACHELINE_32 - 1;
shift = 0;
}
/* we exit without freeing if idx is not the last descriptor in the cacheline */
if ((idx & mask) != mask)
return;
for (i = idx & (~mask); i <= idx; i++)
macb->rx_ring[i << shift].addr &= ~MACB_BIT(RX_USED);
}
static void reclaim_rx_buffers(struct macb_device *macb,
unsigned int new_tail)
{
unsigned int i;
unsigned int count;
i = macb->rx_tail;
macb_invalidate_ring_desc(macb, RX);
while (i > new_tail) {
if (macb->config->hw_dma_cap & HW_DMA_CAP_64B)
count = i * 2;
else
count = i;
macb->rx_ring[count].addr &= ~MACB_BIT(RX_USED);
reclaim_rx_buffer(macb, i);
i++;
if (i > MACB_RX_RING_SIZE)
if (i >= MACB_RX_RING_SIZE)
i = 0;
}
while (i < new_tail) {
if (macb->config->hw_dma_cap & HW_DMA_CAP_64B)
count = i * 2;
else
count = i;
macb->rx_ring[count].addr &= ~MACB_BIT(RX_USED);
reclaim_rx_buffer(macb, i);
i++;
}

View File

@ -321,6 +321,11 @@ config PHY_XILINX_GMII2RGMII
as bridge between MAC connected over GMII and external phy that
is connected over RGMII interface.
config PHY_XWAY
bool "Intel XWAY PHY support"
help
This adds support for the Intel XWAY (formerly Lantiq) Gbe PHYs.
config PHY_ETHERNET_ID
bool "Read ethernet PHY id"
depends on DM_GPIO

View File

@ -34,6 +34,7 @@ obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o
obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o
obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
obj-$(CONFIG_PHY_XWAY) += intel_xway.o
obj-$(CONFIG_PHY_ETHERNET_ID) += ethernet_id.o
obj-$(CONFIG_PHY_VITESSE) += vitesse.o
obj-$(CONFIG_PHY_MSCC) += mscc.o

View File

@ -136,7 +136,7 @@ static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
*fw_addr = NULL;
*fw_length = 0;
debug("Loading Acquantia microcode from %s %s\n",
debug("Loading Aquantia microcode from %s %s\n",
CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME);
ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
if (ret < 0)
@ -163,7 +163,7 @@ static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
*fw_addr = addr;
*fw_length = length;
debug("Found Acquantia microcode.\n");
debug("Found Aquantia microcode.\n");
cleanup:
if (ret < 0) {
@ -257,7 +257,7 @@ static int aquantia_upload_firmware(struct phy_device *phydev)
strlcpy(version, (char *)&addr[dram_offset + VERSION_STRING_OFFSET],
VERSION_STRING_SIZE);
printf("%s loading firmare version '%s'\n", phydev->dev->name, version);
printf("%s loading firmware version '%s'\n", phydev->dev->name, version);
/* stall the microcprocessor */
phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
@ -288,7 +288,7 @@ static int aquantia_upload_firmware(struct phy_device *phydev)
phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, UP_RUN_STALL_OVERRIDE);
printf("%s firmare loading done.\n", phydev->dev->name);
printf("%s firmware loading done.\n", phydev->dev->name);
done:
free(addr);
return ret;

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <phy.h>
#include <linux/bitfield.h>
#define XWAY_MDIO_MIICTRL 0x17 /* mii control */
#define XWAY_MDIO_MIICTRL_RXSKEW_MASK GENMASK(14, 12)
#define XWAY_MDIO_MIICTRL_TXSKEW_MASK GENMASK(10, 8)
static int xway_config(struct phy_device *phydev)
{
ofnode node = phy_get_ofnode(phydev);
u32 val = 0;
if (ofnode_valid(node)) {
u32 rx_delay, tx_delay;
rx_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", 2000);
tx_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", 2000);
val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, rx_delay / 500);
val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, tx_delay / 500);
phy_modify(phydev, MDIO_DEVAD_NONE, XWAY_MDIO_MIICTRL,
XWAY_MDIO_MIICTRL_TXSKEW_MASK |
XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
}
genphy_config_aneg(phydev);
return 0;
}
static struct phy_driver XWAY_driver = {
.name = "XWAY",
.uid = 0xD565A400,
.mask = 0xffffff00,
.features = PHY_GBIT_FEATURES,
.config = xway_config,
.startup = genphy_startup,
.shutdown = genphy_shutdown,
};
int phy_xway_init(void)
{
phy_register(&XWAY_driver);
return 0;
}

View File

@ -556,6 +556,9 @@ int phy_init(void)
#ifdef CONFIG_PHY_XILINX
phy_xilinx_init();
#endif
#ifdef CONFIG_PHY_XWAY
phy_xway_init();
#endif
#ifdef CONFIG_PHY_MSCC
phy_mscc_init();
#endif

84
include/linux/litex.h Normal file
View File

@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Common LiteX header providing
* helper functions for accessing CSRs.
*
* Copyright (C) 2019-2020 Antmicro <www.antmicro.com>
*/
#ifndef _LINUX_LITEX_H
#define _LINUX_LITEX_H
#include <linux/io.h>
#include <asm/byteorder.h>
static inline void _write_litex_subregister(u32 val, void __iomem *addr)
{
writel((u32 __force)cpu_to_le32(val), addr);
}
static inline u32 _read_litex_subregister(void __iomem *addr)
{
return le32_to_cpu((__le32 __force)readl(addr));
}
/*
* LiteX SoC Generator, depending on the configuration, can split a single
* logical CSR (Control&Status Register) into a series of consecutive physical
* registers.
*
* For example, in the configuration with 8-bit CSR Bus, a 32-bit aligned,
* 32-bit wide logical CSR will be laid out as four 32-bit physical
* subregisters, each one containing one byte of meaningful data.
*
* For Linux support, upstream LiteX enforces a 32-bit wide CSR bus, which
* means that only larger-than-32-bit CSRs will be split across multiple
* subregisters (e.g., a 64-bit CSR will be spread across two consecutive
* 32-bit subregisters).
*
* For details see: https://github.com/enjoy-digital/litex/wiki/CSR-Bus
*/
static inline void litex_write8(void __iomem *reg, u8 val)
{
_write_litex_subregister(val, reg);
}
static inline void litex_write16(void __iomem *reg, u16 val)
{
_write_litex_subregister(val, reg);
}
static inline void litex_write32(void __iomem *reg, u32 val)
{
_write_litex_subregister(val, reg);
}
static inline void litex_write64(void __iomem *reg, u64 val)
{
_write_litex_subregister(val >> 32, reg);
_write_litex_subregister(val, reg + 4);
}
static inline u8 litex_read8(void __iomem *reg)
{
return _read_litex_subregister(reg);
}
static inline u16 litex_read16(void __iomem *reg)
{
return _read_litex_subregister(reg);
}
static inline u32 litex_read32(void __iomem *reg)
{
return _read_litex_subregister(reg);
}
static inline u64 litex_read64(void __iomem *reg)
{
return ((u64)_read_litex_subregister(reg) << 32) |
_read_litex_subregister(reg + 4);
}
#endif /* _LINUX_LITEX_H */

View File

@ -365,6 +365,7 @@ struct vlan_ethernet_hdr {
#define PROT_NCSI 0x88f8 /* NC-SI control packets */
#define IPPROTO_ICMP 1 /* Internet Control Message Protocol */
#define IPPROTO_TCP 6 /* Transmission Control Protocol */
#define IPPROTO_UDP 17 /* User Datagram Protocol */
/*
@ -560,7 +561,7 @@ extern int net_restart_wrap; /* Tried all network devices */
enum proto_t {
BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI
TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
};
extern char net_boot_file_name[1024];/* Boot File name */
@ -690,19 +691,36 @@ static inline void net_send_packet(uchar *pkt, int len)
(void) eth_send(pkt, len);
}
/*
* Transmit "net_tx_packet" as UDP packet, performing ARP request if needed
* (ether will be populated)
/**
* net_send_ip_packet() - Transmit "net_tx_packet" as UDP or TCP packet,
* send ARP request if needed (ether will be populated)
* @ether: Raw packet buffer
* @dest: IP address to send the datagram to
* @dport: Destination UDP port
* @sport: Source UDP port
* @payload_len: Length of data after the UDP header
* @action: TCP action to be performed
* @tcp_seq_num: TCP sequence number of this transmission
* @tcp_ack_num: TCP stream acknolegement number
*
* @param ether Raw packet buffer
* @param dest IP address to send the datagram to
* @param dport Destination UDP port
* @param sport Source UDP port
* @param payload_len Length of data after the UDP header
* Return: 0 on success, other value on failure
*/
int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
int payload_len, int proto, u8 action, u32 tcp_seq_num,
u32 tcp_ack_num);
/**
* net_send_tcp_packet() - Transmit TCP packet.
* @payload_len: length of payload
* @dport: Destination TCP port
* @sport: Source TCP port
* @action: TCP action to be performed
* @tcp_seq_num: TCP sequence number of this transmission
* @tcp_ack_num: TCP stream acknolegement number
*
* Return: 0 on success, other value on failure
*/
int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action,
u32 tcp_seq_num, u32 tcp_ack_num);
int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport,
int sport, int payload_len);

299
include/net/tcp.h Normal file
View File

@ -0,0 +1,299 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* TCP Support with SACK for file transfer.
*
* Copyright 2017 Duncan Hare, All rights reserved.
*/
#define TCP_ACTIVITY 127 /* Number of packets received */
/* before console progress mark */
/**
* struct ip_tcp_hdr - IP and TCP header
* @ip_hl_v: header length and version
* @ip_tos: type of service
* @ip_len: total length
* @ip_id: identification
* @ip_off: fragment offset field
* @ip_ttl: time to live
* @ip_p: protocol
* @ip_sum: checksum
* @ip_src: Source IP address
* @ip_dst: Destination IP address
* @tcp_src: TCP source port
* @tcp_dst: TCP destination port
* @tcp_seq: TCP sequence number
* @tcp_ack: TCP Acknowledgment number
* @tcp_hlen: 4 bits TCP header Length/4, 4 bits reserved, 2 more bits reserved
* @tcp_flag: flags of TCP
* @tcp_win: TCP windows size
* @tcp_xsum: Checksum
* @tcp_ugr: Pointer to urgent data
*/
struct ip_tcp_hdr {
u8 ip_hl_v;
u8 ip_tos;
u16 ip_len;
u16 ip_id;
u16 ip_off;
u8 ip_ttl;
u8 ip_p;
u16 ip_sum;
struct in_addr ip_src;
struct in_addr ip_dst;
u16 tcp_src;
u16 tcp_dst;
u32 tcp_seq;
u32 tcp_ack;
u8 tcp_hlen;
u8 tcp_flags;
u16 tcp_win;
u16 tcp_xsum;
u16 tcp_ugr;
} __packed;
#define IP_TCP_HDR_SIZE (sizeof(struct ip_tcp_hdr))
#define TCP_HDR_SIZE (IP_TCP_HDR_SIZE - IP_HDR_SIZE)
#define TCP_DATA 0x00 /* Data Packet - internal use only */
#define TCP_FIN 0x01 /* Finish flag */
#define TCP_SYN 0x02 /* Synch (start) flag */
#define TCP_RST 0x04 /* reset flag */
#define TCP_PUSH 0x08 /* Push - Notify app */
#define TCP_ACK 0x10 /* Acknowledgment of data received */
#define TCP_URG 0x20 /* Urgent */
#define TCP_ECE 0x40 /* Congestion control */
#define TCP_CWR 0x80 /* Congestion Control */
/*
* TCP header options, Seq, MSS, and SACK
*/
#define TCP_SACK 32 /* Number of packets analyzed */
/* on leading edge of stream */
#define TCP_O_END 0x00 /* End of option list */
#define TCP_1_NOP 0x01 /* Single padding NOP */
#define TCP_O_NOP 0x01010101 /* NOPs pad to 32 bit boundary */
#define TCP_O_MSS 0x02 /* MSS Size option */
#define TCP_O_SCL 0x03 /* Window Scale option */
#define TCP_P_SACK 0x04 /* SACK permitted */
#define TCP_V_SACK 0x05 /* SACK values */
#define TCP_O_TS 0x08 /* Timestamp option */
#define TCP_OPT_LEN_2 0x02
#define TCP_OPT_LEN_3 0x03
#define TCP_OPT_LEN_4 0x04
#define TCP_OPT_LEN_6 0x06
#define TCP_OPT_LEN_8 0x08
#define TCP_OPT_LEN_A 0x0a /* Timestamp Length */
#define TCP_MSS 1460 /* Max segment size */
#define TCP_SCALE 0x01 /* Scale */
/**
* struct tcp_mss - TCP option structure for MSS (Max segment size)
* @kind: Field ID
* @len: Field length
* @mss: Segment size value
*/
struct tcp_mss {
u8 kind;
u8 len;
u16 mss;
} __packed;
/**
* struct tcp_scale - TCP option structure for Windows scale
* @kind: Field ID
* @len: Field length
* @scale: windows shift value used for networks with many hops.
* Typically 4 or more hops
*/
struct tcp_scale {
u8 kind;
u8 len;
u8 scale;
} __packed;
/**
* struct tcp_sack_p - TCP option structure for SACK permitted
* @kind: Field ID
* @len: Field length
*/
struct tcp_sack_p {
u8 kind;
u8 len;
} __packed;
/**
* struct sack_edges - structure for SACK edges
* @l: Left edge of stream
* @r: right edge of stream
*/
struct sack_edges {
u32 l;
u32 r;
} __packed;
#define TCP_SACK_SIZE (sizeof(struct sack_edges))
/*
* A TCP stream has holes when packets are missing or disordered.
* A hill is the inverse of a hole, and is data received.
* TCP received hills (a sequence of data), and inferrs Holes
* from the "hills" or packets received.
*/
#define TCP_SACK_HILLS 4
/**
* struct tcp_sack_v - TCP option structure for SACK
* @kind: Field ID
* @len: Field length
* @hill: L & R window edges
*/
struct tcp_sack_v {
u8 kind;
u8 len;
struct sack_edges hill[TCP_SACK_HILLS];
} __packed;
/**
* struct tcp_t_opt - TCP option structure for time stamps
* @kind: Field ID
* @len: Field length
* @t_snd: Sender timestamp
* @t_rcv: Receiver timestamp
*/
struct tcp_t_opt {
u8 kind;
u8 len;
u32 t_snd;
u32 t_rcv;
} __packed;
#define TCP_TSOPT_SIZE (sizeof(struct tcp_t_opt))
/*
* ip tcp structure with options
*/
/**
* struct ip_tcp_hdr_o - IP + TCP header + TCP options
* @hdr: IP + TCP header
* @mss: TCP MSS Option
* @scale: TCP Windows Scale Option
* @sack_p: TCP Sack-Permitted Option
* @t_opt: TCP Timestamp Option
* @end: end of options
*/
struct ip_tcp_hdr_o {
struct ip_tcp_hdr hdr;
struct tcp_mss mss;
struct tcp_scale scale;
struct tcp_sack_p sack_p;
struct tcp_t_opt t_opt;
u8 end;
} __packed;
#define IP_TCP_O_SIZE (sizeof(struct ip_tcp_hdr_o))
/**
* struct ip_tcp_hdr_s - IP + TCP header + TCP options
* @hdr: IP + TCP header
* @t_opt: TCP Timestamp Option
* @sack_v: TCP SACK Option
* @end: end of options
*/
struct ip_tcp_hdr_s {
struct ip_tcp_hdr hdr;
struct tcp_t_opt t_opt;
struct tcp_sack_v sack_v;
u8 end;
} __packed;
#define IP_TCP_SACK_SIZE (sizeof(struct ip_tcp_hdr_s))
/*
* TCP pseudo header definitions
*/
#define PSEUDO_PAD_SIZE 8
/**
* struct pseudo_hdr - Pseudo Header
* @padding: pseudo hdr size = ip_tcp hdr size
* @p_src: Source IP address
* @p_dst: Destination IP address
* @rsvd: reserved
* @p: protocol
* @len: length of header
*/
struct pseudo_hdr {
u8 padding[PSEUDO_PAD_SIZE];
struct in_addr p_src;
struct in_addr p_dst;
u8 rsvd;
u8 p;
u16 len;
} __packed;
#define PSEUDO_HDR_SIZE (sizeof(struct pseudo_hdr)) - PSEUDO_PAD_SIZE
/**
* union tcp_build_pkt - union for building TCP/IP packet.
* @ph: pseudo header
* @ip: IP and TCP header plus TCP options
* @sack: IP and TCP header plus SACK options
* @raw: buffer
*
* Build Pseudo header in packed buffer
* first, calculate TCP checksum, then build IP header in packed buffer.
*
*/
union tcp_build_pkt {
struct pseudo_hdr ph;
struct ip_tcp_hdr_o ip;
struct ip_tcp_hdr_s sack;
uchar raw[1600];
} __packed;
/**
* enum tcp_state - TCP State machine states for connection
* @TCP_CLOSED: Need to send SYN to connect
* @TCP_SYN_SENT: Trying to connect, waiting for SYN ACK
* @TCP_ESTABLISHED: both server & client have a connection
* @TCP_CLOSE_WAIT: Rec FIN, passed to app for FIN, ACK rsp
* @TCP_CLOSING: Rec FIN, sent FIN, ACK waiting for ACK
* @TCP_FIN_WAIT_1: Sent FIN waiting for response
* @TCP_FIN_WAIT_2: Rec ACK from FIN sent, waiting for FIN
*/
enum tcp_state {
TCP_CLOSED,
TCP_SYN_SENT,
TCP_ESTABLISHED,
TCP_CLOSE_WAIT,
TCP_CLOSING,
TCP_FIN_WAIT_1,
TCP_FIN_WAIT_2
};
enum tcp_state tcp_get_tcp_state(void);
void tcp_set_tcp_state(enum tcp_state new_state);
int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
u8 action, u32 tcp_seq_num, u32 tcp_ack_num);
/**
* rxhand_tcp() - An incoming packet handler.
* @pkt: pointer to the application packet
* @dport: destination UDP port
* @sip: source IP address
* @sport: source UDP port
* @len: packet length
*/
typedef void rxhand_tcp(uchar *pkt, unsigned int dport,
struct in_addr sip, unsigned int sport,
unsigned int len);
void tcp_set_tcp_handler(rxhand_tcp *f);
void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int len);
u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
int tcp_len, int pkt_len);

22
include/net/wget.h Normal file
View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Duncan Hare Copyright 2017
*/
/**
* wget_start() - begin wget
*/
void wget_start(void);
enum wget_state {
WGET_CLOSED,
WGET_CONNECTING,
WGET_CONNECTED,
WGET_TRANSFERRING,
WGET_TRANSFERRED
};
#define DEBUG_WGET 0 /* Set to 1 for debug messages */
#define SERVER_PORT 80
#define WGET_RETRY_COUNT 30
#define WGET_TIMEOUT 2000UL

View File

@ -380,6 +380,7 @@ int phy_teranetics_init(void);
int phy_ti_init(void);
int phy_vitesse_init(void);
int phy_xilinx_init(void);
int phy_xway_init(void);
int phy_mscc_init(void);
int phy_fixed_init(void);
int phy_ncsi_init(void);

View File

@ -174,6 +174,22 @@ config BOOTP_MAX_ROOT_PATH_LEN
help
Select maximal length of option 17 root path.
config PROT_TCP
bool "TCP stack"
help
Enable a generic tcp framework that allows defining a custom
handler for tcp protocol.
config PROT_TCP_SACK
bool "TCP SACK support"
depends on PROT_TCP
help
TCP protocol with SACK. SACK means selective acknowledgements.
By turning this option on TCP will learn what segments are already
received. So that it improves TCP's retransmission efficiency.
This option should be turn on if you want to achieve the fastest
file transfer possible.
endif # if NET
config SYS_RX_ETH_BUFFER

View File

@ -30,6 +30,8 @@ obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o
obj-$(CONFIG_CMD_WOL) += wol.o
obj-$(CONFIG_PROT_UDP) += udp.o
obj-$(CONFIG_PROT_TCP) += tcp.o
obj-$(CONFIG_CMD_WGET) += wget.o
# Disable this warning as it is triggered by:
# sprintf(buf, index ? "foo%d" : "foo", index)

View File

@ -117,6 +117,8 @@
#if defined(CONFIG_CMD_WOL)
#include "wol.h"
#endif
#include <net/tcp.h>
#include <net/wget.h>
/** BOOTP EXTENTIONS **/
@ -387,6 +389,8 @@ int net_init(void)
/* Only need to setup buffer pointers once. */
first_call = 0;
if (IS_ENABLED(CONFIG_PROT_TCP))
tcp_set_tcp_state(TCP_CLOSED);
}
return net_init_loop();
@ -514,6 +518,11 @@ restart:
nfs_start();
break;
#endif
#if defined(CONFIG_CMD_WGET)
case WGET:
wget_start();
break;
#endif
#if defined(CONFIG_CMD_CDP)
case CDP:
cdp_start();
@ -833,6 +842,16 @@ int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport,
IPPROTO_UDP, 0, 0, 0);
}
#if defined(CONFIG_PROT_TCP)
int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action,
u32 tcp_seq_num, u32 tcp_ack_num)
{
return net_send_ip_packet(net_server_ethaddr, net_server_ip, dport,
sport, payload_len, IPPROTO_TCP, action,
tcp_seq_num, tcp_ack_num);
}
#endif
int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
int payload_len, int proto, u8 action, u32 tcp_seq_num,
u32 tcp_ack_num)
@ -864,6 +883,14 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
payload_len);
pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
break;
#if defined(CONFIG_PROT_TCP)
case IPPROTO_TCP:
pkt_hdr_size = eth_hdr_size
+ tcp_set_tcp_header(pkt + eth_hdr_size, dport, sport,
payload_len, action, tcp_seq_num,
tcp_ack_num);
break;
#endif
default:
return -EINVAL;
}
@ -924,7 +951,11 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
int offset8, start, len, done = 0;
u16 ip_off = ntohs(ip->ip_off);
if (ip->ip_len < IP_MIN_FRAG_DATAGRAM_SIZE)
/*
* Calling code already rejected <, but we don't have to deal
* with an IP fragment with no payload.
*/
if (ntohs(ip->ip_len) <= IP_HDR_SIZE)
return NULL;
/* payload starts after IP header, this fragment is in there */
@ -934,6 +965,10 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
start = offset8 * 8;
len = ntohs(ip->ip_len) - IP_HDR_SIZE;
/* All but last fragment must have a multiple-of-8 payload. */
if ((len & 7) && (ip_off & IP_FLAGS_MFRAG))
return NULL;
if (start + len > IP_MAXUDP) /* fragment extends too far */
return NULL;
@ -977,10 +1012,14 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
}
/*
* There is some overlap: fix the hole list. This code doesn't
* deal with a fragment that overlaps with two different holes
* (thus being a superset of a previously-received fragment).
* There is some overlap: fix the hole list. This code deals
* with a fragment that overlaps with two different holes
* (thus being a superset of a previously-received fragment)
* by only using the part of the fragment that fits in the
* first hole.
*/
if (h->last_byte < start + len)
len = h->last_byte - start;
if ((h >= thisfrag) && (h->last_byte <= start + len)) {
/* complete overlap with hole: remove hole */
@ -1032,8 +1071,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
if (!done)
return NULL;
localip->ip_len = htons(total_len);
*lenp = total_len + IP_HDR_SIZE;
localip->ip_len = htons(*lenp);
return localip;
}
@ -1208,9 +1247,9 @@ void net_process_received_packet(uchar *in_packet, int len)
case PROT_IP:
debug_cond(DEBUG_NET_PKT, "Got IP\n");
/* Before we start poking the header, make sure it is there */
if (len < IP_UDP_HDR_SIZE) {
if (len < IP_HDR_SIZE) {
debug("len bad %d < %lu\n", len,
(ulong)IP_UDP_HDR_SIZE);
(ulong)IP_HDR_SIZE);
return;
}
/* Check the packet length */
@ -1219,6 +1258,10 @@ void net_process_received_packet(uchar *in_packet, int len)
return;
}
len = ntohs(ip->ip_len);
if (len < IP_HDR_SIZE) {
debug("bad ip->ip_len %d < %d\n", len, (int)IP_HDR_SIZE);
return;
}
debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
len, ip->ip_hl_v & 0xff);
@ -1226,7 +1269,7 @@ void net_process_received_packet(uchar *in_packet, int len)
if ((ip->ip_hl_v & 0xf0) != 0x40)
return;
/* Can't deal with IP options (headers != 20 bytes) */
if ((ip->ip_hl_v & 0x0f) > 0x05)
if ((ip->ip_hl_v & 0x0f) != 0x05)
return;
/* Check the Checksum of the header */
if (!ip_checksum_ok((uchar *)ip, IP_HDR_SIZE)) {
@ -1273,11 +1316,20 @@ void net_process_received_packet(uchar *in_packet, int len)
if (ip->ip_p == IPPROTO_ICMP) {
receive_icmp(ip, len, src_ip, et);
return;
#if defined(CONFIG_PROT_TCP)
} else if (ip->ip_p == IPPROTO_TCP) {
debug_cond(DEBUG_DEV_PKT,
"TCP PH (to=%pI4, from=%pI4, len=%d)\n",
&dst_ip, &src_ip, len);
rxhand_tcp_f((union tcp_build_pkt *)ip, len);
return;
#endif
} else if (ip->ip_p != IPPROTO_UDP) { /* Only UDP packets */
return;
}
if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > ntohs(ip->ip_len))
if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > len - IP_HDR_SIZE)
return;
debug_cond(DEBUG_DEV_PKT,

720
net/tcp.c Normal file
View File

@ -0,0 +1,720 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2017 Duncan Hare, all rights reserved.
*/
/*
* General Desription:
*
* TCP support for the wget command, for fast file downloading.
*
* HTTP/TCP Receiver:
*
* Prerequisites: - own ethernet address
* - own IP address
* - Server IP address
* - Server with TCP
* - TCP application (eg wget)
* Next Step HTTPS?
*/
#include <common.h>
#include <command.h>
#include <console.h>
#include <env_internal.h>
#include <errno.h>
#include <net.h>
#include <net/tcp.h>
/*
* TCP sliding window control used by us to request re-TX
*/
static struct tcp_sack_v tcp_lost;
/* TCP option timestamp */
static u32 loc_timestamp;
static u32 rmt_timestamp;
static u32 tcp_seq_init;
static u32 tcp_ack_edge;
static u32 tcp_seq_max;
static int tcp_activity_count;
/*
* Search for TCP_SACK and review the comments before the code section
* TCP_SACK is the number of packets at the front of the stream
*/
enum pkt_state {PKT, NOPKT};
struct sack_r {
struct sack_edges se;
enum pkt_state st;
};
static struct sack_r edge_a[TCP_SACK];
static unsigned int sack_idx;
static unsigned int prev_len;
/*
* TCP lengths are stored as a rounded up number of 32 bit words.
* Add 3 to length round up, rounded, then divided into the
* length in 32 bit words.
*/
#define LEN_B_TO_DW(x) ((x) >> 2)
#define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3))
#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
#define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
/* TCP connection state */
static enum tcp_state current_tcp_state;
/* Current TCP RX packet handler */
static rxhand_tcp *tcp_packet_handler;
/**
* tcp_get_tcp_state() - get current TCP state
*
* Return: Current TCP state
*/
enum tcp_state tcp_get_tcp_state(void)
{
return current_tcp_state;
}
/**
* tcp_set_tcp_state() - set current TCP state
* @new_state: new TCP state
*/
void tcp_set_tcp_state(enum tcp_state new_state)
{
current_tcp_state = new_state;
}
static void dummy_handler(uchar *pkt, unsigned int dport,
struct in_addr sip, unsigned int sport,
unsigned int len)
{
}
/**
* tcp_set_tcp_handler() - set a handler to receive data
* @f: handler
*/
void tcp_set_tcp_handler(rxhand_tcp *f)
{
debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f);
if (!f)
tcp_packet_handler = dummy_handler;
else
tcp_packet_handler = f;
}
/**
* tcp_set_pseudo_header() - set TCP pseudo header
* @pkt: the packet
* @src: source IP address
* @dest: destinaion IP address
* @tcp_len: tcp length
* @pkt_len: packet length
*
* Return: the checksum of the packet
*/
u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
int tcp_len, int pkt_len)
{
union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
int checksum_len;
/*
* Pseudo header
*
* Zero the byte after the last byte so that the header checksum
* will always work.
*/
pkt[pkt_len] = 0;
net_copy_ip((void *)&b->ph.p_src, &src);
net_copy_ip((void *)&b->ph.p_dst, &dest);
b->ph.rsvd = 0;
b->ph.p = IPPROTO_TCP;
b->ph.len = htons(tcp_len);
checksum_len = tcp_len + PSEUDO_HDR_SIZE;
debug_cond(DEBUG_DEV_PKT,
"TCP Pesudo Header (to=%pI4, from=%pI4, Len=%d)\n",
&b->ph.p_dst, &b->ph.p_src, checksum_len);
return compute_ip_checksum(pkt + PSEUDO_PAD_SIZE, checksum_len);
}
/**
* net_set_ack_options() - set TCP options in acknowledge packets
* @b: the packet
*
* Return: TCP header length
*/
int net_set_ack_options(union tcp_build_pkt *b)
{
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
b->sack.t_opt.kind = TCP_O_TS;
b->sack.t_opt.len = TCP_OPT_LEN_A;
b->sack.t_opt.t_snd = htons(loc_timestamp);
b->sack.t_opt.t_rcv = rmt_timestamp;
b->sack.sack_v.kind = TCP_1_NOP;
b->sack.sack_v.len = 0;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
if (tcp_lost.len > TCP_OPT_LEN_2) {
debug_cond(DEBUG_DEV_PKT, "TCP ack opt lost.len %x\n",
tcp_lost.len);
b->sack.sack_v.len = tcp_lost.len;
b->sack.sack_v.kind = TCP_V_SACK;
b->sack.sack_v.hill[0].l = htonl(tcp_lost.hill[0].l);
b->sack.sack_v.hill[0].r = htonl(tcp_lost.hill[0].r);
/*
* These SACK structures are initialized with NOPs to
* provide TCP header alignment padding. There are 4
* SACK structures used for both header padding and
* internally.
*/
b->sack.sack_v.hill[1].l = htonl(tcp_lost.hill[1].l);
b->sack.sack_v.hill[1].r = htonl(tcp_lost.hill[1].r);
b->sack.sack_v.hill[2].l = htonl(tcp_lost.hill[2].l);
b->sack.sack_v.hill[2].r = htonl(tcp_lost.hill[2].r);
b->sack.sack_v.hill[3].l = TCP_O_NOP;
b->sack.sack_v.hill[3].r = TCP_O_NOP;
}
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
TCP_TSOPT_SIZE +
tcp_lost.len));
} else {
b->sack.sack_v.kind = 0;
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
TCP_TSOPT_SIZE));
}
/*
* This returns the actual rounded up length of the
* TCP header to add to the total packet length
*/
return GET_TCP_HDR_LEN_IN_BYTES(b->sack.hdr.tcp_hlen);
}
/**
* net_set_ack_options() - set TCP options in SYN packets
* @b: the packet
*/
void net_set_syn_options(union tcp_build_pkt *b)
{
if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
tcp_lost.len = 0;
b->ip.hdr.tcp_hlen = 0xa0;
b->ip.mss.kind = TCP_O_MSS;
b->ip.mss.len = TCP_OPT_LEN_4;
b->ip.mss.mss = htons(TCP_MSS);
b->ip.scale.kind = TCP_O_SCL;
b->ip.scale.scale = TCP_SCALE;
b->ip.scale.len = TCP_OPT_LEN_3;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
b->ip.sack_p.kind = TCP_P_SACK;
b->ip.sack_p.len = TCP_OPT_LEN_2;
} else {
b->ip.sack_p.kind = TCP_1_NOP;
b->ip.sack_p.len = TCP_1_NOP;
}
b->ip.t_opt.kind = TCP_O_TS;
b->ip.t_opt.len = TCP_OPT_LEN_A;
loc_timestamp = get_ticks();
rmt_timestamp = 0;
b->ip.t_opt.t_snd = 0;
b->ip.t_opt.t_rcv = 0;
b->ip.end = TCP_O_END;
}
int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
{
union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
int pkt_hdr_len;
int pkt_len;
int tcp_len;
/*
* Header: 5 32 bit words. 4 bits TCP header Length,
* 4 bits reserved options
*/
b->ip.hdr.tcp_flags = action;
pkt_hdr_len = IP_TCP_HDR_SIZE;
b->ip.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
switch (action) {
case TCP_SYN:
debug_cond(DEBUG_DEV_PKT,
"TCP Hdr:SYN (%pI4, %pI4, sq=%d, ak=%d)\n",
&net_server_ip, &net_ip,
tcp_seq_num, tcp_ack_num);
tcp_activity_count = 0;
net_set_syn_options(b);
tcp_seq_num = 0;
tcp_ack_num = 0;
pkt_hdr_len = IP_TCP_O_SIZE;
if (current_tcp_state == TCP_SYN_SENT) { /* Too many SYNs */
action = TCP_FIN;
current_tcp_state = TCP_FIN_WAIT_1;
} else {
current_tcp_state = TCP_SYN_SENT;
}
break;
case TCP_ACK:
pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
b->ip.hdr.tcp_flags = action;
debug_cond(DEBUG_DEV_PKT,
"TCP Hdr:ACK (%pI4, %pI4, s=%d, a=%d, A=%x)\n",
&net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num,
action);
break;
case TCP_FIN:
debug_cond(DEBUG_DEV_PKT,
"TCP Hdr:FIN (%pI4, %pI4, s=%d, a=%d)\n",
&net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);
payload_len = 0;
pkt_hdr_len = IP_TCP_HDR_SIZE;
current_tcp_state = TCP_FIN_WAIT_1;
break;
/* Notify connection closing */
case (TCP_FIN | TCP_ACK):
case (TCP_FIN | TCP_ACK | TCP_PUSH):
if (current_tcp_state == TCP_CLOSE_WAIT)
current_tcp_state = TCP_CLOSING;
tcp_ack_edge++;
debug_cond(DEBUG_DEV_PKT,
"TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%d, a=%d, A=%x)\n",
&net_server_ip, &net_ip,
tcp_seq_num, tcp_ack_edge, action);
fallthrough;
default:
pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK;
debug_cond(DEBUG_DEV_PKT,
"TCP Hdr:dft (%pI4, %pI4, s=%d, a=%d, A=%x)\n",
&net_server_ip, &net_ip,
tcp_seq_num, tcp_ack_num, action);
}
pkt_len = pkt_hdr_len + payload_len;
tcp_len = pkt_len - IP_HDR_SIZE;
/* TCP Header */
b->ip.hdr.tcp_ack = htonl(tcp_ack_edge);
b->ip.hdr.tcp_src = htons(sport);
b->ip.hdr.tcp_dst = htons(dport);
b->ip.hdr.tcp_seq = htonl(tcp_seq_num);
tcp_seq_num = tcp_seq_num + payload_len;
/*
* TCP window size - TCP header variable tcp_win.
* Change tcp_win only if you have an understanding of network
* overrun, congestion, TCP segment sizes, TCP windows, TCP scale,
* queuing theory and packet buffering. If there are too few buffers,
* there will be data loss, recovery may work or the sending TCP,
* the server, could abort the stream transmission.
* MSS is governed by maximum Ethernet frame length.
* The number of buffers is governed by the desire to have a queue of
* full buffers to be processed at the destination to maximize
* throughput. Temporary memory use for the boot phase on modern
* SOCs is may not be considered a constraint to buffer space, if
* it is, then the u-boot tftp or nfs kernel netboot should be
* considered.
*/
b->ip.hdr.tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
b->ip.hdr.tcp_xsum = 0;
b->ip.hdr.tcp_ugr = 0;
b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, net_server_ip,
tcp_len, pkt_len);
net_set_ip_header((uchar *)&b->ip, net_server_ip, net_ip,
pkt_len, IPPROTO_TCP);
return pkt_hdr_len;
}
/**
* tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer)
* @tcp_seq_num: TCP sequence start number
* @len: the length of sequence numbers
* @tcp_seq_max: maximum of sequence numbers
*/
void tcp_hole(u32 tcp_seq_num, u32 len, u32 tcp_seq_max)
{
u32 idx_sack, sack_in;
u32 sack_end = TCP_SACK - 1;
u32 hill = 0;
enum pkt_state expect = PKT;
u32 seq = tcp_seq_num - tcp_seq_init;
u32 hol_l = tcp_ack_edge - tcp_seq_init;
u32 hol_r = 0;
/* Place new seq number in correct place in receive array */
if (prev_len == 0)
prev_len = len;
idx_sack = sack_idx + ((tcp_seq_num - tcp_ack_edge) / prev_len);
if (idx_sack < TCP_SACK) {
edge_a[idx_sack].se.l = tcp_seq_num;
edge_a[idx_sack].se.r = tcp_seq_num + len;
edge_a[idx_sack].st = PKT;
/*
* The fin (last) packet is not the same length as data
* packets, and if it's length is recorded and used for
* array index calculation, calculation breaks.
*/
if (prev_len < len)
prev_len = len;
}
debug_cond(DEBUG_DEV_PKT,
"TCP 1 seq %d, edg %d, len %d, sack_idx %d, sack_end %d\n",
seq, hol_l, len, sack_idx, sack_end);
/* Right edge of contiguous stream, is the left edge of first hill */
hol_l = tcp_seq_num - tcp_seq_init;
hol_r = hol_l + len;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
tcp_lost.len = TCP_OPT_LEN_2;
debug_cond(DEBUG_DEV_PKT,
"TCP 1 in %d, seq %d, pkt_l %d, pkt_r %d, sack_idx %d, sack_end %d\n",
idx_sack, seq, hol_l, hol_r, sack_idx, sack_end);
for (sack_in = sack_idx; sack_in < sack_end && hill < TCP_SACK_HILLS;
sack_in++) {
switch (expect) {
case NOPKT:
switch (edge_a[sack_in].st) {
case NOPKT:
debug_cond(DEBUG_INT_STATE, "N");
break;
case PKT:
debug_cond(DEBUG_INT_STATE, "n");
if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
tcp_lost.hill[hill].l =
edge_a[sack_in].se.l;
tcp_lost.hill[hill].r =
edge_a[sack_in].se.r;
}
expect = PKT;
break;
}
break;
case PKT:
switch (edge_a[sack_in].st) {
case NOPKT:
debug_cond(DEBUG_INT_STATE, "p");
if (sack_in > sack_idx &&
hill < TCP_SACK_HILLS) {
hill++;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
tcp_lost.len += TCP_OPT_LEN_8;
}
expect = NOPKT;
break;
case PKT:
debug_cond(DEBUG_INT_STATE, "P");
if (tcp_ack_edge == edge_a[sack_in].se.l) {
tcp_ack_edge = edge_a[sack_in].se.r;
edge_a[sack_in].st = NOPKT;
sack_idx++;
} else {
if (IS_ENABLED(CONFIG_PROT_TCP_SACK) &&
hill < TCP_SACK_HILLS)
tcp_lost.hill[hill].r =
edge_a[sack_in].se.r;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK) &&
sack_in == sack_end - 1)
tcp_lost.hill[hill].r =
edge_a[sack_in].se.r;
}
break;
}
break;
}
}
debug_cond(DEBUG_INT_STATE, "\n");
if (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || tcp_lost.len <= TCP_OPT_LEN_2)
sack_idx = 0;
}
/**
* tcp_parse_options() - parsing TCP options
* @o: pointer to the option field.
* @o_len: length of the option field.
*/
void tcp_parse_options(uchar *o, int o_len)
{
struct tcp_t_opt *tsopt;
uchar *p = o;
/*
* NOPs are options with a zero length, and thus are special.
* All other options have length fields.
*/
for (p = o; p < (o + o_len); p = p + p[1]) {
if (!p[1])
return; /* Finished processing options */
switch (p[0]) {
case TCP_O_END:
return;
case TCP_O_MSS:
case TCP_O_SCL:
case TCP_P_SACK:
case TCP_V_SACK:
break;
case TCP_O_TS:
tsopt = (struct tcp_t_opt *)p;
rmt_timestamp = tsopt->t_snd;
return;
}
/* Process optional NOPs */
if (p[0] == TCP_O_NOP)
p++;
}
}
static u8 tcp_state_machine(u8 tcp_flags, u32 *tcp_seq_num, int payload_len)
{
u8 tcp_fin = tcp_flags & TCP_FIN;
u8 tcp_syn = tcp_flags & TCP_SYN;
u8 tcp_rst = tcp_flags & TCP_RST;
u8 tcp_push = tcp_flags & TCP_PUSH;
u8 tcp_ack = tcp_flags & TCP_ACK;
u8 action = TCP_DATA;
int i;
/*
* tcp_flags are examined to determine TX action in a given state
* tcp_push is interpreted to mean "inform the app"
* urg, ece, cer and nonce flags are not supported.
*
* exe and crw are use to signal and confirm knowledge of congestion.
* This TCP only sends a file request and acks. If it generates
* congestion, the network is broken.
*/
debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY %x\n", action);
if (tcp_rst) {
action = TCP_DATA;
current_tcp_state = TCP_CLOSED;
net_set_state(NETLOOP_FAIL);
debug_cond(DEBUG_INT_STATE, "TCP Reset %x\n", tcp_flags);
return TCP_RST;
}
switch (current_tcp_state) {
case TCP_CLOSED:
debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags);
if (tcp_ack)
action = TCP_DATA;
else if (tcp_syn)
action = TCP_RST;
else if (tcp_fin)
action = TCP_DATA;
break;
case TCP_SYN_SENT:
debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT %x, %d\n",
tcp_flags, *tcp_seq_num);
if (tcp_fin) {
action = action | TCP_PUSH;
current_tcp_state = TCP_CLOSE_WAIT;
}
if (tcp_syn) {
action = action | TCP_ACK | TCP_PUSH;
if (tcp_ack) {
tcp_seq_init = *tcp_seq_num;
*tcp_seq_num = *tcp_seq_num + 1;
tcp_seq_max = *tcp_seq_num;
tcp_ack_edge = *tcp_seq_num;
sack_idx = 0;
edge_a[sack_idx].se.l = *tcp_seq_num;
edge_a[sack_idx].se.r = *tcp_seq_num;
prev_len = 0;
current_tcp_state = TCP_ESTABLISHED;
for (i = 0; i < TCP_SACK; i++)
edge_a[i].st = NOPKT;
}
} else if (tcp_ack) {
action = TCP_DATA;
}
break;
case TCP_ESTABLISHED:
debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags);
if (*tcp_seq_num > tcp_seq_max)
tcp_seq_max = *tcp_seq_num;
if (payload_len > 0) {
tcp_hole(*tcp_seq_num, payload_len, tcp_seq_max);
tcp_fin = TCP_DATA; /* cause standalone FIN */
}
if ((tcp_fin) &&
(!IS_ENABLED(CONFIG_PROT_TCP_SACK) ||
tcp_lost.len <= TCP_OPT_LEN_2)) {
action = action | TCP_FIN | TCP_PUSH | TCP_ACK;
current_tcp_state = TCP_CLOSE_WAIT;
} else if (tcp_ack) {
action = TCP_DATA;
}
if (tcp_syn)
action = TCP_ACK + TCP_RST;
else if (tcp_push)
action = action | TCP_PUSH;
break;
case TCP_CLOSE_WAIT:
debug_cond(DEBUG_INT_STATE, "TCP_CLOSE_WAIT (%x)\n", tcp_flags);
action = TCP_DATA;
break;
case TCP_FIN_WAIT_2:
debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags);
if (tcp_ack) {
action = TCP_PUSH | TCP_ACK;
current_tcp_state = TCP_CLOSED;
puts("\n");
} else if (tcp_syn) {
action = TCP_DATA;
} else if (tcp_fin) {
action = TCP_DATA;
}
break;
case TCP_FIN_WAIT_1:
debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags);
if (tcp_fin) {
action = TCP_ACK | TCP_FIN;
current_tcp_state = TCP_FIN_WAIT_2;
}
if (tcp_syn)
action = TCP_RST;
if (tcp_ack) {
current_tcp_state = TCP_CLOSED;
tcp_seq_num = tcp_seq_num + 1;
}
break;
case TCP_CLOSING:
debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags);
if (tcp_ack) {
action = TCP_PUSH;
current_tcp_state = TCP_CLOSED;
puts("\n");
} else if (tcp_syn) {
action = TCP_RST;
} else if (tcp_fin) {
action = TCP_DATA;
}
break;
}
return action;
}
/**
* rxhand_tcp_f() - process receiving data and call data handler.
* @b: the packet
* @pkt_len: the length of packet.
*/
void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len)
{
int tcp_len = pkt_len - IP_HDR_SIZE;
u16 tcp_rx_xsum = b->ip.hdr.ip_sum;
u8 tcp_action = TCP_DATA;
u32 tcp_seq_num, tcp_ack_num;
struct in_addr action_and_state;
int tcp_hdr_len, payload_len;
/* Verify IP header */
debug_cond(DEBUG_DEV_PKT,
"TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n",
&b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len);
b->ip.hdr.ip_src = net_server_ip;
b->ip.hdr.ip_dst = net_ip;
b->ip.hdr.ip_sum = 0;
if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) {
debug_cond(DEBUG_DEV_PKT,
"TCP RX IP xSum Error (%pI4, =%pI4, len=%d)\n",
&net_ip, &net_server_ip, pkt_len);
return;
}
/* Build pseudo header and verify TCP header */
tcp_rx_xsum = b->ip.hdr.tcp_xsum;
b->ip.hdr.tcp_xsum = 0;
if (tcp_rx_xsum != tcp_set_pseudo_header((uchar *)b, b->ip.hdr.ip_src,
b->ip.hdr.ip_dst, tcp_len,
pkt_len)) {
debug_cond(DEBUG_DEV_PKT,
"TCP RX TCP xSum Error (%pI4, %pI4, len=%d)\n",
&net_ip, &net_server_ip, tcp_len);
return;
}
tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen);
payload_len = tcp_len - tcp_hdr_len;
if (tcp_hdr_len > TCP_HDR_SIZE)
tcp_parse_options((uchar *)b + IP_TCP_HDR_SIZE,
tcp_hdr_len - TCP_HDR_SIZE);
/*
* Incoming sequence and ack numbers are server's view of the numbers.
* The app must swap the numbers when responding.
*/
tcp_seq_num = ntohl(b->ip.hdr.tcp_seq);
tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
/* Packets are not ordered. Send to app as received. */
tcp_action = tcp_state_machine(b->ip.hdr.tcp_flags,
&tcp_seq_num, payload_len);
tcp_activity_count++;
if (tcp_activity_count > TCP_ACTIVITY) {
puts("| ");
tcp_activity_count = 0;
}
if ((tcp_action & TCP_PUSH) || payload_len > 0) {
debug_cond(DEBUG_DEV_PKT,
"TCP Notify (action=%x, Seq=%d,Ack=%d,Pay%d)\n",
tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
action_and_state.s_addr = tcp_action;
(*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len,
tcp_seq_num, action_and_state,
tcp_ack_num, payload_len);
} else if (tcp_action != TCP_DATA) {
debug_cond(DEBUG_DEV_PKT,
"TCP Action (action=%x,Seq=%d,Ack=%d,Pay=%d)\n",
tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
/*
* Warning: Incoming Ack & Seq sequence numbers are transposed
* here to outgoing Seq & Ack sequence numbers
*/
net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src),
ntohs(b->ip.hdr.tcp_dst),
(tcp_action & (~TCP_PUSH)),
tcp_seq_num, tcp_ack_num);
}
}

View File

@ -708,10 +708,55 @@ static int tftp_init_load_addr(void)
return 0;
}
static int saved_tftp_block_size_option;
static void sanitize_tftp_block_size_option(enum proto_t protocol)
{
int cap, max_defrag;
switch (protocol) {
case TFTPGET:
max_defrag = config_opt_enabled(CONFIG_IP_DEFRAG, CONFIG_NET_MAXDEFRAG, 0);
if (max_defrag) {
/* Account for IP, UDP and TFTP headers. */
cap = max_defrag - (20 + 8 + 4);
/* RFC2348 sets a hard upper limit. */
cap = min(cap, 65464);
break;
}
/*
* If not CONFIG_IP_DEFRAG, cap at the same value as
* for tftp put, namely normal MTU minus protocol
* overhead.
*/
fallthrough;
case TFTPPUT:
default:
/*
* U-Boot does not support IP fragmentation on TX, so
* this must be small enough that it fits normal MTU
* (and small enough that it fits net_tx_packet which
* has room for PKTSIZE_ALIGN bytes).
*/
cap = 1468;
}
if (tftp_block_size_option > cap) {
printf("Capping tftp block size option to %d (was %d)\n",
cap, tftp_block_size_option);
saved_tftp_block_size_option = tftp_block_size_option;
tftp_block_size_option = cap;
}
}
void tftp_start(enum proto_t protocol)
{
#if CONFIG_NET_TFTP_VARS
char *ep; /* Environment pointer */
__maybe_unused char *ep; /* Environment pointer */
if (saved_tftp_block_size_option) {
tftp_block_size_option = saved_tftp_block_size_option;
saved_tftp_block_size_option = 0;
}
if (IS_ENABLED(CONFIG_NET_TFTP_VARS)) {
/*
* Allow the user to choose TFTP blocksize and timeout.
@ -745,7 +790,9 @@ void tftp_start(enum proto_t protocol)
tftp_timeout_count_max);
tftp_timeout_count_max = 0;
}
#endif
}
sanitize_tftp_block_size_option(protocol);
debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
tftp_block_size_option, tftp_window_size_option, timeout_ms);

438
net/wget.c Normal file
View File

@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0
/*
* WGET/HTTP support driver based on U-BOOT's nfs.c
* Copyright Duncan Hare <dh@synoia.com> 2017
*/
#include <command.h>
#include <common.h>
#include <env.h>
#include <image.h>
#include <mapmem.h>
#include <net.h>
#include <net/tcp.h>
#include <net/wget.h>
static const char bootfile1[] = "GET ";
static const char bootfile3[] = " HTTP/1.0\r\n\r\n";
static const char http_eom[] = "\r\n\r\n";
static const char http_ok[] = "200";
static const char content_len[] = "Content-Length";
static const char linefeed[] = "\r\n";
static struct in_addr web_server_ip;
static int our_port;
static int wget_timeout_count;
struct pkt_qd {
uchar *pkt;
unsigned int tcp_seq_num;
unsigned int len;
};
/*
* This is a control structure for out of order packets received.
* The actual packet bufers are in the kernel space, and are
* expected to be overwritten by the downloaded image.
*/
static struct pkt_qd pkt_q[PKTBUFSRX / 4];
static int pkt_q_idx;
static unsigned long content_length;
static unsigned int packets;
static unsigned int initial_data_seq_num;
static enum wget_state current_wget_state;
static char *image_url;
static unsigned int wget_timeout = WGET_TIMEOUT;
static enum net_loop_state wget_loop_state;
/* Timeout retry parameters */
static u8 retry_action; /* actions for TCP retry */
static unsigned int retry_tcp_ack_num; /* TCP retry acknowledge number*/
static unsigned int retry_tcp_seq_num; /* TCP retry sequence number */
static int retry_len; /* TCP retry length */
/**
* store_block() - store block in memory
* @src: source of data
* @offset: offset
* @len: length
*/
static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
{
ulong newsize = offset + len;
uchar *ptr;
ptr = map_sysmem(image_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;
}
/**
* wget_send_stored() - wget response dispatcher
*
* WARNING, This, and only this, is the place in wget.c where
* SEQUENCE NUMBERS are swapped between incoming (RX)
* and outgoing (TX).
* Procedure wget_handler() is correct for RX traffic.
*/
static void wget_send_stored(void)
{
u8 action = retry_action;
int len = retry_len;
unsigned int tcp_ack_num = retry_tcp_ack_num + len;
unsigned int tcp_seq_num = retry_tcp_seq_num;
uchar *ptr, *offset;
switch (current_wget_state) {
case WGET_CLOSED:
debug_cond(DEBUG_WGET, "wget: send SYN\n");
current_wget_state = WGET_CONNECTING;
net_send_tcp_packet(0, SERVER_PORT, our_port, action,
tcp_seq_num, tcp_ack_num);
packets = 0;
break;
case WGET_CONNECTING:
pkt_q_idx = 0;
net_send_tcp_packet(0, SERVER_PORT, our_port, action,
tcp_seq_num, tcp_ack_num);
ptr = net_tx_packet + net_eth_hdr_size() +
IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
offset = ptr;
memcpy(offset, &bootfile1, strlen(bootfile1));
offset += strlen(bootfile1);
memcpy(offset, image_url, strlen(image_url));
offset += strlen(image_url);
memcpy(offset, &bootfile3, strlen(bootfile3));
offset += strlen(bootfile3);
net_send_tcp_packet((offset - ptr), SERVER_PORT, our_port,
TCP_PUSH, tcp_seq_num, tcp_ack_num);
current_wget_state = WGET_CONNECTED;
break;
case WGET_CONNECTED:
case WGET_TRANSFERRING:
case WGET_TRANSFERRED:
net_send_tcp_packet(0, SERVER_PORT, our_port, action,
tcp_seq_num, tcp_ack_num);
break;
}
}
static void wget_send(u8 action, unsigned int tcp_ack_num,
unsigned int tcp_seq_num, int len)
{
retry_action = action;
retry_tcp_ack_num = tcp_ack_num;
retry_tcp_seq_num = tcp_seq_num;
retry_len = len;
wget_send_stored();
}
void wget_fail(char *error_message, unsigned int tcp_seq_num,
unsigned int tcp_ack_num, u8 action)
{
printf("wget: Transfer Fail - %s\n", error_message);
net_set_timeout_handler(0, NULL);
wget_send(action, tcp_seq_num, tcp_ack_num, 0);
}
void wget_success(u8 action, unsigned int tcp_seq_num,
unsigned int tcp_ack_num, int len, int packets)
{
printf("Packets received %d, Transfer Successful\n", packets);
wget_send(action, tcp_seq_num, tcp_ack_num, len);
}
/*
* Interfaces of U-BOOT
*/
static void wget_timeout_handler(void)
{
if (++wget_timeout_count > WGET_RETRY_COUNT) {
puts("\nRetry count exceeded; starting again\n");
wget_send(TCP_RST, 0, 0, 0);
net_start_again();
} else {
puts("T ");
net_set_timeout_handler(wget_timeout +
WGET_TIMEOUT * wget_timeout_count,
wget_timeout_handler);
wget_send_stored();
}
}
#define PKT_QUEUE_OFFSET 0x20000
#define PKT_QUEUE_PACKET_SIZE 0x800
static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
struct in_addr action_and_state,
unsigned int tcp_ack_num, unsigned int len)
{
u8 action = action_and_state.s_addr;
uchar *pkt_in_q;
char *pos;
int hlen, i;
uchar *ptr1;
pkt[len] = '\0';
pos = strstr((char *)pkt, http_eom);
if (!pos) {
debug_cond(DEBUG_WGET,
"wget: Connected, data before Header %p\n", pkt);
pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET +
(pkt_q_idx * PKT_QUEUE_PACKET_SIZE);
ptr1 = map_sysmem((phys_addr_t)pkt_in_q, len);
memcpy(ptr1, pkt, len);
unmap_sysmem(ptr1);
pkt_q[pkt_q_idx].pkt = pkt_in_q;
pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num;
pkt_q[pkt_q_idx].len = len;
pkt_q_idx++;
} else {
debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt);
/* sizeof(http_eom) - 1 is the string length of (http_eom) */
hlen = pos - (char *)pkt + sizeof(http_eom) - 1;
pos = strstr((char *)pkt, linefeed);
if (pos > 0)
i = pos - (char *)pkt;
else
i = hlen;
printf("%.*s", i, pkt);
current_wget_state = WGET_TRANSFERRING;
if (strstr((char *)pkt, http_ok) == 0) {
debug_cond(DEBUG_WGET,
"wget: Connected Bad Xfer\n");
initial_data_seq_num = tcp_seq_num + hlen;
wget_loop_state = NETLOOP_FAIL;
wget_send(action, tcp_seq_num, tcp_ack_num, len);
} else {
debug_cond(DEBUG_WGET,
"wget: Connctd pkt %p hlen %x\n",
pkt, hlen);
initial_data_seq_num = tcp_seq_num + hlen;
pos = strstr((char *)pkt, content_len);
if (!pos) {
content_length = -1;
} else {
pos += sizeof(content_len) + 2;
strict_strtoul(pos, 10, &content_length);
debug_cond(DEBUG_WGET,
"wget: Connected Len %lu\n",
content_length);
}
net_boot_file_size = 0;
if (len > hlen)
store_block(pkt + hlen, 0, len - hlen);
debug_cond(DEBUG_WGET,
"wget: Connected Pkt %p hlen %x\n",
pkt, hlen);
for (i = 0; i < pkt_q_idx; i++) {
ptr1 = map_sysmem(
(phys_addr_t)(pkt_q[i].pkt),
pkt_q[i].len);
store_block(ptr1,
pkt_q[i].tcp_seq_num -
initial_data_seq_num,
pkt_q[i].len);
unmap_sysmem(ptr1);
debug_cond(DEBUG_WGET,
"wget: Connctd pkt Q %p len %x\n",
pkt_q[i].pkt, pkt_q[i].len);
}
}
}
wget_send(action, tcp_seq_num, tcp_ack_num, len);
}
/**
* wget_handler() - handler of wget
* @pkt: the pointer to the payload
* @tcp_seq_num: tcp sequence number
* @action_and_state: TCP state
* @tcp_ack_num: tcp acknowledge number
* @len: length of the payload
*
* In the "application push" invocation, the TCP header with all
* its information is pointed to by the packet pointer.
*/
static void wget_handler(uchar *pkt, unsigned int tcp_seq_num,
struct in_addr action_and_state,
unsigned int tcp_ack_num, unsigned int len)
{
enum tcp_state wget_tcp_state = tcp_get_tcp_state();
u8 action = action_and_state.s_addr;
net_set_timeout_handler(wget_timeout, wget_timeout_handler);
packets++;
switch (current_wget_state) {
case WGET_CLOSED:
debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n");
break;
case WGET_CONNECTING:
debug_cond(DEBUG_WGET,
"wget: Connecting In len=%x, Seq=%x, Ack=%x\n",
len, tcp_seq_num, tcp_ack_num);
if (!len) {
if (wget_tcp_state == TCP_ESTABLISHED) {
debug_cond(DEBUG_WGET,
"wget: Cting, send, len=%x\n", len);
wget_send(action, tcp_seq_num, tcp_ack_num,
len);
} else {
printf("%.*s", len, pkt);
wget_fail("wget: Handler Connected Fail\n",
tcp_seq_num, tcp_ack_num, action);
}
}
break;
case WGET_CONNECTED:
debug_cond(DEBUG_WGET, "wget: Connected seq=%x, len=%x\n",
tcp_seq_num, len);
if (!len) {
wget_fail("Image not found, no data returned\n",
tcp_seq_num, tcp_ack_num, action);
} else {
wget_connected(pkt, tcp_seq_num, action_and_state,
tcp_ack_num, len);
}
break;
case WGET_TRANSFERRING:
debug_cond(DEBUG_WGET,
"wget: Transferring, seq=%x, ack=%x,len=%x\n",
tcp_seq_num, tcp_ack_num, len);
if (tcp_seq_num >= initial_data_seq_num &&
store_block(pkt, tcp_seq_num - initial_data_seq_num,
len) != 0) {
wget_fail("wget: store error\n",
tcp_seq_num, tcp_ack_num, action);
return;
}
switch (wget_tcp_state) {
case TCP_FIN_WAIT_2:
wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len);
fallthrough;
case TCP_SYN_SENT:
case TCP_CLOSING:
case TCP_FIN_WAIT_1:
case TCP_CLOSED:
net_set_state(NETLOOP_FAIL);
break;
case TCP_ESTABLISHED:
wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num,
len);
wget_loop_state = NETLOOP_SUCCESS;
break;
case TCP_CLOSE_WAIT: /* End of transfer */
current_wget_state = WGET_TRANSFERRED;
wget_send(action | TCP_ACK | TCP_FIN,
tcp_seq_num, tcp_ack_num, len);
break;
}
break;
case WGET_TRANSFERRED:
printf("Packets received %d, Transfer Successful\n", packets);
net_set_state(wget_loop_state);
break;
}
}
#define RANDOM_PORT_START 1024
#define RANDOM_PORT_RANGE 0x4000
/**
* random_port() - make port a little random (1024-17407)
*
* Return: random port number from 1024 to 17407
*
* This keeps the math somewhat trivial to compute, and seems to work with
* all supported protocols/clients/servers
*/
static unsigned int random_port(void)
{
return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
}
#define BLOCKSIZE 512
void wget_start(void)
{
image_url = strchr(net_boot_file_name, ':');
if (image_url > 0) {
web_server_ip = string_to_ip(net_boot_file_name);
++image_url;
net_server_ip = web_server_ip;
} else {
web_server_ip = net_server_ip;
image_url = net_boot_file_name;
}
debug_cond(DEBUG_WGET,
"wget: Transfer HTTP Server %pI4; our IP %pI4\n",
&web_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)
debug_cond(DEBUG_WGET,
"wget: sending through gateway %pI4",
&net_gateway);
}
debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
if (net_boot_file_expected_size_in_blocks) {
debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
net_boot_file_expected_size_in_blocks * BLOCKSIZE);
print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
"");
}
debug_cond(DEBUG_WGET,
"\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
net_set_timeout_handler(wget_timeout, wget_timeout_handler);
tcp_set_tcp_handler(wget_handler);
wget_timeout_count = 0;
current_wget_state = WGET_CLOSED;
our_port = random_port();
/*
* Zero out server ether to force arp resolution in case
* the server ip for the previous u-boot command, for example dns
* is not the same as the web server ip.
*/
memset(net_server_ethaddr, 0, 6);
wget_send(TCP_SYN, 0, 0, 0);
}

View File

@ -20,3 +20,4 @@ ifdef CONFIG_SANDBOX
obj-$(CONFIG_CMD_SETEXPR) += setexpr.o
endif
obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
obj-$(CONFIG_CMD_WGET) += wget.o

206
test/cmd/wget.c Normal file
View File

@ -0,0 +1,206 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Linaro
*
* (C) Copyright 2022
* Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <env.h>
#include <fdtdec.h>
#include <log.h>
#include <malloc.h>
#include <net.h>
#include <net/tcp.h>
#include <net/wget.h>
#include <asm/eth.h>
#include <dm/test.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <test/lib.h>
#include <test/test.h>
#include <test/ut.h>
#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
#define LEN_B_TO_DW(x) ((x) >> 2)
static int sb_arp_handler(struct udevice *dev, void *packet,
unsigned int len)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
int ret = 0;
if (ntohs(arp->ar_op) == ARPOP_REQUEST) {
priv->fake_host_ipaddr = net_read_ip(&arp->ar_spa);
ret = sandbox_eth_recv_arp_req(dev);
if (ret)
return ret;
ret = sandbox_eth_arp_req_to_reply(dev, packet, len);
return ret;
}
return -EPROTONOSUPPORT;
}
static int sb_syn_handler(struct udevice *dev, void *packet,
unsigned int len)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
struct ethernet_hdr *eth = packet;
struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
struct ethernet_hdr *eth_send;
struct ip_tcp_hdr *tcp_send;
/* Don't allow the buffer to overrun */
if (priv->recv_packets >= PKTBUFSRX)
return 0;
eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
eth_send->et_protlen = htons(PROT_IP);
tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
tcp_send->tcp_src = tcp->tcp_dst;
tcp_send->tcp_dst = tcp->tcp_src;
tcp_send->tcp_seq = htonl(0);
tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
tcp_send->tcp_flags = TCP_SYN | TCP_ACK;
tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
tcp_send->tcp_xsum = 0;
tcp_send->tcp_ugr = 0;
tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
tcp->ip_src,
tcp->ip_dst,
TCP_HDR_SIZE,
IP_TCP_HDR_SIZE);
net_set_ip_header((uchar *)tcp_send,
tcp->ip_src,
tcp->ip_dst,
IP_TCP_HDR_SIZE,
IPPROTO_TCP);
priv->recv_packet_length[priv->recv_packets] =
ETHER_HDR_SIZE + IP_TCP_HDR_SIZE;
++priv->recv_packets;
return 0;
}
static int sb_ack_handler(struct udevice *dev, void *packet,
unsigned int len)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
struct ethernet_hdr *eth = packet;
struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
struct ethernet_hdr *eth_send;
struct ip_tcp_hdr *tcp_send;
void *data;
int pkt_len;
int payload_len = 0;
const char *payload1 = "HTTP/1.1 200 OK\r\n"
"Content-Length: 30\r\n\r\n\r\n"
"<html><body>Hi</body></html>\r\n";
/* Don't allow the buffer to overrun */
if (priv->recv_packets >= PKTBUFSRX)
return 0;
eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
eth_send->et_protlen = htons(PROT_IP);
tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
tcp_send->tcp_src = tcp->tcp_dst;
tcp_send->tcp_dst = tcp->tcp_src;
data = (void *)tcp_send + IP_TCP_HDR_SIZE;
if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1) {
tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
payload_len = strlen(payload1);
memcpy(data, payload1, payload_len);
tcp_send->tcp_flags = TCP_ACK;
} else if (ntohl(tcp->tcp_seq) == 2) {
tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
payload_len = 0;
tcp_send->tcp_flags = TCP_ACK | TCP_FIN;
}
tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
tcp_send->tcp_xsum = 0;
tcp_send->tcp_ugr = 0;
pkt_len = IP_TCP_HDR_SIZE + payload_len;
tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
tcp->ip_src,
tcp->ip_dst,
pkt_len - IP_HDR_SIZE,
pkt_len);
net_set_ip_header((uchar *)tcp_send,
tcp->ip_src,
tcp->ip_dst,
pkt_len,
IPPROTO_TCP);
if (ntohl(tcp->tcp_seq) == 1 || ntohl(tcp->tcp_seq) == 2) {
priv->recv_packet_length[priv->recv_packets] =
ETHER_HDR_SIZE + IP_TCP_HDR_SIZE + payload_len;
++priv->recv_packets;
}
return 0;
}
static int sb_http_handler(struct udevice *dev, void *packet,
unsigned int len)
{
struct ethernet_hdr *eth = packet;
struct ip_hdr *ip;
struct ip_tcp_hdr *tcp;
if (ntohs(eth->et_protlen) == PROT_ARP) {
return sb_arp_handler(dev, packet, len);
} else if (ntohs(eth->et_protlen) == PROT_IP) {
ip = packet + ETHER_HDR_SIZE;
if (ip->ip_p == IPPROTO_TCP) {
tcp = packet + ETHER_HDR_SIZE;
if (tcp->tcp_flags == TCP_SYN)
return sb_syn_handler(dev, packet, len);
else if (tcp->tcp_flags & TCP_ACK && !(tcp->tcp_flags & TCP_SYN))
return sb_ack_handler(dev, packet, len);
return 0;
}
return -EPROTONOSUPPORT;
}
return -EPROTONOSUPPORT;
}
static int net_test_wget(struct unit_test_state *uts)
{
sandbox_eth_set_tx_handler(0, sb_http_handler);
sandbox_eth_set_priv(0, uts);
env_set("ethact", "eth@10002000");
env_set("ethrotate", "no");
env_set("loadaddr", "0x20000");
ut_assertok(run_command("wget ${loadaddr} 1.1.2.2:/index.html", 0));
sandbox_eth_set_tx_handler(0, NULL);
ut_assertok(console_record_reset_enable());
run_command("md5sum ${loadaddr} ${filesize}", 0);
ut_assert_nextline("md5 for 00020000 ... 0002001f ==> 234af48e94b0085060249ecb5942ab57");
ut_assertok(ut_check_console_end(uts));
return 0;
}
LIB_TEST(net_test_wget, 0);