]>
Commit | Line | Data |
---|---|---|
a346ca79 JH |
1 | /* |
2 | * Copyright (c) 2015 National Instruments | |
3 | * | |
4 | * (C) Copyright 2015 | |
5 | * Joe Hershberger <[email protected]> | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0 | |
8 | */ | |
9 | ||
10 | #include <asm/eth-raw-os.h> | |
11 | #include <common.h> | |
12 | #include <dm.h> | |
13 | #include <malloc.h> | |
14 | #include <net.h> | |
15 | ||
16 | DECLARE_GLOBAL_DATA_PTR; | |
17 | ||
22f68524 JH |
18 | static int reply_arp; |
19 | static IPaddr_t arp_ip; | |
a346ca79 JH |
20 | |
21 | static int sb_eth_raw_start(struct udevice *dev) | |
22 | { | |
23 | struct eth_sandbox_raw_priv *priv = dev_get_priv(dev); | |
24 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
25 | const char *interface; | |
26 | ||
27 | debug("eth_sandbox_raw: Start\n"); | |
28 | ||
29 | interface = fdt_getprop(gd->fdt_blob, dev->of_offset, | |
30 | "host-raw-interface", NULL); | |
31 | if (interface == NULL) | |
32 | return -EINVAL; | |
33 | ||
22f68524 JH |
34 | if (strcmp(interface, "lo") == 0) { |
35 | priv->local = 1; | |
36 | setenv("ipaddr", "127.0.0.1"); | |
37 | setenv("serverip", "127.0.0.1"); | |
38 | } | |
a346ca79 JH |
39 | return sandbox_eth_raw_os_start(interface, pdata->enetaddr, priv); |
40 | } | |
41 | ||
42 | static int sb_eth_raw_send(struct udevice *dev, void *packet, int length) | |
43 | { | |
44 | struct eth_sandbox_raw_priv *priv = dev_get_priv(dev); | |
45 | ||
46 | debug("eth_sandbox_raw: Send packet %d\n", length); | |
47 | ||
22f68524 JH |
48 | if (priv->local) { |
49 | struct ethernet_hdr *eth = packet; | |
50 | ||
51 | if (ntohs(eth->et_protlen) == PROT_ARP) { | |
52 | struct arp_hdr *arp = packet + ETHER_HDR_SIZE; | |
53 | ||
54 | /** | |
55 | * localhost works on a higher-level API in Linux than | |
56 | * ARP packets, so fake it | |
57 | */ | |
58 | arp_ip = NetReadIP(&arp->ar_tpa); | |
59 | reply_arp = 1; | |
60 | return 0; | |
61 | } | |
62 | packet += ETHER_HDR_SIZE; | |
63 | length -= ETHER_HDR_SIZE; | |
64 | } | |
a346ca79 JH |
65 | return sandbox_eth_raw_os_send(packet, length, priv); |
66 | } | |
67 | ||
68 | static int sb_eth_raw_recv(struct udevice *dev, uchar **packetp) | |
69 | { | |
22f68524 | 70 | struct eth_pdata *pdata = dev_get_platdata(dev); |
a346ca79 | 71 | struct eth_sandbox_raw_priv *priv = dev_get_priv(dev); |
22f68524 | 72 | int retval = 0; |
a346ca79 JH |
73 | int length; |
74 | ||
22f68524 JH |
75 | if (reply_arp) { |
76 | struct arp_hdr *arp = (void *)net_rx_packets[0] + | |
77 | ETHER_HDR_SIZE; | |
78 | ||
79 | /* | |
80 | * Fake an ARP response. The u-boot network stack is sending an | |
81 | * ARP request (to find the MAC address to address the actual | |
82 | * packet to) and requires an ARP response to continue. Since | |
83 | * this is the localhost interface, there is no Etherent level | |
84 | * traffic at all, so there is no way to send an ARP request or | |
85 | * to get a response. For this reason we fake the response to | |
86 | * make the u-boot network stack happy. | |
87 | */ | |
88 | arp->ar_hrd = htons(ARP_ETHER); | |
89 | arp->ar_pro = htons(PROT_IP); | |
90 | arp->ar_hln = ARP_HLEN; | |
91 | arp->ar_pln = ARP_PLEN; | |
92 | arp->ar_op = htons(ARPOP_REPLY); | |
93 | /* Any non-zero MAC address will work */ | |
94 | memset(&arp->ar_sha, 0x01, ARP_HLEN); | |
95 | /* Use whatever IP we were looking for (always 127.0.0.1?) */ | |
96 | NetWriteIP(&arp->ar_spa, arp_ip); | |
97 | memcpy(&arp->ar_tha, pdata->enetaddr, ARP_HLEN); | |
98 | NetWriteIP(&arp->ar_tpa, NetOurIP); | |
99 | length = ARP_HDR_SIZE; | |
100 | } else { | |
101 | /* If local, the Ethernet header won't be included; skip it */ | |
102 | uchar *pktptr = priv->local ? | |
103 | net_rx_packets[0] + ETHER_HDR_SIZE : net_rx_packets[0]; | |
104 | ||
105 | retval = sandbox_eth_raw_os_recv(pktptr, &length, priv); | |
106 | } | |
a346ca79 JH |
107 | |
108 | if (!retval && length) { | |
22f68524 JH |
109 | if (priv->local) { |
110 | struct ethernet_hdr *eth = (void *)net_rx_packets[0]; | |
111 | ||
112 | /* Fill in enough of the missing Ethernet header */ | |
113 | memcpy(eth->et_dest, pdata->enetaddr, ARP_HLEN); | |
114 | memset(eth->et_src, 0x01, ARP_HLEN); | |
115 | eth->et_protlen = htons(reply_arp ? PROT_ARP : PROT_IP); | |
116 | reply_arp = 0; | |
117 | length += ETHER_HDR_SIZE; | |
118 | } | |
119 | ||
a346ca79 JH |
120 | debug("eth_sandbox_raw: received packet %d\n", |
121 | length); | |
122 | *packetp = net_rx_packets[0]; | |
123 | return length; | |
124 | } | |
125 | return retval; | |
126 | } | |
127 | ||
128 | static void sb_eth_raw_stop(struct udevice *dev) | |
129 | { | |
130 | struct eth_sandbox_raw_priv *priv = dev_get_priv(dev); | |
131 | ||
132 | debug("eth_sandbox_raw: Stop\n"); | |
133 | ||
134 | sandbox_eth_raw_os_stop(priv); | |
135 | } | |
136 | ||
137 | static const struct eth_ops sb_eth_raw_ops = { | |
138 | .start = sb_eth_raw_start, | |
139 | .send = sb_eth_raw_send, | |
140 | .recv = sb_eth_raw_recv, | |
141 | .stop = sb_eth_raw_stop, | |
142 | }; | |
143 | ||
144 | static int sb_eth_raw_ofdata_to_platdata(struct udevice *dev) | |
145 | { | |
146 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
147 | ||
148 | pdata->iobase = dev_get_addr(dev); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | static const struct udevice_id sb_eth_raw_ids[] = { | |
153 | { .compatible = "sandbox,eth-raw" }, | |
154 | { } | |
155 | }; | |
156 | ||
157 | U_BOOT_DRIVER(eth_sandbox_raw) = { | |
158 | .name = "eth_sandbox_raw", | |
159 | .id = UCLASS_ETH, | |
160 | .of_match = sb_eth_raw_ids, | |
161 | .ofdata_to_platdata = sb_eth_raw_ofdata_to_platdata, | |
162 | .ops = &sb_eth_raw_ops, | |
163 | .priv_auto_alloc_size = sizeof(struct eth_sandbox_raw_priv), | |
164 | .platdata_auto_alloc_size = sizeof(struct eth_pdata), | |
165 | }; |