1 //==========================================================================
5 // Network device driver for the synthetic target
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2002, 2003 Bart Veer
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.
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
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.
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.
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.
36 // Alternative licenses for eCos may be arranged by contacting the
37 // copyright holder(s).
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: bartv
47 //####DESCRIPTIONEND####
48 //==========================================================================
50 #include <pkgconf/devs_eth_ecosynth.h>
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>
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>
69 # define IFF_PROMISC 0
72 #include <cyg/hal/hal_io.h>
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.
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.
87 #define ETHERNET_MINTU 14
88 #define ETHERNET_MAXTU 1514
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
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];
108 #define SYNTH_ETH_INSTANCE( _number_) \
109 static synth_eth synth_eth##_number_ = { \
117 MAC: { 0, 0, 0, 0, 0, 0 }, \
119 interrupt_handle: 0 \
121 ETH_DRV_SC(synth_eth_sc##_number_, \
122 (void*) &synth_eth##_number_, \
127 synth_eth_can_send, \
132 synth_eth_intvector); \
133 NETDEVTAB_ENTRY(synth_eth_netdev##_number_, \
134 "synth_eth" #_number_, \
136 &synth_eth_sc##_number_);
138 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH0
139 SYNTH_ETH_INSTANCE(0);
141 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH1
142 SYNTH_ETH_INSTANCE(1);
144 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH2
145 SYNTH_ETH_INSTANCE(2);
147 #ifdef CYGVAR_DEVS_ETH_ECOSYNTH_ETH3
148 SYNTH_ETH_INSTANCE(3);
151 // ----------------------------------------------------------------------------
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.
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.
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.
181 synth_eth_can_send(struct eth_drv_sc* sc)
183 synth_eth* eth = (synth_eth*)(sc->driver_private);
184 return synth_auxiliary_running && eth->up && !eth->in_send && !eth->tx_done;
188 synth_eth_send(struct eth_drv_sc* sc,
189 struct eth_drv_sg* sg_list, int sg_len, int total_len,
192 synth_eth* eth = (synth_eth*)(sc->driver_private);
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");
199 if (synth_auxiliary_running && eth->up) {
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");
207 CYG_POSTCONDITION(buf == &(eth->tx_data[total_len]), "sg list lengths should match total_len");
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);
213 // The transfer has now completed, one way or another, so inform
214 // the higher-level code immediately.
216 eth_drv_dsr(eth->interrupt, 0, (cyg_addrword_t) sc);
220 // ----------------------------------------------------------------------------
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.
232 synth_eth_recv(struct eth_drv_sc* sc, struct eth_drv_sg* sg_list, int sg_len)
234 synth_eth* eth = (synth_eth*)(sc->driver_private);
235 unsigned char* buf = eth->rx_data;
236 int len = eth->rx_len;
239 for (i = 0; i < sg_len; i++) {
240 if (0 == sg_list[i].buf) {
242 } else if (len <= sg_list[i].len) {
243 memcpy((void*)sg_list[i].buf, buf, len);
246 memcpy((void*)sg_list[i].buf, buf, sg_list[i].len);
247 buf += sg_list[i].len;
248 len -= sg_list[i].len;
253 // The ISR does not have to do anything, the DSR does the real work.
255 synth_eth_isr(cyg_vector_t vector, cyg_addrword_t data)
257 cyg_drv_interrupt_acknowledge(vector);
258 return CYG_ISR_CALL_DSR;
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.
266 synth_eth_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
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");
273 eth_drv_dsr(vector, count, data);
276 // ----------------------------------------------------------------------------
277 // Delivery. This is invoked by a thread inside the TCP/IP stack, or by
278 // the poll function.
280 synth_eth_deliver(struct eth_drv_sc* sc)
282 synth_eth* eth = (synth_eth*)(sc->driver_private);
286 (*sc->funs->eth_drv->tx_done)(sc, eth->tx_key, 1);
288 while (eth->rx_pending) {
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);
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.
318 synth_eth_poll(struct eth_drv_sc* sc)
320 synth_eth* eth = (synth_eth*)(sc->driver_private);
321 if (synth_auxiliary_running && eth->up) {
324 synth_eth_deliver(sc);
328 synth_eth_intvector(struct eth_drv_sc* sc)
330 synth_eth* eth = (synth_eth*)(sc->driver_private);
331 return eth->interrupt;
334 // ----------------------------------------------------------------------------
337 // SET_MAC_ADDRESS is not currently implemented, and probably should
338 // not be implemented because the underlying ethernet device may not
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.
347 // GET_IF_STATS_UD and GET_IF_STATS are not currently implemented
349 synth_eth_ioctl(struct eth_drv_sc* sc, unsigned long key, void* data, int data_length)
351 synth_eth* eth = (synth_eth*)(sc->driver_private);
355 case ETH_DRV_SET_MC_ALL:
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);
364 case ETH_DRV_SET_MC_LIST:
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);
381 // ----------------------------------------------------------------------------
382 // Starting and stopping an interface. This includes restarting in
385 synth_eth_start(struct eth_drv_sc* sc, unsigned char* enaddr, int flags)
387 synth_eth* eth = (synth_eth*)(sc->driver_private);
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);
397 if (enaddr != (unsigned char*)0) {
398 memcpy(enaddr, eth->MAC, 6);
403 synth_eth_stop(struct eth_drv_sc* sc)
405 synth_eth* eth = (synth_eth*)(sc->driver_private);
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);
416 // ----------------------------------------------------------------------------
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
425 synth_eth_init(struct cyg_netdevtab_entry* ndp)
428 struct eth_drv_sc* sc = (struct eth_drv_sc*)(ndp->device_instance);
429 struct synth_eth* eth = (struct synth_eth*)(sc->driver_private);
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);
435 if (-1 != eth->synth_id) {
436 unsigned char data[7];
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,
447 &(eth->interrupt_handle),
448 &(eth->interrupt_data));
449 cyg_drv_interrupt_attach(eth->interrupt_handle);
450 cyg_drv_interrupt_unmask(eth->interrupt);
453 (*sc->funs->eth_drv->init)(sc, eth->MAC);
456 if (eth->multi_supported) {
457 sc->sc_arpcom.ac_if.if_flags |= IFF_ALLMULTI;