From 89376b7141edb6f927fb940c27391cda6e67c785 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 19 Feb 2009 19:55:40 +0000 Subject: Initial import. --- bsd_eth_drivers/libbsdport/Makefile.am | 75 ++++++ bsd_eth_drivers/libbsdport/README | 29 ++ bsd_eth_drivers/libbsdport/alldrv.c | 16 ++ bsd_eth_drivers/libbsdport/bus.h | 324 +++++++++++++++++++++++ bsd_eth_drivers/libbsdport/callout.h | 43 +++ bsd_eth_drivers/libbsdport/contigmalloc.c | 33 +++ bsd_eth_drivers/libbsdport/devicet.c | 374 ++++++++++++++++++++++++++ bsd_eth_drivers/libbsdport/devicet.h | 125 +++++++++ bsd_eth_drivers/libbsdport/ifmedia.c | 91 +++++++ bsd_eth_drivers/libbsdport/ifstuff.c | 79 ++++++ bsd_eth_drivers/libbsdport/libbsdport.h | 380 +++++++++++++++++++++++++++ bsd_eth_drivers/libbsdport/libbsdport_api.h | 84 ++++++ bsd_eth_drivers/libbsdport/libbsdport_post.h | 95 +++++++ bsd_eth_drivers/libbsdport/malloc.c | 17 ++ bsd_eth_drivers/libbsdport/mutex.h | 55 ++++ bsd_eth_drivers/libbsdport/rtems_callout.c | 250 ++++++++++++++++++ bsd_eth_drivers/libbsdport/rtems_taskqueue.c | 308 ++++++++++++++++++++++ bsd_eth_drivers/libbsdport/rtems_udelay.c | 192 ++++++++++++++ bsd_eth_drivers/libbsdport/rtems_udelay.h | 22 ++ bsd_eth_drivers/libbsdport/rtems_verscheck.h | 29 ++ bsd_eth_drivers/libbsdport/sysbus.c | 267 +++++++++++++++++++ bsd_eth_drivers/libbsdport/taskqueue.h | 69 +++++ 22 files changed, 2957 insertions(+) create mode 100644 bsd_eth_drivers/libbsdport/Makefile.am create mode 100644 bsd_eth_drivers/libbsdport/README create mode 100644 bsd_eth_drivers/libbsdport/alldrv.c create mode 100644 bsd_eth_drivers/libbsdport/bus.h create mode 100644 bsd_eth_drivers/libbsdport/callout.h create mode 100644 bsd_eth_drivers/libbsdport/contigmalloc.c create mode 100644 bsd_eth_drivers/libbsdport/devicet.c create mode 100644 bsd_eth_drivers/libbsdport/devicet.h create mode 100644 bsd_eth_drivers/libbsdport/ifmedia.c create mode 100644 bsd_eth_drivers/libbsdport/ifstuff.c create mode 100644 bsd_eth_drivers/libbsdport/libbsdport.h create mode 100644 bsd_eth_drivers/libbsdport/libbsdport_api.h create mode 100644 bsd_eth_drivers/libbsdport/libbsdport_post.h create mode 100644 bsd_eth_drivers/libbsdport/malloc.c create mode 100644 bsd_eth_drivers/libbsdport/mutex.h create mode 100644 bsd_eth_drivers/libbsdport/rtems_callout.c create mode 100644 bsd_eth_drivers/libbsdport/rtems_taskqueue.c create mode 100644 bsd_eth_drivers/libbsdport/rtems_udelay.c create mode 100644 bsd_eth_drivers/libbsdport/rtems_udelay.h create mode 100644 bsd_eth_drivers/libbsdport/rtems_verscheck.h create mode 100644 bsd_eth_drivers/libbsdport/sysbus.c create mode 100644 bsd_eth_drivers/libbsdport/taskqueue.h (limited to 'bsd_eth_drivers/libbsdport') diff --git a/bsd_eth_drivers/libbsdport/Makefile.am b/bsd_eth_drivers/libbsdport/Makefile.am new file mode 100644 index 0000000..44096b5 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/Makefile.am @@ -0,0 +1,75 @@ +# $Id$ +AUTOMAKE_OPTIONS=foreign + +include $(top_srcdir)/rtems-pre.am + +libbsdport_a_SOURCES = rtems_callout.c rtems_taskqueue.c rtems_udelay.c +libbsdport_a_SOURCES += ifstuff.c devicet.c alldrv.c contigmalloc.c +libbsdport_a_SOURCES += sysbus.c malloc.c ifmedia.c + +libbsdport_a_SOURCES += rtems_verscheck.h bus.h callout.h devicet.h +libbsdport_a_SOURCES += libbsdport.h libbsdport_post.h mutex.h +libbsdport_a_SOURCES += rtems_udelay.h rtems_verscheck.h taskqueue.h + +include_bsp_HEADERS = libbsdport_api.h + +lib_LIBRARIES = libbsdport.a + +include $(top_srcdir)/rtems.am + +LINKS = +LINKS+=sys/taskqueue.h +LINKS+=sys/bus.h +LINKS+=sys/mutex.h +LINKS+=bsp/rtems_verscheck.h + +DUMMYHEADERS = +DUMMYHEADERS+=dummyheaders/machine/bus.h +DUMMYHEADERS+=dummyheaders/machine/resource.h +DUMMYHEADERS+=dummyheaders/dev/pci/pcivar.h +DUMMYHEADERS+=dummyheaders/dev/pci/pcireg.h +DUMMYHEADERS+=dummyheaders/dev/mii/miivar.h +DUMMYHEADERS+=dummyheaders/sys/module.h +DUMMYHEADERS+=dummyheaders/sys/rman.h +DUMMYHEADERS+=dummyheaders/sys/kthread.h +DUMMYHEADERS+=dummyheaders/sys/endian.h +DUMMYHEADERS+=dummyheaders/net/if_vlan_var.h +DUMMYHEADERS+=dummyheaders/netinet/ip6.h +DUMMYHEADERS+=dummyheaders/vm/pmap.h +DUMMYHEADERS+=dummyheaders/miibus_if.h + +DUMMYHEADERS+=dummyheaders/miidevs.h +DUMMYHEADERS+=dummyheaders/dev/mii/brgphyreg.h + +BUILT_SOURCES= + +include ../links.am + +# +# I found no good way to have something made first thing. +# all-local is made after 'all' and xxx_DEPENDENCIES are +# after OBJECTS are made :-( +$(libbsdport_a_OBJECTS): $(DUMMYHEADERS) $(LINKS) + +if FALSE +CLOBBER_ADDITIONS = +CLOBBER_ADDITIONS += $(srcdir)/dummyheaders +CLOBBER_ADDITIONS += $(addprefix $(srcdir)/,$(sort $(foreach n,$(LINKS),$(firstword $(subst /, ,$(n)))))) + +dummyheaders/%: + @if [ ! -d $(srcdir)/`dirname $@` ] ; then mkdir -p $(srcdir)/`dirname $@`; fi + @touch $(srcdir)/$@ + +# for each name listed in LINKS, create parent directories (if needed) +# and a symlink to file in . +# E.g., LINKS=a/b/c.h +# creates a/b/c.h -> ../../c.h +$(LINKS): + @if [ ! -d $(srcdir)/$(dir $@) ] ; then mkdir -p $(srcdir)/$(dir $@); fi + @ln -s `echo $@ | sed -e 's%[^/]\+[/]\+%../%g'` $(srcdir)/$@ + + +distclean-local: + $(RM) -r $(CLOBBER_ADDITIONS) +endif + diff --git a/bsd_eth_drivers/libbsdport/README b/bsd_eth_drivers/libbsdport/README new file mode 100644 index 0000000..14fd4a5 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/README @@ -0,0 +1,29 @@ +This is a library and collection of header files +intended to ease porting recent FreeBSD (as of 7/2007) +drivers to RTEMS. Currently, only the PCI bus is supported. + +A lot of macro-magic is used in the 'libbsdport.h' +and 'libbsdport_post.h' headers which are te be included +from the driver source file prior and after the driver +includes the normal headers it uses: + +At the top of if_xxx.c add: + +#ifdef __rtems__ +#include +#endif + +... leave all normal include statements in place... +#include + +lastly, add: + +#ifdef __rtems__ +#include +#endif + +Note that libbsdport does not provide the full freebsd +kernel API but only a subset. + +<< MORE TO COME >> + diff --git a/bsd_eth_drivers/libbsdport/alldrv.c b/bsd_eth_drivers/libbsdport/alldrv.c new file mode 100644 index 0000000..0738179 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/alldrv.c @@ -0,0 +1,16 @@ +#include +#include "libbsdport_api.h" + +driver_t *libbsdport_netdriver_table_all[] = { + &libbsdport_em_driver, + &libbsdport_pcn_driver, + &libbsdport_le_pci_driver, + &libbsdport_re_driver, + 0 +}; + +/* weak alias defaults to a table that includes all currently supported drivers */ +extern driver_t *libbsdport_netdriver_table + [ + sizeof(libbsdport_netdriver_table_all)/sizeof(libbsdport_netdriver_table_all[0]) + ] __attribute__((weak,alias("libbsdport_netdriver_table_all"))); diff --git a/bsd_eth_drivers/libbsdport/bus.h b/bsd_eth_drivers/libbsdport/bus.h new file mode 100644 index 0000000..76d78a8 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/bus.h @@ -0,0 +1,324 @@ +#ifndef LIBBSDPORT_SYS_BUS_H +#define LIBBSDPORT_SYS_BUS_H + +#include +#include +#include +#include +#include + +typedef uint32_t bus_addr_t; +typedef size_t bus_size_t; + +typedef enum { + bus_space_mem = 0, + bus_space_io = 1 +} bus_space_tag_t; + +struct resource; + +typedef bus_addr_t bus_space_handle_t; + +/* The 'bus_space_xxx()' inlines can be helped if the + * tag is hardcoded in the driver so that the compiler + * can optimize part of the implementation away. + */ + +#define BUS_SPACE_BARRIER_WRITE 1 +#define BUS_SPACE_BARRIER_READ 2 + +#if defined(__i386__) + +#include + +static inline void +bus_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, int width, int type) +{ +} + +#define BUS_SPACE_DECL(type, width, nwidth) \ +static inline type \ +bus_space_read_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) \ +{ \ +type v; \ + if ( bus_space_io == t ) { \ + /* this is a macro setting the second argument */ \ + inport_##width( h+o, v ); \ + } else { \ + v = *(volatile type __attribute__((may_alias)) *)(h+o); \ + } \ + return v; \ +} \ + \ +static inline void \ +bus_space_write_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, type v) \ +{ \ + if ( bus_space_io == t ) { \ + outport_##width( h+o, v ); \ + } else { \ + *(volatile type __attribute__((may_alias)) *)(h+o) = v; \ + }\ +} + +BUS_SPACE_DECL(u_int32_t, long, 4) +BUS_SPACE_DECL(u_int16_t, word, 2) +BUS_SPACE_DECL(u_int8_t, byte, 1) + +#elif defined(__PPC__) + +#include + +#if defined(_IO_BASE) && _IO_BASE == 0 +#define BUS_SPACE_ALWAYS_MEM 1 +#else +#define BUS_SPACE_ALWAYS_MEM 0 +#endif + +static inline void +bus_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, int width, int type) +{ + asm volatile("eieio"); +} + + +#define BUS_SPACE_DECL(type, width, nwidth, op) \ +static inline type \ +bus_space_read_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) \ +{ \ +type v; \ + if ( !BUS_SPACE_ALWAYS_MEM && bus_space_io == t ) { \ + /* this is a macro setting the second argument */ \ + v = in_##op((volatile type *)(_IO_BASE+h+o)); \ + } else { \ + v = in_##op((volatile type *)(h+o)); \ + } \ + return v; \ +} \ + \ +static inline void \ +bus_space_write_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, type v) \ +{ \ + if ( !BUS_SPACE_ALWAYS_MEM && bus_space_io == t ) { \ + out_##op((volatile type *)(_IO_BASE+h+o), v); \ + } else { \ + out_##op((volatile type *)(h+o), v); \ + }\ +} + +BUS_SPACE_DECL(u_int32_t, long, 4, le32) +BUS_SPACE_DECL(u_int16_t, word, 2, le16) +BUS_SPACE_DECL(u_int8_t, byte, 1, 8) + +#undef BUS_SPACE_ALWAYS_MEM + +#else +#error "Missing definitions of bus_space_XXX() for this CPU architecture" +#endif + + +#undef BUS_SPACE_DECL + +#ifndef BUS_PROBE_DEFAULT +#define BUS_PROBE_DEFAULT 0 +#endif + +/* error codes are > 0 ; low priority says that probe + * was successful but another driver returning BUS_PROBE_DEFAULT + * is to be preferred... + */ +#ifndef BUS_PROBE_LOW_PRIORITY +#define BUS_PROBE_LOW_PRIORITY (-1) +#endif + + + +/* types -> -1 means unsupported */ +#define SYS_RES_IOPORT 1 +#define SYS_RES_MEMORY 2 +#define SYS_RES_IRQ 3 + +/* flags (1<<31) means unsupported */ +#define RF_ACTIVE (1<<1) +#define RF_SHAREABLE (1<<2) + +struct resource * +bus_alloc_resource_any(device_t dev, int type, int *prid, unsigned flags); + +#define FILTER_STRAY 1 +#define FILTER_HANDLED 0 + +typedef void (*driver_intr_t)(void *); +typedef int (*driver_filter_t)(void *); + +int +bus_setup_intr(device_t dev, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep); + +/* Flags currently ignored... */ +#define INTR_MPSAFE 0 +#define INTR_TYPE_NET 0 +/* INTR_FAST indicates that a 'handler' is actually + * a 'fast' handler which already uses taskqueues + */ +#define INTR_FAST 1 + +int +bus_teardown_intr(device_t dev, struct resource *r, void *cookiep); + +static inline int +bus_release_resource(device_t dev, int type, int rid, struct resource *r) +{ + return 0; +} + +#define bus_generic_detach(dev) do {} while (0) + +#define bus_generic_suspend(dev) (0) +#define bus_generic_resume(dev) (0) + +bus_space_handle_t +rman_get_bushandle(struct resource *r); + +bus_space_tag_t +rman_get_bustag(struct resource *r); + +#ifndef BUS_DMA_NOWAIT +/* ignored anyways */ +#define BUS_DMA_NOWAIT 0 +#endif + +#ifndef BUS_DMA_WAITOK +/* ignored anyways */ +#define BUS_DMA_WAITOK 0 +#endif + +#ifndef BUS_DMA_COHERENT +/* ignored anyways */ +#define BUS_DMA_COHERENT 0 +#endif + +#ifndef BUS_DMA_ZERO +/* ignored anyways */ +#define BUS_DMA_ZERO 0 +#endif + +#ifndef BUS_DMA_ALLOCNOW +/* ignored anyways */ +#define BUS_DMA_ALLOCNOW 0 +#endif + +/* unused */ +#ifndef BUS_SPACE_MAXADDR +#define BUS_SPACE_MAXADDR 0xdeadbeef +#endif + +/* unused */ +#ifndef BUS_SPACE_MAXADDR_32BIT +#define BUS_SPACE_MAXADDR_32BIT 0xdeadbeef +#endif + +/* unused */ +#ifndef BUS_SPACE_MAXSIZE_32BIT +#define BUS_SPACE_MAXSIZE_32BIT 0x10000000 +#endif + +typedef struct _bus_dma_tag_t { + unsigned alignment; + unsigned maxsize; + unsigned maxsegs; +} * bus_dma_tag_t; + +typedef struct _bus_dma_segment_t { + bus_addr_t ds_addr; + bus_size_t ds_len; +} bus_dma_segment_t; + +typedef void *bus_dmamap_t; + +int +bus_dma_tag_create(void *parent, unsigned alignment, unsigned bounds, uint32_t lowadd, uint32_t hiaddr, void (*filter)(void*), void *filterarg, unsigned maxsize, int nsegs, unsigned maxsegsize, unsigned flags, void (*lockfunc)(void*), void *lockarg, bus_dma_tag_t *ptag); + +void +bus_dma_tag_destroy(bus_dma_tag_t tag); + +int +bus_dmamem_alloc(bus_dma_tag_t tag, void **p_vaddr, unsigned flags, bus_dmamap_t *p_map); + +void +bus_dmamem_free(bus_dma_tag_t tag, void *vaddr, bus_dmamap_t map); + +#ifndef CPU2BUSADDR +#ifndef PCI_DRAM_OFFSET +#define PCI_DRAM_OFFSET 0 +#endif +#define CPU2BUSADDR(x) ((uint32_t)(x) + (PCI_DRAM_OFFSET)) +#endif + +#define kvtop(a) CPU2BUSADDR((bus_addr_t)(a)) +#define vtophys(a) CPU2BUSADDR((bus_addr_t)(a)) + + +static inline int +bus_dmamap_load_mbuf_sg(bus_dma_tag_t tag, bus_dmamap_t map, struct mbuf *m_head, bus_dma_segment_t *segs, int *pnsegs, unsigned flags) +{ +struct mbuf *m; +int n; + for ( m=m_head, n=0; m; m=m->m_next, n++ ) { + if ( n >= tag->maxsegs ) { + return EFBIG; + } + segs[n].ds_addr = CPU2BUSADDR(mtod(m, unsigned)); + segs[n].ds_len = m->m_len; + } + *pnsegs = n; + return 0; +} + +static inline bus_dma_tag_t +bus_get_dma_tag(device_t dev) +{ + return 0; +} + +typedef void bus_dmamap_callback_t (void *arg, bus_dma_segment_t *segs, int nseg, int error); + +static inline int +bus_dmamap_load(bus_dma_tag_t tag, bus_dmamap_t map, caddr_t vaddr, bus_size_t size, bus_dmamap_callback_t cb, void *arg, unsigned flags) +{ +bus_dma_segment_t segs[1]; + segs[0].ds_addr = CPU2BUSADDR(vaddr); + segs[0].ds_len = size; + cb(arg, segs, 1, 0); + return 0; +} + +typedef void bus_dmamap_callback2_t (void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, int error); + +static inline int +bus_dmamap_load_mbuf(bus_dma_tag_t tag, bus_dmamap_t map, struct mbuf *m_head, bus_dmamap_callback2_t cb, void *arg, unsigned flags) +{ +/* hopefully there's enough stack ... */ +bus_dma_segment_t segs[tag->maxsegs]; +struct mbuf *m; +int n; +bus_size_t sz; + for ( m=m_head, sz=0, n=0; m; m=m->m_next, n++ ) { + if ( n >= tag->maxsegs ) { + cb(arg, segs, n, sz, EFBIG); + return EFBIG; + } + segs[n].ds_addr = CPU2BUSADDR(mtod(m, unsigned)); + sz += (segs[n].ds_len = m->m_len); + } + cb(arg, segs, n, sz, 0); + return 0; +} + +#define bus_dmamap_unload(tag, map) do {} while (0) + +/* should we do something if we have no HW snooping ? */ +#define bus_dmamap_sync(tag, map, flags) do { membarrier_rw(); } while (0) + +#define bus_dmamap_create(tag, flags, pmap) ( *(pmap) = 0, 0 ) +#define bus_dmamap_destroy(tag, map) do {} while (0) + +#endif diff --git a/bsd_eth_drivers/libbsdport/callout.h b/bsd_eth_drivers/libbsdport/callout.h new file mode 100644 index 0000000..3f586bf --- /dev/null +++ b/bsd_eth_drivers/libbsdport/callout.h @@ -0,0 +1,43 @@ +#ifndef _SYS_CALLOUT_H +#define _SYS_CALLOUT_H /* include this to override rtems stack's */ + +/* RTEMS systm.h still declares old timout stuff which is not + * fully compatible with more recent 'callout' functionality. + * + * Also: our struct callout it incompatible with the one + * declared in rtems' sys/callout.h. + * Make sure to include the proper header (first). + */ + +typedef unsigned callout_time_t; + +struct callout { + struct callout *c_next; + struct callout **c_pprev; + void (*c_func)(void*); + void *c_arg; + struct mtx *c_mtx; + callout_time_t c_time; +}; + +/* We cannot stop a callout that's in progress */ + +void +callout_stop(struct callout *c); + +#define callout_drain callout_stop + +void +callout_reset(struct callout *c, int ticks, void (*fn)(void*), void *arg); + +void +callout_init(struct callout *c, int mpsafe); + +void +callout_init_mtx(struct callout *c, struct mtx *m, unsigned flags); + +/* Initialize callout facility [networking must have been initialized already] */ +rtems_id +rtems_callout_initialize(); + +#endif diff --git a/bsd_eth_drivers/libbsdport/contigmalloc.c b/bsd_eth_drivers/libbsdport/contigmalloc.c new file mode 100644 index 0000000..7c9f1b7 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/contigmalloc.c @@ -0,0 +1,33 @@ +#include + +#define _KERNEL +#include +#include + +void * +contigmalloc( + unsigned long size, + int type, + int flags, + unsigned long lo, + unsigned long hi, + unsigned long align, + unsigned long bound) +{ +void *ptr = rtems_bsdnet_malloc(size + sizeof(ptr) + align-1, type, flags); +char *rval = 0; + if ( ptr ) { + unsigned tmp = (unsigned)ptr + align - 1; + tmp -= tmp % align; + rval = (char*)tmp; + /* save backlink */ + *(void**)(rval+size) = ptr; + } + return rval; +} + +void +contigfree(void *ptr, size_t size, int type) +{ + rtems_bsdnet_free( *(void**)((unsigned)ptr + size), type); +} diff --git a/bsd_eth_drivers/libbsdport/devicet.c b/bsd_eth_drivers/libbsdport/devicet.c new file mode 100644 index 0000000..48ddd9a --- /dev/null +++ b/bsd_eth_drivers/libbsdport/devicet.c @@ -0,0 +1,374 @@ +#define DEVICET_EXTERN_INLINE + +#include "devicet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbsdport_api.h" + +#define DEBUG + +extern void real_libc_free(void*); + +static STAILQ_HEAD(devq_t, device) devq = STAILQ_HEAD_INITIALIZER(devq); + +static device_t +devalloc(driver_t *dr) +{ +void *m; +device_t rval; +int l = sizeof(*rval) + dr->softc_size + DEVICE_SOFTC_ALIGNMENT -1; + + if ( !(m = malloc( l, M_DEVBUF, M_NOWAIT )) ) + return 0; + + memset(m, 0, l); + + rval = (device_t)(((uintptr_t)m + (DEVICE_SOFTC_ALIGNMENT-1)) & ~(DEVICE_SOFTC_ALIGNMENT-1)); + rval->rawmem = m; + rval->type = dr->type; + rval->name = dr->name; + rval->drv = dr; + + return rval; +} + +static void +devclean(device_t dev) +{ + assert( !dev->attached ); + memset(device_get_softc(dev), 0, dev->drv->softc_size); + real_libc_free(dev->desc); + dev->desc = 0; + dev->unit = 0; + dev->nameunit[0]=0; + memset( &dev->bushdr, 0, sizeof(dev->bushdr)); +} + +static void +devfree(device_t dev) +{ + /* paranoia */ + devclean(dev); + dev->drv = 0; + free(dev->rawmem, M_DEVBUF); +} + +static int +devattach(device_t dev, int unit, struct rtems_bsdnet_ifconfig *cfg) +{ +int error; + +#ifdef DEBUG + printf("Now attaching %s%d: (0x%x:%x.%x)\n", + dev->name, unit, + dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun); +#endif + + dev->unit = unit; + dev->ifconfig = cfg; + sprintf(dev->nameunit,"%s%d",dev->drv->name,unit); + + /* Try to attach */ + if ( (error = dev->drv->methods->attach(dev)) ) { + fprintf(stderr,"Attaching '%s%d' failed: %s", dev->drv->name, unit, strerror(error)); + return error; + } + /* Successfully attached new device */ + dev->attached = 1; + cfg->name = (char*)device_get_nameunit(dev); + STAILQ_INSERT_TAIL(&devq, dev, list); + return 0; +} + +static int +devequal(device_t a, device_t b) +{ + if ( a->type != b->type ) + return 0; + switch ( a->type ) { + case DEV_TYPE_PCI: + return a->bushdr.pci.bus == b->bushdr.pci.bus + && a->bushdr.pci.dev == b->bushdr.pci.dev + && a->bushdr.pci.fun == b->bushdr.pci.fun; + + default: + rtems_panic("devequal: Unsupported device type %i\n", a->type); + } + return 0; +} + +/* Check if a particular device is already listed */ +static device_t +devattached(device_t dev) +{ +struct device *ldev; + STAILQ_FOREACH(ldev, &devq, list) { + if ( devequal(ldev, dev) ) + return ldev; + } + return 0; +} + + +int +device_printf(device_t dev, const char *fmt, ...) +{ +int rval; +va_list ap; + rval = fprintf(stdout,"%s:",device_get_nameunit(dev)); + va_start(ap, fmt); + rval += vfprintf(stdout,fmt,ap); + va_end(ap); + return rval; +} + +static uint32_t +get_pci_triple(const char *drvnam) +{ +unsigned b,d,f; + if ( drvnam && 3 == sscanf(drvnam,"%i:%i.%i",&b,&d,&f) ) + return (b<<8) | PCI_DEVFN(d,f); + return -1; +} + +static void +get_name_unit(const char *drvnam, char *nm, int *punit) +{ +int l = strlen(drvnam); +int i; + if ( l > 0 ) { + for ( i=l-1; i>=0 && isdigit(drvnam[i]); i-- ) + /* nothing else to do */; + if ( 1 != sscanf(drvnam+i,"%d",punit) ) + *punit = 0; /* wildcard */ + strncpy(nm, drvnam, i+1); + nm[i+1]=0; + } else { + /* wildcards */ + *nm = 0; + *punit = 0; + } +} + +static int +matches(driver_t *dr, const char *pat) +{ + if ( 0 == *pat || '*' == *pat ) + return 1; + return !strcmp(pat, dr->name); +} + +static int +pci_slot_empty(int b, int d, int f) +{ +uint16_t id; + pci_read_config_word(b,d,f,PCI_VENDOR_ID,&id); + return ( 0xffff == id ); +} + +static int +pci_is_ether(int b, int d, int f) +{ +uint16_t dclass; + if ( pci_slot_empty(b,d,f) ) + return 0; + pci_read_config_word(b,d,f,PCI_CLASS_DEVICE, &dclass); + return PCI_CLASS_NETWORK_ETHERNET == dclass; +} + +/* this catches the case of an unpopulated slot (returning 0) */ +static int +pci_num_functions(int b, int d) +{ +uint8_t h; + if ( pci_slot_empty(b,d,0) ) + return 0; + pci_read_config_byte(b,d,0,PCI_HEADER_TYPE,&h); + return (h & 0x80) ? PCI_MAX_FUNCTIONS : 1; /* multifunction device ? */ +} + +int +libbsdport_netdriver_dump(FILE *f) +{ +struct device *ldev; +int ndevs; +unsigned w; + + if ( !f ) + f = stdout; + + ndevs = 0; + fprintf(f, "PCI Network device information:\n"); + rtems_bsdnet_semaphore_obtain(); + STAILQ_FOREACH(ldev, &devq, list) { + /* ASSUME LIST ELEMENTS DO NOT DISAPPEAR + * so we can release the lock while printing... + */ + rtems_bsdnet_semaphore_release(); + w=fprintf(f,"%-6s -- (0x%x:%x.%x)", + device_get_nameunit(ldev), + ldev->bushdr.pci.bus, + ldev->bushdr.pci.dev, + ldev->bushdr.pci.fun); + for ( ; w < 24 ; w++) + fputc(' ',f); + if ( ldev->desc ) + fprintf(f," %s",ldev->desc); + fputc('\n',f); + + + ndevs++; + rtems_bsdnet_semaphore_obtain(); + } + rtems_bsdnet_semaphore_release(); + return ndevs; +} + +#define UNITMATCH(wanted, unit, bdfunit) \ + ((wanted) < 0 ? ((wanted) & 0xffff) == (bdfunit) : (wanted) == (unit)) + +int +libbsdport_netdriver_attach(struct rtems_bsdnet_ifconfig *cfg, int attaching) +{ +char nm[20]; /* copy of the name */ +int unit,thisunit,wantedunit; +int i,b,d,f; +int prob; +driver_t *dr; +device_t dev = 0; +device_t tmpdev; +int error = 0; +int bdfunit; + + if ( !attaching ) + return ENOTSUP; + + if ( (wantedunit = get_pci_triple(cfg->name)) < 0 ) { + get_name_unit(cfg->name, nm, &wantedunit); + } else { + wantedunit |= 1<<31; + nm[0]=0; + } +#ifdef DEBUG + printf("Wanted unit is 0x%x, pattern '%s'\n", wantedunit, nm); +#endif + + unit = 0; + for ( i=0; (dr=libbsdport_netdriver_table[i]); i++ ) { + /* Find matching driver */ +#ifdef DEBUG + printf("Trying driver '%s' ...", dr->name); +#endif + if ( matches(dr, nm) ) { +#ifdef DEBUG + printf("MATCH\n"); +#endif + + assert( dr->methods ); + + thisunit = 0; + + if ( DEV_TYPE_PCI != dr->type ) { + fprintf(stderr,"Non-PCI driver '%s' not supported; skipping\n", dr->name); + continue; + } + + dev = devalloc(dr); + for ( b=0; bbushdr.pci.bus = b; + dev->bushdr.pci.dev = d; + dev->bushdr.pci.fun = f; + + bdfunit = (b<<8) | PCI_DEVFN(d,f); + +#ifdef DEBUG + printf("Probing PCI 0x%x:%x.%x\n", + bdfunit>>8, PCI_SLOT(bdfunit), PCI_FUNC(bdfunit)); +#endif + + /* has this device been attached already ? */ + if ( (tmpdev = devattached(dev)) ) { + if ( dev->drv == tmpdev->drv ) + thisunit++; + unit++; + if ( UNITMATCH(wantedunit, unit, bdfunit) ) { + fprintf(stderr,"Device '%s' has already been attached\n", device_get_nameunit(dev)); + error = EBUSY; + goto bail; + } + } else { + switch ( ( prob = dr->methods->probe(dev) ) ) { + /* LOW_PRIORITY currently unsupported; list preferred drivers first */ + case BUS_PROBE_LOW_PRIORITY: + case BUS_PROBE_DEFAULT: + /* accepted */ + thisunit++; + unit++; + /* wanted unit == 0 means next avail. + * unit is acceptable. + */ +#ifdef DEBUG + printf("->SUCCESS\n"); +#endif + if ( 0 == wantedunit || UNITMATCH(wantedunit, unit, bdfunit) ) { + error = devattach(dev, thisunit, cfg); + if ( !error ) + dev = 0; /* is now on list */ + goto bail; + } + break; + + default: +#ifdef DEBUG + printf("->FAILED\n"); +#endif + /* probe failed */ + break; + } + } + devclean(dev); + } /* for all functions */ + } /* for all busses + slots */ + devfree(dev); dev = 0; + } /* matching driver */ +#ifdef DEBUG + else printf("NO MATCH\n"); +#endif + } /* for all drivers */ + + /* Nothing found */ + error = ENODEV; +bail: + if (dev) + devfree(dev); + return error; +} + +device_t +libbsdport_netdriver_get_dev(const char *name) +{ +struct device *ldev; + + if ( !name ) + return 0; + + rtems_bsdnet_semaphore_obtain(); + STAILQ_FOREACH(ldev, &devq, list) { + if ( !strcmp(name, device_get_nameunit(ldev)) ) + break; + } + rtems_bsdnet_semaphore_release(); + return ldev; +} diff --git a/bsd_eth_drivers/libbsdport/devicet.h b/bsd_eth_drivers/libbsdport/devicet.h new file mode 100644 index 0000000..bbb4e71 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/devicet.h @@ -0,0 +1,125 @@ +#ifndef RTEMS_NETDEV_T_DECL_H +#define RTEMS_NETDEV_T_DECL_H + +#include +#include +#include +#include + +#include + +#if RTEMS_REV_AT_LEAST(4,8,99) +#include +#else +#include +#endif + +#include + +/* se we can generate a non-inlined version somewhere */ +#ifndef DEVICET_EXTERN_INLINE +#define DEVICET_EXTERN_INLINE extern inline +#endif + +/* unused for now: */ +typedef int devclass_t; + +typedef struct device *device_t; + +typedef struct _pcidev_t { + unsigned short bus; + unsigned char dev; + unsigned char fun; +} pcidev_t; + +#define DEV_TYPE_PCI 1 + +typedef int device_probe_t (device_t); +typedef int device_attach_t (device_t); +typedef int device_detach_t (device_t); +typedef int device_resume_t (device_t); +typedef int device_suspend_t (device_t); + +typedef struct device_methods { + int (*probe ) (device_t); + int (*attach) (device_t); + void (*shutdown) (device_t); + int (*detach) (device_t); + int (*irq_check_dis) (device_t); + void (*irq_en) (device_t); +} device_method_t; + +struct driver { + const char *name; + device_method_t *methods; + int type; + int softc_size; +}; + +#define DEVICE_SOFTC_ALIGNMENT 16 + +struct device { + union { + pcidev_t pci; + } bushdr; + int type; + STAILQ_ENTRY(device) list; + const char *name; + char nameunit[16]; /* NEVER use knowledge about the size of this -- we may change it */ + int unit; + char *desc; + driver_t *drv; + int attached; + void *rawmem; /* back pointer */ + struct rtems_bsdnet_ifconfig *ifconfig; + char softc[] __attribute__ ((aligned(DEVICE_SOFTC_ALIGNMENT), may_alias)); + /* a pointer to back to the device is installed past the 'softc' */ +}; + +static inline device_t +rtems_softc2dev(void *softc) +{ +uintptr_t diff = (uintptr_t)&((device_t)(0))->softc - (uintptr_t)(device_t)(0); + return (device_t)((uintptr_t)softc - diff); +} + +#define device_set_desc_copy(dev, nm) \ + do { real_libc_free((dev)->desc); (dev)->desc = strdup((nm)); } while (0) + +#define device_set_desc(dev, nm) device_set_desc_copy(dev, nm) + +static inline const char * +device_get_nameunit(device_t dev) +{ + return dev->nameunit; +} + +static inline const char * +device_get_name(device_t dev) +{ + return dev->name; +} + +static inline int +device_get_unit(device_t dev) +{ + return dev->unit; +} + +DEVICET_EXTERN_INLINE void * +device_get_softc(device_t dev) +{ + return dev->softc; +} + +#define device_delete_child(dev,bus) do {} while (0) + +static inline int +device_is_attached(device_t dev) +{ + return dev->attached; +} + +int device_printf(device_t dev, const char *fmt, ...); + +#endif diff --git a/bsd_eth_drivers/libbsdport/ifmedia.c b/bsd_eth_drivers/libbsdport/ifmedia.c new file mode 100644 index 0000000..77c3bd3 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/ifmedia.c @@ -0,0 +1,91 @@ +#include +#define _KERNEL +#include +#include + +#include + +#if RTEMS_REV_AT_LEAST(4,8,99) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +void +ifmedia_init(struct ifmedia *ifm, int dontcare_mask, + ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) +{ + ifm->ifm_mask = dontcare_mask; + ifm->ifm_media = 0; + ifm->ifm_cur = 0; + ifm->ifm_list.lh_first = NULL; + ifm->ifm_change = change_callback; + ifm->ifm_status = status_callback; +} + +void +ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) +{ +struct ifmedia_entry *ifmen, *ifmep, *ifme; + if ( ( ifme = malloc(sizeof(*ifme), M_DEVBUF, M_NOWAIT) ) ) { + ifme->ifm_media = mword; + ifme->ifm_data = data; + ifme->ifm_aux = aux; + for ( ifmep = LIST_FIRST(&ifm->ifm_list); ifmep; ifmep = ifmen ) { + if ( !(ifmen = LIST_NEXT(ifmep, ifm_list)) ) + break; + } + if ( ifmep ) + LIST_INSERT_AFTER(ifmep, ifme, ifm_list); + else + LIST_INSERT_HEAD( &ifm->ifm_list, ifme, ifm_list); + } +} + +int +ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, u_long cmd) +{ +int rval = 0; +struct ifmediareq ifmr; + if ( SIOCGIFMEDIA == cmd ) { + if ( !ifm->ifm_status ) + return ENOTSUP; + ifm->ifm_status(ifp, &ifmr); + if ( ! (IFM_AVALID & ifmr.ifm_status) ) + return EINVAL; + /* translate */ + ifr->ifr_media = ifmr.ifm_active; + if ( IFM_ACTIVE & ifmr.ifm_status ) + ifr->ifr_media |= IFM_LINK_OK; + /* no way to determine if autoneg is forcefully disabled + * from ifmr :-( + * Look at current ifm_media for now. + */ + if ( IFM_SUBTYPE(ifm->ifm_media) != IFM_AUTO ) + ifr->ifr_media |= IFM_ANEG_DIS; + } else { + if ( !ifm->ifm_change ) + return ENOTSUP; + ifm->ifm_media = ifr->ifr_media; + rval = ifm->ifm_change(ifp); + } + return rval; +} + +void +ifmedia_set(struct ifmedia *ifm, int mword) +{ + ifm->ifm_media = mword; + /* cannot invoke the ifm_change callback because we have + * no ifp here. + */ +} + diff --git a/bsd_eth_drivers/libbsdport/ifstuff.c b/bsd_eth_drivers/libbsdport/ifstuff.c new file mode 100644 index 0000000..40e525e --- /dev/null +++ b/bsd_eth_drivers/libbsdport/ifstuff.c @@ -0,0 +1,79 @@ + +#include "libbsdport.h" + +#define _KERNEL +#include +#include +#include +#include +#include +#include +#include + +static struct ifnet *rtems_bsdnet_if_freelist = 0; + +struct ifnet * +if_alloc(int type) +{ +struct ifnet *rval = 0; + if ( IFT_ETHER == type ) { + /* Hack with freelist allows for debugging drivers as modules */ + if ( (rval = rtems_bsdnet_if_freelist) ) { + /* use softc pointer to link free list */ + rtems_bsdnet_if_freelist = rtems_bsdnet_if_freelist->if_softc; + } + if ( (rval = malloc(sizeof(struct arpcom), M_DEVBUF, M_WAIT)) ) + memset(rval, 0, sizeof(struct arpcom)); + } + return rval; +} + +void +if_free(struct ifnet *ifp) +{ + /* save on free-list so subsequent alloc gets old + * interface back (which is still on the bsdnet stack's list + * of known interfaces. The old rtems stack doesn't provide + * means to remove an interface once it has been attached. + * This hack allows for detaching a *driver* and reattaching + * it to the same interface later (good for development/debugging). + */ + ifp->if_softc = rtems_bsdnet_if_freelist; + rtems_bsdnet_if_freelist = ifp; +} + +/* Ugly hack to allow unloading/reloading the driver core. + * Needed because rtems' bsdnet release doesn't implement + * if_detach(). Therefore, we bring the interface down but + * keep the device record alive... + */ +void +ether_ifdetach(struct ifnet *ifp) +{ + (ifp)->if_flags = 0; + (ifp)->if_ioctl = 0; + (ifp)->if_start = 0; + (ifp)->if_watchdog = 0; + (ifp)->if_init = 0; +} + + +/* copy ethernet addr into arpcom if nothing is set yet */ +void +ether_setaddr(struct ifnet *ifp, u_int8_t *eaddr) +{ +int i; +device_t dev = rtems_softc2dev(ifp->if_softc); + /* If LLADDR has already been set, then use it */ + for ( i=0; i< ETHER_ADDR_LEN; i++ ) { + if ( IF_LLADDR(ifp)[i] ) + break; + } + if ( i >= ETHER_ADDR_LEN ) { + /* not set; see if the ifconfig struct provides one */ + if ( dev->ifconfig && dev->ifconfig->hardware_address ) + memcpy(IF_LLADDR(ifp), dev->ifconfig->hardware_address, ETHER_ADDR_LEN); + else + memcpy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); + } +} diff --git a/bsd_eth_drivers/libbsdport/libbsdport.h b/bsd_eth_drivers/libbsdport/libbsdport.h new file mode 100644 index 0000000..8b88784 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/libbsdport.h @@ -0,0 +1,380 @@ +#ifndef RTEMS_COMPAT_DEFS_H +#define RTEMS_COMPAT_DEFS_H + +#include +#include + +#define _KERNEL +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +/* +#include +*/ + + + +#include + +#ifndef bswap32 +#define bswap32(_x) CPU_swap_u32(_x) +#endif + +#if defined(__LITTLE_ENDIAN__) || defined(__i386__) +static inline uint16_t htole16(uint16_t v) { return v; } +static inline uint32_t htole32(uint32_t v) { return v; } +static inline uint64_t htole64(uint64_t v) { return v; } +static inline uint16_t le16toh(uint16_t v) { return v; } +static inline uint32_t le32toh(uint32_t v) { return v; } +static inline uint64_t le64toh(uint64_t v) { return v; } + +#ifdef __i386__ + +#ifdef __SSE__ +static inline void membarrier_r() { asm volatile("lfence":::"memory"); } +static inline void membarrier_rw() { asm volatile("mfence":::"memory"); } +/* Current x86 CPUs always do in-order stores - prevent the compiler from reordering, neverthelesss */ +static inline void membarrier_w() { asm volatile(/*"sfence"*/"":::"memory"); } +#else +static inline void membarrier_r() { asm volatile("lock; addl $0,0(%%esp)":::"memory"); } +static inline void membarrier_rw() { asm volatile("lock; addl $0,0(%%esp)":::"memory"); } +/* Current x86 CPUs always do in-order stores - prevent the compiler from reordering, neverthelesss */ +static inline void membarrier_w() { asm volatile(/*"lock; addl $0,0(%%esp)"*/"":::"memory"); } +#endif + +#endif + +#elif defined(__BIG_ENDIAN__) +#ifdef __PPC__ +#include + +/* Note the 'may_alias' constructs. They are + * a safeguard agains the alias rule should the + * pointer argument of st_leXX change (again) in + * the future (and it should be safe to use older + * versions of 'byteorder.h' + */ + +static inline uint16_t +htole16(uint16_t v) +{ +uint16_t rval __attribute__((may_alias)); + st_le16((volatile uint16_t*)&rval,v); + return rval; +} + +static inline uint16_t +le16toh(uint16_t v) +{ +uint16_t vv __attribute__((may_alias)) = v; + return ld_le16((volatile uint16_t*)&vv); +} + +static inline uint32_t +htole32(uint32_t v) +{ +uint32_t rval __attribute__((may_alias)); + st_le32((volatile libbsdport_u32_t*)&rval,v); + return rval; +} + +static inline uint32_t +le32toh(uint32_t v) +{ +uint32_t vv __attribute__((may_alias)) = v; + return ld_le32((volatile libbsdport_u32_t*)&vv); +} + +/* Compiler generated floating point instructions for this + * and rtems_bsdnet_newproc()-generated tasks are non-FP + * :-( + */ +static inline uint64_t +htole64(uint64_t v) +{ +union { + libbsdport_u32_t tmp[2] __attribute__((may_alias)); + uint64_t rval __attribute__((may_alias)); +} u; + + st_le32( &u.tmp[0], (unsigned)(v&0xffffffff) ); + st_le32( &u.tmp[1], (unsigned)((v>>32)&0xffffffff) ); + + return u.rval; +} + +static inline void membarrier_r() { asm volatile("sync":::"memory"); } + +static inline void membarrier_rw() { asm volatile("sync":::"memory"); } + +static inline void membarrier_w() { asm volatile("eieio":::"memory"); } + +#else +#error "need htoleXX() implementation for this CPU arch" +#endif + +#else +#error "Unknown CPU endianness" +#endif + +#include +#include + +#ifndef PCIR_BAR +#define PCIR_BAR(x) (0x10+4*(x)) +#endif + +#ifndef PCIR_COMMAND +#define PCIR_COMMAND PCI_COMMAND +#endif + +#ifndef PCIR_REVID +#define PCIR_REVID PCI_REVISION_ID +#endif + +#ifndef PCIR_SUBVEND_0 +#define PCIR_SUBVEND_0 PCI_SUBSYSTEM_VENDOR_ID +#endif + +#ifndef PCIR_SUBDEV_0 +#define PCIR_SUBDEV_0 PCI_SUBSYSTEM_ID +#endif + +#ifndef PCIR_CIS +#define PCIR_CIS PCI_CARDBUS_CIS +#endif + +#ifndef PCIM_CMD_BUSMASTEREN +#define PCIM_CMD_BUSMASTEREN PCI_COMMAND_MASTER +#endif + +#ifndef PCIM_CMD_MEMEN +#define PCIM_CMD_MEMEN PCI_COMMAND_MEMORY +#endif + +#ifndef PCIM_CMD_PORTEN +#define PCIM_CMD_PORTEN PCI_COMMAND_IO +#endif + +#ifndef PCIR_CAP_PTR +#define PCIR_CAP_PTR 0x34 +#endif + +#ifndef PCIR_POWER_STATUS +#define PCIR_POWER_STATUS 0x4 +#endif + +#ifndef PCIM_PSTAT_PME +#define PCIM_PSTAT_PME 0x8000 +#endif + +#ifndef PCIM_PSTAT_PMEENABLE +#define PCIM_PSTAT_PMEENABLE 0x0100 +#endif + +#ifndef PCIY_PMG +#define PCIY_PMG 0x01 +#endif + +#ifndef PCI_RF_DENSE +#define PCI_RF_DENSE 0 +#endif + +static inline uint32_t +pci_read_config(device_t dev, unsigned reg, int width) +{ + switch ( width ) { + default: + case 4: + { + libbsdport_u32_t v; + pci_read_config_dword(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, &v); + return v; + } + case 2: + { + uint16_t v; + pci_read_config_word(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, &v); + return (uint32_t)v; + } + case 1: + { + uint8_t v; + pci_read_config_byte(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, &v); + return (uint32_t)v; + } + } +} + +static inline void +pci_write_config(device_t dev, unsigned reg, uint32_t val, int width) +{ + switch ( width ) { + default: + case 4: + { + pci_write_config_dword(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, val); + } + case 2: + { + pci_write_config_word(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, val); + } + case 1: + { + pci_write_config_byte(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, val); + } + } +} + + +static inline uint16_t +pci_get_vendor(device_t dev) +{ + return pci_read_config(dev, PCI_VENDOR_ID, 2); +} + +static inline uint16_t +pci_get_device(device_t dev) +{ + return pci_read_config(dev, PCI_DEVICE_ID, 2); +} + +static inline uint16_t +pci_get_subvendor(device_t dev) +{ + return pci_read_config(dev, PCIR_SUBVEND_0, 2); +} + +static inline uint16_t +pci_get_subdevice(device_t dev) +{ + return pci_read_config(dev, PCIR_SUBDEV_0, 2); +} + +static inline void +pci_enable_busmaster(device_t dev) +{ + pci_write_config( + dev, + PCI_COMMAND, + pci_read_config(dev, PCI_COMMAND, 2) | PCI_COMMAND_MASTER, + 2 + ); +} + +static inline void +pci_enable_io(device_t dev, int space) +{ + pci_write_config( + dev, + PCI_COMMAND, + pci_read_config(dev, PCI_COMMAND, 2) | space, + 2 + ); +} + +static inline void +pci_disable_io(device_t dev, int space) +{ + pci_write_config( + dev, + PCI_COMMAND, + pci_read_config(dev, PCI_COMMAND, 2) & ~space, + 2 + ); +} + + + +/* MSI / MSIX not supported */ +static inline int +pci_msi_count(device_t dev) { return 0; } + +static inline int +pci_alloc_msi(device_t dev, int *pval) { return -1; } + +static inline int +pci_alloc_msix(device_t dev, int *pval) { return -1; } + +static inline void +pci_release_msi(device_t dev) { } + + + + +#define IFQ_DRV_IS_EMPTY(q) (0 == (q)->ifq_head) +#define IFQ_DRV_DEQUEUE(q,m) IF_DEQUEUE((q),(m)) +#define IFQ_DRV_PREPEND(q,m) IF_PREPEND((q),(m)) + +#define ifq_drv_maxlen ifq_maxlen +#define IFQ_SET_MAXLEN(q, len) do {} while (0) +#define IFQ_SET_READY(q) do {} while (0) + +#define ETHER_BPF_MTAP(ifp, m) do {} while (0) +#define BPF_MTAP(ifp, m) do {} while (0) + +#define IF_LLADDR(ifp) (((struct arpcom *)(ifp))->ac_enaddr) + +#define if_link_state_change(ifp, state) do {} while (0) + +/* if_name should probably be const char * but isn't */ +#define if_initname(ifp, name, unit) \ + do { (ifp)->if_name = (char*)(name); (ifp)->if_unit = (unit); } while (0) + +struct ifnet * if_alloc(int type); + +void if_free(struct ifnet *ifp); + +#define if_printf(ifp,args...) do { printf("%s: ",(ifp)->if_name); printf(args); } while (0) + +void * +contigmalloc( + unsigned long size, + int type, + int flags, + unsigned long lo, + unsigned long hi, + unsigned long align, + unsigned long bound); + +void +contigfree(void *ptr, size_t size, int type); + +/* locking is handled by 'super-lock' outside driver; watch for link intr task, though */ +#define NET_LOCK_GIANT() do {} while (0) +#define NET_UNLOCK_GIANT() do {} while (0) + +#define KASSERT(cond, msg...) \ + do { \ + if ( ! (cond) ) { \ + rtems_panic msg; \ + } \ + } while (0) + +#define __FBSDID(x) +#define MODULE_DEPEND(x1,x2,x3,x4,x5) + +#define mii_mediachg(mii) do {} while (0) + +void * +real_libc_malloc(size_t); + +void +real_libc_free(void*); + +#endif diff --git a/bsd_eth_drivers/libbsdport/libbsdport_api.h b/bsd_eth_drivers/libbsdport/libbsdport_api.h new file mode 100644 index 0000000..ba3e54e --- /dev/null +++ b/bsd_eth_drivers/libbsdport/libbsdport_api.h @@ -0,0 +1,84 @@ +#ifndef LIBBSDPORT_API_H +#define LIBBSDPORT_API_H + +#include +#include + +/* $Id$ */ + +/* User API to libbsdport driver attach function, driver table etc. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct driver driver_t; + +/* NULL terminated list of all drivers configured into the system. + * To be defined by the application. + */ +extern driver_t *libbsdport_netdriver_table[]; + +/* Drivers ported so far: */ +/* Intel E1000 chips */ +extern driver_t libbsdport_em_driver; +/* AMD 79C971..976 pcnet PCI */ +extern driver_t libbsdport_pcn_driver; +/* RealTek RTL8139, 8168, 8169, 8169S, 8110, 8101E, and 8111 PCI */ +extern driver_t libbsdport_re_driver; +/* AMD/Lance older (and later) chips; this driver also supports what 'pcn' + * does but might not be as efficient. + * NOTE: The 'le_pci' driver works with the pcnet32 (79C970A) emulation + * of qemu. + */ +extern driver_t libbsdport_le_pci_driver; + + +/* Generic driver attach function (can be used in rtems_bsdnet_ifconfig). + * This routine selects a driver/device combination based on + * - drivers available / listed in libbsdport_netdriver_table[]; + * - devices detected in PCI config space compatible with a listed + * driver. + * - name and unit specified in the rtems_bsdnet_ifconfig.name field + * (empty name: "" is a wildcard). + * + * E.g. assume that + * 1) the 'em' and 'pcn' drivers are listed in the table. + * 2) a AMD Am79C973 chip is somewhere on the PCI bus + * 3) ifconfig name is "" + * -> the 'pcn' driver is selected and the only AMD chip present + * is used as 'pcn1'. + * -> If the name was 'em' or 'pcn2' no device would be found + * (no em device found; no 2nd pcn device found). + * + * Now assume that you have a 82544 and two AMD 79C973 devices: + * + * name: "" picks the first of the three chips found in PCI space + * name: "pcn" picks the first AMD chip found + * name: "em" picks the first (and only) 82544 + * name: "pcn2" picks the second AMD chip + * + * Also, it is possible to specify a PCI-triple: :. + * i.e., name: "2:3.0" tries to find a driver that supports the device + * at bus #2, slot #2. + * + * NOTE: detaching a driver is not supported (since rtems bsdnet cannot detach + * an interface). + */ +int +libbsdport_netdriver_attach(struct rtems_bsdnet_ifconfig *cfg, int attaching); + +/* Print information about all attached drivers to FILE (stdout if NULL) + * + * RETURNS: number of devices attached so far. + * + * BUGS: more info should be printed. + */ +int +libbsdport_netdriver_dump(FILE *f); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsd_eth_drivers/libbsdport/libbsdport_post.h b/bsd_eth_drivers/libbsdport/libbsdport_post.h new file mode 100644 index 0000000..fcf7e2d --- /dev/null +++ b/bsd_eth_drivers/libbsdport/libbsdport_post.h @@ -0,0 +1,95 @@ +/* This file is to be included from 'if_xxx.c' AFTER all other + * includes so we can override some rtems-bsdnet things... + */ +#if 1 +/* These are defined in sys/sysctl.h and we + * could one day add support ... + */ +#undef SYSCTL_ADD_PROC +#define SYSCTL_ADD_PROC(unused...) do { } while (0) + +#undef SYSCTL_ADD_INT +#define SYSCTL_ADD_INT(unused...) do { } while (0) +#endif + +#include +#include + +/* include after & friends */ +#include + +#define if_drv_flags if_flags +#define IFF_DRV_RUNNING IFF_RUNNING +#define IFF_DRV_OACTIVE IFF_OACTIVE + +/* FIXME: should implement m_defrag() */ +#define m_defrag(m_headp, opt) NULL + +static inline struct mbuf * +m_getcl(int how, int type, unsigned flags) +{ +struct mbuf *mp = 0; + if ( ! (flags & M_PKTHDR) ) { + printk("m_getcl: DUNNO WHAT TO DO HERE\n"); + return 0; + } + MGETHDR(mp, M_DONTWAIT, MT_DATA); + if ( mp ) { + MCLGET( mp, M_DONTWAIT ); + if ( !(mp->m_flags & M_EXT) ) { + m_freem(mp); + mp = 0; + } + } + return mp; +} + +static inline void +ether_input_skipping(struct ifnet *ifp, struct mbuf *m) +{ struct ether_header *eh; + eh = mtod(m, struct ether_header*); +#if 1 + m_adj(m, sizeof(struct ether_header)); +#else + /* faster hack */ + m->m_data += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_pkthdr.len -= sizeof(struct ether_header); +#endif + /* some drivers don't set this */ + m->m_pkthdr.rcvif = ifp; + ether_input(ifp, eh, m); +} + +void +ether_setaddr(struct ifnet *ifp, u_int8_t *eaddr); + +#define ether_ifattach(ifp, eaddr) \ + do { \ + (ifp)->if_output = ether_output; \ + if ( !(ifp)->if_addrlist ) { \ + /* reattach hack; do this only the first time -- detaching is not implemented, however!! */ \ + ether_setaddr(ifp, eaddr); \ + if_attach(ifp); \ + ether_ifattach(ifp); \ + } \ + } while (0) + + +/* Not 100% sure this is correct */ +#define M_MOVE_PKTHDR(to, from) \ + do { \ + (to)->m_flags = ((from)->m_flags & M_COPYFLAGS) | ((to)->m_flags & M_EXT); \ + if (((to)->m_flags & M_EXT) == 0) \ + (to)->m_data = (to)->m_pktdat; \ + (to)->m_pkthdr = (from)->m_pkthdr; \ + (from)->m_flags &= ~M_PKTHDR; \ + } while (0) + +#define ETHER_SIOCMULTIFRAG(e, c, ifr, ifp) \ + ( ENETRESET != (e = (SIOCADDMULTI == (c) ? \ + ether_addmulti((ifr), (struct arpcom*)(ifp)) : \ + ether_delmulti((ifr), (struct arpcom*)(ifp)) ))) \ + +#define arp_ifinit(ifp, ifa) arp_ifinit((struct arpcom *)ifp, ifa) + diff --git a/bsd_eth_drivers/libbsdport/malloc.c b/bsd_eth_drivers/libbsdport/malloc.c new file mode 100644 index 0000000..116b0e5 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/malloc.c @@ -0,0 +1,17 @@ +#include + +/* sometimes we want the original versions, + * not malloc/free shadowed by rtems' bsdnet port + */ + +void * +real_libc_malloc(size_t s) +{ + return malloc(s); +} + +void +real_libc_free(void *p) +{ + free(p); +} diff --git a/bsd_eth_drivers/libbsdport/mutex.h b/bsd_eth_drivers/libbsdport/mutex.h new file mode 100644 index 0000000..5476e37 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/mutex.h @@ -0,0 +1,55 @@ +#ifndef _RTEMS_BSDNET_MUTEX_H +#define _RTEMS_BSDNET_MUTEX_H + +/* NOTE: mutexes should never be necessary since + * the RTEMS BSD code protects everything with a + * big fat lock + */ + +struct mtx { + rtems_id mtx_id; +}; + +#define MTX_DEF 0 /* default sleeping lock */ +#define MTX_RECURSE 4 /* nesting */ + +#define MTX_NETWORK_LOCK "xxx" + +static inline void +mtx_init(struct mtx *m, const char *name, const char *type, int opts) +{ + /* Set ID to zero in case they want to submit this mutex + * to callout_init_mtx() + */ + m->mtx_id = 0; +} + +static inline int +mtx_initialized(struct mtx *m) +{ + return m->mtx_id == 0; +} + +static inline void +mtx_lock(struct mtx *m) +{ +} + +static inline void +mtx_unlock(struct mtx *m) +{ +} + +static inline void +mtx_destroy(struct mtx *m) +{ +} + +/* what ? */ +#define MA_OWNED 1 +static inline void +mtx_assert(struct mtx *m, int what) +{ +} + +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_callout.c b/bsd_eth_drivers/libbsdport/rtems_callout.c new file mode 100644 index 0000000..ce9a477 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_callout.c @@ -0,0 +1,250 @@ +#include +#include +#include + +#include +#include + +#include "mutex.h" +#include "callout.h" + +#define STATIC static + +/* Implementation modelled after + * + * "Redesign the BSD Callout and Timer Facilities" + * Adam M. Costello, George Varghese + * Dpt. of Computer Science, Washington University, 1995. + */ + +#include + +/* rely on networking semaphore for now */ +#define LIST_KEY_DECL(k) +#define LIST_LOCK(k) do {} while (0) +#define LIST_UNLOCK(k) do {} while (0) + +#define WHEELBITS 5 + +#define WHEELMASK ((1<<(WHEELBITS))-1) + +#define CALLOUT_EVENT RTEMS_EVENT_1 +#define KILL_EVENT RTEMS_EVENT_2 + + +typedef void (*timeout_t)(void*); + +STATIC volatile callout_time_t hard_ticks = 0; +STATIC volatile callout_time_t soft_ticks = 0; + +STATIC struct callout *c_wheel[1<c_pprev == 0 && c->c_next == 0 ); + if ( (c->c_next = *where) ) + (*where)->c_pprev = &c->c_next; + c->c_pprev = where; + *where = c; +} + +static inline void +c_deq(struct callout *c) +{ +struct callout *n; + assert( c->c_pprev ); + if ( (n = *c->c_pprev = c->c_next) ) + n->c_pprev = c->c_pprev; + c->c_next = 0; + c->c_pprev = 0; +} + +static inline void +softclock() +{ +struct callout *c, *n; +rtems_interrupt_level k1; +callout_time_t st,ht; +LIST_KEY_DECL(k); + + /* I believe this is free of a race condition (softclock + * and hardclock both update volatile 'soft_ticks' variable): + * a) 'hardclock' runs at IRQ level and is atomic + * b) inside while loop 'soft_ticks' is != 'hard_ticks' + * c) hardclock only modifies soft_ticks if 'soft_ticks'=='hard_ticks' + * hence this could only happen just after the update of 'soft_ticks' + * at the end of the while loop completes. + */ + + while ( 1 ) { + /* Must atomically read 'soft_ticks' and 'hard_ticks' -- otherwise, + * hardclock might update both but we get one old and one new value + */ + rtems_interrupt_disable(k1); + st = soft_ticks; + ht = hard_ticks; + rtems_interrupt_enable(k1); + if ( st == ht ) + break; /* caught up */ + + /* at this point, we know that st != ht and therefore, + * hardclock will only increment hard_ticks but leave + * soft_ticks alone. + */ + + st++; + + LIST_LOCK(k); + for ( c = c_wheel[ st & WHEELMASK ]; c; c=n ) { + n = c->c_next; + if ( c->c_time <= 0 ) { + /* this one expired */ + c_deq(c); + if ( c->c_func ) + c->c_func(c->c_arg); + } else { + c->c_time--; + } + } + LIST_UNLOCK(k); + soft_ticks = st; + /* here, soft_ticks could have caught up and + * a hardclock occurring here could also + * update soft_ticks. + */ + } +} + +static inline void +hardclock(rtems_id tid) +{ + if ( hard_ticks++ == soft_ticks && !c_wheel[hard_ticks & WHEELMASK] ) { + /* nothing to do */ + soft_ticks++; + } else { + rtems_event_send(tid, CALLOUT_EVENT); + } +} + +static void +calloutTick(rtems_id myself, void *arg) +{ +rtems_id tid = (rtems_id)arg; + + hardclock(tid); + + rtems_timer_fire_after(myself, 1, calloutTick, arg); +} + +static void +calloutTask(void *arg) +{ +rtems_event_set ev; +rtems_status_code sc; +rtems_id ticker = 0; +rtems_id me; + + sc = rtems_timer_create(rtems_build_name('b','s','d','c'), &ticker); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "Creation of timer failed\n"); + goto bail; + } + rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &me); + + rtems_timer_fire_after(ticker, 1, calloutTick, (void*)me); + + while ( 1 ) { + sc = rtems_bsdnet_event_receive (CALLOUT_EVENT | KILL_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &ev); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "calloutTask: unable to receive event; terminating\n"); + break; + } + if ( ev & KILL_EVENT ) { + break; + } + softclock(); + } +bail: + rtems_timer_delete(ticker); + rtems_task_delete(RTEMS_SELF); +} + + +/* We cannot stop a callout that's in progress */ + +void +callout_stop(struct callout *c) +{ +LIST_KEY_DECL(k); + + if ( !c->c_pprev ) + return; /* not currently on a list */ + + LIST_LOCK(k); + /* remove from list */ + c_deq(c); + LIST_UNLOCK(k); +} + + +void +callout_reset(struct callout *c, int ticks, timeout_t fn, void *arg) +{ +LIST_KEY_DECL(k); +int i; + + if ( ticks <= 0 ) + ticks = 1; + + callout_stop(c); + + c->c_func = fn; + c->c_arg = arg; + + LIST_LOCK(k); + i = (hard_ticks + ticks) & WHEELMASK; + c->c_time = ticks >> WHEELBITS; + + /* enqueue */ + c_enq(&c_wheel[i], c); + + LIST_UNLOCK(k); +} + +static rtems_id callout_tid = 0; + +void +callout_init(struct callout *c, int mpsafe) +{ + /* non thread-safe lazy init in case nobody cared to do it ... */ + if ( !callout_tid ) + rtems_callout_initialize(); + memset(c,0,sizeof(*c)); +} + +void +callout_init_mtx(struct callout *c, struct mtx *m, unsigned flags) +{ + if ( m->mtx_id ) + rtems_panic("callout_init_mtx: using mutex not supported\n"); + callout_init(c,0); + c->c_mtx = m; +} + +rtems_id +rtems_callout_initialize() +{ + if ( !callout_tid ) + callout_tid=rtems_bsdnet_newproc ("cout", 4096, calloutTask, NULL); + return callout_tid; +} + +#ifdef DEBUG +void +_cexpModuleInitialize(void*u) +{ + rtems_bsdnet_initialize_network(); + rtems_callout_initialize(); +} +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_taskqueue.c b/bsd_eth_drivers/libbsdport/rtems_taskqueue.c new file mode 100644 index 0000000..97153ca --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_taskqueue.c @@ -0,0 +1,308 @@ +#include +#include + +#include +#include + +#include "taskqueue.h" + +/* +#define STATIC static +*/ +#undef DEBUG + +#ifdef DEBUG +#include +#ifndef STATIC +#define STATIC +#endif +#else +#ifndef STATIC +#define STATIC static +#endif +#endif + +#define TQ_WAKE_EVENT RTEMS_EVENT_0 + +/* This implementation is extremely simple; we assume + * that all taskqueues (and as a matter of fact there is + * only a single one) are manipulated with the rtems + * bsdnet semaphore held. I.e., + * taskqueue_enqueue() + * taskqueue_drain() + * etc. + * are called from an environment that holds the + * bsdnet semaphore. + * Likewise, the thread that works the taskqueue + * holds the semaphore while doing so. + * + */ + +/* use single-linked list; 'drain' which would benefit from + * double-linked list is seldom used and performance doesn't + * matter much there. OTOH, the frequent case of working + * the list + enqueueing is more efficient for the single-linked + * list. +struct task { + struct task *ta_next; + int ta_pending; + int ta_priority; + task_fn ta_fn; + void *ta_fn_arg; +}; + */ + +struct taskqueue { + struct task anchor; + struct task *tail; + tq_enq_fn enq_fn; + void *enq_fn_arg; + rtems_id tid; +}; + + +STATIC struct taskqueue the_taskqueue = { + { 0, 0, 0, 0, 0 }, + &the_taskqueue.anchor, + taskqueue_thread_enqueue, + &taskqueue_fast, + 0 +}; + +struct taskqueue *taskqueue_fast = &the_taskqueue; + +struct taskqueue * +taskqueue_create(const char *name, int mflags, tq_enq_fn enq_fn, void *arg) +{ + if ( enq_fn != taskqueue_thread_enqueue ) + rtems_panic("rtems_taskqueue: attempt to create non-standard TQ; implementation needs to be modified\n"); + return &the_taskqueue; +} + +struct taskqueue * +taskqueue_create_fast(const char *name, int mflags, tq_enq_fn enq_fn, void *arg) +{ + return taskqueue_create(name, mflags, enq_fn, arg); +} + +/* taskqueue_enqueue must be allowed from an ISR; + * hence, all critical list manipulation must lock out + * interrupts... + */ +int +taskqueue_enqueue(struct taskqueue *tq, struct task *ta) +{ +rtems_interrupt_level l; + +rtems_interrupt_disable(l); + if ( 0 == ta->ta_pending ++ ) { + /* hook into list */ + ta->ta_next = 0; + tq->tail->ta_next = ta; + tq->tail = ta; + } + tq->enq_fn(tq->enq_fn_arg); +rtems_interrupt_enable(l); + return 0; +} + +void +taskqueue_thread_enqueue(void *ctxt) +{ +int dopost; +/* pointer-to-pointer is what bsd provides; we currently + * follow the scheme even we don't directly use the argument + * passed to taskqueue_create... + */ +struct taskqueue *tq = *(struct taskqueue **)ctxt; + /* If this is the first entry on the list then the + * task needs to be notified... + */ + dopost = ( tq->anchor.ta_next == tq->tail && 1 == tq->tail->ta_pending ); + + if ( dopost ) + rtems_event_send(tq->tid, TQ_WAKE_EVENT); +} + +/* Returns 0 on success */ +int +taskqueue_start_threads(struct taskqueue **ptq, int count, int prio, const char *fmt, ...) +{ + if ( count != 1 ) + rtems_panic("rtems_taskqueue: taskqueue_start_threads cannot currently deal with count != 1\n"); + + /* Do (non thread-safe) lazy init as a fallback */ + if ( ! the_taskqueue.tid ) + rtems_taskqueue_initialize(); + return 0; +} + +void +taskqueue_drain(struct taskqueue *tq, struct task *ta) +{ +rtems_interrupt_level l; +struct task *p, *q; +int i; + + /* find predecessor; searching the list should be + * safe; an ISR might append a new record to the tail + * while we are working but that should be OK. + */ + for ( p = &tq->anchor; (q = p->ta_next); p=q ) { + if ( q == ta ) { + rtems_interrupt_disable(l); + /* found; do work */ + /* remember 'pending' count and extract */ + i = ta->ta_pending; + ta->ta_pending = 0; + p->ta_next = ta->ta_next; + ta->ta_next = 0; + /* adjust tail */ + if ( tq->tail == q ) + tq->tail = p; + rtems_interrupt_enable(l); + for ( ; i>0; i-- ) { + ta->ta_fn(ta->ta_fn_arg, i); + } + return; + } + } +} + +/* work the task queue and return + * nonzero if the list is not empty + * (which means that some callback has + * rescheduled itself) + */ +static void * +taskqueue_work(struct taskqueue *tq) +{ +rtems_interrupt_level l; +struct task *p, *q; +task_fn f; +void *arg; +int i; + +/* work off a temporary list in case any callback reschedules + * itself or if new tasks are queued from an ISR. + */ +rtems_interrupt_disable(l); + p = tq->anchor.ta_next; + + tq->anchor.ta_next = 0; + tq->tail = &tq->anchor; +rtems_interrupt_enable(l); + + while ( (q=p) ) { + rtems_interrupt_disable(l); + i = q->ta_pending; + q->ta_pending = 0; + /* extract */ + p = q->ta_next; + q->ta_next = 0; + f = q->ta_fn; + arg = q->ta_fn_arg; + rtems_interrupt_enable(l); + for ( ; i>0; i-- ) { + f(arg, i); + } + } + return tq->anchor.ta_next; +} + +void +taskqueue_free(struct taskqueue *tq) +{ + taskqueue_work(tq); +} + +static void +taskqueueDoWork(void *arg) +{ +struct taskqueue *tq = arg; +rtems_event_set evs; +rtems_status_code sc; + while ( 1 ) { + sc = rtems_bsdnet_event_receive(TQ_WAKE_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &evs); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,"rtems_taskqueue: taskqueueDoWork() unable to receive wakup event\n"); + rtems_panic("Can't proceed\n"); + } + if ( taskqueue_work(tq) ) { +#if 0 + /* chance to reschedule */ + rtems_bsdnet_semaphore_release(); + rtems_task_wake_after(0); + rtems_bsdnet_semaphore_obtain(); +#else + /* hopefully, releasing the semaphore (as part of bsdnet_event_receive) + * and obtaining the event (which has been posted already) + * yields the CPU if necessary... + */ +#endif + } + } +} + +#ifdef DEBUG +struct task_dbg { + struct task t; + char *nm; +}; + +struct task_dbg taskA = { + {0}, + "taskA" +}; + +struct task_dbg taskB = { + {0}, + "taskB" +}; + +struct task_dbg taskC = { + {0}, + "taskC" +}; + +static void the_task_fn(void *arg, int pending) +{ +struct task_dbg *td = arg; + printf("%s (pending: %i)\n", td->nm, pending); + /* Test rescheduling */ + if ( pending > 3 ) + taskqueue_enqueue(&the_taskqueue,&td->t); +} + +void taskqueue_dump() +{ +struct task *p; + printf("Anchor %p, Tail %p\n", &the_taskqueue.anchor, the_taskqueue.tail); + for ( p = the_taskqueue.anchor.ta_next; p; p=p->ta_next ) { + printf("%p: (pending %2i, next %p)\n", + p, p->ta_pending, p->ta_next); + } +} +#endif + +rtems_id +rtems_taskqueue_initialize() +{ +#ifdef DEBUG + TASK_INIT( &taskA.t, 0, the_task_fn, &taskA ); + TASK_INIT( &taskB.t, 0, the_task_fn, &taskB ); + TASK_INIT( &taskC.t, 0, the_task_fn, &taskC ); +#endif + if ( ! the_taskqueue.tid ) + the_taskqueue.tid = rtems_bsdnet_newproc("tskq", 10000, taskqueueDoWork, &the_taskqueue); + return the_taskqueue.tid; +} + +#ifdef DEBUG +void +_cexpModuleInitialize(void *u) +{ + rtems_bsdnet_initialize_network(); + the_taskqueue.tid = rtems_taskqueue_initialize(); +} +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_udelay.c b/bsd_eth_drivers/libbsdport/rtems_udelay.c new file mode 100644 index 0000000..d8f80f4 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_udelay.c @@ -0,0 +1,192 @@ +#include +#include +#include + +#include "rtems_udelay.h" + +#if defined(__PPC__) + +#include + +/* Ouch. stupid bookE doesn't implement mftb so we must + * use mfspr (which wouldn't work on classic ppc if we + * were in user mode but luckily we're not. + */ +static inline uint64_t __read_hires_clicks() +{ +uint32_t tbl, tbu1, tbu2; + asm volatile( + " mfspr %0, %4 \n" + " mfspr %1, %3 \n" + " mfspr %2, %4 \n" + :"=r"(tbu1),"=r"(tbl),"=r"(tbu2) + :"i"(TBRL), "i"(TBRU) + ); + if ( tbu1 != tbu2 ) + asm volatile("mfspr %0, %1":"=r"(tbl):"i"(TBRL)); + return ((uint64_t)tbu2<<32) | tbl; +} + +#define __rtems_hires_kHz (BSP_bus_frequency/BSP_time_base_divisor) + +#elif defined(__i386__) + +static inline uint64_t __read_hires_clicks() +{ +uint32_t lo,hi; + asm volatile("rdtsc":"=a"(lo),"=d"(hi)); + return ( (uint64_t)hi << 32 ) | lo; +} + +#else +#error "rtems_udelay.c not ported to this CPU yet" +#endif + +#ifndef __rtems_hires_kHz +/* Clock frequency of high-resolution timer */ +uint32_t __rtems_hires_kHz = 0; +uint32_t rtems_udelay_calibrate(); +#endif + + +void rtems_usec_delay(uint32_t usecs) +{ +uint64_t clicks = __read_hires_clicks(); +int ticks; + if (usecs > 10) { + if ( _ISR_Is_in_progress() ) { + rtems_panic("rtems_usec_delay for more than 10us in ISR!!"); + } + if ( _ISR_Get_level() > 0 ) { + rtems_panic("rtems_usec_delay for more than 10us with IRQs disabled!!"); + } + } + ticks = usecs/rtems_configuration_get_microseconds_per_tick(); +#ifndef __rtems_hires_kHz + /* If it's not a macro; do lazy init */ + if ( 0 == __rtems_hires_kHz ) { + uint64_t clicks = rtems_udelay_calibrate(); + __rtems_hires_kHz = (clicks * 1000) / rtems_configuration_get_microseconds_per_tick(); + ticks--; + } +#endif + clicks += (usecs * __rtems_hires_kHz)/1000; + if ( ticks > 0 ) + rtems_task_wake_after(ticks); + + while ( clicks > __read_hires_clicks() ) + /* busy wait */; +} + +/* This doesn't belong here; also, the RTEMS timeout() implementation is buggy: + * if a timeout is added when the networking task is asleep then I believe 'timeout()' + * is unable to schedule a wakeup. Therefore, I implemented the 'callout' facility. + */ +#ifdef UNTESTED +/* Must be executed with the network semaphore held */ +void +rtems_bsdnet_untimeout(timeout_func_t fn, void *arg) +{ +register struct callout *l, *p; + + for ( l = &calltodo; (p=l->c_next); l=p ) { + if ( p->c_func == fn && p->c_arg == arg ) { + register struct callout *n + /* found it */ + if ( (n = p->c_next) && p->c_time > 0 ) { + /* adjust time of following entry */ + n->c_time += p->c_time; + } + /* extract */ + l->c_next = n; + /* return to extract first occurrence; continue + * to extract all + */ +#if 0 + return; +#else + p = l; +#endif + } + } +} +#endif + +struct caldat { + uint64_t t0,t1,t2,t3; + rtems_id thetid; +}; + +#ifndef __rtems_hires_kHz +static void +tickmeas1(rtems_id myself, void *arg) +{ +struct caldat *p = arg; + p->t2 = __read_hires_clicks(); + rtems_event_send(p->thetid, RTEMS_EVENT_0); +} + +static void +tickmeas0(rtems_id myself, void *arg) +{ +struct caldat *p = arg; + p->t1 = __read_hires_clicks(); + rtems_timer_fire_after( myself, 1, tickmeas1, arg ); +} + +/* Calibrate high-resolution timer */ +uint32_t +rtems_udelay_calibrate() +{ +rtems_id timer; +rtems_status_code sc; +rtems_event_set ev; +struct caldat d; + + /* measure a clock tick with the hires timer; + * note that we can't just sleep for 1 tick because + * that results in sleeping for an unknown fraction + * of a tick... + */ + sc = rtems_timer_create( rtems_build_name('h','r','e','s'), &timer ); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to create timer:%i\n", sc); + } + sc = rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &d.thetid); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to read my own TID:%i\n", sc); + } +#ifdef DEBUG + d.t0 = __read_hires_clicks(); +#endif + sc = rtems_timer_fire_after(timer, 1, tickmeas0, &d); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to fire timer:%i\n", sc); + } + sc = rtems_event_receive( RTEMS_EVENT_0, RTEMS_EVENT_ANY | RTEMS_WAIT , RTEMS_NO_TIMEOUT, &ev); +#ifdef DEBUG + d.t3 = __read_hires_clicks(); +#endif + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to synchronize with timer:%i\n", sc); + } + rtems_timer_delete(timer); +#ifdef DEBUG + printf("Diffs: %llu %llu %llu\n", + d.t3-d.t2, d.t2-d.t1, d.t1-d.t0); +#endif + return d.t2-d.t1; +} +#endif + +#ifdef DEBUG +unsigned +hdiff(unsigned s) +{ +uint64_t now = __read_hires_clicks(); + rtems_task_wake_after(s); + now = __read_hires_clicks() - now; + printf("Diff was %llu clicks\n",now); + return (unsigned)now; +} +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_udelay.h b/bsd_eth_drivers/libbsdport/rtems_udelay.h new file mode 100644 index 0000000..32a3b62 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_udelay.h @@ -0,0 +1,22 @@ +#ifndef RTEMS_UDELAY_Y +#define RTEMS_UDELAY_Y + +#ifdef __cplusplus +extern "C" { +#endif + +/* Delay execution for n microseconds. The current task + * is suspended for multiples of OS 'ticks' and busy-waits + * for fractions thereof. + * The routine panics if requested to delay for more than + * 10us in an ISR or IRQ-disabled section of code. + */ +void rtems_usec_delay(uint32_t usecs); + +#define DELAY(usecs) rtems_usec_delay(usecs) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_verscheck.h b/bsd_eth_drivers/libbsdport/rtems_verscheck.h new file mode 100644 index 0000000..519f817 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_verscheck.h @@ -0,0 +1,29 @@ +#ifndef RTEMS_VERSION_CHECKER_H +#define RTEMS_VERSION_CHECKER_H +/* $Id$ Macros to check rtems version dependent API features :-( */ + +#include + +#define RTEMS_REV_LATER_THAN(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ > (re)) \ + ) + +#define RTEMS_REV_AT_LEAST(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ >= (re)) \ + ) + +/* + * unfortunately, (powerpc) libcpu/io.h didn't follow the change from + * unsigned -> uin32_t :--( + */ +#if RTEMS_REV_AT_LEAST(4,8,0) +typedef uint32_t libbsdport_u32_t; +#else +typedef unsigned libbsdport_u32_t; +#endif + +#endif diff --git a/bsd_eth_drivers/libbsdport/sysbus.c b/bsd_eth_drivers/libbsdport/sysbus.c new file mode 100644 index 0000000..fc0ae0b --- /dev/null +++ b/bsd_eth_drivers/libbsdport/sysbus.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#if !RTEMS_REV_AT_LEAST(4,6,99) || !defined(BSP_SHARED_HANDLER_SUPPORT) + +#include + +#else + +static void noop(const rtems_irq_connect_data *unused) {}; +static int noop1(const rtems_irq_connect_data *unused) { return 0;}; + +/* Finally have an ISR arg but the API still sucks.. */ +static int +bspExtInstallSharedISR(int irqLine, void (*isr)(void *), void * uarg, int flags) +{ +rtems_irq_connect_data suck = {0}; + suck.name = irqLine; + suck.hdl = isr; + suck.handle = uarg; + suck.on = noop; + suck.off = noop; + suck.isOn = noop1; + return ! BSP_install_rtems_shared_irq_handler(&suck); +} + +static int +bspExtRemoveSharedISR(int irqLine, void (*isr)(void *), void *uarg) +{ +rtems_irq_connect_data suck = {0}; + suck.name = irqLine; + suck.hdl = isr; + suck.handle = uarg; + suck.on = noop; + suck.off = noop; + suck.isOn = noop1; + return ! BSP_remove_rtems_irq_handler(&suck); +} +#endif + + +struct resource * +bus_alloc_resource_any(device_t dev, int type, int *prid, unsigned flags) +{ +bus_addr_t ba; +int isio; + switch ( type ) { + default: + break; + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + { + libbsdport_u32_t d; + pci_read_config_dword( + dev->bushdr.pci.bus, + dev->bushdr.pci.dev, + dev->bushdr.pci.fun, + *prid, + &d); + ba = d; + isio = (ba & PCI_BASE_ADDRESS_SPACE_IO) ? 1 : 0; + if ( (type == SYS_RES_IOPORT) != (isio != 0) ) + return 0; /* wrong type */ + + return (struct resource *) ba; + } + case SYS_RES_IRQ: + { + uint8_t line; + pci_read_config_byte( + dev->bushdr.pci.bus, + dev->bushdr.pci.dev, + dev->bushdr.pci.fun, + PCI_INTERRUPT_LINE, + &line); + ba = line; + /* MSI not implemented */ + return (struct resource*) ba; + } + } + rtems_panic("bus_alloc_resource_any: unknown/unimplemented resource type %i\n", type); + /* never get here */ + return (struct resource*)0; +} + +struct irq_cookie { + device_t dev; + driver_filter_t handler; + void (*work)(void*); + void *arg; + /* cache methods */ + int (*irq_check_dis)(device_t d); + void (*irq_en) (device_t d); + struct task task; +}; + +static int +sysbus_isr(void *arg) +{ +struct irq_cookie *info = arg; +int rval; +#ifdef DEBUG + printk("Sysbus IRQ\n"); +#endif + /* Check if we have an IRQ pending and disable further interrupts */ + rval = info->irq_check_dis(info->dev); + if ( FILTER_HANDLED == rval ) { + /* enqueue work */ + taskqueue_enqueue(taskqueue_fast, &info->task); + } + return rval; +} + +static void +sysbus_taskfn(void *arg, int pending) +{ +struct irq_cookie *info = arg; + + /* do work */ + info->work(info->arg); + + /* reenable interrupts */ + if ( info->irq_en ) + info->irq_en(info->dev); +} + +int +bus_setup_intr(device_t dev, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep) +{ +int rval; +struct irq_cookie *info = 0; + + if ( filter && handler ) { + rtems_panic("bus_setup_intr for both: filter & handler not implemented\n"); + } + + if ( (flags & INTR_FAST) && filter ) { + rtems_panic("bus_setup_intr for both: filter & INTR_FAST not implemented\n"); + /* handler is a fast handler already */ + filter = (driver_filter_t) handler; + handler = 0; + } + + if ( handler ) { + if ( !dev->drv ) { + device_printf(dev, "bus_setup_intr: device has no driver attached\n"); + return EINVAL; + } else if ( !dev->drv->methods->irq_check_dis ) { + device_printf(dev, "bus_setup_intr: driver has no 'irq_dis' method\n"); + return EINVAL; + } + } + + if ( ! (info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)) ) + return ENOMEM; + + info->dev = dev; + info->handler = filter; + info->work = handler; + info->arg = arg; + + if ( handler ) { + TASK_INIT(&info->task, 0, sysbus_taskfn, info); + /* make sure taskqueue facility is initialized */ + rtems_taskqueue_initialize(); + /* install our own filter */ + filter = sysbus_isr; + arg = info; + info->irq_check_dis = dev->drv->methods->irq_check_dis; + info->irq_en = dev->drv->methods->irq_en; + } else { + TASK_INIT(&info->task, 0, 0, 0); + } + + rval = bspExtInstallSharedISR((int)r, (void (*)(void*))filter, arg, 0); + + if ( rval ) { + free(info, M_DEVBUF); + return rval; + } + + if ( cookiep ) + *cookiep = info; + return rval; +} + +int +bus_teardown_intr(device_t dev, struct resource *r, void *cookiep) +{ +int rval; +struct irq_cookie *info = cookiep; + rval = bspExtRemoveSharedISR((int)r, (void (*)(void*))info->handler, info->arg); + if ( 0 == rval ) { + if ( info->task.ta_fn ) { + taskqueue_drain(taskqueue_fast, &info->task); + } + free(info, M_DEVBUF); + } + return rval; +} + +bus_space_handle_t +rman_get_bushandle(struct resource *r) +{ +bus_space_handle_t h = (bus_space_handle_t)r; +bus_space_handle_t msk = (PCI_BASE_ADDRESS_SPACE_IO & h) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK; + return h & msk; +} + +bus_space_tag_t +rman_get_bustag(struct resource *r) +{ +bus_space_handle_t h = (bus_space_handle_t)r; + return (PCI_BASE_ADDRESS_SPACE_IO & h) ? bus_space_io : bus_space_mem; +} + +int +bus_dma_tag_create(void *parent, unsigned alignment, unsigned bounds, uint32_t lowadd, uint32_t hiaddr, void (*filter)(void*), void *filterarg, unsigned maxsize, int nsegs, unsigned maxsegsize, unsigned flags, void (*lockfunc)(void*), void *lockarg, bus_dma_tag_t *ptag) +{ +bus_dma_tag_t tag; + if ( filter || lockfunc ) + return ENOTSUP; + if ( ! (tag = malloc(sizeof(*tag), M_DEVBUF, M_NOWAIT)) ) + return ENOMEM; + /* save some information */ + tag->alignment = alignment; + tag->maxsize = maxsize; + tag->maxsegs = nsegs; + *ptag = tag; + return 0; +} + +void +bus_dma_tag_destroy(bus_dma_tag_t tag) +{ + free(tag, M_DEVBUF); +} + +int +bus_dmamem_alloc(bus_dma_tag_t tag, void **p_vaddr, unsigned flags, bus_dmamap_t *p_map) +{ +uintptr_t a; + if ( ! (*p_map = malloc(tag->maxsize + tag->alignment, M_DEVBUF, M_NOWAIT)) ) + return ENOMEM; + a = ((uintptr_t)*p_map + tag->alignment - 1 ) & ~(tag->alignment - 1); + *p_vaddr = (void*)a; + return 0; +} + +void +bus_dmamem_free(bus_dma_tag_t tag, void *vaddr, bus_dmamap_t map) +{ + free(map, M_DEVBUF); +} diff --git a/bsd_eth_drivers/libbsdport/taskqueue.h b/bsd_eth_drivers/libbsdport/taskqueue.h new file mode 100644 index 0000000..d700edf --- /dev/null +++ b/bsd_eth_drivers/libbsdport/taskqueue.h @@ -0,0 +1,69 @@ +#ifndef RTEMS_TASKQUEUE_H +#define RTEMS_TASKQUEUE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct taskqueue; + +typedef void (*task_fn)(void *ctxt, int pending); + +/* forwarded 'ctxt' that was passed to taskqueue_create() */ +typedef void (*tq_enq_fn)(void *ctxt); + +struct task { + struct task *ta_next; + int ta_pending; + int ta_priority; + task_fn ta_fn; + void *ta_fn_arg; +}; + +struct taskqueue * +taskqueue_create(const char *name, int mflags, tq_enq_fn, void *ctxt); + +struct taskqueue * +taskqueue_create_fast(const char *name, int mflags, tq_enq_fn, void *ctxt); + +int +taskqueue_enqueue(struct taskqueue *tq, struct task *ta); + +#define taskqueue_enqueue_fast(_q,_t) taskqueue_enqueue(_q,_t) + +void +taskqueue_thread_enqueue(void *ctxt); + +#define PI_NET 150 +/* Returns 0 on success */ +int +taskqueue_start_threads(struct taskqueue **ptq, int count, int prio, const char *fmt, ...); + +void +taskqueue_drain(struct taskqueue *tq, struct task *ta); + +void +taskqueue_free(struct taskqueue *tq); + +#define TASK_INIT(task, pri, fn, arg) \ + do { \ + (task)->ta_next = 0; \ + (task)->ta_priority = (pri); \ + (task)->ta_pending = 0; \ + (task)->ta_fn = (fn); \ + (task)->ta_fn_arg = (arg); \ + } while (0) + +extern struct taskqueue *taskqueue_fast; + +/* Initialize taskqueue facility [networking must have been initialized already] */ +rtems_id +rtems_taskqueue_initialize(); + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3