]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/eth/synth/ecosynth/v2_0/src/syntheth.c
Initial revision
[karo-tx-redboot.git] / packages / devs / eth / synth / ecosynth / v2_0 / src / syntheth.c
1 //==========================================================================
2 //
3 //      syntheth.c
4 //
5 //      Network device driver for the synthetic target
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2002, 2003 Bart Veer
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting the
37 // copyright holder(s).
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):    bartv
44 // Contributors: bartv
45 // Date:         2002-08-07
46 //
47 //####DESCRIPTIONEND####
48 //==========================================================================
49
50 #include <pkgconf/devs_eth_ecosynth.h>
51
52 #include <cyg/infra/cyg_type.h>
53 #include <cyg/infra/cyg_ass.h>
54 #include <cyg/hal/hal_arch.h>
55 #include <cyg/infra/diag.h>
56 #include <cyg/hal/drv_api.h>
57 #include <errno.h>
58 #include <string.h>
59
60 #define __ECOS 1
61 #include <sys/types.h>
62 #include <cyg/io/eth/netdev.h>
63 #include <cyg/io/eth/eth_drv.h>
64 #include <cyg/io/eth/eth_drv_stats.h>
65
66 #ifdef CYGPKG_NET
67 # include <net/if.h>
68 #else
69 # define IFF_PROMISC 0
70 #endif
71
72 #include <cyg/hal/hal_io.h>
73 #include "protocol.h"
74
75 // ----------------------------------------------------------------------------
76 // Device instances. The synthetic target ethernet package can support
77 // up to four ethernet devices, eth0 to eth3. A synth_eth structure
78 // holds the data that is specific to a given device. Each device
79 // needs an instance of this structure, followed by ETH_DRV_SC and
80 // NETDEVTAB_ENTRY macros. Another macro SYNTH_ETH_INSTANCE takes
81 // care of all that, to avoid unnecessary duplication of code here.
82 //
83 // NOTE: unfortunately this involves duplicating the eth_hwr_funs
84 // structure. This could be eliminated but it would require bypassing
85 // the ETH_DRV_SC macro.
86
87 #define ETHERNET_MINTU 14
88 #define ETHERNET_MAXTU 1514
89
90
91 typedef struct synth_eth {
92     int             synth_id;           // Device id within the auxiliary
93     int             up;                 // Has there been a call to start()?
94     int             in_send;            // Detect recursive calls
95     int             tx_done;
96     unsigned long   tx_key;             // Allow mbuf's to be freed
97     volatile int    rx_pending;         // There is pending data.
98     int             rx_len;             // Length of buffered data.
99     unsigned char   MAC[6];             // Obtained from the underlying ethernet device
100     cyg_vector_t    interrupt;          // Interrupt number allocated by the auxiliary
101     int             multi_supported;    // Does the driver support multicasting?
102     cyg_handle_t    interrupt_handle;   // Allow the ISR and DSR to be installed.
103     cyg_interrupt   interrupt_data;
104     unsigned char   tx_data[ETHERNET_MAXTU];
105     unsigned char   rx_data[ETHERNET_MAXTU];
106 } synth_eth;
107
108 #define SYNTH_ETH_INSTANCE( _number_)           \
109 static synth_eth synth_eth##_number_ = {        \
110     synth_id:   -1,                             \
111     up:          1,                             \
112     in_send:     0,                             \
113     tx_done:     0,                             \
114     tx_key:      0L,                            \
115     rx_pending:  0,                             \
116     rx_len:      0,                             \
117     MAC:         { 0, 0, 0, 0, 0, 0 },          \
118     interrupt:   0,                             \
119     interrupt_handle: 0                         \
120 };                                              \
121 ETH_DRV_SC(synth_eth_sc##_number_,              \
122            (void*) &synth_eth##_number_,        \
123            "eth" #_number_,                     \
124            synth_eth_start,                     \
125            synth_eth_stop,                      \
126            synth_eth_ioctl,                     \
127            synth_eth_can_send,                  \
128            synth_eth_send,                      \
129            synth_eth_recv,                      \
130            synth_eth_deliver,                   \
131            synth_eth_poll,                      \
132            synth_eth_intvector);                \
133 NETDEVTAB_ENTRY(synth_eth_netdev##_number_,     \
134                 "synth_eth" #_number_,          \
135                 synth_eth_init,                 \
136                 &synth_eth_sc##_number_);
137
138 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH0
139 SYNTH_ETH_INSTANCE(0);
140 #endif
141 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH1
142 SYNTH_ETH_INSTANCE(1);
143 #endif
144 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH2
145 SYNTH_ETH_INSTANCE(2);
146 #endif
147 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH3
148 SYNTH_ETH_INSTANCE(3);
149 #endif
150
151 // ----------------------------------------------------------------------------
152 // Data transmits.
153 //
154 // The eCos application will just send the data to the auxiliary,
155 // which will in turn pass it on to the rawether utility. There is no
156 // need for any response. Flow control is implicit: if the eCos
157 // application tries to send ethernet packets too quickly then those
158 // get passed on to the auxiliary, which in turn will pass them on to
159 // the rawether process. If rawether is still busy with the previous
160 // packet then the auxiliary will block on a pipe write, and in turn
161 // the eCos application will block. As long as rawether manages to
162 // complete its operations reasonably quickly these blocks should not
163 // be noticeable to the user.
164 //
165 // So can_send() should always return true for an interface that is up
166 // and running. The send operation needs to take the sg list, turn it
167 // into a single buffer, and transmit it to the auxiliary. At that
168 // point the transmission is already complete so eth_drv_dsr() should
169 // be called to call deliver() and release the buffer.
170 //
171 // However there are some complications. The first is polled operation,
172 // where eth_drv_dsr() is a no-op and should not really be called at
173 // all because there are no interrupts going off. The second is that
174 // calling eth_drv_dsr() directly will cause recursive operation:
175 // send() -> dsr -> can_send()/send() -> ...
176 // This is a bad idea, so can_send() has to check that we are not
177 // already inside a send(). Data transmission will proceed merrily
178 // once the send has returned.
179
180 static int
181 synth_eth_can_send(struct eth_drv_sc* sc)
182 {
183     synth_eth*  eth = (synth_eth*)(sc->driver_private);
184     return synth_auxiliary_running && eth->up && !eth->in_send && !eth->tx_done;
185 }
186
187 static void
188 synth_eth_send(struct eth_drv_sc* sc,
189                struct eth_drv_sg* sg_list, int sg_len, int total_len,
190                unsigned long key)
191 {
192     synth_eth*  eth = (synth_eth*)(sc->driver_private);
193
194     CYG_PRECONDITION((total_len >= ETHERNET_MINTU) && (total_len <= ETHERNET_MAXTU), "Only normal-sized ethernet packets are supported");
195     CYG_PRECONDITION(!eth->in_send && !eth->tx_done, "Ethernet device must not still be in use for transmits");
196
197     eth->in_send = 1;
198     eth->tx_key  = key;
199     if (synth_auxiliary_running && eth->up) {
200         int i;
201         unsigned char* buf = eth->tx_data;
202         for (i = 0; i < sg_len; i++) {
203             memcpy(buf, (void*) sg_list[i].buf, sg_list[i].len);
204             buf += sg_list[i].len;
205             CYG_LOOP_INVARIANT(buf <= &(eth->tx_data[ETHERNET_MAXTU]), "sg list must not exceed ethernet MTU");
206         }
207         CYG_POSTCONDITION(buf == &(eth->tx_data[total_len]), "sg list lengths should match total_len");
208
209         synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_TX, 0, 0, eth->tx_data, total_len,
210                                 (void*) 0, (unsigned char*)0, (int*)0, 0);
211     }
212
213     // The transfer has now completed, one way or another, so inform
214     // the higher-level code immediately.
215     eth->tx_done = 1;
216     eth_drv_dsr(eth->interrupt, 0, (cyg_addrword_t) sc);
217     eth->in_send = 0;
218 }
219
220 // ----------------------------------------------------------------------------
221 // Receives.
222 //
223 // These are rather more complicated because there are real interrupts
224 // involved, and polling needs to be supported as well. The actual
225 // transfer of data from auxiliary to eCos happens inside deliver(),
226 // and the data is buffered up in the synth_eth structure. All that
227 // needs to be done here is scatter the existing data into the
228 // sg_list. If higher-level code has run out of space then the
229 // sg_list may contain null pointers.
230
231 static void
232 synth_eth_recv(struct eth_drv_sc* sc, struct eth_drv_sg* sg_list, int sg_len)
233 {
234     synth_eth*      eth     = (synth_eth*)(sc->driver_private);
235     unsigned char*  buf     = eth->rx_data;
236     int             len     = eth->rx_len;
237     int             i;
238
239     for (i = 0; i < sg_len; i++) {
240         if (0 == sg_list[i].buf) {
241             break;
242         } else if (len <= sg_list[i].len) {
243             memcpy((void*)sg_list[i].buf, buf, len);
244             break;
245         } else {
246             memcpy((void*)sg_list[i].buf, buf, sg_list[i].len);
247             buf += sg_list[i].len;
248             len -= sg_list[i].len;
249         }
250     }
251 }
252
253 // The ISR does not have to do anything, the DSR does the real work.
254 static cyg_uint32
255 synth_eth_isr(cyg_vector_t vector, cyg_addrword_t data)
256 {
257     cyg_drv_interrupt_acknowledge(vector);
258     return CYG_ISR_CALL_DSR;
259 }
260
261 // The DSR also does not have to do very much. The data argument is
262 // actually the eth_drv_sc structure, which must match the vector.
263 // Interrupts only go off when there are pending receives, so set the
264 // rx_pending flag and call the generic DSR to do the real work.
265 static void
266 synth_eth_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
267 {
268     struct eth_drv_sc* sc = (struct eth_drv_sc*) data;
269     synth_eth* eth        = (synth_eth*)(sc->driver_private);
270     CYG_ASSERT(eth->interrupt == vector, "Interrupt vectors cannot change during a run");
271
272     eth->rx_pending = 1;
273     eth_drv_dsr(vector, count, data);
274 }
275
276 // ----------------------------------------------------------------------------
277 // Delivery. This is invoked by a thread inside the TCP/IP stack, or by
278 // the poll function.
279 static void
280 synth_eth_deliver(struct eth_drv_sc* sc)
281 {
282     synth_eth* eth = (synth_eth*)(sc->driver_private);
283
284     if (eth->tx_done) {
285         eth->tx_done = 0;
286         (*sc->funs->eth_drv->tx_done)(sc, eth->tx_key, 1);
287     }
288     while (eth->rx_pending) {
289         int more = 1;
290         eth->rx_pending = 0;
291
292         while (more && eth->up && synth_auxiliary_running) {
293             synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_RX, 0, 0, (void*) 0, 0,
294                                     &more, eth->rx_data, &(eth->rx_len), ETHERNET_MAXTU);
295             CYG_LOOP_INVARIANT(!more || (0 != eth->rx_len), "Auxiliary must send at least one packet if several are available");
296             if (eth->rx_len > 0) {
297                 CYG_ASSERT((eth->rx_len >= ETHERNET_MINTU) && (eth->rx_len <= ETHERNET_MAXTU), "Only normal-sized ethernet packets are supported");
298                 // Inform higher-level code that data is available.
299                 // This should result in a call to recv() with a
300                 // suitable sg_list. If out of memory, recv()
301                 // will see a null pointer.
302                 (*sc->funs->eth_drv->recv)(sc, eth->rx_len);
303             }
304         };
305     }
306 }
307
308
309 // ----------------------------------------------------------------------------
310 // Polling support. Transmits are relatively straightforward because
311 // all the hard work is handled by send(). Receives are rather more
312 // complicated because interrupts are disabled so we never know when
313 // there is really pending data. However deliver() will do the right
314 // thing even if there is no data, so simply faking up an interrupt
315 // is enough. This does mean extra traffic between application and
316 // auxiliary, but polling does rather imply that.
317 static void
318 synth_eth_poll(struct eth_drv_sc* sc)
319 {
320     synth_eth* eth = (synth_eth*)(sc->driver_private);
321     if (synth_auxiliary_running && eth->up) {
322         eth->rx_pending = 1;
323     }
324     synth_eth_deliver(sc);
325 }
326
327 static int
328 synth_eth_intvector(struct eth_drv_sc* sc)
329 {
330     synth_eth* eth = (synth_eth*)(sc->driver_private);
331     return eth->interrupt;
332 }
333
334 // ----------------------------------------------------------------------------
335 // ioctl()'s.
336 //
337 // SET_MAC_ADDRESS is not currently implemented, and probably should
338 // not be implemented because the underlying ethernet device may not
339 // support it.
340 //
341 // SET_MC_ALL is supported if the underlying hardware does. This is
342 // needed for IPV6 support. More selective multicasting via
343 // SET_MC_LIST is not supported, because it imposes too heavy a
344 // requirement on the underlying Linux device. SET_MC_LIST can be
345 // used to disable multicast support.
346 //
347 // GET_IF_STATS_UD and GET_IF_STATS are not currently implemented
348 static int
349 synth_eth_ioctl(struct eth_drv_sc* sc, unsigned long key, void* data, int data_length)
350 {
351     synth_eth* eth = (synth_eth*)(sc->driver_private);
352     int result = EINVAL;
353     
354     switch(key) {
355       case ETH_DRV_SET_MC_ALL:
356         {
357             if (eth->multi_supported) {
358                 synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_MULTIALL, 1, 0, (unsigned char*) 0, 0,
359                                        (void*)0, (unsigned char*)0, (int*)0, 0);
360                 result = 0;
361             }
362             break;
363         }
364       case ETH_DRV_SET_MC_LIST:
365         {
366             struct eth_drv_mc_list* mcl = (struct eth_drv_mc_list*) data;
367             if (eth->multi_supported && (0 == mcl->len)) {
368                 synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_MULTIALL, 0, 0, (unsigned char*) 0, 0,
369                                        (void*)0, (unsigned char*)0, (int*)0, 0);
370                 result = 0;
371             }
372             break;
373         }
374       default:
375         break;
376     }
377     
378     return result;
379 }
380
381 // ----------------------------------------------------------------------------
382 // Starting and stopping an interface. This includes restarting in
383 // promiscuous mode.
384 static void
385 synth_eth_start(struct eth_drv_sc* sc, unsigned char* enaddr, int flags)
386 {
387     synth_eth* eth = (synth_eth*)(sc->driver_private);
388
389     eth->up         = 0;
390     eth->rx_pending = 0;
391     
392     if ((-1 != eth->synth_id) && synth_auxiliary_running) {
393         synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_START, flags & IFF_PROMISC, 0, (void*) 0, 0,
394                                 (int*)0, (void*) 0, (int*) 0, 0);
395     }
396     eth->up = 1;
397     if (enaddr != (unsigned char*)0) {
398         memcpy(enaddr, eth->MAC, 6);
399     }
400 }
401
402 static void
403 synth_eth_stop(struct eth_drv_sc* sc)
404 {
405     synth_eth* eth = (synth_eth*)(sc->driver_private);
406
407     eth->up         = 0;
408     if ((-1 != eth->synth_id) && synth_auxiliary_running) {
409         synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_STOP, 0, 0, (void*) 0, 0,
410                                 (int*) 0, (void*) 0, (int*) 0, 0);
411     }
412     eth->rx_pending = 0;
413 }
414
415
416 // ----------------------------------------------------------------------------
417 // Initialization.
418 //
419 // This requires instantiating a device of class ethernet with a specific
420 // device name held in the eth_drv_sc structure. No additional device data
421 // is needed. If a device can be instantiated then the interrupt vector
422 // and the MAC address can be obtained, and an interrupt handler can be
423 // installed.
424 static bool
425 synth_eth_init(struct cyg_netdevtab_entry* ndp)
426 {
427     bool result = false;
428     struct eth_drv_sc* sc   = (struct eth_drv_sc*)(ndp->device_instance);
429     struct synth_eth*  eth  = (struct synth_eth*)(sc->driver_private);
430
431     if (synth_auxiliary_running) {
432         eth->synth_id = synth_auxiliary_instantiate("devs/eth/synth/ecosynth", SYNTH_MAKESTRING(CYGPKG_DEVS_ETH_ECOSYNTH), "ethernet",
433                                                     sc->dev_name, (const char*) 0);
434         
435         if (-1 != eth->synth_id) {
436             unsigned char data[7];
437             result = true;
438             synth_auxiliary_xchgmsg(eth->synth_id, SYNTH_ETH_GETPARAMS, 0, 0, (const unsigned char*) 0, 0,
439                                     (int *)&(eth->interrupt), data, 0, 7);
440             memcpy(eth->MAC, data, 6);
441             eth->multi_supported = data[6];
442             cyg_drv_interrupt_create(eth->interrupt,
443                                      0,
444                                      (CYG_ADDRWORD) sc,
445                                      &synth_eth_isr,
446                                      &synth_eth_dsr,
447                                      &(eth->interrupt_handle),
448                                      &(eth->interrupt_data));
449             cyg_drv_interrupt_attach(eth->interrupt_handle);
450             cyg_drv_interrupt_unmask(eth->interrupt);
451         }
452     }
453     (*sc->funs->eth_drv->init)(sc, eth->MAC);
454
455 #ifdef CYGPKG_NET
456     if (eth->multi_supported) {
457       sc->sc_arpcom.ac_if.if_flags |= IFF_ALLMULTI;
458     }
459 #endif    
460     return result;
461 }