unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / net / udp.c
1 //==========================================================================
2 //
3 //      net/udp.c
4 //
5 //      Stand-alone UDP networking support for RedBoot
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Red Hat, Inc.
12 // Copyright (C) 2002, 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas
46 // Date:         2000-07-14
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <redboot.h>
57 #include <net/net.h>
58
59 #ifdef UDP_STATS
60 static int udp_rx_total;
61 static int udp_rx_handled;
62 static int udp_rx_cksum;
63 static int udp_rx_dropped;
64 #endif
65
66 #define MAX_UDP_DATA (ETH_MAX_PKTLEN - (ETH_HDR_SIZE + \
67                                         sizeof(ip_header_t)  + \
68                                         sizeof(udp_header_t)))
69
70 /*
71  * A major assumption is that only a very small number of sockets will
72  * active, so a simple linear search of those sockets is acceptible.
73  */
74 static udp_socket_t *udp_list;
75
76
77 /*
78  * Install a handler for incoming udp packets.
79  * Caller provides the udp_socket_t structure.
80  * Returns zero if successful, -1 if socket is already used.
81  */
82 int
83 __udp_install_listener(udp_socket_t *s, word port, udp_handler_t handler)
84 {
85     udp_socket_t *p;
86
87     /*
88      * Make sure we only have one handler per port.
89      */
90     for (p = udp_list; p; p = p->next)
91         if (p->our_port == port)
92             return -1;
93     
94     s->our_port = htons(port);
95     s->handler = handler;
96     s->next = udp_list;
97     udp_list = s;
98
99     return 0;
100 }
101
102
103 /*
104  * Remove the handler for the given socket.
105  */
106 void
107 __udp_remove_listener(word port)
108 {
109     udp_socket_t *prev, *s;
110
111     for (prev = NULL, s = udp_list; s; prev = s, s = s->next)
112         if (s->our_port == htons(port)) {
113             if (prev)
114                 prev->next = s->next;
115             else
116                 udp_list = s->next;
117         }
118 }
119
120
121 /*
122  * Handle incoming UDP packets.
123  */
124 void
125 __udp_handler(pktbuf_t *pkt, ip_route_t *r)
126 {
127     udp_header_t *udp = pkt->udp_hdr;
128     ip_header_t  *ip = pkt->ip_hdr;
129     udp_socket_t *s;
130
131     if (udp->checksum == 0xffff)
132         udp->checksum = 0;
133
134     /* copy length for pseudo sum calculation */
135     ip->length = udp->length;
136
137     if (__sum((word *)udp, ntohs(udp->length), __pseudo_sum(ip)) == 0) {
138         for (s = udp_list; s; s = s->next) {
139             if (s->our_port == udp->dest_port) {
140                 (*s->handler)(s, ((char *)udp) + sizeof(udp_header_t),
141                               ntohs(udp->length) - sizeof(udp_header_t),
142                               r, ntohs(udp->src_port));
143                 __pktbuf_free(pkt);
144                 return;
145             }
146         }
147     }
148     __pktbuf_free(pkt);
149 }
150
151
152 /*
153  * Send a UDP packet.
154  */
155 int
156 __udp_send(void *buf, int len, ip_route_t *dest_ip,
157            word dest_port, word src_port)
158 {
159     pktbuf_t *pkt;
160     udp_header_t *udp;
161     ip_header_t *ip;
162     unsigned short cksum;
163     int ret;
164
165     /* dumb */
166     if (len > MAX_UDP_DATA)
167         return -1;
168
169     /* just drop it if can't get a buffer */
170     if ((pkt = __pktbuf_alloc(ETH_MAX_PKTLEN)) == NULL)
171         return -1;
172
173     udp = pkt->udp_hdr;
174     ip = pkt->ip_hdr;
175
176     pkt->pkt_bytes = len + sizeof(udp_header_t);
177
178     udp->src_port = htons(src_port);
179     udp->dest_port = htons(dest_port);
180     udp->length = htons(pkt->pkt_bytes);
181     udp->checksum = 0;
182
183     memcpy(((char *)udp) + sizeof(udp_header_t), buf, len);
184
185     /* fill in some pseudo-header fields */
186     memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
187     memcpy(ip->destination, dest_ip->ip_addr, sizeof(ip_addr_t));
188     ip->protocol = IP_PROTO_UDP;
189     ip->length = udp->length;
190
191     cksum = __sum((word *)udp, pkt->pkt_bytes, __pseudo_sum(ip));
192     udp->checksum = htons(cksum);
193
194     ret = __ip_send(pkt, IP_PROTO_UDP, dest_ip);
195     __pktbuf_free(pkt);
196     return ret;
197 }
198
199 int
200 __udp_sendto(void *data, int len, struct sockaddr_in *server, 
201              struct sockaddr_in *local)
202 {
203     ip_route_t rt;
204
205    if (__arp_lookup((ip_addr_t *)&server->sin_addr, &rt) < 0) {
206        diag_printf("%s: Can't find address of server\n", __FUNCTION__);
207        return -1;
208    } else {
209       __udp_send(data, len, &rt, ntohs(server->sin_port), ntohs(local->sin_port));
210        return 0;
211    }
212 }
213
214 static char               *recvfrom_buf;
215 static int                 recvfrom_len;
216 static struct sockaddr_in *recvfrom_server;
217
218 static void
219 __udp_recvfrom_handler(udp_socket_t *skt, void *buf, int len,
220                        ip_route_t *src_route, word src_port)
221 {
222     if (recvfrom_server == NULL || recvfrom_buf == NULL)
223         return;
224
225     if (recvfrom_server->sin_port && recvfrom_server->sin_port != htons(src_port))
226         return;
227
228     // Move data to waiting buffer
229     recvfrom_len = len;
230     memcpy(recvfrom_buf, buf, len);
231     if (recvfrom_server) {
232         recvfrom_server->sin_port = htons(src_port);
233         memcpy(&recvfrom_server->sin_addr, &src_route->ip_addr, sizeof(src_route->ip_addr));
234         recvfrom_buf = NULL;  // Tell reader we got a packet
235     } else {
236         diag_printf("udp_recvfrom - dropped packet of %d bytes\n", len);
237     }
238 }
239
240 int
241 __udp_recvfrom(void *data, int len, struct sockaddr_in *server, 
242                struct sockaddr_in *local, struct timeval *timo)
243 {
244     int res, my_port, total_ms;
245     udp_socket_t skt;
246     unsigned long start;
247
248     my_port = ntohs(local->sin_port);
249     if (__udp_install_listener(&skt, my_port, __udp_recvfrom_handler) < 0) {
250         return -1;
251     }
252     recvfrom_buf = data;
253     recvfrom_len = len;
254     recvfrom_server = server;
255     total_ms = (timo->tv_sec * 1000) + (timo->tv_usec / 1000);
256     start = MS_TICKS();
257     res = -1;
258     do {
259         __enet_poll();  // Handle the hardware
260         if (!recvfrom_buf) {
261             // Data have arrived
262             res = recvfrom_len;
263             break;
264         }
265     } while ((MS_TICKS_DELAY() - start) < total_ms);
266     __udp_remove_listener(my_port);
267     return res;
268 }