net: Improve the speed of netconsole
[oweals/u-boot.git] / drivers / net / netconsole.c
1 /*
2  * (C) Copyright 2004
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.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 <stdio_dev.h>
27 #include <net.h>
28
29 DECLARE_GLOBAL_DATA_PTR;
30
31 #ifndef CONFIG_NETCONSOLE_BUFFER_SIZE
32 #define CONFIG_NETCONSOLE_BUFFER_SIZE 512
33 #endif
34
35 static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE];
36 static int input_size; /* char count in input buffer */
37 static int input_offset; /* offset to valid chars in input buffer */
38 static int input_recursion;
39 static int output_recursion;
40 static int net_timeout;
41 static uchar nc_ether[6]; /* server enet address */
42 static IPaddr_t nc_ip; /* server ip */
43 static short nc_out_port; /* target output port */
44 static short nc_in_port; /* source input port */
45 static const char *output_packet; /* used by first send udp */
46 static int output_packet_len;
47 /*
48  * Start with a default last protocol.
49  * We are only interested in NETCONS or not.
50  */
51 enum proto_t net_loop_last_protocol = BOOTP;
52
53 static void nc_wait_arp_handler(uchar *pkt, unsigned dest,
54                                  IPaddr_t sip, unsigned src,
55                                  unsigned len)
56 {
57         net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */
58 }
59
60 static void nc_handler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
61                         unsigned len)
62 {
63         if (input_size)
64                 net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */
65 }
66
67 static void nc_timeout(void)
68 {
69         net_set_state(NETLOOP_SUCCESS);
70 }
71
72 void NcStart(void)
73 {
74         if (!output_packet_len || memcmp(nc_ether, NetEtherNullAddr, 6)) {
75                 /* going to check for input packet */
76                 net_set_udp_handler(nc_handler);
77                 NetSetTimeout(net_timeout, nc_timeout);
78         } else {
79                 /* send arp request */
80                 uchar *pkt;
81                 net_set_arp_handler(nc_wait_arp_handler);
82                 pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
83                 memcpy(pkt, output_packet, output_packet_len);
84                 NetSendUDPPacket(nc_ether, nc_ip, nc_out_port, nc_in_port,
85                         output_packet_len);
86         }
87 }
88
89 int nc_input_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len)
90 {
91         int end, chunk;
92
93         if (dest != nc_in_port || !len)
94                 return 0; /* not for us */
95
96         debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt);
97
98         if (input_size == sizeof(input_buffer))
99                 return 1; /* no space */
100         if (len > sizeof(input_buffer) - input_size)
101                 len = sizeof(input_buffer) - input_size;
102
103         end = input_offset + input_size;
104         if (end > sizeof(input_buffer))
105                 end -= sizeof(input_buffer);
106
107         chunk = len;
108         if (end + len > sizeof(input_buffer)) {
109                 chunk = sizeof(input_buffer) - end;
110                 memcpy(input_buffer, pkt + chunk, len - chunk);
111         }
112         memcpy(input_buffer + end, pkt, chunk);
113
114         input_size += len;
115
116         return 1;
117 }
118
119 static void nc_send_packet(const char *buf, int len)
120 {
121         struct eth_device *eth;
122         int inited = 0;
123         uchar *pkt;
124         uchar *ether;
125         IPaddr_t ip;
126
127         debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf);
128
129         eth = eth_get_dev();
130         if (eth == NULL)
131                 return;
132
133         if (!memcmp(nc_ether, NetEtherNullAddr, 6)) {
134                 if (eth->state == ETH_STATE_ACTIVE)
135                         return; /* inside net loop */
136                 output_packet = buf;
137                 output_packet_len = len;
138                 NetLoop(NETCONS); /* wait for arp reply and send packet */
139                 output_packet_len = 0;
140                 return;
141         }
142
143         if (eth->state != ETH_STATE_ACTIVE) {
144                 if (eth_is_on_demand_init()) {
145                         if (eth_init(gd->bd) < 0)
146                                 return;
147                         eth_set_last_protocol(NETCONS);
148                 } else
149                         eth_init_state_only(gd->bd);
150
151                 inited = 1;
152         }
153         pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
154         memcpy(pkt, buf, len);
155         ether = nc_ether;
156         ip = nc_ip;
157         NetSendUDPPacket(ether, ip, nc_out_port, nc_in_port, len);
158
159         if (inited) {
160                 if (eth_is_on_demand_init())
161                         eth_halt();
162                 else
163                         eth_halt_state_only();
164         }
165 }
166
167 static int nc_start(void)
168 {
169         int netmask, our_ip;
170         char *p;
171
172         nc_out_port = 6666; /* default port */
173         nc_in_port = nc_out_port;
174
175         if (getenv("ncip")) {
176
177                 nc_ip = getenv_IPaddr("ncip");
178                 if (!nc_ip)
179                         return -1;      /* ncip is 0.0.0.0 */
180                 p = strchr(getenv("ncip"), ':');
181                 if (p != NULL) {
182                         nc_out_port = simple_strtoul(p + 1, NULL, 10);
183                         nc_in_port = nc_out_port;
184                 }
185         } else
186                 nc_ip = ~0; /* ncip is not set, so broadcast */
187
188         p = getenv("ncoutport");
189         if (p != NULL)
190                 nc_out_port = simple_strtoul(p, NULL, 10);
191         p = getenv("ncinport");
192         if (p != NULL)
193                 nc_in_port = simple_strtoul(p, NULL, 10);
194
195         our_ip = getenv_IPaddr("ipaddr");
196         netmask = getenv_IPaddr("netmask");
197
198         if (nc_ip == ~0 ||                              /* 255.255.255.255 */
199             ((netmask & our_ip) == (netmask & nc_ip) && /* on the same net */
200             (netmask | nc_ip) == ~0))           /* broadcast to our net */
201                 memset(nc_ether, 0xff, sizeof(nc_ether));
202         else
203                 memset(nc_ether, 0, sizeof(nc_ether));  /* force arp request */
204
205         /*
206          * Initialize the static IP settings and buffer pointers
207          * incase we call NetSendUDPPacket before NetLoop
208          */
209         net_init();
210
211         return 0;
212 }
213
214 static void nc_putc(char c)
215 {
216         if (output_recursion)
217                 return;
218         output_recursion = 1;
219
220         nc_send_packet(&c, 1);
221
222         output_recursion = 0;
223 }
224
225 static void nc_puts(const char *s)
226 {
227         int len;
228
229         if (output_recursion)
230                 return;
231         output_recursion = 1;
232
233         len = strlen(s);
234         while (len) {
235                 int send_len = min(len, sizeof(input_buffer));
236                 nc_send_packet(s, send_len);
237                 len -= send_len;
238                 s += send_len;
239         }
240
241         output_recursion = 0;
242 }
243
244 static int nc_getc(void)
245 {
246         uchar c;
247
248         input_recursion = 1;
249
250         net_timeout = 0;        /* no timeout */
251         while (!input_size)
252                 NetLoop(NETCONS);
253
254         input_recursion = 0;
255
256         c = input_buffer[input_offset++];
257
258         if (input_offset >= sizeof(input_buffer))
259                 input_offset -= sizeof(input_buffer);
260         input_size--;
261
262         return c;
263 }
264
265 static int nc_tstc(void)
266 {
267         struct eth_device *eth;
268
269         if (input_recursion)
270                 return 0;
271
272         if (input_size)
273                 return 1;
274
275         eth = eth_get_dev();
276         if (eth && eth->state == ETH_STATE_ACTIVE)
277                 return 0;       /* inside net loop */
278
279         input_recursion = 1;
280
281         net_timeout = 1;
282         NetLoop(NETCONS);       /* kind of poll */
283
284         input_recursion = 0;
285
286         return input_size != 0;
287 }
288
289 int drv_nc_init(void)
290 {
291         struct stdio_dev dev;
292         int rc;
293
294         memset(&dev, 0, sizeof(dev));
295
296         strcpy(dev.name, "nc");
297         dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
298         dev.start = nc_start;
299         dev.putc = nc_putc;
300         dev.puts = nc_puts;
301         dev.getc = nc_getc;
302         dev.tstc = nc_tstc;
303
304         rc = stdio_register(&dev);
305
306         return (rc == 0) ? 1 : rc;
307 }