]> git.karo-electronics.de Git - karo-tx-uboot.git/blob - net/bootme.c
lpc32xx: add GPIO support
[karo-tx-uboot.git] / net / bootme.c
1 /*
2  * Copyright (C) 2012 Lothar Waßmann <LW@KARO-electronics.de>
3  * based on: code from RedBoot (C) Uwe Steinkohl <US@KARO-electronics.de>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <command.h>
26 #include <net.h>
27 #include <wince.h>
28 #include <asm/errno.h>
29
30 DECLARE_GLOBAL_DATA_PTR;
31
32 static enum bootme_state bootme_state;
33 static int bootme_src_port = 0xdeadface;
34 static int bootme_dst_port = 0xdeadbeef;
35 static uchar bootme_ether[ETH_ALEN];
36 static IPaddr_t bootme_ip;
37 static int bootme_timed_out;
38 static const char *output_packet; /* used by first send udp */
39 static int output_packet_len;
40 static unsigned long bootme_timeout;
41 static bootme_hand_f *bootme_packet_handler;
42
43 #ifdef DEBUG
44 static void __attribute__((unused)) ce_dump_block(const void *ptr, int length)
45 {
46         const char *p = ptr;
47         int i;
48         int j;
49
50         for (i = 0; i < length; i++) {
51                 if (!(i % 16)) {
52                         printf("\n%p: ", ptr + i);
53                 }
54
55                 printf("%02x ", p[i]);
56                 if (!((i + 1) % 16)){
57                         printf("      ");
58                         for (j = i - 15; j <= i; j++){
59                                 if((p[j] > 0x1f) && (p[j] < 0x7f)) {
60                                         printf("%c", p[j]);
61                                 } else {
62                                         printf(".");
63                                 }
64                         }
65                 }
66         }
67         printf("\n");
68 }
69 #else
70 static inline void ce_dump_block(void *ptr, int length)
71 {
72 }
73 #endif
74
75 static void bootme_timeout_handler(void)
76 {
77         printf("%s\n", __func__);
78         net_set_state(NETLOOP_SUCCESS);
79         bootme_timed_out++;
80 }
81
82 static inline int env_changed(int *id)
83 {
84         int env_id = get_env_id();
85
86         if (*id != env_id) {
87                 debug("env_id: %d -> %d\n", *id, env_id);
88                 *id = env_id;
89                 return 1;
90         }
91         return 0;
92 }
93
94 static int env_id;
95
96 static int is_broadcast(IPaddr_t ip)
97 {
98         static IPaddr_t netmask;
99         static IPaddr_t our_ip;
100
101         return (ip == ~0 ||                             /* 255.255.255.255 */
102             ((netmask & our_ip) == (netmask & ip) &&    /* on the same net */
103             (netmask | ip) == ~0));             /* broadcast to our net */
104 }
105
106 static int check_net_config(void)
107 {
108         if (env_changed(&env_id)) {
109                 char *p;
110                 char *bip;
111
112                 bootme_dst_port = EDBG_DOWNLOAD_PORT;
113                 if (bootme_ip == 0) {
114                         bip = getenv("bootmeip");
115                         if (bip) {
116                                 bootme_ip = getenv_IPaddr("bootmeip");
117                                 if (!bootme_ip)
118                                         return -EINVAL;
119                                 p = strchr(bip, ':');
120                                 if (p) {
121                                         bootme_dst_port = simple_strtoul(p + 1, NULL, 10);
122                                 }
123                         } else {
124                                 memset(&bootme_ip, 0xff, sizeof(bootme_ip));
125                         }
126                 }
127
128                 p = getenv("bootme_dst_port");
129                 if (p)
130                         bootme_dst_port = simple_strtoul(p, NULL, 10);
131
132                 p = getenv("bootme_src_port");
133                 if (p)
134                         bootme_src_port = simple_strtoul(p, NULL, 10);
135                 else
136                         bootme_src_port = bootme_dst_port;
137
138                 if (is_broadcast(bootme_ip))
139                         memset(bootme_ether, 0xff, sizeof(bootme_ether));
140                 else
141                         memset(bootme_ether, 0, sizeof(bootme_ether));
142
143                 net_init();
144                 NetServerIP = bootme_ip;
145         }
146         return 0;
147 }
148
149 static void bootme_wait_arp_handler(uchar *pkt, unsigned dest,
150                                 IPaddr_t sip, unsigned src,
151                                 unsigned len)
152 {
153         net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */
154 }
155
156 static inline char next_cursor(char c)
157 {
158         switch(c) {
159         case '-':
160                 return '\\';
161         case '\\':
162                 return '|';
163         case '|':
164                 return '/';
165         case '/':
166                 return '-';
167         }
168         return 0;
169 }
170
171 static void bootme_handler(uchar *pkt, unsigned dest_port, IPaddr_t src_ip,
172                         unsigned src_port, unsigned len)
173 {
174         uchar *eth_pkt = pkt;
175         unsigned eth_len = len;
176         static char cursor = '|';
177         enum bootme_state last_state = bootme_state;
178
179         debug("received packet of len %d from %pI4:%d to port %d\n",
180                 len, &src_ip, src_port, dest_port);
181         ce_dump_block(pkt, len);
182
183         if (!bootme_packet_handler) {
184                 printf("No packet handler set for BOOTME protocol; dropping packet\n");
185                 return;
186         }
187         if (dest_port != bootme_src_port || !len)
188                 return; /* not for us */
189
190         printf("%c\x08", cursor);
191         cursor = next_cursor(cursor);
192
193         if (!is_broadcast(bootme_ip) && src_ip != bootme_ip) {
194                 debug("src_ip %pI4 does not match destination IP %pI4\n",
195                         &src_ip, &bootme_ip);
196                 return; /* not from our server */
197         }
198         if (bootme_state == BOOTME_INIT || bootme_state == BOOTME_DEBUG_INIT) {
199                 struct ethernet_hdr *eth = (struct ethernet_hdr *)(pkt -
200                                         NetEthHdrSize() - IP_UDP_HDR_SIZE);
201                 memcpy(bootme_ether, eth->et_src, sizeof(bootme_ether));
202                 printf("Target MAC address set to %pM\n", bootme_ether);
203
204                 if (is_broadcast(bootme_ip)) {
205                         NetCopyIP(&bootme_ip, &src_ip);
206                 }
207         }
208         if (bootme_state == BOOTME_INIT) {
209                 bootme_src_port = EDBG_SVC_PORT;
210                 debug("%s: bootme_src_port set to %d\n", __func__, bootme_src_port);
211         }
212
213         debug("bootme_dst_port %d -> %d\n", bootme_dst_port, src_port);
214         bootme_dst_port = src_port;
215
216         bootme_state = bootme_packet_handler(eth_pkt, eth_len);
217         debug("bootme_packet_handler() returned %d\n", bootme_state);
218         if (bootme_state != last_state)
219                 debug("%s@%d: bootme_state: %d -> %d\n", __func__, __LINE__,
220                         last_state, bootme_state);
221         switch (bootme_state) {
222         case BOOTME_INIT:
223         case BOOTME_DEBUG_INIT:
224                 break;
225
226         case BOOTME_DOWNLOAD:
227                 if (last_state != BOOTME_INIT)
228                         NetBootFileXferSize += len - 4;
229                 /* fallthru */
230         case BOOTME_DEBUG:
231                 if (last_state == BOOTME_INIT ||
232                         last_state == BOOTME_DEBUG_INIT)
233                         bootme_timeout = 3 * 1000;
234                 NetSetTimeout(bootme_timeout, bootme_timeout_handler);
235                 break;
236
237         case BOOTME_DONE:
238                 net_set_state(NETLOOP_SUCCESS);
239                 bootme_packet_handler = NULL;
240                 break;
241
242         case BOOTME_ERROR:
243                 net_set_state(NETLOOP_FAIL);
244                 bootme_packet_handler = NULL;
245         }
246 }
247
248 void BootmeStart(void)
249 {
250         if (bootme_state != BOOTME_DOWNLOAD)
251                 check_net_config();
252
253         if (output_packet_len == 0 ||
254                 is_valid_ether_addr(bootme_ether)) {
255                 /* wait for incoming packet */
256                 net_set_udp_handler(bootme_handler);
257                 bootme_timed_out = 0;
258                 NetSetTimeout(bootme_timeout, bootme_timeout_handler);
259         } else {
260                 /* send ARP request */
261                 uchar *pkt;
262
263                 net_set_arp_handler(bootme_wait_arp_handler);
264                 assert(NetTxPacket != NULL);
265                 pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
266                 memcpy(pkt, output_packet, output_packet_len);
267                 debug("%s@%d: Sending ARP request:\n", __func__, __LINE__);
268                 ce_dump_block(pkt, output_packet_len);
269                 NetSendUDPPacket(bootme_ether, bootme_ip, bootme_dst_port,
270                                 bootme_src_port, output_packet_len);
271                 output_packet_len = 0;
272         }
273 }
274
275 int bootme_send_frame(const void *buf, size_t len)
276 {
277         int ret;
278         struct eth_device *eth;
279         uchar *pkt;
280
281         eth = eth_get_dev();
282         if (eth == NULL)
283                 return -EINVAL;
284
285         if (bootme_state == BOOTME_INIT || bootme_state == BOOTME_DEBUG_INIT)
286                 check_net_config();
287
288         debug("%s: buf: %p len: %u from %pI4:%d to %pI4:%d\n",
289                 __func__, buf, len, &NetOurIP, bootme_src_port, &bootme_ip,
290                 bootme_dst_port);
291
292         if (is_zero_ether_addr(bootme_ether)) {
293                 output_packet = buf;
294                 output_packet_len = len;
295                 /* wait for arp reply and send packet */
296                 ret = NetLoop(BOOTME);
297                 if (ret < 0) {
298                         /* drop packet */
299                         output_packet_len = 0;
300                         return ret;
301                 }
302                 if (bootme_timed_out)
303                         return -ETIMEDOUT;
304                 return 0;
305         }
306
307         if (eth->state != ETH_STATE_ACTIVE) {
308                 if (eth_is_on_demand_init()) {
309                         ret = eth_init(gd->bd);
310                         if (ret < 0)
311                                 return ret;
312                         eth_set_last_protocol(BOOTME);
313                 } else {
314                         eth_init_state_only(gd->bd);
315                 }
316         }
317
318         assert(NetTxPacket != NULL);
319         pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
320         memcpy(pkt, buf, len);
321
322         ret = NetSendUDPPacket(bootme_ether, bootme_ip, bootme_dst_port,
323                         bootme_src_port, len);
324         if (ret)
325                 printf("Failed to send packet: %d\n", ret);
326
327         return ret;
328 }
329
330 static void bootme_init(IPaddr_t server_ip)
331 {
332         debug("%s@%d: bootme_state: %d -> %d\n", __func__, __LINE__,
333                 bootme_state, BOOTME_INIT);
334         bootme_state = BOOTME_INIT;
335         bootme_ip = server_ip;
336         /* force reconfiguration in check_net_config() */
337         env_id = 0;
338 }
339
340 int BootMeDownload(bootme_hand_f *handler)
341 {
342         int ret;
343
344         bootme_packet_handler = handler;
345
346         ret = NetLoop(BOOTME);
347         if (ret < 0)
348                 return BOOTME_ERROR;
349         if (bootme_timed_out && bootme_state != BOOTME_INIT)
350                 return BOOTME_ERROR;
351
352         return bootme_state;
353 }
354
355 int BootMeDebugStart(bootme_hand_f *handler)
356 {
357         int ret;
358
359         bootme_packet_handler = handler;
360
361         bootme_init(bootme_ip);
362         debug("%s@%d: bootme_state: %d -> %d\n", __func__, __LINE__,
363                 bootme_state, BOOTME_DEBUG_INIT);
364         bootme_state = BOOTME_DEBUG_INIT;
365
366         bootme_timeout = 3 * 1000;
367         NetSetTimeout(bootme_timeout, bootme_timeout_handler);
368
369         ret = NetLoop(BOOTME);
370         if (ret < 0)
371                 return BOOTME_ERROR;
372         if (bootme_timed_out)
373                 return BOOTME_DONE;
374         return bootme_state;
375 }
376
377 int BootMeRequest(IPaddr_t server_ip, const void *buf, size_t len, int timeout)
378 {
379         bootme_init(server_ip);
380         bootme_timeout = timeout * 1000;
381         bootme_timed_out = 0;
382         NetSetTimeout(bootme_timeout, bootme_timeout_handler);
383         return bootme_send_frame(buf, len);
384 }