source: rtems/c/src/lib/libbsp/powerpc/psim/network/if_sim.c @ c499856

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 10.8 KB
Line 
1/* Trivial driver for PSIM's emulated ethernet device 'hw_ethtap'.
2 *
3 * NOTE: At the time of this writing (2009/1) 'hw_ethtap' requires
4 *       a patched version of PSIM -- the vanilla version does not
5 *       implement this device.
6 *       Also, support for this device is currently only available
7 *       on a linux host.
8 *
9 * Author/Copyright: Till Straumann <Till.Straumann@TU-Berlin.de>
10 *
11 * LICENSE
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.org/license/LICENSE.
15 *
16 */
17
18#include <bsp.h>
19#include <rtems.h>
20#include <bsp/irq.h>
21#include <psim.h>
22#include <libcpu/io.h>
23#include <inttypes.h>
24
25
26#ifndef KERNEL
27#define KERNEL
28#endif
29#ifndef _KERNEL
30#define _KERNEL
31#endif
32
33#include <rtems/rtems_bsdnet.h>
34#include <sys/mbuf.h>
35#include <sys/socket.h>
36#include <sys/sockio.h>
37#include <net/ethernet.h>
38#include <net/if.h>
39#include <netinet/in.h>
40#include <netinet/if_ether.h>
41
42#include <stdio.h>
43
44#define IFSIM_SLOTS 1
45
46#define DRVNAME "if_sim"
47
48#define IFSIM_TX_BUF_REG 1
49#define IFSIM_TX_STA_REG 2
50
51#define IFSIM_RX_BUF_REG 4
52#define IFSIM_RX_STA_REG 5
53
54#define IFSIM_RX_CNT_MSK  0xffff
55#define IFSIM_RX_STA_DONE (1<<31)
56
57#define IFSIM_RX_ERR_NOSPC      (1<<20)  /* buffer too small   */
58#define IFSIM_RX_ERR_DMA    (1<<21)  /* DMA error          */
59#define IFSIM_RX_ERR_RD     (1<<23)  /* file read error    */
60
61#define IFSIM_TX_STA_LST  (1<<16)
62#define IFSIM_TX_STA_DONE (1<<31)
63
64#define IFSIM_IEN_REG    6
65#define IFSIM_IRQ_REG    7
66
67#define IFSIM_RX_IRQ     (1<<0)
68
69#define IFSIM_MACA0_REG 8
70#define IFSIM_MACA1_REG 9
71
72#define IFSIM_CSR_REG  10
73#define IFSIM_CSR_PROM    (1<<0)
74/* Enable CRC generation/checking */
75#define IFSIM_CSR_CRCEN   (1<<1)
76
77
78#define RX_BUF_ALIGNMENT 1
79#define ETH_RX_OFFSET    0
80
81/*
82 * Align 'p' up to a multiple of 'a' which must be
83 * a power of two. Result is cast to (uintptr_t)
84 */
85#define ALIGNTO(p,a)    ((((uintptr_t)(p)) + (a) - 1) & ~((a)-1))
86
87
88typedef volatile unsigned reg_t;
89
90struct ifsim_private {
91        reg_t *base;
92        void  *rbuf;
93        unsigned rx_cserrs;
94        unsigned rx_err_nospc;
95        unsigned rx_err_dma;
96        unsigned rx_err_rd;
97        unsigned rx_nobufs;
98        unsigned rx_irqs;
99};
100
101struct ifsim_softc {
102        struct arpcom          arpcom;
103        struct ifsim_private   pvt;
104};
105
106struct ifsim_softc theIfSims[IFSIM_SLOTS] = {{{{0}}} };
107
108rtems_id           ifsim_tid = 0;
109
110__inline__ uint32_t
111ifsim_in(struct ifsim_softc *sc, unsigned regno)
112{
113        return in_be32( sc->pvt.base + regno );
114}
115
116__inline__ void
117ifsim_out(struct ifsim_softc *sc, unsigned regno, uint32_t v)
118{
119        out_be32(sc->pvt.base + regno, v);
120}
121
122static void *
123alloc_mbuf_rx(int *psz, uintptr_t *paddr)
124{
125struct mbuf     *m;
126unsigned long   l,o;
127
128    MGETHDR(m, M_DONTWAIT, MT_DATA);
129    if ( !m )
130        return 0;
131    MCLGET(m, M_DONTWAIT);
132    if ( ! (m->m_flags & M_EXT) ) {
133        m_freem(m);
134        return 0;
135    }
136
137    o = mtod(m, unsigned long);
138    l = ALIGNTO(o, RX_BUF_ALIGNMENT) - o;
139
140    /* align start of buffer */
141    m->m_data += l;
142
143    /* reduced length */
144    l = MCLBYTES - l;
145
146    m->m_len   = m->m_pkthdr.len = l;
147    *psz       = m->m_len;
148    *paddr     = mtod(m, unsigned long);
149
150    return (void*) m;
151}
152
153static int
154get_rxbuf(struct ifsim_softc *sc)
155{
156int         sz;
157uintptr_t   addr;
158void        *nbuf;
159
160        nbuf = alloc_mbuf_rx(&sz, &addr);
161
162        if ( nbuf ) {
163                sc->pvt.rbuf = nbuf;
164                ifsim_out(sc, IFSIM_RX_BUF_REG, addr);
165                ifsim_out(sc, IFSIM_RX_STA_REG, sz);
166                return 0;
167        }
168        return -1;
169}
170
171/* set/clear promiscuous mode */
172static void
173ifsim_upd_promisc(struct ifsim_softc *sc)
174{
175uint32_t ncsr, csr;
176
177        ncsr = csr = ifsim_in(sc, IFSIM_CSR_REG);
178
179        if ( sc->arpcom.ac_if.if_flags & IFF_PROMISC )
180                ncsr |=  IFSIM_CSR_PROM;
181        else
182                ncsr &= ~IFSIM_CSR_PROM;
183
184        if ( ncsr != csr )
185                ifsim_out(sc, IFSIM_CSR_REG, ncsr);
186}
187
188static void
189ifsim_init(void *arg)
190{
191struct ifsim_softc *sc = arg;
192struct ifnet       *ifp = &sc->arpcom.ac_if;
193
194        if ( 0 == get_rxbuf(sc) ) {
195                ifsim_upd_promisc(sc);
196                ifp->if_flags |= IFF_RUNNING;
197        }
198}
199
200static void
201ifsim_start(struct ifnet *ifp)
202{
203struct ifsim_softc *sc = ifp->if_softc;
204struct mbuf        *m, *mh, *m1;
205
206        while ( ifp->if_snd.ifq_head ) {
207                IF_DEQUEUE( &ifp->if_snd, mh );
208                for ( m=mh, m1 = m->m_next ; m1 ; m1 = m1->m_next ) {
209                        ifsim_out(sc, IFSIM_TX_BUF_REG, mtod(m, uint32_t));
210                        ifsim_out(sc, IFSIM_TX_STA_REG, m->m_len);
211                        /* dummy-busywait; the emulated hardware DMAs our
212                         * data away 'immediately' i.e., this loop is
213                         * never executed
214                         */
215                        while ( ! (IFSIM_TX_STA_DONE & ifsim_in(sc, IFSIM_TX_STA_REG)) )
216                                /* Loop */;
217                        m = m1;
218                }
219                ifsim_out(sc, IFSIM_TX_BUF_REG, mtod(m, uint32_t));
220                ifsim_out(sc, IFSIM_TX_STA_REG, m->m_len | IFSIM_TX_STA_LST);
221
222                /* dummy-busywait; the emulated hardware DMAs our
223                 * data away 'immediately' i.e., this loop is
224                 * never executed
225                 */
226                while ( ! (IFSIM_TX_STA_DONE & ifsim_in(sc, IFSIM_TX_STA_REG)) )
227                        /* Loop */;
228
229                m_freem(mh);
230        }
231}
232
233static int
234ifsim_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
235{
236struct ifsim_softc *sc  = ifp->if_softc;
237int                rval = 0;
238int                f;
239
240        switch (cmd) {
241
242                case SIOCSIFFLAGS:
243                        f = ifp->if_flags;
244                        if ( f & IFF_UP ) {
245                                if ( ! (f & IFF_RUNNING) ) {
246                                        ifsim_init(sc);
247                                } else {
248                                        ifsim_upd_promisc(sc);
249                                }
250                                /* FIXME: handle other flags here */
251                        } else {
252                                if ( f & IFF_RUNNING ) {
253                                        printk("WARNING: bringing "DRVNAME" down not really implemented\n");
254                                        ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
255                                }
256                        }
257
258                break;
259
260                case SIO_RTEMS_SHOW_STATS:
261                        printf("RX bad chksum  : %u\n", sc->pvt.rx_cserrs);
262                        printf("RX no space    : %u\n", sc->pvt.rx_err_nospc);
263                        printf("RX bad DMA     : %u\n", sc->pvt.rx_err_dma);
264                        printf("RX read errors : %u\n", sc->pvt.rx_err_rd);
265                        printf("rx_nobufs      : %u\n", sc->pvt.rx_nobufs);
266                        printf("rx_irqs        : %u\n", sc->pvt.rx_irqs);
267                break;
268
269                default:
270                        rval = ether_ioctl(ifp, cmd, data);
271                break;
272        }
273
274        return rval;
275}
276
277static void ifsim_irq_on(const rtems_irq_connect_data *pd)
278{
279struct ifsim_softc *sc = pd->handle;
280        ifsim_out(sc, IFSIM_IEN_REG, IFSIM_RX_IRQ);
281}
282
283static void ifsim_irq_off(const rtems_irq_connect_data *pd)
284{
285struct ifsim_softc *sc = pd->handle;
286        ifsim_out(sc, IFSIM_IEN_REG, 0);
287}
288
289static int ifsim_irq_ison(const rtems_irq_connect_data *pd)
290{
291struct ifsim_softc *sc = pd->handle;
292        return ifsim_in(sc, IFSIM_IEN_REG) & IFSIM_RX_IRQ ? 1 : 0;
293}
294
295static void
296ifsim_isr(void *arg)
297{
298struct ifsim_softc *sc = arg;
299
300        sc->pvt.rx_irqs++;
301#ifdef IRQ_DEBUG
302        printk("ISR happened\n");
303#endif
304
305        ifsim_out(sc, IFSIM_IEN_REG, 0);
306        rtems_bsdnet_event_send(ifsim_tid, (1<<(sc-theIfSims)));
307}
308
309static void
310ifsim_task(void *arg)
311{
312struct ifsim_softc *sc;
313uint32_t           sta;
314struct ifnet       *ifp;
315unsigned           len;
316rtems_event_set    evs;
317
318        while (1) {
319
320                rtems_bsdnet_event_receive(
321                        ((1<<IFSIM_SLOTS)-1),
322                        RTEMS_WAIT | RTEMS_EVENT_ANY,
323                        RTEMS_NO_TIMEOUT,
324                        &evs);
325
326                evs &= ((1<<IFSIM_SLOTS)-1);
327
328#ifdef IRQ_DEBUG
329                printk("Task got evs %u\n", evs);
330#endif
331
332                for ( sc = theIfSims; evs; evs>>=1, sc++ ) {
333
334                        if ( ! ( evs & 1 ) )
335                                continue;
336
337                        ifp = &sc->arpcom.ac_if;
338
339                        while ( ifsim_in(sc, IFSIM_IRQ_REG) & IFSIM_RX_IRQ ) {
340                                struct mbuf *m = sc->pvt.rbuf;
341
342                                sta = ifsim_in(sc, IFSIM_RX_STA_REG);
343
344
345                                if ( (sta & IFSIM_RX_STA_DONE) ) {
346
347                                        if ( (ifp->if_flags & IFF_RUNNING) ) {
348                                                if ( 0 == get_rxbuf(sc) ) {
349                                                        /* enqueue packet */
350                                                        struct ether_header *eh;
351                                                        uint32_t            crc_net, crc;
352                                                        int                 crc_len;
353
354                                                        crc_len = (IFSIM_CSR_CRCEN & ifsim_in(sc, IFSIM_CSR_REG)) ? sizeof(crc_net) : 0;
355
356                                                        len = (sta & IFSIM_RX_CNT_MSK) - crc_len;
357
358                                                        eh = (struct ether_header*)(mtod(m,unsigned long) + ETH_RX_OFFSET);
359
360                                                        m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header) - ETH_RX_OFFSET;
361                                                        m->m_data += sizeof(struct ether_header) + ETH_RX_OFFSET;
362                                                        m->m_pkthdr.rcvif = ifp;
363
364#ifdef DEBUG_WO_BSDNET
365                                                        {
366                                                                int i;
367                                                                for ( i=0; i<len + crc_len; ) {
368                                                                        printk("%02X ",((char*)eh)[i]);
369                                                                        if ( 0 == (++i & 0xf) )
370                                                                                fputc('\n',stdout);
371                                                                }
372                                                                if ( i & 0xf )
373                                                                        fputc('\n', stdout);
374                                                                printk("*****\n");
375                                                        }
376#endif
377
378                                                        if ( crc_len
379                                                                 && (memcpy(&crc_net, (char*)eh + len, crc_len),
380                                                                (crc = (ether_crc32_le((uint8_t *)eh, len) ^ 0xffffffff)) != crc_net) ) {
381                                                                printk("CSUM: me 0x%08X, them 0x%08x\n", crc, crc_net);
382                                                                sc->pvt.rx_cserrs++;
383                                                        } else {
384
385                                                                ifp->if_ipackets++;
386                                                                ifp->if_ibytes  += len;
387
388#ifdef DEBUG_WO_BSDNET
389                                                                m_freem(m);
390#else
391                                                                ether_input(ifp, eh, m);
392#endif
393
394                                                                m = 0;
395                                                        }
396
397                                                } else {
398                                                        /* throw away and reuse buffer */
399                                                        sc->pvt.rx_nobufs++;
400                                                }
401                                        }
402                                } else {
403                                        /* throw away and reuse buffer */
404                                        if ( (sta & IFSIM_RX_ERR_NOSPC) )
405                                                sc->pvt.rx_err_nospc++;
406                                        if ( (sta & IFSIM_RX_ERR_DMA) )
407                                                sc->pvt.rx_err_dma++;
408                                        if ( (sta & IFSIM_RX_ERR_RD) )
409                                                sc->pvt.rx_err_rd++;
410                                }
411
412                                if ( m ) {
413                                        /* reuse */
414                                        ifsim_out(sc, IFSIM_RX_STA_REG, m->m_pkthdr.len);
415                                }
416                        }
417                        /* re-enable interrupt */
418                        ifsim_out(sc, IFSIM_IEN_REG, IFSIM_RX_IRQ);
419                }
420        }
421}
422
423int
424rtems_ifsim_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching)
425{
426char                   *unitName;
427int                    unit;
428struct ifsim_softc     *sc;
429struct ifnet           *ifp;
430uint32_t               v;
431rtems_irq_connect_data ihdl;
432
433        if ( !attaching )
434                return -1;
435
436        unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName);
437
438        if ( unit <= 0 || unit > IFSIM_SLOTS ) {
439                printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, IFSIM_SLOTS);
440                return 1;
441        }
442
443        sc  = &theIfSims[unit-1];
444        ifp = &sc->arpcom.ac_if;
445
446        memset(&sc->pvt, 0, sizeof(sc->pvt));
447
448        sc->pvt.base = (reg_t*)ifcfg->port;
449
450        if ( 0 == sc->pvt.base )
451                sc->pvt.base = (reg_t*)PSIM.Ethtap;
452
453
454        sc->pvt.rbuf = 0;
455
456        if ( !ifsim_tid ) {
457                ifsim_tid = rtems_bsdnet_newproc("IFSM", 10000, ifsim_task, 0);
458        }
459
460        ihdl.name   = ifcfg->irno;
461        ihdl.hdl    = ifsim_isr;
462        ihdl.handle = sc;
463        ihdl.on     = ifsim_irq_on;
464        ihdl.off    = ifsim_irq_off;
465        ihdl.isOn   = ifsim_irq_ison;
466
467        if ( ! BSP_install_rtems_irq_handler(&ihdl) ) {
468                printk(DRVNAME"_attach(): installing ISR failed!\n");
469                return -1;
470        }
471
472        if ( ifcfg->hardware_address ) {
473                memcpy(sc->arpcom.ac_enaddr, ifcfg->hardware_address, ETHER_ADDR_LEN);
474        } else {
475                v = ifsim_in(sc, IFSIM_MACA0_REG);
476                memcpy(sc->arpcom.ac_enaddr, &v, 4);
477                v = ifsim_in(sc, IFSIM_MACA1_REG);
478                memcpy(sc->arpcom.ac_enaddr+4, &v, 2);
479        }
480
481        ifp->if_softc     = sc;
482        ifp->if_unit      = unit;
483        ifp->if_name      = unitName;
484
485        ifp->if_mtu       = ifcfg->mtu ? ifcfg->mtu : ETHERMTU;
486
487        ifp->if_init      = ifsim_init;
488        ifp->if_ioctl     = ifsim_ioctl;
489        ifp->if_start     = ifsim_start;
490        ifp->if_output    = ether_output;
491
492        ifp->if_watchdog  = 0;
493        ifp->if_timer     = 0;
494
495        ifp->if_flags     = IFF_BROADCAST | IFF_SIMPLEX;
496
497        ifp->if_snd.ifq_maxlen = ifqmaxlen;
498
499        if_attach(ifp);
500        ether_ifattach(ifp);
501
502        return 0;
503}
Note: See TracBrowser for help on using the repository browser.