diff options
Diffstat (limited to 'libdde_linux26/examples')
49 files changed, 8660 insertions, 0 deletions
diff --git a/libdde_linux26/examples/.svn/all-wcprops b/libdde_linux26/examples/.svn/all-wcprops new file mode 100644 index 00000000..b30dbfeb --- /dev/null +++ b/libdde_linux26/examples/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 59 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples +END +Makefile +K 25 +svn:wc:ra_dav:version-url +V 68 +/repos/tudos/!svn/ver/443/trunk/l4/pkg/dde/linux26/examples/Makefile +END diff --git a/libdde_linux26/examples/.svn/entries b/libdde_linux26/examples/.svn/entries new file mode 100644 index 00000000..bee27e97 --- /dev/null +++ b/libdde_linux26/examples/.svn/entries @@ -0,0 +1,74 @@ +9 + +dir +465 +http://svn.tudos.org/repos/tudos/trunk/l4/pkg/dde/linux26/examples +http://svn.tudos.org/repos/tudos + + + +2009-05-20T14:32:55.606606Z +455 +l4check + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +a704ac0b-3a55-4d43-a2a9-7be6f07c34fb + +ne2k +dir + +unittest +dir + +dde26_test +dir + +bug_timersleep +dir + +Makefile +file + + + + +2009-11-15T17:17:14.000000Z +e5078d83828da469a57cbec2f20b2d91 +2009-03-26T03:50:31.959106Z +443 +l4check + + + + + + + + + + + + + + + + + + + + + +209 + diff --git a/libdde_linux26/examples/.svn/format b/libdde_linux26/examples/.svn/format new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/libdde_linux26/examples/.svn/format @@ -0,0 +1 @@ +9 diff --git a/libdde_linux26/examples/.svn/text-base/Makefile.svn-base b/libdde_linux26/examples/.svn/text-base/Makefile.svn-base new file mode 100644 index 00000000..8717a0b5 --- /dev/null +++ b/libdde_linux26/examples/.svn/text-base/Makefile.svn-base @@ -0,0 +1,13 @@ +PKGDIR ?= ../.. +L4DIR ?= $(PKGDIR)/../.. + +include $(L4DIR)/mk/Makeconf +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26),y) +TARGET = dde26_test ne2k bug_timersleep +endif + +include $(L4DIR)/mk/subdir.mk + + diff --git a/libdde_linux26/examples/Makefile b/libdde_linux26/examples/Makefile new file mode 100644 index 00000000..8717a0b5 --- /dev/null +++ b/libdde_linux26/examples/Makefile @@ -0,0 +1,13 @@ +PKGDIR ?= ../.. +L4DIR ?= $(PKGDIR)/../.. + +include $(L4DIR)/mk/Makeconf +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26),y) +TARGET = dde26_test ne2k bug_timersleep +endif + +include $(L4DIR)/mk/subdir.mk + + diff --git a/libdde_linux26/examples/bug_timersleep/.svn/all-wcprops b/libdde_linux26/examples/bug_timersleep/.svn/all-wcprops new file mode 100644 index 00000000..e0897b47 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/all-wcprops @@ -0,0 +1,29 @@ +K 25 +svn:wc:ra_dav:version-url +V 74 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/bug_timersleep +END +main.c +K 25 +svn:wc:ra_dav:version-url +V 81 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/bug_timersleep/main.c +END +WhatIsThis +K 25 +svn:wc:ra_dav:version-url +V 85 +/repos/tudos/!svn/ver/443/trunk/l4/pkg/dde/linux26/examples/bug_timersleep/WhatIsThis +END +Makeconf.local +K 25 +svn:wc:ra_dav:version-url +V 89 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/bug_timersleep/Makeconf.local +END +Makefile +K 25 +svn:wc:ra_dav:version-url +V 83 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/bug_timersleep/Makefile +END diff --git a/libdde_linux26/examples/bug_timersleep/.svn/entries b/libdde_linux26/examples/bug_timersleep/.svn/entries new file mode 100644 index 00000000..4475ed37 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/entries @@ -0,0 +1,164 @@ +9 + +dir +465 +http://svn.tudos.org/repos/tudos/trunk/l4/pkg/dde/linux26/examples/bug_timersleep +http://svn.tudos.org/repos/tudos + + + +2009-05-20T14:32:55.606606Z +455 +l4check + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +a704ac0b-3a55-4d43-a2a9-7be6f07c34fb + +main.c +file + + + + +2009-11-15T17:17:14.000000Z +9ef2d314e37201c0d6813c088a9bdf9b +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +3348 + +WhatIsThis +file + + + + +2009-11-15T17:17:14.000000Z +b3d846298a511fba776c557db5645ad7 +2009-03-26T03:50:31.959106Z +443 +l4check + + + + + + + + + + + + + + + + + + + + + +504 + +Makeconf.local +file + + + + +2009-11-15T17:17:14.000000Z +075b1f05a72c4dc89dcd7e4e97e3da21 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +108 + +Makefile +file + + + + +2009-11-15T17:17:14.000000Z +896d9d28b3e9e8626a8229ba171f9890 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +585 + diff --git a/libdde_linux26/examples/bug_timersleep/.svn/format b/libdde_linux26/examples/bug_timersleep/.svn/format new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/format @@ -0,0 +1 @@ +9 diff --git a/libdde_linux26/examples/bug_timersleep/.svn/text-base/Makeconf.local.svn-base b/libdde_linux26/examples/bug_timersleep/.svn/text-base/Makeconf.local.svn-base new file mode 100644 index 00000000..28ebade5 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/text-base/Makeconf.local.svn-base @@ -0,0 +1 @@ +#LIBS += -lferret_client -lferret_producer -lferret_util -lferret_comm -lferret_fpages -lferret_local_names diff --git a/libdde_linux26/examples/bug_timersleep/.svn/text-base/Makefile.svn-base b/libdde_linux26/examples/bug_timersleep/.svn/text-base/Makefile.svn-base new file mode 100644 index 00000000..a1f39f00 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/text-base/Makefile.svn-base @@ -0,0 +1,25 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +TARGET = dde26_bug_timersleep + +SRC_C = main.c + +LIBS += -ldde_linux26.o -lddekit -lio -lomega0 -llist_alloc + +PRIVATE_INCDIR = $(PKGDIR_ABS)/linux26/include $(MY_DDE_INCDIR) $(MY_LINUX26_INCDIR) \ + $(OBJ_BASE)/include/uclibc + +LIBCINCDIR = -nostdinc $(I_GCCINCDIR) +DEFINES = -D__KERNEL__ -DDDE_LINUX $(KBUILD_DEFINES) +CPPFLAGS += $(KBUILD_CPPFLAGS) + +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk diff --git a/libdde_linux26/examples/bug_timersleep/.svn/text-base/WhatIsThis.svn-base b/libdde_linux26/examples/bug_timersleep/.svn/text-base/WhatIsThis.svn-base new file mode 100644 index 00000000..3fb57408 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/text-base/WhatIsThis.svn-base @@ -0,0 +1,16 @@ +=== Bug reported by Andre Puschmann === + +main.c contains Linux kernel code that triggers 2 problems: + +1) list order violation in the list alloc lib +2) threads hang after scheduling a very short timer, because + the timer thread seems to miss some events + + +Fixes +===== +1) correct locking for ddekit's memory allocation functions + solves the problem +2) fixed DDEKit's timer implementation to not use IPC for + notifications (because some got lost for atomicity reasons) + but use a counting semaphore diff --git a/libdde_linux26/examples/bug_timersleep/.svn/text-base/main.c.svn-base b/libdde_linux26/examples/bug_timersleep/.svn/text-base/main.c.svn-base new file mode 100644 index 00000000..a02e6841 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/.svn/text-base/main.c.svn-base @@ -0,0 +1,150 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/version.h> +#include <linux/kthread.h> + +//BEER for L4 +#include <l4/sys/kdebug.h> +#include <l4/util/util.h> +#include <l4/dde/linux26/dde26.h> + +#if 0 +#include <l4/ferret/client.h> +#include <l4/ferret/clock.h> +#include <l4/ferret/types.h> +#include <l4/ferret/util.h> +#include <l4/ferret/sensors/list_producer.h> +#include <l4/ferret/sensors/list_producer_wrap.h> +#endif + +static int idle_thread(void *); +int __init init_module(void); +void __exit cleanup_module(void); +#if 0 +ferret_list_local_t *sensor; +#endif + +enum Events +{ + DDE_INIT = 10, + DDE_INITCALLS = 11, + INIT_MODULE = 12, + START_THREAD = 13, + THREAD_INIT = 14, + THREAD_SLEEP_START = 15, + THREAD_SLEEP_END = 16 +}; + +atomic_t val = ATOMIC_INIT(0); + +static int idle_thread(void *priv_data) +{ + int master_thread_counter = 0; +#if 0 + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, me,THREAD_INIT); +#endif + + while (1) + { + master_thread_counter++; + if ((master_thread_counter % 100) == 0) + atomic_inc(&val); +#if 0 + printk(KERN_INFO "master_thread_counter: %d jiffies: %d\n", + master_thread_counter, jiffies); +#endif + + // do something interesting + + // wait for 10ms assuming HZ=100 + set_current_state(TASK_INTERRUPTIBLE); +// ferret_list_post_1t1wc(1, sensor, 42, 1, 0, me,THREAD_SLEEP_START); + schedule_timeout(1); +// ferret_list_post_1t1wc(1, sensor, 42, 1, 0, me,THREAD_SLEEP_END); + //msleep(10); + } +} + +int __init init_module(void) +{ + int i; + printk(KERN_INFO "init module\n"); + + for (i = 0; i < 10; i++) + { +// ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),START_THREAD); + kthread_run(idle_thread, NULL, "IDLE THREAD"); + udelay(1000); + } + // no error check + return 0; +} + +void __exit cleanup_module(void) +{ + printk(KERN_INFO "exit module\n"); + // kill thread +} + +MODULE_AUTHOR("test"); +MODULE_DESCRIPTION("test"); +MODULE_LICENSE("GPL"); + +static void *kmalloc_wrap(size_t sz) +{ return kmalloc(sz,GFP_KERNEL); } + +int main(void) +{ + int old, v = 12000; + printk("Initializing Ferret\n"); +#if 0 + int ret = ferret_create(42, 1, 0, FERRET_LIST, 0, + "64:100000", sensor, &kmalloc_wrap); + printk("Created sensor: %d\n", ret); + + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),DDE_INIT); +#endif + printk("Initializing DDE base system.\n"); + +#if 0 + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),DDE_INITCALLS); +#endif + +#if 0 + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),INIT_MODULE); +#endif + init_module(); + + while (1) + { + l4_sleep(3000); + old = v; + v = atomic_read(&val); + printk("%d - %d = %d\n", v, old, v-old); + if (v - old == 0) + { + printk("System halt!? %d\n", v); + //enter_kdebug(".."); +#if 0 + ferret_list_entry_common_t * elc; + + int idx = ferret_list_dequeue(sensor); + elc = (ferret_list_entry_common_t *)ferret_list_e4i(sensor->glob, idx); + elc->major = FERRET_MONCON_MAJOR; + elc->minor = FERRET_MONCON_SERSEND; + strncpy(elc->data8, "foobar", 7); + ferret_list_commit(sensor, idx); + printk("Dumped %d\n", idx); + l4_sleep(10000); +#endif + enter_kdebug(""); + } + } + + l4_sleep_forever(); + return -1; +} diff --git a/libdde_linux26/examples/bug_timersleep/Makeconf.local b/libdde_linux26/examples/bug_timersleep/Makeconf.local new file mode 100644 index 00000000..28ebade5 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/Makeconf.local @@ -0,0 +1 @@ +#LIBS += -lferret_client -lferret_producer -lferret_util -lferret_comm -lferret_fpages -lferret_local_names diff --git a/libdde_linux26/examples/bug_timersleep/Makefile b/libdde_linux26/examples/bug_timersleep/Makefile new file mode 100644 index 00000000..a1f39f00 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/Makefile @@ -0,0 +1,25 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +TARGET = dde26_bug_timersleep + +SRC_C = main.c + +LIBS += -ldde_linux26.o -lddekit -lio -lomega0 -llist_alloc + +PRIVATE_INCDIR = $(PKGDIR_ABS)/linux26/include $(MY_DDE_INCDIR) $(MY_LINUX26_INCDIR) \ + $(OBJ_BASE)/include/uclibc + +LIBCINCDIR = -nostdinc $(I_GCCINCDIR) +DEFINES = -D__KERNEL__ -DDDE_LINUX $(KBUILD_DEFINES) +CPPFLAGS += $(KBUILD_CPPFLAGS) + +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk diff --git a/libdde_linux26/examples/bug_timersleep/WhatIsThis b/libdde_linux26/examples/bug_timersleep/WhatIsThis new file mode 100644 index 00000000..3fb57408 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/WhatIsThis @@ -0,0 +1,16 @@ +=== Bug reported by Andre Puschmann === + +main.c contains Linux kernel code that triggers 2 problems: + +1) list order violation in the list alloc lib +2) threads hang after scheduling a very short timer, because + the timer thread seems to miss some events + + +Fixes +===== +1) correct locking for ddekit's memory allocation functions + solves the problem +2) fixed DDEKit's timer implementation to not use IPC for + notifications (because some got lost for atomicity reasons) + but use a counting semaphore diff --git a/libdde_linux26/examples/bug_timersleep/main.c b/libdde_linux26/examples/bug_timersleep/main.c new file mode 100644 index 00000000..a02e6841 --- /dev/null +++ b/libdde_linux26/examples/bug_timersleep/main.c @@ -0,0 +1,150 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/version.h> +#include <linux/kthread.h> + +//BEER for L4 +#include <l4/sys/kdebug.h> +#include <l4/util/util.h> +#include <l4/dde/linux26/dde26.h> + +#if 0 +#include <l4/ferret/client.h> +#include <l4/ferret/clock.h> +#include <l4/ferret/types.h> +#include <l4/ferret/util.h> +#include <l4/ferret/sensors/list_producer.h> +#include <l4/ferret/sensors/list_producer_wrap.h> +#endif + +static int idle_thread(void *); +int __init init_module(void); +void __exit cleanup_module(void); +#if 0 +ferret_list_local_t *sensor; +#endif + +enum Events +{ + DDE_INIT = 10, + DDE_INITCALLS = 11, + INIT_MODULE = 12, + START_THREAD = 13, + THREAD_INIT = 14, + THREAD_SLEEP_START = 15, + THREAD_SLEEP_END = 16 +}; + +atomic_t val = ATOMIC_INIT(0); + +static int idle_thread(void *priv_data) +{ + int master_thread_counter = 0; +#if 0 + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, me,THREAD_INIT); +#endif + + while (1) + { + master_thread_counter++; + if ((master_thread_counter % 100) == 0) + atomic_inc(&val); +#if 0 + printk(KERN_INFO "master_thread_counter: %d jiffies: %d\n", + master_thread_counter, jiffies); +#endif + + // do something interesting + + // wait for 10ms assuming HZ=100 + set_current_state(TASK_INTERRUPTIBLE); +// ferret_list_post_1t1wc(1, sensor, 42, 1, 0, me,THREAD_SLEEP_START); + schedule_timeout(1); +// ferret_list_post_1t1wc(1, sensor, 42, 1, 0, me,THREAD_SLEEP_END); + //msleep(10); + } +} + +int __init init_module(void) +{ + int i; + printk(KERN_INFO "init module\n"); + + for (i = 0; i < 10; i++) + { +// ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),START_THREAD); + kthread_run(idle_thread, NULL, "IDLE THREAD"); + udelay(1000); + } + // no error check + return 0; +} + +void __exit cleanup_module(void) +{ + printk(KERN_INFO "exit module\n"); + // kill thread +} + +MODULE_AUTHOR("test"); +MODULE_DESCRIPTION("test"); +MODULE_LICENSE("GPL"); + +static void *kmalloc_wrap(size_t sz) +{ return kmalloc(sz,GFP_KERNEL); } + +int main(void) +{ + int old, v = 12000; + printk("Initializing Ferret\n"); +#if 0 + int ret = ferret_create(42, 1, 0, FERRET_LIST, 0, + "64:100000", sensor, &kmalloc_wrap); + printk("Created sensor: %d\n", ret); + + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),DDE_INIT); +#endif + printk("Initializing DDE base system.\n"); + +#if 0 + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),DDE_INITCALLS); +#endif + +#if 0 + ferret_list_post_1t1wc(1, sensor, 42, 1, 0, l4_myself(),INIT_MODULE); +#endif + init_module(); + + while (1) + { + l4_sleep(3000); + old = v; + v = atomic_read(&val); + printk("%d - %d = %d\n", v, old, v-old); + if (v - old == 0) + { + printk("System halt!? %d\n", v); + //enter_kdebug(".."); +#if 0 + ferret_list_entry_common_t * elc; + + int idx = ferret_list_dequeue(sensor); + elc = (ferret_list_entry_common_t *)ferret_list_e4i(sensor->glob, idx); + elc->major = FERRET_MONCON_MAJOR; + elc->minor = FERRET_MONCON_SERSEND; + strncpy(elc->data8, "foobar", 7); + ferret_list_commit(sensor, idx); + printk("Dumped %d\n", idx); + l4_sleep(10000); +#endif + enter_kdebug(""); + } + } + + l4_sleep_forever(); + return -1; +} diff --git a/libdde_linux26/examples/dde26_test/.svn/all-wcprops b/libdde_linux26/examples/dde26_test/.svn/all-wcprops new file mode 100644 index 00000000..6fec0785 --- /dev/null +++ b/libdde_linux26/examples/dde26_test/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 70 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/dde26_test +END +main.c +K 25 +svn:wc:ra_dav:version-url +V 77 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/dde26_test/main.c +END +Makefile +K 25 +svn:wc:ra_dav:version-url +V 79 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/dde26_test/Makefile +END diff --git a/libdde_linux26/examples/dde26_test/.svn/entries b/libdde_linux26/examples/dde26_test/.svn/entries new file mode 100644 index 00000000..8a7ab4c5 --- /dev/null +++ b/libdde_linux26/examples/dde26_test/.svn/entries @@ -0,0 +1,96 @@ +9 + +dir +465 +http://svn.tudos.org/repos/tudos/trunk/l4/pkg/dde/linux26/examples/dde26_test +http://svn.tudos.org/repos/tudos + + + +2009-05-20T14:32:55.606606Z +455 +l4check + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +a704ac0b-3a55-4d43-a2a9-7be6f07c34fb + +main.c +file + + + + +2009-11-15T17:17:14.000000Z +605a540cd2dba4981825383619cfcddd +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +13242 + +Makefile +file + + + + +2009-11-15T17:17:14.000000Z +9d1aa1fef27a10b5882bbd2c22cd991c +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +401 + diff --git a/libdde_linux26/examples/dde26_test/.svn/format b/libdde_linux26/examples/dde26_test/.svn/format new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/libdde_linux26/examples/dde26_test/.svn/format @@ -0,0 +1 @@ +9 diff --git a/libdde_linux26/examples/dde26_test/.svn/text-base/Makefile.svn-base b/libdde_linux26/examples/dde26_test/.svn/text-base/Makefile.svn-base new file mode 100644 index 00000000..444a1f2d --- /dev/null +++ b/libdde_linux26/examples/dde26_test/.svn/text-base/Makefile.svn-base @@ -0,0 +1,21 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26_COMMON),y) +TARGET = dde26_test +endif + +SRC_C = main.c + +LIBS += -ldde_linux26.o -lddekit -lio -llist_alloc -lparsecmdline + +# DDE configuration +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk diff --git a/libdde_linux26/examples/dde26_test/.svn/text-base/main.c.svn-base b/libdde_linux26/examples/dde26_test/.svn/text-base/main.c.svn-base new file mode 100644 index 00000000..88d33ba6 --- /dev/null +++ b/libdde_linux26/examples/dde26_test/.svn/text-base/main.c.svn-base @@ -0,0 +1,502 @@ +/* + * \brief DDE for Linux 2.6 test program + * \author Bjoern Doebel <doebel@os.inf.tu-dresden.de> + * \author Christian Helmuth <ch12@os.inf.tu-dresden.de> + * \date 2007-01-22 + */ + +#include <asm/current.h> + +#include <linux/kernel.h> +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +//#include <linux/kthread.h> + +#include <l4/dde/dde.h> +#include <l4/dde/ddekit/initcall.h> +#include <l4/dde/linux26/dde26.h> + +#include <l4/util/parse_cmd.h> +#include <l4/util/util.h> +#include <l4/log/l4log.h> + +/* We define 4 initcalls and see if these are executed + * in the beginning. + */ +static __init void foo(void) { printk("foo module_init\n"); } +static __init void bar(void) { printk("bar device_initcall\n"); } +static __init void bla(void) { printk("bla arch_initcall\n"); } +static __init void blub(void) { printk("blub subsys_initcall\n"); } +module_init(foo); +device_initcall(bar); +arch_initcall(bla); +subsys_initcall(blub); + +/*********************************************************************** + ** Test 1: Check whether the current() macro works. ** + ***********************************************************************/ +static void current_test(void) +{ + struct task_struct *t = NULL; + + printk("Current() test.\n"); + + t = current; + printk("\tt = %p\n", t); +} + + +/*********************************************************************** + ** Test 2: Getting complicated. Test startup of some kernel threads ** + ** and wait for them to finish using completions. ** + ***********************************************************************/ +#define NUM_KTHREADS 5 +static struct completion _kthread_completions_[NUM_KTHREADS]; + +static int kernel_thread_func(void *arg) +{ + printk("\t\tKernel thread %d\n", (int)arg); + printk("\t\tcurrent = %p\n", current); + + /* do some work */ + msleep(200); + + complete_and_exit( &_kthread_completions_[(int)arg], 0 ); + return 0; +} + + +static void kernel_thread_test(void) +{ + int i; + printk("Testing kernel_thread()\n"); + for (i=0; i < NUM_KTHREADS; i++) { + int j; + printk("\tInitializing completion for kernel thread.%x\n", i+1); + init_completion(&_kthread_completions_[i]); + printk("\tStarting kthread.%x\n", i+1); + j = kernel_thread(kernel_thread_func, (void *)i, 0); + printk("\treturn: %d\n", j); + } + + for (i=0; i < NUM_KTHREADS; i++) { + printk("\tWaiting for kthread.%x to complete.\n", i+1); + wait_for_completion(&_kthread_completions_[i]); + printk("\tkthread.%x has exited.\n", i+1); + } +} + + +/****************************************************************************** + ** Test 3: Test kernel wait queues: start a thread incrementing wait_value, ** + ** and sleep until wait_value is larger than 6 for the first time. ** + ******************************************************************************/ +static DECLARE_WAIT_QUEUE_HEAD(_wq_head); +static int wait_value = 0; +static struct completion wq_completion; + +static int inc_func(void *arg) +{ + int i = 0; + + printk("\033[33mI am counting up wait_value.\033[0m\n"); + for (i=0; i<10; i++) + { + printk("\033[33mwait_value: %d\033[0m\n", ++wait_value); + wake_up(&_wq_head); + msleep(500); + } + complete_and_exit(&wq_completion, 0); +} + + +static void wq_test(void) +{ + int pid; + printk("\033[32mWait_queue test. I'm waiting vor wait_value to become >6.\033[0m\n"); + + init_completion(&wq_completion); + pid = kernel_thread(inc_func, 0, 0); + + wait_event(_wq_head, wait_value > 6); + printk("\033[32;1mwait_value > 6 occured!\033[0m\n"); + + wait_for_completion(&wq_completion); + printk("\033[32mtest done.\033[0m\n"); +} + + +/**************************************************************************** + ** Test 4: Tasklets ** + ****************************************************************************/ +static void tasklet_func(unsigned long i) +{ + printk("TASKLET: %d\n", i); +} + + +static DECLARE_TASKLET(low0, tasklet_func, 0); +static DECLARE_TASKLET(low1, tasklet_func, 1); +static DECLARE_TASKLET(low2, tasklet_func, 2); +static DECLARE_TASKLET_DISABLED(low3, tasklet_func, 3); + +static DECLARE_TASKLET(hi0, tasklet_func, 10); +static DECLARE_TASKLET(hi1, tasklet_func, 11); +static DECLARE_TASKLET_DISABLED(hi2, tasklet_func, 12); + + +static void tasklet_test(void) +{ + printk("BEGIN TASKLET TEST\n"); + + l4dde26_softirq_init(); + + printk("sleep 1000 msec\n"); + msleep(1000); + + printk("Scheduling tasklets 0-2 immediately. 3 is disabled for 2 seconds.\n"); + tasklet_schedule(&low0); + + tasklet_schedule(&low1); + tasklet_schedule(&low2); + tasklet_schedule(&low3); + msleep(2000); + tasklet_enable(&low3); + + msleep(1000); + + printk("Scheduling hi_tasklets 10-12, and tasklets 0-2\n"); + tasklet_hi_schedule(&hi0); + tasklet_hi_schedule(&hi1); + tasklet_hi_schedule(&hi2); + tasklet_schedule(&low0); + tasklet_schedule(&low1); + tasklet_schedule(&low2); + tasklet_enable(&hi2); + + msleep(1000); + printk("Scheduling (disabled) tasklet 3 twice - should only run once after enabling.\n"); + tasklet_disable(&low3); + tasklet_schedule(&low3); + tasklet_schedule(&low3); + tasklet_enable(&low3); + + msleep(1000); + + printk("END TASKLET TEST\n"); +} + + +/****************************************************************************** + ** Test 5: Timers ** + ** ** + ** Schedule a periodic timer printing "tick" every second. Additionally, ** + ** schedule timers for 5, 10, 15, 20, and 25 seconds. Timer at 15s will ** + ** deactivate the 20s timer. ** + ******************************************************************************/ + +static struct timer_list _timer; +static struct timer_list _timer5; +static struct timer_list _timer10; +static struct timer_list _timer15; +static struct timer_list _timer20; +static struct timer_list _timer25; + +static void tick_func(unsigned long d) +{ + printk("tick (%ld)\n", jiffies); + _timer.expires = jiffies + HZ; + add_timer(&_timer); +} + + +static void timer_func(unsigned long d) +{ + printk("timer_func: %lu\n", d); + + if (d == 15) { + printk("De-scheduling 20s timer.\n"); + del_timer(&_timer20); + } + + if (timer_pending(&_timer20)) + printk("timer for 20s still pending.\n"); + else + printk("timer for 20s has been disabled.\n"); +} + + +static void timer_test(void) +{ + l4dde26_init_timers(); + + printk("BEGIN TIMER TEST\n"); + printk("jiffies(%p): %ld, HZ(%p): %ld\n", &jiffies, jiffies, &HZ, HZ); + + setup_timer(&_timer, tick_func, 0); + _timer.expires = jiffies + HZ; + add_timer(&_timer); + + setup_timer(&_timer5, timer_func, 5); + _timer5.expires = jiffies + 5*HZ; + setup_timer(&_timer10, timer_func, 10); + _timer10.expires = jiffies + 10*HZ; + setup_timer(&_timer15, timer_func, 15); + _timer15.expires = jiffies + 15*HZ; + setup_timer(&_timer20, timer_func, 20); + _timer20.expires = jiffies + 20*HZ; + setup_timer(&_timer25, timer_func, 25); + _timer25.expires = jiffies + 25*HZ; + + add_timer(&_timer5); + add_timer(&_timer10); + add_timer(&_timer15); + add_timer(&_timer20); + add_timer(&_timer25); + + msleep(30000); + + del_timer(&_timer); + printk("END TIMER TEST\n"); +} + + +/****************************** + ** Test 6: Memory subsystem ** + ******************************/ + +static void memory_kmem_cache_test(void) +{ + struct kmem_cache *cache0; + struct obj0 + { + unsigned foo; + unsigned bar; + }; + static struct obj0 *p0[1024]; + + struct kmem_cache *cache1; + struct obj1 + { + char foo[50]; + unsigned *bar; + }; + static struct obj1 *p1[256]; + + cache0 = kmem_cache_create("obj0", sizeof(*p0[0]), 0, 0, 0); + cache1 = kmem_cache_create("obj1", sizeof(*p1[0]), 0, 0, 0); + printk("kmem caches: %p %p\n", cache0, cache1); + + unsigned i; + for (i = 0; i < 1024; ++i) + p0[i] = kmem_cache_alloc(cache0, i); + + for (i = 0; i < 256; ++i) + p1[i] = kmem_cache_alloc(cache1, i); + + for (i = 256; i > 0; --i) + kmem_cache_free(cache1, p1[i-1]); + + for (i = 1024; i > 0; --i) + kmem_cache_free(cache0, p0[i-1]); + + kmem_cache_destroy(cache1); + kmem_cache_destroy(cache0); + printk("Done testing kmem_cache_alloc() & co.\n"); +} + + +static void memory_page_alloc_test(void) +{ + unsigned long p[4]; + p[0] = __get_free_page(GFP_KERNEL); + p[1] = __get_free_pages(GFP_KERNEL, 1); + p[2] = __get_free_pages(GFP_KERNEL, 2); + p[3] = __get_free_pages(GFP_KERNEL, 3); + printk("pages: %p %p %p %p\n", p[0], p[1], p[2], p[3]); + + free_pages(p[0], 0); + free_pages(p[1], 1); + free_pages(p[2], 2); + free_pages(p[3], 3); + printk("Freed pages\n"); +} + + +static void memory_kmalloc_test(void) +{ + // XXX initialized by dde26_init()! +// l4dde26_kmalloc_init(); + + const unsigned count = 33; + char *p[count]; + + int i; + for (i = 0; i < count; ++i) { + p[i] = kmalloc(32 + i*15, GFP_KERNEL); + *p[i] = i; + printk("p[%d] = %p\n", i, p[i]); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + + for (i = 0; i < count; ++i) { + p[i] = kmalloc(3000 + i*20, GFP_KERNEL); + *p[i] = i; + printk("p[%d] = %p\n", i, p[i]); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + +} + + +static void memory_test(void) +{ + printk("memory test\n"); + if (1) memory_kmem_cache_test(); + if (1) memory_page_alloc_test(); + if (1) memory_kmalloc_test(); + printk("End of memory test\n"); +} + + +/**************************************************************************** + ** Test 7: KThreads ** + ****************************************************************************/ +void kthread_test(void) +{ +} + + +/**************************************************************************** + ** Test 8: Work queues ** + ****************************************************************************/ +static void work_queue_func(struct work_struct *data); +static void work_queue_func2(struct work_struct *data); +static struct workqueue_struct *_wq; +static DECLARE_WORK(_wobj, work_queue_func); +static DECLARE_WORK(_wobj2, work_queue_func2); +static int wq_cnt = 0; + +static void work_queue_func(struct work_struct *data) +{ + printk("(1) Work queue function... Do some work here...\n"); + if (++wq_cnt < 5) + queue_work(_wq, &_wobj); +} + + +static void work_queue_func2(struct work_struct *data) +{ + printk("(2) Work queue function 2... Do some work here...\n"); + if (++wq_cnt < 10) + schedule_work(&_wobj2); +} + + +static void work_queue_test(void) +{ + int i; + printk("BEGIN WQ TEST\n"); + _wq = create_workqueue("HelloWQ"); + BUG_ON(_wq == NULL); + queue_work(_wq, &_wobj); + schedule_work(&_wobj2); + printk("END WQ TEST\n"); +} + + +/**************************************************************************** + ** Test 9: PCI ** + ****************************************************************************/ + +void pci_test(void) +{ + l4dde26_init_pci(); +} + + +/************************************************* + ** Main routine (switch on desired tests here) ** + *************************************************/ + +int main(int argc, const char **argv) +{ + int test_current = 1; + int test_kernel_thread = 1; + int test_wait = 1; + int test_tasklet = 1; + int test_timer = 1; + int test_memory = 1; + int test_kthread = 1; + int test_work = 1; + int test_pci = 1; + + msleep(1000); + + if (parse_cmdline(&argc, &argv, + 'c', "current", "test current() function", + PARSE_CMD_SWITCH, 1, &test_current, + 'k', "kernel-thread", "test startup of kernel threads", + PARSE_CMD_SWITCH, 1, &test_kernel_thread, + 'w', "waitqueue", "test wait queues", + PARSE_CMD_SWITCH, 1, &test_wait, + 't', "tasklet", "test tasklets", + PARSE_CMD_SWITCH, 1, &test_tasklet, + 'T', "timer", "test timers", + PARSE_CMD_SWITCH, 1, &test_timer, + 'm', "memory", "test memory management", + PARSE_CMD_SWITCH, 1, &test_memory, + 'K', "kthread", "test kthreads", + PARSE_CMD_SWITCH, 1, &test_kthread, + 'W', "workqueue", "test work queues", + PARSE_CMD_SWITCH, 1, &test_work, + 'p', "pci", "test PCI stuff", + PARSE_CMD_SWITCH, 1, &test_pci, + 0, 0)) + return 1; + + printk("DDEKit test. Carrying out tests:\n"); + printk("\t* current()\n"); + printk("\t* kernel_thread()\n"); + printk("\t* wait queues\n"); + printk("\t* tasklets\n"); + printk("\t* timers\n"); + printk("\t* memory management\n"); + printk("\t* kthreads\n"); + printk("\t* work queues\n"); + printk("\t* PCI subsystem\n"); + +#if 0 + printk("l4dde26_init()\n"); + l4dde26_init(); + printk("l4dde26_process_init()\n"); + l4dde26_process_init(); + printk("l4dde26_do_initcalls()\n"); + l4dde26_do_initcalls(); +#endif + + printk("Init done. Running tests.\n"); + if (test_current) current_test(); + if (test_kernel_thread) kernel_thread_test(); + if (test_wait) wq_test(); + if (test_tasklet) tasklet_test(); + if (test_timer) timer_test(); + if (test_memory) memory_test(); + if (1) kthread_test(); + if (test_work) work_queue_test(); +// if (test_pci) pci_test(); + printk("Test done.\n"); + + l4_sleep_forever(); + + return 0; +} diff --git a/libdde_linux26/examples/dde26_test/Makefile b/libdde_linux26/examples/dde26_test/Makefile new file mode 100644 index 00000000..444a1f2d --- /dev/null +++ b/libdde_linux26/examples/dde26_test/Makefile @@ -0,0 +1,21 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26_COMMON),y) +TARGET = dde26_test +endif + +SRC_C = main.c + +LIBS += -ldde_linux26.o -lddekit -lio -llist_alloc -lparsecmdline + +# DDE configuration +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk diff --git a/libdde_linux26/examples/dde26_test/main.c b/libdde_linux26/examples/dde26_test/main.c new file mode 100644 index 00000000..88d33ba6 --- /dev/null +++ b/libdde_linux26/examples/dde26_test/main.c @@ -0,0 +1,502 @@ +/* + * \brief DDE for Linux 2.6 test program + * \author Bjoern Doebel <doebel@os.inf.tu-dresden.de> + * \author Christian Helmuth <ch12@os.inf.tu-dresden.de> + * \date 2007-01-22 + */ + +#include <asm/current.h> + +#include <linux/kernel.h> +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +//#include <linux/kthread.h> + +#include <l4/dde/dde.h> +#include <l4/dde/ddekit/initcall.h> +#include <l4/dde/linux26/dde26.h> + +#include <l4/util/parse_cmd.h> +#include <l4/util/util.h> +#include <l4/log/l4log.h> + +/* We define 4 initcalls and see if these are executed + * in the beginning. + */ +static __init void foo(void) { printk("foo module_init\n"); } +static __init void bar(void) { printk("bar device_initcall\n"); } +static __init void bla(void) { printk("bla arch_initcall\n"); } +static __init void blub(void) { printk("blub subsys_initcall\n"); } +module_init(foo); +device_initcall(bar); +arch_initcall(bla); +subsys_initcall(blub); + +/*********************************************************************** + ** Test 1: Check whether the current() macro works. ** + ***********************************************************************/ +static void current_test(void) +{ + struct task_struct *t = NULL; + + printk("Current() test.\n"); + + t = current; + printk("\tt = %p\n", t); +} + + +/*********************************************************************** + ** Test 2: Getting complicated. Test startup of some kernel threads ** + ** and wait for them to finish using completions. ** + ***********************************************************************/ +#define NUM_KTHREADS 5 +static struct completion _kthread_completions_[NUM_KTHREADS]; + +static int kernel_thread_func(void *arg) +{ + printk("\t\tKernel thread %d\n", (int)arg); + printk("\t\tcurrent = %p\n", current); + + /* do some work */ + msleep(200); + + complete_and_exit( &_kthread_completions_[(int)arg], 0 ); + return 0; +} + + +static void kernel_thread_test(void) +{ + int i; + printk("Testing kernel_thread()\n"); + for (i=0; i < NUM_KTHREADS; i++) { + int j; + printk("\tInitializing completion for kernel thread.%x\n", i+1); + init_completion(&_kthread_completions_[i]); + printk("\tStarting kthread.%x\n", i+1); + j = kernel_thread(kernel_thread_func, (void *)i, 0); + printk("\treturn: %d\n", j); + } + + for (i=0; i < NUM_KTHREADS; i++) { + printk("\tWaiting for kthread.%x to complete.\n", i+1); + wait_for_completion(&_kthread_completions_[i]); + printk("\tkthread.%x has exited.\n", i+1); + } +} + + +/****************************************************************************** + ** Test 3: Test kernel wait queues: start a thread incrementing wait_value, ** + ** and sleep until wait_value is larger than 6 for the first time. ** + ******************************************************************************/ +static DECLARE_WAIT_QUEUE_HEAD(_wq_head); +static int wait_value = 0; +static struct completion wq_completion; + +static int inc_func(void *arg) +{ + int i = 0; + + printk("\033[33mI am counting up wait_value.\033[0m\n"); + for (i=0; i<10; i++) + { + printk("\033[33mwait_value: %d\033[0m\n", ++wait_value); + wake_up(&_wq_head); + msleep(500); + } + complete_and_exit(&wq_completion, 0); +} + + +static void wq_test(void) +{ + int pid; + printk("\033[32mWait_queue test. I'm waiting vor wait_value to become >6.\033[0m\n"); + + init_completion(&wq_completion); + pid = kernel_thread(inc_func, 0, 0); + + wait_event(_wq_head, wait_value > 6); + printk("\033[32;1mwait_value > 6 occured!\033[0m\n"); + + wait_for_completion(&wq_completion); + printk("\033[32mtest done.\033[0m\n"); +} + + +/**************************************************************************** + ** Test 4: Tasklets ** + ****************************************************************************/ +static void tasklet_func(unsigned long i) +{ + printk("TASKLET: %d\n", i); +} + + +static DECLARE_TASKLET(low0, tasklet_func, 0); +static DECLARE_TASKLET(low1, tasklet_func, 1); +static DECLARE_TASKLET(low2, tasklet_func, 2); +static DECLARE_TASKLET_DISABLED(low3, tasklet_func, 3); + +static DECLARE_TASKLET(hi0, tasklet_func, 10); +static DECLARE_TASKLET(hi1, tasklet_func, 11); +static DECLARE_TASKLET_DISABLED(hi2, tasklet_func, 12); + + +static void tasklet_test(void) +{ + printk("BEGIN TASKLET TEST\n"); + + l4dde26_softirq_init(); + + printk("sleep 1000 msec\n"); + msleep(1000); + + printk("Scheduling tasklets 0-2 immediately. 3 is disabled for 2 seconds.\n"); + tasklet_schedule(&low0); + + tasklet_schedule(&low1); + tasklet_schedule(&low2); + tasklet_schedule(&low3); + msleep(2000); + tasklet_enable(&low3); + + msleep(1000); + + printk("Scheduling hi_tasklets 10-12, and tasklets 0-2\n"); + tasklet_hi_schedule(&hi0); + tasklet_hi_schedule(&hi1); + tasklet_hi_schedule(&hi2); + tasklet_schedule(&low0); + tasklet_schedule(&low1); + tasklet_schedule(&low2); + tasklet_enable(&hi2); + + msleep(1000); + printk("Scheduling (disabled) tasklet 3 twice - should only run once after enabling.\n"); + tasklet_disable(&low3); + tasklet_schedule(&low3); + tasklet_schedule(&low3); + tasklet_enable(&low3); + + msleep(1000); + + printk("END TASKLET TEST\n"); +} + + +/****************************************************************************** + ** Test 5: Timers ** + ** ** + ** Schedule a periodic timer printing "tick" every second. Additionally, ** + ** schedule timers for 5, 10, 15, 20, and 25 seconds. Timer at 15s will ** + ** deactivate the 20s timer. ** + ******************************************************************************/ + +static struct timer_list _timer; +static struct timer_list _timer5; +static struct timer_list _timer10; +static struct timer_list _timer15; +static struct timer_list _timer20; +static struct timer_list _timer25; + +static void tick_func(unsigned long d) +{ + printk("tick (%ld)\n", jiffies); + _timer.expires = jiffies + HZ; + add_timer(&_timer); +} + + +static void timer_func(unsigned long d) +{ + printk("timer_func: %lu\n", d); + + if (d == 15) { + printk("De-scheduling 20s timer.\n"); + del_timer(&_timer20); + } + + if (timer_pending(&_timer20)) + printk("timer for 20s still pending.\n"); + else + printk("timer for 20s has been disabled.\n"); +} + + +static void timer_test(void) +{ + l4dde26_init_timers(); + + printk("BEGIN TIMER TEST\n"); + printk("jiffies(%p): %ld, HZ(%p): %ld\n", &jiffies, jiffies, &HZ, HZ); + + setup_timer(&_timer, tick_func, 0); + _timer.expires = jiffies + HZ; + add_timer(&_timer); + + setup_timer(&_timer5, timer_func, 5); + _timer5.expires = jiffies + 5*HZ; + setup_timer(&_timer10, timer_func, 10); + _timer10.expires = jiffies + 10*HZ; + setup_timer(&_timer15, timer_func, 15); + _timer15.expires = jiffies + 15*HZ; + setup_timer(&_timer20, timer_func, 20); + _timer20.expires = jiffies + 20*HZ; + setup_timer(&_timer25, timer_func, 25); + _timer25.expires = jiffies + 25*HZ; + + add_timer(&_timer5); + add_timer(&_timer10); + add_timer(&_timer15); + add_timer(&_timer20); + add_timer(&_timer25); + + msleep(30000); + + del_timer(&_timer); + printk("END TIMER TEST\n"); +} + + +/****************************** + ** Test 6: Memory subsystem ** + ******************************/ + +static void memory_kmem_cache_test(void) +{ + struct kmem_cache *cache0; + struct obj0 + { + unsigned foo; + unsigned bar; + }; + static struct obj0 *p0[1024]; + + struct kmem_cache *cache1; + struct obj1 + { + char foo[50]; + unsigned *bar; + }; + static struct obj1 *p1[256]; + + cache0 = kmem_cache_create("obj0", sizeof(*p0[0]), 0, 0, 0); + cache1 = kmem_cache_create("obj1", sizeof(*p1[0]), 0, 0, 0); + printk("kmem caches: %p %p\n", cache0, cache1); + + unsigned i; + for (i = 0; i < 1024; ++i) + p0[i] = kmem_cache_alloc(cache0, i); + + for (i = 0; i < 256; ++i) + p1[i] = kmem_cache_alloc(cache1, i); + + for (i = 256; i > 0; --i) + kmem_cache_free(cache1, p1[i-1]); + + for (i = 1024; i > 0; --i) + kmem_cache_free(cache0, p0[i-1]); + + kmem_cache_destroy(cache1); + kmem_cache_destroy(cache0); + printk("Done testing kmem_cache_alloc() & co.\n"); +} + + +static void memory_page_alloc_test(void) +{ + unsigned long p[4]; + p[0] = __get_free_page(GFP_KERNEL); + p[1] = __get_free_pages(GFP_KERNEL, 1); + p[2] = __get_free_pages(GFP_KERNEL, 2); + p[3] = __get_free_pages(GFP_KERNEL, 3); + printk("pages: %p %p %p %p\n", p[0], p[1], p[2], p[3]); + + free_pages(p[0], 0); + free_pages(p[1], 1); + free_pages(p[2], 2); + free_pages(p[3], 3); + printk("Freed pages\n"); +} + + +static void memory_kmalloc_test(void) +{ + // XXX initialized by dde26_init()! +// l4dde26_kmalloc_init(); + + const unsigned count = 33; + char *p[count]; + + int i; + for (i = 0; i < count; ++i) { + p[i] = kmalloc(32 + i*15, GFP_KERNEL); + *p[i] = i; + printk("p[%d] = %p\n", i, p[i]); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + + for (i = 0; i < count; ++i) { + p[i] = kmalloc(3000 + i*20, GFP_KERNEL); + *p[i] = i; + printk("p[%d] = %p\n", i, p[i]); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + +} + + +static void memory_test(void) +{ + printk("memory test\n"); + if (1) memory_kmem_cache_test(); + if (1) memory_page_alloc_test(); + if (1) memory_kmalloc_test(); + printk("End of memory test\n"); +} + + +/**************************************************************************** + ** Test 7: KThreads ** + ****************************************************************************/ +void kthread_test(void) +{ +} + + +/**************************************************************************** + ** Test 8: Work queues ** + ****************************************************************************/ +static void work_queue_func(struct work_struct *data); +static void work_queue_func2(struct work_struct *data); +static struct workqueue_struct *_wq; +static DECLARE_WORK(_wobj, work_queue_func); +static DECLARE_WORK(_wobj2, work_queue_func2); +static int wq_cnt = 0; + +static void work_queue_func(struct work_struct *data) +{ + printk("(1) Work queue function... Do some work here...\n"); + if (++wq_cnt < 5) + queue_work(_wq, &_wobj); +} + + +static void work_queue_func2(struct work_struct *data) +{ + printk("(2) Work queue function 2... Do some work here...\n"); + if (++wq_cnt < 10) + schedule_work(&_wobj2); +} + + +static void work_queue_test(void) +{ + int i; + printk("BEGIN WQ TEST\n"); + _wq = create_workqueue("HelloWQ"); + BUG_ON(_wq == NULL); + queue_work(_wq, &_wobj); + schedule_work(&_wobj2); + printk("END WQ TEST\n"); +} + + +/**************************************************************************** + ** Test 9: PCI ** + ****************************************************************************/ + +void pci_test(void) +{ + l4dde26_init_pci(); +} + + +/************************************************* + ** Main routine (switch on desired tests here) ** + *************************************************/ + +int main(int argc, const char **argv) +{ + int test_current = 1; + int test_kernel_thread = 1; + int test_wait = 1; + int test_tasklet = 1; + int test_timer = 1; + int test_memory = 1; + int test_kthread = 1; + int test_work = 1; + int test_pci = 1; + + msleep(1000); + + if (parse_cmdline(&argc, &argv, + 'c', "current", "test current() function", + PARSE_CMD_SWITCH, 1, &test_current, + 'k', "kernel-thread", "test startup of kernel threads", + PARSE_CMD_SWITCH, 1, &test_kernel_thread, + 'w', "waitqueue", "test wait queues", + PARSE_CMD_SWITCH, 1, &test_wait, + 't', "tasklet", "test tasklets", + PARSE_CMD_SWITCH, 1, &test_tasklet, + 'T', "timer", "test timers", + PARSE_CMD_SWITCH, 1, &test_timer, + 'm', "memory", "test memory management", + PARSE_CMD_SWITCH, 1, &test_memory, + 'K', "kthread", "test kthreads", + PARSE_CMD_SWITCH, 1, &test_kthread, + 'W', "workqueue", "test work queues", + PARSE_CMD_SWITCH, 1, &test_work, + 'p', "pci", "test PCI stuff", + PARSE_CMD_SWITCH, 1, &test_pci, + 0, 0)) + return 1; + + printk("DDEKit test. Carrying out tests:\n"); + printk("\t* current()\n"); + printk("\t* kernel_thread()\n"); + printk("\t* wait queues\n"); + printk("\t* tasklets\n"); + printk("\t* timers\n"); + printk("\t* memory management\n"); + printk("\t* kthreads\n"); + printk("\t* work queues\n"); + printk("\t* PCI subsystem\n"); + +#if 0 + printk("l4dde26_init()\n"); + l4dde26_init(); + printk("l4dde26_process_init()\n"); + l4dde26_process_init(); + printk("l4dde26_do_initcalls()\n"); + l4dde26_do_initcalls(); +#endif + + printk("Init done. Running tests.\n"); + if (test_current) current_test(); + if (test_kernel_thread) kernel_thread_test(); + if (test_wait) wq_test(); + if (test_tasklet) tasklet_test(); + if (test_timer) timer_test(); + if (test_memory) memory_test(); + if (1) kthread_test(); + if (test_work) work_queue_test(); +// if (test_pci) pci_test(); + printk("Test done.\n"); + + l4_sleep_forever(); + + return 0; +} diff --git a/libdde_linux26/examples/ne2k/.svn/all-wcprops b/libdde_linux26/examples/ne2k/.svn/all-wcprops new file mode 100644 index 00000000..ba1cfa01 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/all-wcprops @@ -0,0 +1,53 @@ +K 25 +svn:wc:ra_dav:version-url +V 64 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k +END +arping.c +K 25 +svn:wc:ra_dav:version-url +V 73 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/arping.c +END +8390.c +K 25 +svn:wc:ra_dav:version-url +V 71 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/8390.c +END +main.c +K 25 +svn:wc:ra_dav:version-url +V 71 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/main.c +END +arping.h +K 25 +svn:wc:ra_dav:version-url +V 73 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/arping.h +END +8390.h +K 25 +svn:wc:ra_dav:version-url +V 71 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/8390.h +END +ne2k-pci.c +K 25 +svn:wc:ra_dav:version-url +V 75 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/ne2k-pci.c +END +lib8390.c +K 25 +svn:wc:ra_dav:version-url +V 74 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/lib8390.c +END +Makefile +K 25 +svn:wc:ra_dav:version-url +V 73 +/repos/tudos/!svn/ver/455/trunk/l4/pkg/dde/linux26/examples/ne2k/Makefile +END diff --git a/libdde_linux26/examples/ne2k/.svn/entries b/libdde_linux26/examples/ne2k/.svn/entries new file mode 100644 index 00000000..b8581989 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/entries @@ -0,0 +1,300 @@ +9 + +dir +465 +http://svn.tudos.org/repos/tudos/trunk/l4/pkg/dde/linux26/examples/ne2k +http://svn.tudos.org/repos/tudos + + + +2009-05-20T14:32:55.606606Z +455 +l4check + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +a704ac0b-3a55-4d43-a2a9-7be6f07c34fb + +arping.c +file + + + + +2009-11-15T17:17:14.000000Z +7ecf555fdf42cf807c50307a4d606c1f +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +5020 + +8390.c +file + + + + +2009-11-15T17:17:14.000000Z +3d38acf72b8158d4ad5177a249629995 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +2262 + +main.c +file + + + + +2009-11-15T17:17:14.000000Z +dea13f4d4bf665c902dd7374bd71ced5 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +2336 + +arping.h +file + + + + +2009-11-15T17:17:14.000000Z +0824ab97e55f86b3b57e79e8f2f3a8c2 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +375 + +8390.h +file + + + + +2009-11-15T17:17:14.000000Z +7f695007d1e1230448538e76664346c8 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +9629 + +ne2k-pci.c +file + + + + +2009-11-15T17:17:14.000000Z +87272d5c5070aef58772ef09202657de +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +21164 + +lib8390.c +file + + + + +2009-11-15T17:17:14.000000Z +7de96b11d54826c5dac5c04aee240d79 +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +35915 + +Makefile +file + + + + +2009-11-15T17:17:14.000000Z +0028681d378a3d65e7efa5fdae1d84be +2009-05-20T14:32:55.606606Z +455 +l4check + + + + + + + + + + + + + + + + + + + + + +706 + diff --git a/libdde_linux26/examples/ne2k/.svn/format b/libdde_linux26/examples/ne2k/.svn/format new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/format @@ -0,0 +1 @@ +9 diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/8390.c.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/8390.c.svn-base new file mode 100644 index 00000000..ec3e22e6 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/8390.c.svn-base @@ -0,0 +1,109 @@ +/* 8390 core for usual drivers */ + +static const char version[] = + "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include "lib8390.c" + +int ei_open(struct net_device *dev) +{ + return __ei_open(dev); +} +EXPORT_SYMBOL(ei_open); + +int ei_close(struct net_device *dev) +{ + return __ei_close(dev); +} +EXPORT_SYMBOL(ei_close); + +int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + return __ei_start_xmit(skb, dev); +} +EXPORT_SYMBOL(ei_start_xmit); + +struct net_device_stats *ei_get_stats(struct net_device *dev) +{ + return __ei_get_stats(dev); +} +EXPORT_SYMBOL(ei_get_stats); + +void ei_set_multicast_list(struct net_device *dev) +{ + __ei_set_multicast_list(dev); +} +EXPORT_SYMBOL(ei_set_multicast_list); + +void ei_tx_timeout(struct net_device *dev) +{ + __ei_tx_timeout(dev); +} +EXPORT_SYMBOL(ei_tx_timeout); + +irqreturn_t ei_interrupt(int irq, void *dev_id) +{ + return __ei_interrupt(irq, dev_id); +} +EXPORT_SYMBOL(ei_interrupt); + +#ifdef CONFIG_NET_POLL_CONTROLLER +void ei_poll(struct net_device *dev) +{ + __ei_poll(dev); +} +EXPORT_SYMBOL(ei_poll); +#endif + +const struct net_device_ops ei_netdev_ops = { + .ndo_open = ei_open, + .ndo_stop = ei_close, + .ndo_start_xmit = ei_start_xmit, + .ndo_tx_timeout = ei_tx_timeout, + .ndo_get_stats = ei_get_stats, + .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ei_poll, +#endif +}; +EXPORT_SYMBOL(ei_netdev_ops); + +struct net_device *__alloc_ei_netdev(int size) +{ + struct net_device *dev = ____alloc_ei_netdev(size); +#ifdef CONFIG_COMPAT_NET_DEV_OPS + if (dev) { + dev->hard_start_xmit = ei_start_xmit; + dev->get_stats = ei_get_stats; + dev->set_multicast_list = ei_set_multicast_list; + dev->tx_timeout = ei_tx_timeout; + } +#endif + return dev; +} +EXPORT_SYMBOL(__alloc_ei_netdev); + +void NS8390_init(struct net_device *dev, int startp) +{ + __NS8390_init(dev, startp); +} +EXPORT_SYMBOL(NS8390_init); + +#if defined(MODULE) + +static int __init ns8390_module_init(void) +{ + return 0; +} + +static void __exit ns8390_module_exit(void) +{ +} + +module_init(ns8390_module_init); +module_exit(ns8390_module_exit); +#endif /* MODULE */ +MODULE_LICENSE("GPL"); diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/8390.h.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/8390.h.svn-base new file mode 100644 index 00000000..3c61d6d2 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/8390.h.svn-base @@ -0,0 +1,231 @@ +/* Generic NS8390 register definitions. */ +/* This file is part of Donald Becker's 8390 drivers, and is distributed + under the same license. Auto-loading of 8390.o only in v2.2 - Paul G. + Some of these names and comments originated from the Crynwr + packet drivers, which are distributed under the GPL. */ + +#ifndef _8390_h +#define _8390_h + +#include <linux/if_ether.h> +#include <linux/ioport.h> +#include <linux/skbuff.h> + +#define TX_PAGES 12 /* Two Tx slots */ + +#define ETHER_ADDR_LEN 6 + +/* The 8390 specific per-packet-header format. */ +struct e8390_pkt_hdr { + unsigned char status; /* status */ + unsigned char next; /* pointer to next packet. */ + unsigned short count; /* header + packet length in bytes */ +}; + +#ifdef notdef +extern int ei_debug; +#else +#define ei_debug 1 +#endif + +#ifdef CONFIG_NET_POLL_CONTROLLER +extern void ei_poll(struct net_device *dev); +extern void eip_poll(struct net_device *dev); +#endif + + +/* Without I/O delay - non ISA or later chips */ +extern void NS8390_init(struct net_device *dev, int startp); +extern int ei_open(struct net_device *dev); +extern int ei_close(struct net_device *dev); +extern irqreturn_t ei_interrupt(int irq, void *dev_id); +extern void ei_tx_timeout(struct net_device *dev); +extern int ei_start_xmit(struct sk_buff *skb, struct net_device *dev); +extern void ei_set_multicast_list(struct net_device *dev); +extern struct net_device_stats *ei_get_stats(struct net_device *dev); + +extern const struct net_device_ops ei_netdev_ops; + +extern struct net_device *__alloc_ei_netdev(int size); +static inline struct net_device *alloc_ei_netdev(void) +{ + return __alloc_ei_netdev(0); +} + +/* With I/O delay form */ +extern void NS8390p_init(struct net_device *dev, int startp); +extern int eip_open(struct net_device *dev); +extern int eip_close(struct net_device *dev); +extern irqreturn_t eip_interrupt(int irq, void *dev_id); +extern void eip_tx_timeout(struct net_device *dev); +extern int eip_start_xmit(struct sk_buff *skb, struct net_device *dev); +extern void eip_set_multicast_list(struct net_device *dev); +extern struct net_device_stats *eip_get_stats(struct net_device *dev); + +extern const struct net_device_ops eip_netdev_ops; + +extern struct net_device *__alloc_eip_netdev(int size); +static inline struct net_device *alloc_eip_netdev(void) +{ + return __alloc_eip_netdev(0); +} + +/* You have one of these per-board */ +struct ei_device { + const char *name; + void (*reset_8390)(struct net_device *); + void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int); + void (*block_output)(struct net_device *, int, const unsigned char *, int); + void (*block_input)(struct net_device *, int, struct sk_buff *, int); + unsigned long rmem_start; + unsigned long rmem_end; + void __iomem *mem; + unsigned char mcfilter[8]; + unsigned open:1; + unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */ + unsigned bigendian:1; /* 16-bit big endian mode. Do NOT */ + /* set this on random 8390 clones! */ + unsigned txing:1; /* Transmit Active */ + unsigned irqlock:1; /* 8390's intrs disabled when '1'. */ + unsigned dmaing:1; /* Remote DMA Active */ + unsigned char tx_start_page, rx_start_page, stop_page; + unsigned char current_page; /* Read pointer in buffer */ + unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */ + unsigned char txqueue; /* Tx Packet buffer queue length. */ + short tx1, tx2; /* Packet lengths for ping-pong tx. */ + short lasttx; /* Alpha version consistency check. */ + unsigned char reg0; /* Register '0' in a WD8013 */ + unsigned char reg5; /* Register '5' in a WD8013 */ + unsigned char saved_irq; /* Original dev->irq value. */ + u32 *reg_offset; /* Register mapping table */ + spinlock_t page_lock; /* Page register locks */ + unsigned long priv; /* Private field to store bus IDs etc. */ +#ifdef AX88796_PLATFORM + unsigned char rxcr_base; /* default value for RXCR */ +#endif +}; + +/* The maximum number of 8390 interrupt service routines called per IRQ. */ +#define MAX_SERVICE 12 + +/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */ +#define TX_TIMEOUT (20*HZ/100) + +#define ei_status (*(struct ei_device *)netdev_priv(dev)) + +/* Some generic ethernet register configurations. */ +#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */ +#define E8390_RX_IRQ_MASK 0x5 + +#ifdef AX88796_PLATFORM +#define E8390_RXCONFIG (ei_status.rxcr_base | 0x04) +#define E8390_RXOFF (ei_status.rxcr_base | 0x20) +#else +#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */ +#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */ +#endif + +#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */ +#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */ + + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* + * Only generate indirect loads given a machine that needs them. + * - removed AMIGA_PCMCIA from this list, handled as ISA io now + * - the _p for generates no delay by default 8390p.c overrides this. + */ + +#ifndef ei_inb +#define ei_inb(_p) inb(_p) +#define ei_outb(_v,_p) outb(_v,_p) +#define ei_inb_p(_p) inb(_p) +#define ei_outb_p(_v,_p) outb(_v,_p) +#endif + +#ifndef EI_SHIFT +#define EI_SHIFT(x) (x) +#endif + +#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */ +#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */ +#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */ +#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */ +#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */ +#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */ +#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */ +#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */ +#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */ +#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */ +#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */ +#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */ +#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */ +#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */ +#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */ +#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */ +#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */ +#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */ +#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */ +#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */ +#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */ +#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */ +#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */ +#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */ +#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in EN0_DCFG - Data config register */ +#define ENDCFG_WTS 0x01 /* word transfer mode selection */ +#define ENDCFG_BOS 0x02 /* byte order selection */ + +/* Page 1 register offsets. */ +#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */ +#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */ +#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */ +#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */ +#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +#endif /* _8390_h */ diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/Makefile.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/Makefile.svn-base new file mode 100644 index 00000000..2a7b1543 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/Makefile.svn-base @@ -0,0 +1,32 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26_NET),y) +TARGET = ne2k_dde26 +endif + +SRC_C = ne2k-pci.c 8390.c main.c arping.c + +LIBS += -ldde_linux26_net -ldde_linux26.o -lddekit -lio -lomega0 -llist_alloc + +PRIVATE_INCDIR = $(PKGDIR_ABS)/linux26/include $(MY_DDE_INCDIR) $(MY_LINUX26_INCDIR) \ + $(OBJ_BASE)/include/uclibc + +LIBCINCDIR = -nostdinc $(I_GCCINCDIR) +DEFINES = -D__KERNEL__ -DDDE_LINUX\ + $(KBUILD_DEFINES) +CPPFLAGS += $(KBUILD_CPPFLAGS) + +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk + +foo : + @echo $(L4INCDIR) + @echo $(OBJ_BASE) diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/arping.c.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/arping.c.svn-base new file mode 100644 index 00000000..18876806 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/arping.c.svn-base @@ -0,0 +1,185 @@ +/**************************************************************** + * (c) 2007 Technische Universitaet Dresden * + * This file is part of DROPS, which is distributed under the * + * terms of the GNU General Public License 2. Please see the * + * COPYING file for details. * + ****************************************************************/ + +#include <l4/log/l4log.h> +#include <l4/util/util.h> +#include <l4/util/l4_macros.h> +#include <l4/sys/ipc.h> + +#include <linux/netdevice.h> +#include <linux/if_ether.h> + +#include "arping.h" + +#define PROT_ICMP 1 +#define ICMP_REPLY 0 +#define ICMP_REQ 8 +#define ETH_ALEN 6 + +/* configuration */ +int arping_verbose = 0; // verbose + +#define VERBOSE_LOG(fmt, ...) \ + do { \ + if (arping_verbose) printk(fmt, ##__VA_ARGS__); \ + } while (0); + +char LOG_tag[9] = "arping"; +l4_ssize_t l4libc_heapsize = 32 * 1024; + +static unsigned char broadcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static int exit_somewhen = 0; + + +struct ethernet_hdr +{ + unsigned char dest[6]; + unsigned char src[6]; + unsigned char type[2]; +}; + + +struct ip_hdr +{ + char version_length; + char type; + l4_int16_t length; + l4_int16_t id; + l4_int16_t flags_offset; + char ttl; + char protocol; + l4_int16_t checksum; + l4_int32_t src_ip; + l4_int32_t dest_ip; +}; + + +struct icmp_hdr +{ + char type; + char code; + l4_uint16_t checksum; + l4_uint16_t id; + l4_uint16_t seq_num; +}; + + +static int handle_icmp_packet(struct sk_buff *skb); +static int handle_icmp_packet(struct sk_buff *skb) +{ + char *data = skb->data; + struct ethernet_hdr *eth = NULL; + struct ethernet_hdr *e = NULL; + struct ip_hdr *ip = NULL; + struct ip_hdr *iphdr = NULL; + struct icmp_hdr *icmp = NULL; + struct icmp_hdr *icmp2 = NULL; + int ver, len; + struct sk_buff *snd_skb = NULL; + + eth = (struct ethernet_hdr *)data; + VERBOSE_LOG("dest mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + eth->dest[0], eth->dest[1], eth->dest[2], + eth->dest[3], eth->dest[4], eth->dest[5]); + VERBOSE_LOG("src mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + eth->src[0], eth->src[1], eth->src[2], + eth->src[3], eth->src[4], eth->src[5]); + VERBOSE_LOG("type field = %02x%02x\n", eth->type[0], eth->type[1]); + if (eth->type[0] != 0x08 || eth->type[1] != 0x00) { + printk("unknown ethernet packet type!\n"); + return -1; + } + + ip = (struct ip_hdr *)(data + sizeof(struct ethernet_hdr)); + VERBOSE_LOG("protocol = %02x (2x?)\n", ip->protocol, PROT_ICMP); + if (ip->protocol != PROT_ICMP) + { + printk("Unknown packet type.\n"); + return -1; + } + + VERBOSE_LOG("ICMP packet!\n"); + ver = ip->version_length >> 4; + len = ip->version_length & 0x0F; + VERBOSE_LOG("IP version = %d, length = %d\n", ver, len); + + VERBOSE_LOG("src IP: "NIPQUAD_FMT"\n", NIPQUAD(ip->src_ip)); + VERBOSE_LOG("dest IP: "NIPQUAD_FMT"\n", NIPQUAD(ip->dest_ip)); + + icmp = (struct icmp_hdr *)(data + sizeof(struct ethernet_hdr) + + sizeof(struct ip_hdr)); + + if (icmp->type != ICMP_REQ) + { + printk("This is no ICMP request.\n"); + return -1; + } + VERBOSE_LOG("Hey this is an ICMP request just for me. :)\n"); + VERBOSE_LOG("ICMP type : %d\n", icmp->type); + VERBOSE_LOG("ICMP code : %d\n", icmp->code); + VERBOSE_LOG("ICMP seq : %d\n", ntohs(icmp->seq_num)); + + snd_skb = alloc_skb(skb->len + skb->dev->hard_header_len, GFP_KERNEL); + memcpy(snd_skb->data, skb->data, skb->len); + + e = (struct ethernet_hdr *)snd_skb->data; + memcpy(e->src, eth->dest, ETH_ALEN); + memcpy(e->dest, eth->src, ETH_ALEN); + VERBOSE_LOG("dest mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + e->dest[0], e->dest[1], e->dest[2], + e->dest[3], e->dest[4], e->dest[5]); + VERBOSE_LOG("src mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + e->src[0], e->src[1], e->src[2], + e->src[3], e->src[4], e->src[5]); + e->type[0] = 0x08; + e->type[1] = 0x00; + + iphdr = (struct ip_hdr *)(snd_skb->data + sizeof(struct ethernet_hdr)); + *iphdr = *ip; + // also switch src and dest + iphdr->src_ip = ip->dest_ip; + iphdr->dest_ip = ip->src_ip; + VERBOSE_LOG("src IP: "NIPQUAD_FMT"\n", NIPQUAD(iphdr->src_ip)); + VERBOSE_LOG("dest IP: "NIPQUAD_FMT"\n", NIPQUAD(iphdr->dest_ip)); + + icmp2 = (struct icmp_hdr *)(snd_skb->data + sizeof(struct ethernet_hdr) + + sizeof(struct ip_hdr)); + *icmp2 = *icmp; + icmp2->type = ICMP_REPLY; + + snd_skb->dev = skb->dev; + snd_skb->len = skb->len; + + VERBOSE_LOG("sending reply\n"); + skb->dev->hard_start_xmit(snd_skb, skb->dev); + VERBOSE_LOG("done\n"); + + return 0; +} + +ddekit_sem_t *arping_semaphore = NULL; +struct arping_elem *arping_list = NULL; + +int arping(void) +{ + arping_semaphore = ddekit_sem_init(0); + + while(1) + { + ddekit_sem_down(arping_semaphore); + struct arping_elem *elem = arping_list; + arping_list = arping_list->next; + + /* parse packet */ + int err = handle_icmp_packet(elem->skb); + VERBOSE_LOG("handle_icmp_packet: %d\n", err); + + kfree_skb(elem->skb); + kfree(elem); + } +} + diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/arping.h.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/arping.h.svn-base new file mode 100644 index 00000000..2df7f303 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/arping.h.svn-base @@ -0,0 +1,15 @@ +#pragma once + +#include <l4/dde/ddekit/semaphore.h> + +struct arping_elem +{ + struct arping_elem *next; + struct sk_buff *skb; +}; + +extern ddekit_sem_t *arping_semaphore; +extern struct arping_elem *arping_list; + +#define mac_fmt "%02X:%02X:%02X:%02X:%02X:%02X" +#define mac_str(mac) (unsigned char)((mac)[0]), (unsigned char)((mac)[1]),(mac)[2],(mac)[3],(mac)[4],(mac)[5] diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/lib8390.c.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/lib8390.c.svn-base new file mode 100644 index 00000000..789b6cb7 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/lib8390.c.svn-base @@ -0,0 +1,1125 @@ +/* 8390.c: A general NS8390 ethernet driver core for linux. */ +/* + Written 1992-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + + This is the chip-specific code for many 8390-based ethernet adaptors. + This is not a complete driver, it must be combined with board-specific + code such as ne.c, wd.c, 3c503.c, etc. + + Seeing how at least eight drivers use this code, (not counting the + PCMCIA ones either) it is easy to break some card by what seems like + a simple innocent change. Please contact me or Donald if you think + you have found something that needs changing. -- PG + + + Changelog: + + Paul Gortmaker : remove set_bit lock, other cleanups. + Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to + ei_block_input() for eth_io_copy_and_sum(). + Paul Gortmaker : exchange static int ei_pingpong for a #define, + also add better Tx error handling. + Paul Gortmaker : rewrite Rx overrun handling as per NS specs. + Alexey Kuznetsov : use the 8390's six bit hash multicast filter. + Paul Gortmaker : tweak ANK's above multicast changes a bit. + Paul Gortmaker : update packet statistics for v2.1.x + Alan Cox : support arbitary stupid port mappings on the + 68K Macintosh. Support >16bit I/O spaces + Paul Gortmaker : add kmod support for auto-loading of the 8390 + module by all drivers that require it. + Alan Cox : Spinlocking work, added 'BUG_83C690' + Paul Gortmaker : Separate out Tx timeout code from Tx path. + Paul Gortmaker : Remove old unused single Tx buffer code. + Hayato Fujiwara : Add m32r support. + Paul Gortmaker : use skb_padto() instead of stack scratch area + + Sources: + The National Semiconductor LAN Databook, and the 3Com 3c503 databook. + + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/bitops.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/in.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/crc32.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#define NS8390_CORE +#include "8390.h" + +#define BUG_83C690 + +/* These are the operational function interfaces to board-specific + routines. + void reset_8390(struct net_device *dev) + Resets the board associated with DEV, including a hardware reset of + the 8390. This is only called when there is a transmit timeout, and + it is always followed by 8390_init(). + void block_output(struct net_device *dev, int count, const unsigned char *buf, + int start_page) + Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The + "page" value uses the 8390's 256-byte pages. + void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page) + Read the 4 byte, page aligned 8390 header. *If* there is a + subsequent read, it will be of the rest of the packet. + void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) + Read COUNT bytes from the packet buffer into the skb data area. Start + reading from RING_OFFSET, the address as the 8390 sees it. This will always + follow the read of the 8390 header. +*/ +#define ei_reset_8390 (ei_local->reset_8390) +#define ei_block_output (ei_local->block_output) +#define ei_block_input (ei_local->block_input) +#define ei_get_8390_hdr (ei_local->get_8390_hdr) + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef ei_debug +int ei_debug = 1; +#endif + +/* Index to functions. */ +static void ei_tx_intr(struct net_device *dev); +static void ei_tx_err(struct net_device *dev); +void ei_tx_timeout(struct net_device *dev); +static void ei_receive(struct net_device *dev); +static void ei_rx_overrun(struct net_device *dev); + +/* Routines generic to NS8390-based boards. */ +static void NS8390_trigger_send(struct net_device *dev, unsigned int length, + int start_page); +static void do_set_multicast_list(struct net_device *dev); +static void __NS8390_init(struct net_device *dev, int startp); + +/* + * SMP and the 8390 setup. + * + * The 8390 isnt exactly designed to be multithreaded on RX/TX. There is + * a page register that controls bank and packet buffer access. We guard + * this with ei_local->page_lock. Nobody should assume or set the page other + * than zero when the lock is not held. Lock holders must restore page 0 + * before unlocking. Even pure readers must take the lock to protect in + * page 0. + * + * To make life difficult the chip can also be very slow. We therefore can't + * just use spinlocks. For the longer lockups we disable the irq the device + * sits on and hold the lock. We must hold the lock because there is a dual + * processor case other than interrupts (get stats/set multicast list in + * parallel with each other and transmit). + * + * Note: in theory we can just disable the irq on the card _but_ there is + * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs" + * enter lock, take the queued irq. So we waddle instead of flying. + * + * Finally by special arrangement for the purpose of being generally + * annoying the transmit function is called bh atomic. That places + * restrictions on the user context callers as disable_irq won't save + * them. + * + * Additional explanation of problems with locking by Alan Cox: + * + * "The author (me) didn't use spin_lock_irqsave because the slowness of the + * card means that approach caused horrible problems like losing serial data + * at 38400 baud on some chips. Remember many 8390 nics on PCI were ISA + * chips with FPGA front ends. + * + * Ok the logic behind the 8390 is very simple: + * + * Things to know + * - IRQ delivery is asynchronous to the PCI bus + * - Blocking the local CPU IRQ via spin locks was too slow + * - The chip has register windows needing locking work + * + * So the path was once (I say once as people appear to have changed it + * in the mean time and it now looks rather bogus if the changes to use + * disable_irq_nosync_irqsave are disabling the local IRQ) + * + * + * Take the page lock + * Mask the IRQ on chip + * Disable the IRQ (but not mask locally- someone seems to have + * broken this with the lock validator stuff) + * [This must be _nosync as the page lock may otherwise + * deadlock us] + * Drop the page lock and turn IRQs back on + * + * At this point an existing IRQ may still be running but we can't + * get a new one + * + * Take the lock (so we know the IRQ has terminated) but don't mask + * the IRQs on the processor + * Set irqlock [for debug] + * + * Transmit (slow as ****) + * + * re-enable the IRQ + * + * + * We have to use disable_irq because otherwise you will get delayed + * interrupts on the APIC bus deadlocking the transmit path. + * + * Quite hairy but the chip simply wasn't designed for SMP and you can't + * even ACK an interrupt without risking corrupting other parallel + * activities on the chip." [lkml, 25 Jul 2007] + */ + + + +/** + * ei_open - Open/initialize the board. + * @dev: network device to initialize + * + * This routine goes all-out, setting everything + * up anew at each open, even though many of these registers should only + * need to be set once at boot. + */ +static int __ei_open(struct net_device *dev) +{ + unsigned long flags; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + + if (dev->watchdog_timeo <= 0) + dev->watchdog_timeo = TX_TIMEOUT; + + /* + * Grab the page lock so we own the register set, then call + * the init function. + */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + __NS8390_init(dev, 1); + /* Set the flag before we drop the lock, That way the IRQ arrives + after its set and we get no silly warnings */ + netif_start_queue(dev); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + ei_local->irqlock = 0; + return 0; +} + +/** + * ei_close - shut down network device + * @dev: network device to close + * + * Opposite of ei_open(). Only used when "ifconfig <devname> down" is done. + */ +static int __ei_close(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned long flags; + + /* + * Hold the page lock during close + */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + __NS8390_init(dev, 0); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + netif_stop_queue(dev); + return 0; +} + +/** + * ei_tx_timeout - handle transmit time out condition + * @dev: network device which has apparently fallen asleep + * + * Called by kernel when device never acknowledges a transmit has + * completed (or failed) - i.e. never posted a Tx related interrupt. + */ + +static void __ei_tx_timeout(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int txsr, isr, tickssofar = jiffies - dev->trans_start; + unsigned long flags; + + dev->stats.tx_errors++; + + spin_lock_irqsave(&ei_local->page_lock, flags); + txsr = ei_inb(e8390_base+EN0_TSR); + isr = ei_inb(e8390_base+EN0_ISR); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n", + dev->name, (txsr & ENTSR_ABT) ? "excess collisions." : + (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar); + + if (!isr && !dev->stats.tx_packets) + { + /* The 8390 probably hasn't gotten on the cable yet. */ + ei_local->interface_num ^= 1; /* Try a different xcvr. */ + } + + /* Ugly but a reset can be slow, yet must be protected */ + + disable_irq_nosync_lockdep(dev->irq); + spin_lock(&ei_local->page_lock); + + /* Try to restart the card. Perhaps the user has fixed something. */ + ei_reset_8390(dev); + __NS8390_init(dev, 1); + + spin_unlock(&ei_local->page_lock); + enable_irq_lockdep(dev->irq); + netif_wake_queue(dev); +} + +/** + * ei_start_xmit - begin packet transmission + * @skb: packet to be sent + * @dev: network device to which packet is sent + * + * Sends a packet to an 8390 network device. + */ + +static int __ei_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int send_length = skb->len, output_page; + unsigned long flags; + char buf[ETH_ZLEN]; + char *data = skb->data; + + if (skb->len < ETH_ZLEN) { + memset(buf, 0, ETH_ZLEN); /* more efficient than doing just the needed bits */ + memcpy(buf, data, skb->len); + send_length = ETH_ZLEN; + data = buf; + } + + /* Mask interrupts from the ethercard. + SMP: We have to grab the lock here otherwise the IRQ handler + on another CPU can flip window and race the IRQ mask set. We end + up trashing the mcast filter not disabling irqs if we don't lock */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + ei_outb_p(0x00, e8390_base + EN0_IMR); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + + /* + * Slow phase with lock held. + */ + + disable_irq_nosync_lockdep_irqsave(dev->irq, &flags); + + spin_lock(&ei_local->page_lock); + + ei_local->irqlock = 1; + + /* + * We have two Tx slots available for use. Find the first free + * slot, and then perform some sanity checks. With two Tx bufs, + * you get very close to transmitting back-to-back packets. With + * only one Tx buf, the transmitter sits idle while you reload the + * card, leaving a substantial gap between each transmitted packet. + */ + + if (ei_local->tx1 == 0) + { + output_page = ei_local->tx_start_page; + ei_local->tx1 = send_length; + if (ei_debug && ei_local->tx2 > 0) + printk(KERN_DEBUG "%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n", + dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing); + } + else if (ei_local->tx2 == 0) + { + output_page = ei_local->tx_start_page + TX_PAGES/2; + ei_local->tx2 = send_length; + if (ei_debug && ei_local->tx1 > 0) + printk(KERN_DEBUG "%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n", + dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing); + } + else + { /* We should never get here. */ + if (ei_debug) + printk(KERN_DEBUG "%s: No Tx buffers free! tx1=%d tx2=%d last=%d\n", + dev->name, ei_local->tx1, ei_local->tx2, ei_local->lasttx); + ei_local->irqlock = 0; + netif_stop_queue(dev); + ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR); + spin_unlock(&ei_local->page_lock); + enable_irq_lockdep_irqrestore(dev->irq, &flags); + dev->stats.tx_errors++; + return 1; + } + + /* + * Okay, now upload the packet and trigger a send if the transmitter + * isn't already sending. If it is busy, the interrupt handler will + * trigger the send later, upon receiving a Tx done interrupt. + */ + + ei_block_output(dev, send_length, data, output_page); + + if (! ei_local->txing) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, send_length, output_page); + dev->trans_start = jiffies; + if (output_page == ei_local->tx_start_page) + { + ei_local->tx1 = -1; + ei_local->lasttx = -1; + } + else + { + ei_local->tx2 = -1; + ei_local->lasttx = -2; + } + } + else ei_local->txqueue++; + + if (ei_local->tx1 && ei_local->tx2) + netif_stop_queue(dev); + else + netif_start_queue(dev); + + /* Turn 8390 interrupts back on. */ + ei_local->irqlock = 0; + ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR); + + spin_unlock(&ei_local->page_lock); + enable_irq_lockdep_irqrestore(dev->irq, &flags); + + dev_kfree_skb (skb); + dev->stats.tx_bytes += send_length; + + return 0; +} + +/** + * ei_interrupt - handle the interrupts from an 8390 + * @irq: interrupt number + * @dev_id: a pointer to the net_device + * + * Handle the ether interface interrupts. We pull packets from + * the 8390 via the card specific functions and fire them at the networking + * stack. We also handle transmit completions and wake the transmit path if + * necessary. We also update the counters and do other housekeeping as + * needed. + */ + +static irqreturn_t __ei_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + unsigned long e8390_base = dev->base_addr; + int interrupts, nr_serviced = 0; + struct ei_device *ei_local = netdev_priv(dev); + + /* + * Protect the irq test too. + */ + + spin_lock(&ei_local->page_lock); + + if (ei_local->irqlock) + { +#if 1 /* This might just be an interrupt for a PCI device sharing this line */ + /* The "irqlock" check is only for testing. */ + printk(ei_local->irqlock + ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n" + : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n", + dev->name, ei_inb_p(e8390_base + EN0_ISR), + ei_inb_p(e8390_base + EN0_IMR)); +#endif + spin_unlock(&ei_local->page_lock); + return IRQ_NONE; + } + + /* Change to page 0 and read the intr status reg. */ + ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); + if (ei_debug > 3) + printk(KERN_DEBUG "%s: interrupt(isr=%#2.2x).\n", dev->name, + ei_inb_p(e8390_base + EN0_ISR)); + + /* !!Assumption!! -- we stay in page 0. Don't break this. */ + while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 + && ++nr_serviced < MAX_SERVICE) + { + if (!netif_running(dev)) { + printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name); + /* rmk - acknowledge the interrupts */ + ei_outb_p(interrupts, e8390_base + EN0_ISR); + interrupts = 0; + break; + } + if (interrupts & ENISR_OVER) + ei_rx_overrun(dev); + else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) + { + /* Got a good (?) packet. */ + ei_receive(dev); + } + /* Push the next to-transmit packet through. */ + if (interrupts & ENISR_TX) + ei_tx_intr(dev); + else if (interrupts & ENISR_TX_ERR) + ei_tx_err(dev); + + if (interrupts & ENISR_COUNTERS) + { + dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0); + dev->stats.rx_crc_errors += ei_inb_p(e8390_base + EN0_COUNTER1); + dev->stats.rx_missed_errors+= ei_inb_p(e8390_base + EN0_COUNTER2); + ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */ + } + + /* Ignore any RDC interrupts that make it back to here. */ + if (interrupts & ENISR_RDC) + { + ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR); + } + + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD); + } + + if (interrupts && ei_debug) + { + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD); + if (nr_serviced >= MAX_SERVICE) + { + /* 0xFF is valid for a card removal */ + if(interrupts!=0xFF) + printk(KERN_WARNING "%s: Too much work at interrupt, status %#2.2x\n", + dev->name, interrupts); + ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */ + } else { + printk(KERN_WARNING "%s: unknown interrupt %#2x\n", dev->name, interrupts); + ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */ + } + } + spin_unlock(&ei_local->page_lock); + return IRQ_RETVAL(nr_serviced > 0); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void __ei_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + __ei_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +/** + * ei_tx_err - handle transmitter error + * @dev: network device which threw the exception + * + * A transmitter error has happened. Most likely excess collisions (which + * is a fairly normal condition). If the error is one where the Tx will + * have been aborted, we try and send another one right away, instead of + * letting the failed packet sit and collect dust in the Tx buffer. This + * is a much better solution as it avoids kernel based Tx timeouts, and + * an unnecessary card reset. + * + * Called with lock held. + */ + +static void ei_tx_err(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + /* ei_local is used on some platforms via the EI_SHIFT macro */ + struct ei_device *ei_local __maybe_unused = netdev_priv(dev); + unsigned char txsr = ei_inb_p(e8390_base+EN0_TSR); + unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU); + +#ifdef VERBOSE_ERROR_DUMP + printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr); + if (txsr & ENTSR_ABT) + printk("excess-collisions "); + if (txsr & ENTSR_ND) + printk("non-deferral "); + if (txsr & ENTSR_CRS) + printk("lost-carrier "); + if (txsr & ENTSR_FU) + printk("FIFO-underrun "); + if (txsr & ENTSR_CDH) + printk("lost-heartbeat "); + printk("\n"); +#endif + + ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */ + + if (tx_was_aborted) + ei_tx_intr(dev); + else + { + dev->stats.tx_errors++; + if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++; + if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++; + if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++; + } +} + +/** + * ei_tx_intr - transmit interrupt handler + * @dev: network device for which tx intr is handled + * + * We have finished a transmit: check for errors and then trigger the next + * packet to be sent. Called with lock held. + */ + +static void ei_tx_intr(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int status = ei_inb(e8390_base + EN0_TSR); + + ei_outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */ + + /* + * There are two Tx buffers, see which one finished, and trigger + * the send of another one if it exists. + */ + ei_local->txqueue--; + + if (ei_local->tx1 < 0) + { + if (ei_local->lasttx != 1 && ei_local->lasttx != -1) + printk(KERN_ERR "%s: bogus last_tx_buffer %d, tx1=%d.\n", + ei_local->name, ei_local->lasttx, ei_local->tx1); + ei_local->tx1 = 0; + if (ei_local->tx2 > 0) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); + dev->trans_start = jiffies; + ei_local->tx2 = -1, + ei_local->lasttx = 2; + } + else ei_local->lasttx = 20, ei_local->txing = 0; + } + else if (ei_local->tx2 < 0) + { + if (ei_local->lasttx != 2 && ei_local->lasttx != -2) + printk("%s: bogus last_tx_buffer %d, tx2=%d.\n", + ei_local->name, ei_local->lasttx, ei_local->tx2); + ei_local->tx2 = 0; + if (ei_local->tx1 > 0) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); + dev->trans_start = jiffies; + ei_local->tx1 = -1; + ei_local->lasttx = 1; + } + else + ei_local->lasttx = 10, ei_local->txing = 0; + } +// else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n", +// dev->name, ei_local->lasttx); + + /* Minimize Tx latency: update the statistics after we restart TXing. */ + if (status & ENTSR_COL) + dev->stats.collisions++; + if (status & ENTSR_PTX) + dev->stats.tx_packets++; + else + { + dev->stats.tx_errors++; + if (status & ENTSR_ABT) + { + dev->stats.tx_aborted_errors++; + dev->stats.collisions += 16; + } + if (status & ENTSR_CRS) + dev->stats.tx_carrier_errors++; + if (status & ENTSR_FU) + dev->stats.tx_fifo_errors++; + if (status & ENTSR_CDH) + dev->stats.tx_heartbeat_errors++; + if (status & ENTSR_OWC) + dev->stats.tx_window_errors++; + } + netif_wake_queue(dev); +} + +/** + * ei_receive - receive some packets + * @dev: network device with which receive will be run + * + * We have a good packet(s), get it/them out of the buffers. + * Called with lock held. + */ + +static void ei_receive(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned char rxing_page, this_frame, next_frame; + unsigned short current_offset; + int rx_pkt_count = 0; + struct e8390_pkt_hdr rx_frame; + int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page; + + while (++rx_pkt_count < 10) + { + int pkt_len, pkt_stat; + + /* Get the rx page (incoming packet pointer). */ + ei_outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD); + rxing_page = ei_inb_p(e8390_base + EN1_CURPAG); + ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); + + /* Remove one frame from the ring. Boundary is always a page behind. */ + this_frame = ei_inb_p(e8390_base + EN0_BOUNDARY) + 1; + if (this_frame >= ei_local->stop_page) + this_frame = ei_local->rx_start_page; + + /* Someday we'll omit the previous, iff we never get this message. + (There is at least one clone claimed to have a problem.) + + Keep quiet if it looks like a card removal. One problem here + is that some clones crash in roughly the same way. + */ + if (ei_debug > 0 && this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF)) + printk(KERN_ERR "%s: mismatched read page pointers %2x vs %2x.\n", + dev->name, this_frame, ei_local->current_page); + + if (this_frame == rxing_page) /* Read all the frames? */ + break; /* Done for now */ + + current_offset = this_frame << 8; + ei_get_8390_hdr(dev, &rx_frame, this_frame); + + pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); + pkt_stat = rx_frame.status; + + next_frame = this_frame + 1 + ((pkt_len+4)>>8); + + /* Check for bogosity warned by 3c503 book: the status byte is never + written. This happened a lot during testing! This code should be + cleaned up someday. */ + if (rx_frame.next != next_frame + && rx_frame.next != next_frame + 1 + && rx_frame.next != next_frame - num_rx_pages + && rx_frame.next != next_frame + 1 - num_rx_pages) { + ei_local->current_page = rxing_page; + ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY); + dev->stats.rx_errors++; + continue; + } + + if (pkt_len < 60 || pkt_len > 1518) + { + if (ei_debug) + printk(KERN_DEBUG "%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n", + dev->name, rx_frame.count, rx_frame.status, + rx_frame.next); + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + } + else if ((pkt_stat & 0x0F) == ENRSR_RXOK) + { + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len+2); + if (skb == NULL) + { + if (ei_debug > 1) + printk(KERN_DEBUG "%s: Couldn't allocate a sk_buff of size %d.\n", + dev->name, pkt_len); + dev->stats.rx_dropped++; + break; + } + else + { + skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ + skb_put(skb, pkt_len); /* Make room */ + ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + if (pkt_stat & ENRSR_PHY) + dev->stats.multicast++; + } + } + else + { + if (ei_debug) + printk(KERN_DEBUG "%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n", + dev->name, rx_frame.status, rx_frame.next, + rx_frame.count); + dev->stats.rx_errors++; + /* NB: The NIC counts CRC, frame and missed errors. */ + if (pkt_stat & ENRSR_FO) + dev->stats.rx_fifo_errors++; + } + next_frame = rx_frame.next; + + /* This _should_ never happen: it's here for avoiding bad clones. */ + if (next_frame >= ei_local->stop_page) { + printk("%s: next frame inconsistency, %#2x\n", dev->name, + next_frame); + next_frame = ei_local->rx_start_page; + } + ei_local->current_page = next_frame; + ei_outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); + } + + /* We used to also ack ENISR_OVER here, but that would sometimes mask + a real overrun, leaving the 8390 in a stopped state with rec'vr off. */ + ei_outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR); + return; +} + +/** + * ei_rx_overrun - handle receiver overrun + * @dev: network device which threw exception + * + * We have a receiver overrun: we have to kick the 8390 to get it started + * again. Problem is that you have to kick it exactly as NS prescribes in + * the updated datasheets, or "the NIC may act in an unpredictable manner." + * This includes causing "the NIC to defer indefinitely when it is stopped + * on a busy network." Ugh. + * Called with lock held. Don't call this with the interrupts off or your + * computer will hate you - it takes 10ms or so. + */ + +static void ei_rx_overrun(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + unsigned char was_txing, must_resend = 0; + /* ei_local is used on some platforms via the EI_SHIFT macro */ + struct ei_device *ei_local __maybe_unused = netdev_priv(dev); + + /* + * Record whether a Tx was in progress and then issue the + * stop command. + */ + was_txing = ei_inb_p(e8390_base+E8390_CMD) & E8390_TRANS; + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); + + if (ei_debug > 1) + printk(KERN_DEBUG "%s: Receiver overrun.\n", dev->name); + dev->stats.rx_over_errors++; + + /* + * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. + * Early datasheets said to poll the reset bit, but now they say that + * it "is not a reliable indicator and subsequently should be ignored." + * We wait at least 10ms. + */ + + mdelay(10); + + /* + * Reset RBCR[01] back to zero as per magic incantation. + */ + ei_outb_p(0x00, e8390_base+EN0_RCNTLO); + ei_outb_p(0x00, e8390_base+EN0_RCNTHI); + + /* + * See if any Tx was interrupted or not. According to NS, this + * step is vital, and skipping it will cause no end of havoc. + */ + + if (was_txing) + { + unsigned char tx_completed = ei_inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); + if (!tx_completed) + must_resend = 1; + } + + /* + * Have to enter loopback mode and then restart the NIC before + * you are allowed to slurp packets up off the ring. + */ + ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); + ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); + + /* + * Clear the Rx ring of all the debris, and ack the interrupt. + */ + ei_receive(dev); + ei_outb_p(ENISR_OVER, e8390_base+EN0_ISR); + + /* + * Leave loopback mode, and resend any packet that got stopped. + */ + ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); + if (must_resend) + ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); +} + +/* + * Collect the stats. This is called unlocked and from several contexts. + */ + +static struct net_device_stats *__ei_get_stats(struct net_device *dev) +{ + unsigned long ioaddr = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned long flags; + + /* If the card is stopped, just return the present stats. */ + if (!netif_running(dev)) + return &dev->stats; + + spin_lock_irqsave(&ei_local->page_lock,flags); + /* Read the counter registers, assuming we are in page 0. */ + dev->stats.rx_frame_errors += ei_inb_p(ioaddr + EN0_COUNTER0); + dev->stats.rx_crc_errors += ei_inb_p(ioaddr + EN0_COUNTER1); + dev->stats.rx_missed_errors+= ei_inb_p(ioaddr + EN0_COUNTER2); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + return &dev->stats; +} + +/* + * Form the 64 bit 8390 multicast table from the linked list of addresses + * associated with this dev structure. + */ + +static inline void make_mc_bits(u8 *bits, struct net_device *dev) +{ + struct dev_mc_list *dmi; + + for (dmi=dev->mc_list; dmi; dmi=dmi->next) + { + u32 crc; + if (dmi->dmi_addrlen != ETH_ALEN) + { + printk(KERN_INFO "%s: invalid multicast address length given.\n", dev->name); + continue; + } + crc = ether_crc(ETH_ALEN, dmi->dmi_addr); + /* + * The 8390 uses the 6 most significant bits of the + * CRC to index the multicast table. + */ + bits[crc>>29] |= (1<<((crc>>26)&7)); + } +} + +/** + * do_set_multicast_list - set/clear multicast filter + * @dev: net device for which multicast filter is adjusted + * + * Set or clear the multicast filter for this adaptor. May be called + * from a BH in 2.1.x. Must be called with lock held. + */ + +static void do_set_multicast_list(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + int i; + struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev); + + if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) + { + memset(ei_local->mcfilter, 0, 8); + if (dev->mc_list) + make_mc_bits(ei_local->mcfilter, dev); + } + else + memset(ei_local->mcfilter, 0xFF, 8); /* mcast set to accept-all */ + + /* + * DP8390 manuals don't specify any magic sequence for altering + * the multicast regs on an already running card. To be safe, we + * ensure multicast mode is off prior to loading up the new hash + * table. If this proves to be not enough, we can always resort + * to stopping the NIC, loading the table and then restarting. + * + * Bug Alert! The MC regs on the SMC 83C690 (SMC Elite and SMC + * Elite16) appear to be write-only. The NS 8390 data sheet lists + * them as r/w so this is a bug. The SMC 83C790 (SMC Ultra and + * Ultra32 EISA) appears to have this bug fixed. + */ + + if (netif_running(dev)) + ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); + ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD); + for(i = 0; i < 8; i++) + { + ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i)); +#ifndef BUG_83C690 + if(ei_inb_p(e8390_base + EN1_MULT_SHIFT(i))!=ei_local->mcfilter[i]) + printk(KERN_ERR "Multicast filter read/write mismap %d\n",i); +#endif + } + ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD); + + if(dev->flags&IFF_PROMISC) + ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR); + else if(dev->flags&IFF_ALLMULTI || dev->mc_list) + ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR); + else + ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); + } + +/* + * Called without lock held. This is invoked from user context and may + * be parallel to just about everything else. Its also fairly quick and + * not called too often. Must protect against both bh and irq users + */ + +static void __ei_set_multicast_list(struct net_device *dev) +{ + unsigned long flags; + struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev); + + spin_lock_irqsave(&ei_local->page_lock, flags); + do_set_multicast_list(dev); + spin_unlock_irqrestore(&ei_local->page_lock, flags); +} + +/** + * ethdev_setup - init rest of 8390 device struct + * @dev: network device structure to init + * + * Initialize the rest of the 8390 device structure. Do NOT __init + * this, as it is used by 8390 based modular drivers too. + */ + +static void ethdev_setup(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + if (ei_debug > 1) + printk(version); + + ether_setup(dev); + + spin_lock_init(&ei_local->page_lock); +} + +/** + * alloc_ei_netdev - alloc_etherdev counterpart for 8390 + * @size: extra bytes to allocate + * + * Allocate 8390-specific net_device. + */ +static struct net_device *____alloc_ei_netdev(int size) +{ + return alloc_netdev(sizeof(struct ei_device) + size, "eth%d", + ethdev_setup); +} + + + + +/* This page of functions should be 8390 generic */ +/* Follow National Semi's recommendations for initializing the "NIC". */ + +/** + * NS8390_init - initialize 8390 hardware + * @dev: network device to initialize + * @startp: boolean. non-zero value to initiate chip processing + * + * Must be called with lock held. + */ + +static void __NS8390_init(struct net_device *dev, int startp) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int i; + int endcfg = ei_local->word16 + ? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0)) + : 0x48; + + if(sizeof(struct e8390_pkt_hdr)!=4) + panic("8390.c: header struct mispacked\n"); + /* Follow National Semi's recommendations for initing the DP83902. */ + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */ + ei_outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */ + /* Clear the remote byte count registers. */ + ei_outb_p(0x00, e8390_base + EN0_RCNTLO); + ei_outb_p(0x00, e8390_base + EN0_RCNTHI); + /* Set to monitor and loopback mode -- this is vital!. */ + ei_outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */ + ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ + /* Set the transmit page and receive ring. */ + ei_outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR); + ei_local->tx1 = ei_local->tx2 = 0; + ei_outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG); + ei_outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ + ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */ + ei_outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG); + /* Clear the pending interrupts and mask. */ + ei_outb_p(0xFF, e8390_base + EN0_ISR); + ei_outb_p(0x00, e8390_base + EN0_IMR); + + /* Copy the station address into the DS8390 registers. */ + + ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */ + for(i = 0; i < 6; i++) + { + ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i)); + if (ei_debug > 1 && ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) + printk(KERN_ERR "Hw. address read/write mismap %d\n",i); + } + + ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); + + netif_start_queue(dev); + ei_local->tx1 = ei_local->tx2 = 0; + ei_local->txing = 0; + + if (startp) + { + ei_outb_p(0xff, e8390_base + EN0_ISR); + ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR); + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); + ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */ + /* 3c503 TechMan says rxconfig only after the NIC is started. */ + ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */ + do_set_multicast_list(dev); /* (re)load the mcast table */ + } +} + +/* Trigger a transmit start, assuming the length is valid. + Always called with the page lock held */ + +static void NS8390_trigger_send(struct net_device *dev, unsigned int length, + int start_page) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local __attribute((unused)) = (struct ei_device *) netdev_priv(dev); + + ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD); + + if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) + { + printk(KERN_WARNING "%s: trigger_send() called with the transmitter busy.\n", + dev->name); + return; + } + ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO); + ei_outb_p(length >> 8, e8390_base + EN0_TCNTHI); + ei_outb_p(start_page, e8390_base + EN0_TPSR); + ei_outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD); +} diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/main.c.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/main.c.svn-base new file mode 100644 index 00000000..8dba2f00 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/main.c.svn-base @@ -0,0 +1,115 @@ +#include <l4/dde/linux26/dde26.h> /* l4dde26_*() */ +#include <l4/dde/linux26/dde26_net.h> /* l4dde26 networking */ +#include <l4/sys/types.h> /* l4_threadid_t */ +#include <l4/sys/ipc.h> /* l4_ipc_*() */ + +#include <linux/netdevice.h> /* struct sk_buff */ +#include <linux/pci.h> /* pci_unregister_driver() */ +#include <linux/init.h> // initcall() +#include <linux/delay.h> // msleep() + +#include "arping.h" + +#include "8390.h" /* that's what we are */ + +extern int arping_verbose; +#define VERBOSE_LOG(fmt, ...) \ + do { \ + if (arping_verbose) printk(fmt, ##__VA_ARGS__); \ + } while (0); + +extern struct pci_driver ne2k_driver; +extern int arping(void); + +void open_nw_dev(void); +void open_nw_dev() +{ + struct net_device *dev; + struct net *net; + + read_lock(&dev_base_lock); + for_each_net(net) { + for_each_netdev(net, dev) { + int err = 0; + printk("dev: '%s'\n", dev->name); + printk("MAC: "mac_fmt"\n", mac_str(dev->dev_addr)); + + err = dev_open(dev); + } + } + read_unlock(&dev_base_lock); +} + +void close_nw_dev(void); +void close_nw_dev(void) +{ + struct net_device *dev; + struct net *net; + + read_lock(&dev_base_lock); + for_each_net(net) { + for_each_netdev(net, dev) { + int err = 0; + + err = dev_close(dev); + printk("closed %s\n", dev->name); + } + } + read_unlock(&dev_base_lock); +} + +static int net_rx_handle(struct sk_buff *skb) +{ + skb_push(skb, skb->dev->hard_header_len); + + struct arping_elem *e = kmalloc(sizeof(struct arping_elem), GFP_KERNEL); + e->skb = skb; + skb_get(skb); + e->next = NULL; + + if (arping_list == NULL) + arping_list = e; + else { + struct arping_elem *f = arping_list; + while (f->next) + f = f->next; + f->next = e; + } + + ddekit_sem_up(arping_semaphore); + + kfree_skb(skb); + + VERBOSE_LOG("freed skb, returning from netif_rx\n"); + return NET_RX_SUCCESS; +} + +//subsys_initcall(l4dde26_init_pci); + +int main(int argc, char **argv); +int main(int argc, char **argv) +{ + l4dde26_init(); + l4dde26_process_init(); + l4dde26_softirq_init(); + + printk("Initializing skb subsystem\n"); + skb_init(); + + l4dde26_do_initcalls(); + printk("Setting rx callback @ %p\n", net_rx_handle); + l4dde26_register_rx_callback(net_rx_handle); + + printk("Opening nw devs.\n"); + open_nw_dev(); + printk("dev is up and ready.\n"); + + arping(); + + close_nw_dev(); + + pci_unregister_driver(&ne2k_driver); + printk("shut down driver\n"); + + return 0; +} diff --git a/libdde_linux26/examples/ne2k/.svn/text-base/ne2k-pci.c.svn-base b/libdde_linux26/examples/ne2k/.svn/text-base/ne2k-pci.c.svn-base new file mode 100644 index 00000000..c9995d36 --- /dev/null +++ b/libdde_linux26/examples/ne2k/.svn/text-base/ne2k-pci.c.svn-base @@ -0,0 +1,727 @@ +/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */ +/* + A Linux device driver for PCI NE2000 clones. + + Authors and other copyright holders: + 1992-2000 by Donald Becker, NE2000 core and various modifications. + 1995-1998 by Paul Gortmaker, core modifications and PCI support. + Copyright 1993 assigned to the United States Government as represented + by the Director, National Security Agency. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Issues remaining: + People are making PCI ne2000 clones! Oh the horror, the horror... + Limited full-duplex support. +*/ + +#define DRV_NAME "ne2k-pci" +#define DRV_VERSION "1.03" +#define DRV_RELDATE "9/22/2003" + + +/* The user-configurable values. + These may be modified when a driver module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ + +#define MAX_UNITS 8 /* More are supported, limit only on options */ +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS]; +static int options[MAX_UNITS]; + +/* Force a non std. amount of memory. Units are 256 byte pages. */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ethtool.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include "8390.h" + +/* These identify the driver base version and may not be removed. */ +static char version[] __devinitdata = +KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " D. Becker/P. Gortmaker\n"; + +#if defined(__powerpc__) +#define inl_le(addr) le32_to_cpu(inl(addr)) +#define inw_le(addr) le16_to_cpu(inw(addr)) +#endif + +#define PFX DRV_NAME ": " + +MODULE_AUTHOR("Donald Becker / Paul Gortmaker"); +MODULE_DESCRIPTION("PCI NE2000 clone driver"); +MODULE_LICENSE("GPL"); + +module_param(debug, int, 0); +module_param_array(options, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +MODULE_PARM_DESC(debug, "debug level (1-2)"); +MODULE_PARM_DESC(options, "Bit 5: full duplex"); +MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); + +/* Some defines that people can play with if so inclined. */ + +/* Use 32 bit data-movement operations instead of 16 bit. */ +#define USE_LONGIO + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Flags. We rename an existing ei_status field to store flags! */ +/* Thus only the low 8 bits are usable for non-init-time flags. */ +#define ne2k_flags reg0 +enum { + ONLY_16BIT_IO=8, ONLY_32BIT_IO=4, /* Chip can do only 16/32-bit xfers. */ + FORCE_FDX=0x20, /* User override. */ + REALTEK_FDX=0x40, HOLTEK_FDX=0x80, + STOP_PG_0x60=0x100, +}; + +enum ne2k_pci_chipsets { + CH_RealTek_RTL_8029 = 0, + CH_Winbond_89C940, + CH_Compex_RL2000, + CH_KTI_ET32P2, + CH_NetVin_NV5000SC, + CH_Via_86C926, + CH_SureCom_NE34, + CH_Winbond_W89C940F, + CH_Holtek_HT80232, + CH_Holtek_HT80229, + CH_Winbond_89C940_8c4a, +}; + + +static struct { + char *name; + int flags; +} pci_clone_list[] __devinitdata = { + {"RealTek RTL-8029", REALTEK_FDX}, + {"Winbond 89C940", 0}, + {"Compex RL2000", 0}, + {"KTI ET32P2", 0}, + {"NetVin NV5000SC", 0}, + {"Via 86C926", ONLY_16BIT_IO}, + {"SureCom NE34", 0}, + {"Winbond W89C940F", 0}, + {"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX}, + {"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 }, + {"Winbond W89C940(misprogrammed)", 0}, + {NULL,} +}; + + +static struct pci_device_id ne2k_pci_tbl[] = { + { 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 }, + { 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 }, + { 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 }, + { 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 }, + { 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC }, + { 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 }, + { 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 }, + { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F }, + { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 }, + { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 }, + { 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl); + + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 +#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT 0x20 + +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + + +static int ne2k_pci_open(struct net_device *dev); +static int ne2k_pci_close(struct net_device *dev); + +static void ne2k_pci_reset_8390(struct net_device *dev); +static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ne2k_pci_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ne2k_pci_block_output(struct net_device *dev, const int count, + const unsigned char *buf, const int start_page); +static const struct ethtool_ops ne2k_pci_ethtool_ops; + + + +/* There is no room in the standard 8390 structure for extra info we need, + so we build a meta/outer-wrapper structure.. */ +struct ne2k_pci_card { + struct net_device *dev; + struct pci_dev *pci_dev; +}; + + + +/* + NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet + buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes + 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be + detected by their SA prefix. + + Reading the SAPROM from a word-wide card with the 8390 set in byte-wide + mode results in doubled values, which can be detected and compensated for. + + The probe is also responsible for initializing the card and filling + in the 'dev' and 'ei_status' structures. +*/ + +static const struct net_device_ops ne2k_netdev_ops = { + .ndo_open = ne2k_pci_open, + .ndo_stop = ne2k_pci_close, + .ndo_start_xmit = ei_start_xmit, + .ndo_tx_timeout = ei_tx_timeout, + .ndo_get_stats = ei_get_stats, + .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ei_poll, +#endif +}; + +static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + int i; + unsigned char SA_prom[32]; + int start_page, stop_page; + int irq, reg0, chip_idx = ent->driver_data; + static unsigned int fnd_cnt; + long ioaddr; + int flags = pci_clone_list[chip_idx].flags; + +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + + fnd_cnt++; + + i = pci_enable_device (pdev); + if (i) + return i; + + ioaddr = pci_resource_start (pdev, 0); + irq = pdev->irq; + + if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) { + dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n"); + return -ENODEV; + } + + if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) { + dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n", + NE_IO_EXTENT, ioaddr); + return -EBUSY; + } + + reg0 = inb(ioaddr); + if (reg0 == 0xFF) + goto err_out_free_res; + + /* Do a preliminary verification that we have a 8390. */ + { + int regd; + outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); + regd = inb(ioaddr + 0x0d); + outb(0xff, ioaddr + 0x0d); + outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); + inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ + if (inb(ioaddr + EN0_COUNTER0) != 0) { + outb(reg0, ioaddr); + outb(regd, ioaddr + 0x0d); /* Restore the old values. */ + goto err_out_free_res; + } + } + + /* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */ + dev = alloc_ei_netdev(); + if (!dev) { + dev_err(&pdev->dev, "cannot allocate ethernet device\n"); + goto err_out_free_res; + } + dev->netdev_ops = &ne2k_netdev_ops; + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Reset card. Who knows what dain-bramaged state it was left in. */ + { + unsigned long reset_start_time = jiffies; + + outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); + + /* This looks like a horrible timing loop, but it should never take + more than a few cycles. + */ + while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) + /* Limit wait: '2' avoids jiffy roll-over. */ + if (jiffies - reset_start_time > 2) { + dev_err(&pdev->dev, + "Card failure (no reset ack).\n"); + goto err_out_free_netdev; + } + + outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */ + } + + /* Read the 16 bytes of station address PROM. + We must first initialize registers, similar to NS8390_init(eifdev, 0). + We can't reliably read the SAPROM address without this. + (I learned the hard way!). */ + { + struct {unsigned char value, offset; } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x49, EN0_DCFG}, /* Set word-wide access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + for (i = 0; i < ARRAY_SIZE(program_seq); i++) + outb(program_seq[i].value, ioaddr + program_seq[i].offset); + + } + + /* Note: all PCI cards have at least 16 bit access, so we don't have + to check for 8 bit cards. Most cards permit 32 bit access. */ + if (flags & ONLY_32BIT_IO) { + for (i = 0; i < 4 ; i++) + ((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT)); + } else + for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++) + SA_prom[i] = inb(ioaddr + NE_DATAPORT); + + /* We always set the 8390 registers for word mode. */ + outb(0x49, ioaddr + EN0_DCFG); + start_page = NESM_START_PG; + + stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG; + + /* Set up the rest of the parameters. */ + dev->irq = irq; + dev->base_addr = ioaddr; + pci_set_drvdata(pdev, dev); + + ei_status.name = pci_clone_list[chip_idx].name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; + ei_status.word16 = 1; + ei_status.ne2k_flags = flags; + if (fnd_cnt < MAX_UNITS) { + if (full_duplex[fnd_cnt] > 0 || (options[fnd_cnt] & FORCE_FDX)) + ei_status.ne2k_flags |= FORCE_FDX; + } + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + + ei_status.reset_8390 = &ne2k_pci_reset_8390; + ei_status.block_input = &ne2k_pci_block_input; + ei_status.block_output = &ne2k_pci_block_output; + ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr; + ei_status.priv = (unsigned long) pdev; + + dev->ethtool_ops = &ne2k_pci_ethtool_ops; + NS8390_init(dev, 0); + + i = register_netdev(dev); + if (i) + goto err_out_free_netdev; + + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SA_prom[i]; + printk("%s: %s found at %#lx, IRQ %d, %pM.\n", + dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq, + dev->dev_addr); + + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + + return 0; + +err_out_free_netdev: + free_netdev (dev); +err_out_free_res: + release_region (ioaddr, NE_IO_EXTENT); + pci_set_drvdata (pdev, NULL); + return -ENODEV; + +} + +/* + * Magic incantation sequence for full duplex on the supported cards. + */ +static inline int set_realtek_fdx(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + + outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */ + outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */ + outb(0x40, ioaddr + 0x06); /* Enable full duplex */ + outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */ + outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */ + return 0; +} + +static inline int set_holtek_fdx(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + + outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20); + return 0; +} + +static int ne2k_pci_set_fdx(struct net_device *dev) +{ + if (ei_status.ne2k_flags & REALTEK_FDX) + return set_realtek_fdx(dev); + else if (ei_status.ne2k_flags & HOLTEK_FDX) + return set_holtek_fdx(dev); + + return -EOPNOTSUPP; +} + +static int ne2k_pci_open(struct net_device *dev) +{ + int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev); + if (ret) + return ret; + + if (ei_status.ne2k_flags & FORCE_FDX) + ne2k_pci_set_fdx(dev); + + ei_open(dev); + return 0; +} + +static int ne2k_pci_close(struct net_device *dev) +{ + ei_close(dev); + free_irq(dev->irq, dev); + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ +static void ne2k_pci_reset_8390(struct net_device *dev) +{ + unsigned long reset_start_time = jiffies; + + if (debug > 1) printk("%s: Resetting the 8390 t=%ld...", + dev->name, jiffies); + + outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2) { + printk("%s: ne2k_pci_reset_8390() did not complete.\n", dev->name); + break; + } + outb(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + + long nic_base = dev->base_addr; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne2k_pci_get_8390_hdr " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + + ei_status.dmaing |= 0x01; + outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); + outb(0, nic_base + EN0_RCNTHI); + outb(0, nic_base + EN0_RSARLO); /* On page boundary */ + outb(ring_page, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + + if (ei_status.ne2k_flags & ONLY_16BIT_IO) { + insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); + } else { + *(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT)); + le16_to_cpus(&hdr->count); + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using outb. */ + +static void ne2k_pci_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + long nic_base = dev->base_addr; + char *buf = skb->data; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne2k_pci_block_input " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + if (ei_status.ne2k_flags & ONLY_32BIT_IO) + count = (count + 3) & 0xFFFC; + outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb(count & 0xff, nic_base + EN0_RCNTLO); + outb(count >> 8, nic_base + EN0_RCNTHI); + outb(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb(ring_offset >> 8, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + + if (ei_status.ne2k_flags & ONLY_16BIT_IO) { + insw(NE_BASE + NE_DATAPORT,buf,count>>1); + if (count & 0x01) { + buf[count-1] = inb(NE_BASE + NE_DATAPORT); + } + } else { + insl(NE_BASE + NE_DATAPORT, buf, count>>2); + if (count & 3) { + buf += count & ~3; + if (count & 2) { + __le16 *b = (__le16 *)buf; + + *b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT)); + buf = (char *)b; + } + if (count & 1) + *buf = inb(NE_BASE + NE_DATAPORT); + } + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void ne2k_pci_block_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page) +{ + long nic_base = NE_BASE; + unsigned long dma_start; + + /* On little-endian it's always safe to round the count up for + word writes. */ + if (ei_status.ne2k_flags & ONLY_32BIT_IO) + count = (count + 3) & 0xFFFC; + else + if (count & 0x01) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne2k_pci_block_output." + "[DMAstat:%d][irqlock:%d]\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE8390_RW_BUGFIX + /* Handle the read-before-write bug the same way as the + Crynwr packet driver -- the NatSemi method doesn't work. + Actually this doesn't always work either, but if you have + problems with your NEx000 this is better than nothing! */ + outb(0x42, nic_base + EN0_RCNTLO); + outb(0x00, nic_base + EN0_RCNTHI); + outb(0x42, nic_base + EN0_RSARLO); + outb(0x00, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); +#endif + outb(ENISR_RDC, nic_base + EN0_ISR); + + /* Now the normal output. */ + outb(count & 0xff, nic_base + EN0_RCNTLO); + outb(count >> 8, nic_base + EN0_RCNTHI); + outb(0x00, nic_base + EN0_RSARLO); + outb(start_page, nic_base + EN0_RSARHI); + outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); + if (ei_status.ne2k_flags & ONLY_16BIT_IO) { + outsw(NE_BASE + NE_DATAPORT, buf, count>>1); + } else { + outsl(NE_BASE + NE_DATAPORT, buf, count>>2); + if (count & 3) { + buf += count & ~3; + if (count & 2) { + __le16 *b = (__le16 *)buf; + + outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT); + buf = (char *)b; + } + } + } + + dma_start = jiffies; + + while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */ + printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name); + ne2k_pci_reset_8390(dev); + NS8390_init(dev,1); + break; + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + +static void ne2k_pci_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ei_device *ei = netdev_priv(dev); + struct pci_dev *pci_dev = (struct pci_dev *) ei->priv; + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(pci_dev)); +} + +static const struct ethtool_ops ne2k_pci_ethtool_ops = { + .get_drvinfo = ne2k_pci_get_drvinfo, +}; + +static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + BUG_ON(!dev); + unregister_netdev(dev); + release_region(dev->base_addr, NE_IO_EXTENT); + free_netdev(dev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM +static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + netif_device_detach(dev); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int ne2k_pci_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + int rc; + + pci_set_power_state(pdev, 0); + pci_restore_state(pdev); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + NS8390_init(dev, 1); + netif_device_attach(dev); + + return 0; +} + +#endif /* CONFIG_PM */ + + +struct pci_driver ne2k_driver = { + .name = DRV_NAME, + .probe = ne2k_pci_init_one, + .remove = __devexit_p(ne2k_pci_remove_one), + .id_table = ne2k_pci_tbl, +#ifdef CONFIG_PM + .suspend = ne2k_pci_suspend, + .resume = ne2k_pci_resume, +#endif /* CONFIG_PM */ + +}; + + +int __init ne2k_pci_init(void) +{ +/* when a module, this is printed whether or not devices are found in probe */ +#ifdef MODULE + printk(version); +#endif + return pci_register_driver(&ne2k_driver); +} + + +static void __exit ne2k_pci_cleanup(void) +{ + pci_unregister_driver (&ne2k_driver); +} + +module_init(ne2k_pci_init); +module_exit(ne2k_pci_cleanup); diff --git a/libdde_linux26/examples/ne2k/8390.c b/libdde_linux26/examples/ne2k/8390.c new file mode 100644 index 00000000..ec3e22e6 --- /dev/null +++ b/libdde_linux26/examples/ne2k/8390.c @@ -0,0 +1,109 @@ +/* 8390 core for usual drivers */ + +static const char version[] = + "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include "lib8390.c" + +int ei_open(struct net_device *dev) +{ + return __ei_open(dev); +} +EXPORT_SYMBOL(ei_open); + +int ei_close(struct net_device *dev) +{ + return __ei_close(dev); +} +EXPORT_SYMBOL(ei_close); + +int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + return __ei_start_xmit(skb, dev); +} +EXPORT_SYMBOL(ei_start_xmit); + +struct net_device_stats *ei_get_stats(struct net_device *dev) +{ + return __ei_get_stats(dev); +} +EXPORT_SYMBOL(ei_get_stats); + +void ei_set_multicast_list(struct net_device *dev) +{ + __ei_set_multicast_list(dev); +} +EXPORT_SYMBOL(ei_set_multicast_list); + +void ei_tx_timeout(struct net_device *dev) +{ + __ei_tx_timeout(dev); +} +EXPORT_SYMBOL(ei_tx_timeout); + +irqreturn_t ei_interrupt(int irq, void *dev_id) +{ + return __ei_interrupt(irq, dev_id); +} +EXPORT_SYMBOL(ei_interrupt); + +#ifdef CONFIG_NET_POLL_CONTROLLER +void ei_poll(struct net_device *dev) +{ + __ei_poll(dev); +} +EXPORT_SYMBOL(ei_poll); +#endif + +const struct net_device_ops ei_netdev_ops = { + .ndo_open = ei_open, + .ndo_stop = ei_close, + .ndo_start_xmit = ei_start_xmit, + .ndo_tx_timeout = ei_tx_timeout, + .ndo_get_stats = ei_get_stats, + .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ei_poll, +#endif +}; +EXPORT_SYMBOL(ei_netdev_ops); + +struct net_device *__alloc_ei_netdev(int size) +{ + struct net_device *dev = ____alloc_ei_netdev(size); +#ifdef CONFIG_COMPAT_NET_DEV_OPS + if (dev) { + dev->hard_start_xmit = ei_start_xmit; + dev->get_stats = ei_get_stats; + dev->set_multicast_list = ei_set_multicast_list; + dev->tx_timeout = ei_tx_timeout; + } +#endif + return dev; +} +EXPORT_SYMBOL(__alloc_ei_netdev); + +void NS8390_init(struct net_device *dev, int startp) +{ + __NS8390_init(dev, startp); +} +EXPORT_SYMBOL(NS8390_init); + +#if defined(MODULE) + +static int __init ns8390_module_init(void) +{ + return 0; +} + +static void __exit ns8390_module_exit(void) +{ +} + +module_init(ns8390_module_init); +module_exit(ns8390_module_exit); +#endif /* MODULE */ +MODULE_LICENSE("GPL"); diff --git a/libdde_linux26/examples/ne2k/8390.h b/libdde_linux26/examples/ne2k/8390.h new file mode 100644 index 00000000..3c61d6d2 --- /dev/null +++ b/libdde_linux26/examples/ne2k/8390.h @@ -0,0 +1,231 @@ +/* Generic NS8390 register definitions. */ +/* This file is part of Donald Becker's 8390 drivers, and is distributed + under the same license. Auto-loading of 8390.o only in v2.2 - Paul G. + Some of these names and comments originated from the Crynwr + packet drivers, which are distributed under the GPL. */ + +#ifndef _8390_h +#define _8390_h + +#include <linux/if_ether.h> +#include <linux/ioport.h> +#include <linux/skbuff.h> + +#define TX_PAGES 12 /* Two Tx slots */ + +#define ETHER_ADDR_LEN 6 + +/* The 8390 specific per-packet-header format. */ +struct e8390_pkt_hdr { + unsigned char status; /* status */ + unsigned char next; /* pointer to next packet. */ + unsigned short count; /* header + packet length in bytes */ +}; + +#ifdef notdef +extern int ei_debug; +#else +#define ei_debug 1 +#endif + +#ifdef CONFIG_NET_POLL_CONTROLLER +extern void ei_poll(struct net_device *dev); +extern void eip_poll(struct net_device *dev); +#endif + + +/* Without I/O delay - non ISA or later chips */ +extern void NS8390_init(struct net_device *dev, int startp); +extern int ei_open(struct net_device *dev); +extern int ei_close(struct net_device *dev); +extern irqreturn_t ei_interrupt(int irq, void *dev_id); +extern void ei_tx_timeout(struct net_device *dev); +extern int ei_start_xmit(struct sk_buff *skb, struct net_device *dev); +extern void ei_set_multicast_list(struct net_device *dev); +extern struct net_device_stats *ei_get_stats(struct net_device *dev); + +extern const struct net_device_ops ei_netdev_ops; + +extern struct net_device *__alloc_ei_netdev(int size); +static inline struct net_device *alloc_ei_netdev(void) +{ + return __alloc_ei_netdev(0); +} + +/* With I/O delay form */ +extern void NS8390p_init(struct net_device *dev, int startp); +extern int eip_open(struct net_device *dev); +extern int eip_close(struct net_device *dev); +extern irqreturn_t eip_interrupt(int irq, void *dev_id); +extern void eip_tx_timeout(struct net_device *dev); +extern int eip_start_xmit(struct sk_buff *skb, struct net_device *dev); +extern void eip_set_multicast_list(struct net_device *dev); +extern struct net_device_stats *eip_get_stats(struct net_device *dev); + +extern const struct net_device_ops eip_netdev_ops; + +extern struct net_device *__alloc_eip_netdev(int size); +static inline struct net_device *alloc_eip_netdev(void) +{ + return __alloc_eip_netdev(0); +} + +/* You have one of these per-board */ +struct ei_device { + const char *name; + void (*reset_8390)(struct net_device *); + void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int); + void (*block_output)(struct net_device *, int, const unsigned char *, int); + void (*block_input)(struct net_device *, int, struct sk_buff *, int); + unsigned long rmem_start; + unsigned long rmem_end; + void __iomem *mem; + unsigned char mcfilter[8]; + unsigned open:1; + unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */ + unsigned bigendian:1; /* 16-bit big endian mode. Do NOT */ + /* set this on random 8390 clones! */ + unsigned txing:1; /* Transmit Active */ + unsigned irqlock:1; /* 8390's intrs disabled when '1'. */ + unsigned dmaing:1; /* Remote DMA Active */ + unsigned char tx_start_page, rx_start_page, stop_page; + unsigned char current_page; /* Read pointer in buffer */ + unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */ + unsigned char txqueue; /* Tx Packet buffer queue length. */ + short tx1, tx2; /* Packet lengths for ping-pong tx. */ + short lasttx; /* Alpha version consistency check. */ + unsigned char reg0; /* Register '0' in a WD8013 */ + unsigned char reg5; /* Register '5' in a WD8013 */ + unsigned char saved_irq; /* Original dev->irq value. */ + u32 *reg_offset; /* Register mapping table */ + spinlock_t page_lock; /* Page register locks */ + unsigned long priv; /* Private field to store bus IDs etc. */ +#ifdef AX88796_PLATFORM + unsigned char rxcr_base; /* default value for RXCR */ +#endif +}; + +/* The maximum number of 8390 interrupt service routines called per IRQ. */ +#define MAX_SERVICE 12 + +/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */ +#define TX_TIMEOUT (20*HZ/100) + +#define ei_status (*(struct ei_device *)netdev_priv(dev)) + +/* Some generic ethernet register configurations. */ +#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */ +#define E8390_RX_IRQ_MASK 0x5 + +#ifdef AX88796_PLATFORM +#define E8390_RXCONFIG (ei_status.rxcr_base | 0x04) +#define E8390_RXOFF (ei_status.rxcr_base | 0x20) +#else +#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */ +#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */ +#endif + +#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */ +#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */ + + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* + * Only generate indirect loads given a machine that needs them. + * - removed AMIGA_PCMCIA from this list, handled as ISA io now + * - the _p for generates no delay by default 8390p.c overrides this. + */ + +#ifndef ei_inb +#define ei_inb(_p) inb(_p) +#define ei_outb(_v,_p) outb(_v,_p) +#define ei_inb_p(_p) inb(_p) +#define ei_outb_p(_v,_p) outb(_v,_p) +#endif + +#ifndef EI_SHIFT +#define EI_SHIFT(x) (x) +#endif + +#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */ +#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */ +#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */ +#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */ +#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */ +#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */ +#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */ +#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */ +#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */ +#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */ +#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */ +#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */ +#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */ +#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */ +#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */ +#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */ +#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */ +#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */ +#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */ +#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */ +#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */ +#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */ +#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */ +#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */ +#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in EN0_DCFG - Data config register */ +#define ENDCFG_WTS 0x01 /* word transfer mode selection */ +#define ENDCFG_BOS 0x02 /* byte order selection */ + +/* Page 1 register offsets. */ +#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */ +#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */ +#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */ +#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */ +#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +#endif /* _8390_h */ diff --git a/libdde_linux26/examples/ne2k/Makefile b/libdde_linux26/examples/ne2k/Makefile new file mode 100644 index 00000000..2a7b1543 --- /dev/null +++ b/libdde_linux26/examples/ne2k/Makefile @@ -0,0 +1,32 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26_NET),y) +TARGET = ne2k_dde26 +endif + +SRC_C = ne2k-pci.c 8390.c main.c arping.c + +LIBS += -ldde_linux26_net -ldde_linux26.o -lddekit -lio -lomega0 -llist_alloc + +PRIVATE_INCDIR = $(PKGDIR_ABS)/linux26/include $(MY_DDE_INCDIR) $(MY_LINUX26_INCDIR) \ + $(OBJ_BASE)/include/uclibc + +LIBCINCDIR = -nostdinc $(I_GCCINCDIR) +DEFINES = -D__KERNEL__ -DDDE_LINUX\ + $(KBUILD_DEFINES) +CPPFLAGS += $(KBUILD_CPPFLAGS) + +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk + +foo : + @echo $(L4INCDIR) + @echo $(OBJ_BASE) diff --git a/libdde_linux26/examples/ne2k/arping.c b/libdde_linux26/examples/ne2k/arping.c new file mode 100644 index 00000000..18876806 --- /dev/null +++ b/libdde_linux26/examples/ne2k/arping.c @@ -0,0 +1,185 @@ +/**************************************************************** + * (c) 2007 Technische Universitaet Dresden * + * This file is part of DROPS, which is distributed under the * + * terms of the GNU General Public License 2. Please see the * + * COPYING file for details. * + ****************************************************************/ + +#include <l4/log/l4log.h> +#include <l4/util/util.h> +#include <l4/util/l4_macros.h> +#include <l4/sys/ipc.h> + +#include <linux/netdevice.h> +#include <linux/if_ether.h> + +#include "arping.h" + +#define PROT_ICMP 1 +#define ICMP_REPLY 0 +#define ICMP_REQ 8 +#define ETH_ALEN 6 + +/* configuration */ +int arping_verbose = 0; // verbose + +#define VERBOSE_LOG(fmt, ...) \ + do { \ + if (arping_verbose) printk(fmt, ##__VA_ARGS__); \ + } while (0); + +char LOG_tag[9] = "arping"; +l4_ssize_t l4libc_heapsize = 32 * 1024; + +static unsigned char broadcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static int exit_somewhen = 0; + + +struct ethernet_hdr +{ + unsigned char dest[6]; + unsigned char src[6]; + unsigned char type[2]; +}; + + +struct ip_hdr +{ + char version_length; + char type; + l4_int16_t length; + l4_int16_t id; + l4_int16_t flags_offset; + char ttl; + char protocol; + l4_int16_t checksum; + l4_int32_t src_ip; + l4_int32_t dest_ip; +}; + + +struct icmp_hdr +{ + char type; + char code; + l4_uint16_t checksum; + l4_uint16_t id; + l4_uint16_t seq_num; +}; + + +static int handle_icmp_packet(struct sk_buff *skb); +static int handle_icmp_packet(struct sk_buff *skb) +{ + char *data = skb->data; + struct ethernet_hdr *eth = NULL; + struct ethernet_hdr *e = NULL; + struct ip_hdr *ip = NULL; + struct ip_hdr *iphdr = NULL; + struct icmp_hdr *icmp = NULL; + struct icmp_hdr *icmp2 = NULL; + int ver, len; + struct sk_buff *snd_skb = NULL; + + eth = (struct ethernet_hdr *)data; + VERBOSE_LOG("dest mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + eth->dest[0], eth->dest[1], eth->dest[2], + eth->dest[3], eth->dest[4], eth->dest[5]); + VERBOSE_LOG("src mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + eth->src[0], eth->src[1], eth->src[2], + eth->src[3], eth->src[4], eth->src[5]); + VERBOSE_LOG("type field = %02x%02x\n", eth->type[0], eth->type[1]); + if (eth->type[0] != 0x08 || eth->type[1] != 0x00) { + printk("unknown ethernet packet type!\n"); + return -1; + } + + ip = (struct ip_hdr *)(data + sizeof(struct ethernet_hdr)); + VERBOSE_LOG("protocol = %02x (2x?)\n", ip->protocol, PROT_ICMP); + if (ip->protocol != PROT_ICMP) + { + printk("Unknown packet type.\n"); + return -1; + } + + VERBOSE_LOG("ICMP packet!\n"); + ver = ip->version_length >> 4; + len = ip->version_length & 0x0F; + VERBOSE_LOG("IP version = %d, length = %d\n", ver, len); + + VERBOSE_LOG("src IP: "NIPQUAD_FMT"\n", NIPQUAD(ip->src_ip)); + VERBOSE_LOG("dest IP: "NIPQUAD_FMT"\n", NIPQUAD(ip->dest_ip)); + + icmp = (struct icmp_hdr *)(data + sizeof(struct ethernet_hdr) + + sizeof(struct ip_hdr)); + + if (icmp->type != ICMP_REQ) + { + printk("This is no ICMP request.\n"); + return -1; + } + VERBOSE_LOG("Hey this is an ICMP request just for me. :)\n"); + VERBOSE_LOG("ICMP type : %d\n", icmp->type); + VERBOSE_LOG("ICMP code : %d\n", icmp->code); + VERBOSE_LOG("ICMP seq : %d\n", ntohs(icmp->seq_num)); + + snd_skb = alloc_skb(skb->len + skb->dev->hard_header_len, GFP_KERNEL); + memcpy(snd_skb->data, skb->data, skb->len); + + e = (struct ethernet_hdr *)snd_skb->data; + memcpy(e->src, eth->dest, ETH_ALEN); + memcpy(e->dest, eth->src, ETH_ALEN); + VERBOSE_LOG("dest mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + e->dest[0], e->dest[1], e->dest[2], + e->dest[3], e->dest[4], e->dest[5]); + VERBOSE_LOG("src mac = %02x:%02x:%02x:%02x:%02x:%02x\n", + e->src[0], e->src[1], e->src[2], + e->src[3], e->src[4], e->src[5]); + e->type[0] = 0x08; + e->type[1] = 0x00; + + iphdr = (struct ip_hdr *)(snd_skb->data + sizeof(struct ethernet_hdr)); + *iphdr = *ip; + // also switch src and dest + iphdr->src_ip = ip->dest_ip; + iphdr->dest_ip = ip->src_ip; + VERBOSE_LOG("src IP: "NIPQUAD_FMT"\n", NIPQUAD(iphdr->src_ip)); + VERBOSE_LOG("dest IP: "NIPQUAD_FMT"\n", NIPQUAD(iphdr->dest_ip)); + + icmp2 = (struct icmp_hdr *)(snd_skb->data + sizeof(struct ethernet_hdr) + + sizeof(struct ip_hdr)); + *icmp2 = *icmp; + icmp2->type = ICMP_REPLY; + + snd_skb->dev = skb->dev; + snd_skb->len = skb->len; + + VERBOSE_LOG("sending reply\n"); + skb->dev->hard_start_xmit(snd_skb, skb->dev); + VERBOSE_LOG("done\n"); + + return 0; +} + +ddekit_sem_t *arping_semaphore = NULL; +struct arping_elem *arping_list = NULL; + +int arping(void) +{ + arping_semaphore = ddekit_sem_init(0); + + while(1) + { + ddekit_sem_down(arping_semaphore); + struct arping_elem *elem = arping_list; + arping_list = arping_list->next; + + /* parse packet */ + int err = handle_icmp_packet(elem->skb); + VERBOSE_LOG("handle_icmp_packet: %d\n", err); + + kfree_skb(elem->skb); + kfree(elem); + } +} + diff --git a/libdde_linux26/examples/ne2k/arping.h b/libdde_linux26/examples/ne2k/arping.h new file mode 100644 index 00000000..2df7f303 --- /dev/null +++ b/libdde_linux26/examples/ne2k/arping.h @@ -0,0 +1,15 @@ +#pragma once + +#include <l4/dde/ddekit/semaphore.h> + +struct arping_elem +{ + struct arping_elem *next; + struct sk_buff *skb; +}; + +extern ddekit_sem_t *arping_semaphore; +extern struct arping_elem *arping_list; + +#define mac_fmt "%02X:%02X:%02X:%02X:%02X:%02X" +#define mac_str(mac) (unsigned char)((mac)[0]), (unsigned char)((mac)[1]),(mac)[2],(mac)[3],(mac)[4],(mac)[5] diff --git a/libdde_linux26/examples/ne2k/lib8390.c b/libdde_linux26/examples/ne2k/lib8390.c new file mode 100644 index 00000000..789b6cb7 --- /dev/null +++ b/libdde_linux26/examples/ne2k/lib8390.c @@ -0,0 +1,1125 @@ +/* 8390.c: A general NS8390 ethernet driver core for linux. */ +/* + Written 1992-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + + This is the chip-specific code for many 8390-based ethernet adaptors. + This is not a complete driver, it must be combined with board-specific + code such as ne.c, wd.c, 3c503.c, etc. + + Seeing how at least eight drivers use this code, (not counting the + PCMCIA ones either) it is easy to break some card by what seems like + a simple innocent change. Please contact me or Donald if you think + you have found something that needs changing. -- PG + + + Changelog: + + Paul Gortmaker : remove set_bit lock, other cleanups. + Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to + ei_block_input() for eth_io_copy_and_sum(). + Paul Gortmaker : exchange static int ei_pingpong for a #define, + also add better Tx error handling. + Paul Gortmaker : rewrite Rx overrun handling as per NS specs. + Alexey Kuznetsov : use the 8390's six bit hash multicast filter. + Paul Gortmaker : tweak ANK's above multicast changes a bit. + Paul Gortmaker : update packet statistics for v2.1.x + Alan Cox : support arbitary stupid port mappings on the + 68K Macintosh. Support >16bit I/O spaces + Paul Gortmaker : add kmod support for auto-loading of the 8390 + module by all drivers that require it. + Alan Cox : Spinlocking work, added 'BUG_83C690' + Paul Gortmaker : Separate out Tx timeout code from Tx path. + Paul Gortmaker : Remove old unused single Tx buffer code. + Hayato Fujiwara : Add m32r support. + Paul Gortmaker : use skb_padto() instead of stack scratch area + + Sources: + The National Semiconductor LAN Databook, and the 3Com 3c503 databook. + + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/bitops.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/in.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/crc32.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#define NS8390_CORE +#include "8390.h" + +#define BUG_83C690 + +/* These are the operational function interfaces to board-specific + routines. + void reset_8390(struct net_device *dev) + Resets the board associated with DEV, including a hardware reset of + the 8390. This is only called when there is a transmit timeout, and + it is always followed by 8390_init(). + void block_output(struct net_device *dev, int count, const unsigned char *buf, + int start_page) + Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The + "page" value uses the 8390's 256-byte pages. + void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page) + Read the 4 byte, page aligned 8390 header. *If* there is a + subsequent read, it will be of the rest of the packet. + void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) + Read COUNT bytes from the packet buffer into the skb data area. Start + reading from RING_OFFSET, the address as the 8390 sees it. This will always + follow the read of the 8390 header. +*/ +#define ei_reset_8390 (ei_local->reset_8390) +#define ei_block_output (ei_local->block_output) +#define ei_block_input (ei_local->block_input) +#define ei_get_8390_hdr (ei_local->get_8390_hdr) + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef ei_debug +int ei_debug = 1; +#endif + +/* Index to functions. */ +static void ei_tx_intr(struct net_device *dev); +static void ei_tx_err(struct net_device *dev); +void ei_tx_timeout(struct net_device *dev); +static void ei_receive(struct net_device *dev); +static void ei_rx_overrun(struct net_device *dev); + +/* Routines generic to NS8390-based boards. */ +static void NS8390_trigger_send(struct net_device *dev, unsigned int length, + int start_page); +static void do_set_multicast_list(struct net_device *dev); +static void __NS8390_init(struct net_device *dev, int startp); + +/* + * SMP and the 8390 setup. + * + * The 8390 isnt exactly designed to be multithreaded on RX/TX. There is + * a page register that controls bank and packet buffer access. We guard + * this with ei_local->page_lock. Nobody should assume or set the page other + * than zero when the lock is not held. Lock holders must restore page 0 + * before unlocking. Even pure readers must take the lock to protect in + * page 0. + * + * To make life difficult the chip can also be very slow. We therefore can't + * just use spinlocks. For the longer lockups we disable the irq the device + * sits on and hold the lock. We must hold the lock because there is a dual + * processor case other than interrupts (get stats/set multicast list in + * parallel with each other and transmit). + * + * Note: in theory we can just disable the irq on the card _but_ there is + * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs" + * enter lock, take the queued irq. So we waddle instead of flying. + * + * Finally by special arrangement for the purpose of being generally + * annoying the transmit function is called bh atomic. That places + * restrictions on the user context callers as disable_irq won't save + * them. + * + * Additional explanation of problems with locking by Alan Cox: + * + * "The author (me) didn't use spin_lock_irqsave because the slowness of the + * card means that approach caused horrible problems like losing serial data + * at 38400 baud on some chips. Remember many 8390 nics on PCI were ISA + * chips with FPGA front ends. + * + * Ok the logic behind the 8390 is very simple: + * + * Things to know + * - IRQ delivery is asynchronous to the PCI bus + * - Blocking the local CPU IRQ via spin locks was too slow + * - The chip has register windows needing locking work + * + * So the path was once (I say once as people appear to have changed it + * in the mean time and it now looks rather bogus if the changes to use + * disable_irq_nosync_irqsave are disabling the local IRQ) + * + * + * Take the page lock + * Mask the IRQ on chip + * Disable the IRQ (but not mask locally- someone seems to have + * broken this with the lock validator stuff) + * [This must be _nosync as the page lock may otherwise + * deadlock us] + * Drop the page lock and turn IRQs back on + * + * At this point an existing IRQ may still be running but we can't + * get a new one + * + * Take the lock (so we know the IRQ has terminated) but don't mask + * the IRQs on the processor + * Set irqlock [for debug] + * + * Transmit (slow as ****) + * + * re-enable the IRQ + * + * + * We have to use disable_irq because otherwise you will get delayed + * interrupts on the APIC bus deadlocking the transmit path. + * + * Quite hairy but the chip simply wasn't designed for SMP and you can't + * even ACK an interrupt without risking corrupting other parallel + * activities on the chip." [lkml, 25 Jul 2007] + */ + + + +/** + * ei_open - Open/initialize the board. + * @dev: network device to initialize + * + * This routine goes all-out, setting everything + * up anew at each open, even though many of these registers should only + * need to be set once at boot. + */ +static int __ei_open(struct net_device *dev) +{ + unsigned long flags; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + + if (dev->watchdog_timeo <= 0) + dev->watchdog_timeo = TX_TIMEOUT; + + /* + * Grab the page lock so we own the register set, then call + * the init function. + */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + __NS8390_init(dev, 1); + /* Set the flag before we drop the lock, That way the IRQ arrives + after its set and we get no silly warnings */ + netif_start_queue(dev); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + ei_local->irqlock = 0; + return 0; +} + +/** + * ei_close - shut down network device + * @dev: network device to close + * + * Opposite of ei_open(). Only used when "ifconfig <devname> down" is done. + */ +static int __ei_close(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned long flags; + + /* + * Hold the page lock during close + */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + __NS8390_init(dev, 0); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + netif_stop_queue(dev); + return 0; +} + +/** + * ei_tx_timeout - handle transmit time out condition + * @dev: network device which has apparently fallen asleep + * + * Called by kernel when device never acknowledges a transmit has + * completed (or failed) - i.e. never posted a Tx related interrupt. + */ + +static void __ei_tx_timeout(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int txsr, isr, tickssofar = jiffies - dev->trans_start; + unsigned long flags; + + dev->stats.tx_errors++; + + spin_lock_irqsave(&ei_local->page_lock, flags); + txsr = ei_inb(e8390_base+EN0_TSR); + isr = ei_inb(e8390_base+EN0_ISR); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n", + dev->name, (txsr & ENTSR_ABT) ? "excess collisions." : + (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar); + + if (!isr && !dev->stats.tx_packets) + { + /* The 8390 probably hasn't gotten on the cable yet. */ + ei_local->interface_num ^= 1; /* Try a different xcvr. */ + } + + /* Ugly but a reset can be slow, yet must be protected */ + + disable_irq_nosync_lockdep(dev->irq); + spin_lock(&ei_local->page_lock); + + /* Try to restart the card. Perhaps the user has fixed something. */ + ei_reset_8390(dev); + __NS8390_init(dev, 1); + + spin_unlock(&ei_local->page_lock); + enable_irq_lockdep(dev->irq); + netif_wake_queue(dev); +} + +/** + * ei_start_xmit - begin packet transmission + * @skb: packet to be sent + * @dev: network device to which packet is sent + * + * Sends a packet to an 8390 network device. + */ + +static int __ei_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int send_length = skb->len, output_page; + unsigned long flags; + char buf[ETH_ZLEN]; + char *data = skb->data; + + if (skb->len < ETH_ZLEN) { + memset(buf, 0, ETH_ZLEN); /* more efficient than doing just the needed bits */ + memcpy(buf, data, skb->len); + send_length = ETH_ZLEN; + data = buf; + } + + /* Mask interrupts from the ethercard. + SMP: We have to grab the lock here otherwise the IRQ handler + on another CPU can flip window and race the IRQ mask set. We end + up trashing the mcast filter not disabling irqs if we don't lock */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + ei_outb_p(0x00, e8390_base + EN0_IMR); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + + /* + * Slow phase with lock held. + */ + + disable_irq_nosync_lockdep_irqsave(dev->irq, &flags); + + spin_lock(&ei_local->page_lock); + + ei_local->irqlock = 1; + + /* + * We have two Tx slots available for use. Find the first free + * slot, and then perform some sanity checks. With two Tx bufs, + * you get very close to transmitting back-to-back packets. With + * only one Tx buf, the transmitter sits idle while you reload the + * card, leaving a substantial gap between each transmitted packet. + */ + + if (ei_local->tx1 == 0) + { + output_page = ei_local->tx_start_page; + ei_local->tx1 = send_length; + if (ei_debug && ei_local->tx2 > 0) + printk(KERN_DEBUG "%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n", + dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing); + } + else if (ei_local->tx2 == 0) + { + output_page = ei_local->tx_start_page + TX_PAGES/2; + ei_local->tx2 = send_length; + if (ei_debug && ei_local->tx1 > 0) + printk(KERN_DEBUG "%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n", + dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing); + } + else + { /* We should never get here. */ + if (ei_debug) + printk(KERN_DEBUG "%s: No Tx buffers free! tx1=%d tx2=%d last=%d\n", + dev->name, ei_local->tx1, ei_local->tx2, ei_local->lasttx); + ei_local->irqlock = 0; + netif_stop_queue(dev); + ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR); + spin_unlock(&ei_local->page_lock); + enable_irq_lockdep_irqrestore(dev->irq, &flags); + dev->stats.tx_errors++; + return 1; + } + + /* + * Okay, now upload the packet and trigger a send if the transmitter + * isn't already sending. If it is busy, the interrupt handler will + * trigger the send later, upon receiving a Tx done interrupt. + */ + + ei_block_output(dev, send_length, data, output_page); + + if (! ei_local->txing) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, send_length, output_page); + dev->trans_start = jiffies; + if (output_page == ei_local->tx_start_page) + { + ei_local->tx1 = -1; + ei_local->lasttx = -1; + } + else + { + ei_local->tx2 = -1; + ei_local->lasttx = -2; + } + } + else ei_local->txqueue++; + + if (ei_local->tx1 && ei_local->tx2) + netif_stop_queue(dev); + else + netif_start_queue(dev); + + /* Turn 8390 interrupts back on. */ + ei_local->irqlock = 0; + ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR); + + spin_unlock(&ei_local->page_lock); + enable_irq_lockdep_irqrestore(dev->irq, &flags); + + dev_kfree_skb (skb); + dev->stats.tx_bytes += send_length; + + return 0; +} + +/** + * ei_interrupt - handle the interrupts from an 8390 + * @irq: interrupt number + * @dev_id: a pointer to the net_device + * + * Handle the ether interface interrupts. We pull packets from + * the 8390 via the card specific functions and fire them at the networking + * stack. We also handle transmit completions and wake the transmit path if + * necessary. We also update the counters and do other housekeeping as + * needed. + */ + +static irqreturn_t __ei_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + unsigned long e8390_base = dev->base_addr; + int interrupts, nr_serviced = 0; + struct ei_device *ei_local = netdev_priv(dev); + + /* + * Protect the irq test too. + */ + + spin_lock(&ei_local->page_lock); + + if (ei_local->irqlock) + { +#if 1 /* This might just be an interrupt for a PCI device sharing this line */ + /* The "irqlock" check is only for testing. */ + printk(ei_local->irqlock + ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n" + : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n", + dev->name, ei_inb_p(e8390_base + EN0_ISR), + ei_inb_p(e8390_base + EN0_IMR)); +#endif + spin_unlock(&ei_local->page_lock); + return IRQ_NONE; + } + + /* Change to page 0 and read the intr status reg. */ + ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); + if (ei_debug > 3) + printk(KERN_DEBUG "%s: interrupt(isr=%#2.2x).\n", dev->name, + ei_inb_p(e8390_base + EN0_ISR)); + + /* !!Assumption!! -- we stay in page 0. Don't break this. */ + while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 + && ++nr_serviced < MAX_SERVICE) + { + if (!netif_running(dev)) { + printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name); + /* rmk - acknowledge the interrupts */ + ei_outb_p(interrupts, e8390_base + EN0_ISR); + interrupts = 0; + break; + } + if (interrupts & ENISR_OVER) + ei_rx_overrun(dev); + else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) + { + /* Got a good (?) packet. */ + ei_receive(dev); + } + /* Push the next to-transmit packet through. */ + if (interrupts & ENISR_TX) + ei_tx_intr(dev); + else if (interrupts & ENISR_TX_ERR) + ei_tx_err(dev); + + if (interrupts & ENISR_COUNTERS) + { + dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0); + dev->stats.rx_crc_errors += ei_inb_p(e8390_base + EN0_COUNTER1); + dev->stats.rx_missed_errors+= ei_inb_p(e8390_base + EN0_COUNTER2); + ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */ + } + + /* Ignore any RDC interrupts that make it back to here. */ + if (interrupts & ENISR_RDC) + { + ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR); + } + + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD); + } + + if (interrupts && ei_debug) + { + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD); + if (nr_serviced >= MAX_SERVICE) + { + /* 0xFF is valid for a card removal */ + if(interrupts!=0xFF) + printk(KERN_WARNING "%s: Too much work at interrupt, status %#2.2x\n", + dev->name, interrupts); + ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */ + } else { + printk(KERN_WARNING "%s: unknown interrupt %#2x\n", dev->name, interrupts); + ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */ + } + } + spin_unlock(&ei_local->page_lock); + return IRQ_RETVAL(nr_serviced > 0); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void __ei_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + __ei_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +/** + * ei_tx_err - handle transmitter error + * @dev: network device which threw the exception + * + * A transmitter error has happened. Most likely excess collisions (which + * is a fairly normal condition). If the error is one where the Tx will + * have been aborted, we try and send another one right away, instead of + * letting the failed packet sit and collect dust in the Tx buffer. This + * is a much better solution as it avoids kernel based Tx timeouts, and + * an unnecessary card reset. + * + * Called with lock held. + */ + +static void ei_tx_err(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + /* ei_local is used on some platforms via the EI_SHIFT macro */ + struct ei_device *ei_local __maybe_unused = netdev_priv(dev); + unsigned char txsr = ei_inb_p(e8390_base+EN0_TSR); + unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU); + +#ifdef VERBOSE_ERROR_DUMP + printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr); + if (txsr & ENTSR_ABT) + printk("excess-collisions "); + if (txsr & ENTSR_ND) + printk("non-deferral "); + if (txsr & ENTSR_CRS) + printk("lost-carrier "); + if (txsr & ENTSR_FU) + printk("FIFO-underrun "); + if (txsr & ENTSR_CDH) + printk("lost-heartbeat "); + printk("\n"); +#endif + + ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */ + + if (tx_was_aborted) + ei_tx_intr(dev); + else + { + dev->stats.tx_errors++; + if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++; + if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++; + if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++; + } +} + +/** + * ei_tx_intr - transmit interrupt handler + * @dev: network device for which tx intr is handled + * + * We have finished a transmit: check for errors and then trigger the next + * packet to be sent. Called with lock held. + */ + +static void ei_tx_intr(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int status = ei_inb(e8390_base + EN0_TSR); + + ei_outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */ + + /* + * There are two Tx buffers, see which one finished, and trigger + * the send of another one if it exists. + */ + ei_local->txqueue--; + + if (ei_local->tx1 < 0) + { + if (ei_local->lasttx != 1 && ei_local->lasttx != -1) + printk(KERN_ERR "%s: bogus last_tx_buffer %d, tx1=%d.\n", + ei_local->name, ei_local->lasttx, ei_local->tx1); + ei_local->tx1 = 0; + if (ei_local->tx2 > 0) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); + dev->trans_start = jiffies; + ei_local->tx2 = -1, + ei_local->lasttx = 2; + } + else ei_local->lasttx = 20, ei_local->txing = 0; + } + else if (ei_local->tx2 < 0) + { + if (ei_local->lasttx != 2 && ei_local->lasttx != -2) + printk("%s: bogus last_tx_buffer %d, tx2=%d.\n", + ei_local->name, ei_local->lasttx, ei_local->tx2); + ei_local->tx2 = 0; + if (ei_local->tx1 > 0) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); + dev->trans_start = jiffies; + ei_local->tx1 = -1; + ei_local->lasttx = 1; + } + else + ei_local->lasttx = 10, ei_local->txing = 0; + } +// else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n", +// dev->name, ei_local->lasttx); + + /* Minimize Tx latency: update the statistics after we restart TXing. */ + if (status & ENTSR_COL) + dev->stats.collisions++; + if (status & ENTSR_PTX) + dev->stats.tx_packets++; + else + { + dev->stats.tx_errors++; + if (status & ENTSR_ABT) + { + dev->stats.tx_aborted_errors++; + dev->stats.collisions += 16; + } + if (status & ENTSR_CRS) + dev->stats.tx_carrier_errors++; + if (status & ENTSR_FU) + dev->stats.tx_fifo_errors++; + if (status & ENTSR_CDH) + dev->stats.tx_heartbeat_errors++; + if (status & ENTSR_OWC) + dev->stats.tx_window_errors++; + } + netif_wake_queue(dev); +} + +/** + * ei_receive - receive some packets + * @dev: network device with which receive will be run + * + * We have a good packet(s), get it/them out of the buffers. + * Called with lock held. + */ + +static void ei_receive(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned char rxing_page, this_frame, next_frame; + unsigned short current_offset; + int rx_pkt_count = 0; + struct e8390_pkt_hdr rx_frame; + int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page; + + while (++rx_pkt_count < 10) + { + int pkt_len, pkt_stat; + + /* Get the rx page (incoming packet pointer). */ + ei_outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD); + rxing_page = ei_inb_p(e8390_base + EN1_CURPAG); + ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); + + /* Remove one frame from the ring. Boundary is always a page behind. */ + this_frame = ei_inb_p(e8390_base + EN0_BOUNDARY) + 1; + if (this_frame >= ei_local->stop_page) + this_frame = ei_local->rx_start_page; + + /* Someday we'll omit the previous, iff we never get this message. + (There is at least one clone claimed to have a problem.) + + Keep quiet if it looks like a card removal. One problem here + is that some clones crash in roughly the same way. + */ + if (ei_debug > 0 && this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF)) + printk(KERN_ERR "%s: mismatched read page pointers %2x vs %2x.\n", + dev->name, this_frame, ei_local->current_page); + + if (this_frame == rxing_page) /* Read all the frames? */ + break; /* Done for now */ + + current_offset = this_frame << 8; + ei_get_8390_hdr(dev, &rx_frame, this_frame); + + pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); + pkt_stat = rx_frame.status; + + next_frame = this_frame + 1 + ((pkt_len+4)>>8); + + /* Check for bogosity warned by 3c503 book: the status byte is never + written. This happened a lot during testing! This code should be + cleaned up someday. */ + if (rx_frame.next != next_frame + && rx_frame.next != next_frame + 1 + && rx_frame.next != next_frame - num_rx_pages + && rx_frame.next != next_frame + 1 - num_rx_pages) { + ei_local->current_page = rxing_page; + ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY); + dev->stats.rx_errors++; + continue; + } + + if (pkt_len < 60 || pkt_len > 1518) + { + if (ei_debug) + printk(KERN_DEBUG "%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n", + dev->name, rx_frame.count, rx_frame.status, + rx_frame.next); + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + } + else if ((pkt_stat & 0x0F) == ENRSR_RXOK) + { + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len+2); + if (skb == NULL) + { + if (ei_debug > 1) + printk(KERN_DEBUG "%s: Couldn't allocate a sk_buff of size %d.\n", + dev->name, pkt_len); + dev->stats.rx_dropped++; + break; + } + else + { + skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ + skb_put(skb, pkt_len); /* Make room */ + ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + if (pkt_stat & ENRSR_PHY) + dev->stats.multicast++; + } + } + else + { + if (ei_debug) + printk(KERN_DEBUG "%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n", + dev->name, rx_frame.status, rx_frame.next, + rx_frame.count); + dev->stats.rx_errors++; + /* NB: The NIC counts CRC, frame and missed errors. */ + if (pkt_stat & ENRSR_FO) + dev->stats.rx_fifo_errors++; + } + next_frame = rx_frame.next; + + /* This _should_ never happen: it's here for avoiding bad clones. */ + if (next_frame >= ei_local->stop_page) { + printk("%s: next frame inconsistency, %#2x\n", dev->name, + next_frame); + next_frame = ei_local->rx_start_page; + } + ei_local->current_page = next_frame; + ei_outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); + } + + /* We used to also ack ENISR_OVER here, but that would sometimes mask + a real overrun, leaving the 8390 in a stopped state with rec'vr off. */ + ei_outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR); + return; +} + +/** + * ei_rx_overrun - handle receiver overrun + * @dev: network device which threw exception + * + * We have a receiver overrun: we have to kick the 8390 to get it started + * again. Problem is that you have to kick it exactly as NS prescribes in + * the updated datasheets, or "the NIC may act in an unpredictable manner." + * This includes causing "the NIC to defer indefinitely when it is stopped + * on a busy network." Ugh. + * Called with lock held. Don't call this with the interrupts off or your + * computer will hate you - it takes 10ms or so. + */ + +static void ei_rx_overrun(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + unsigned char was_txing, must_resend = 0; + /* ei_local is used on some platforms via the EI_SHIFT macro */ + struct ei_device *ei_local __maybe_unused = netdev_priv(dev); + + /* + * Record whether a Tx was in progress and then issue the + * stop command. + */ + was_txing = ei_inb_p(e8390_base+E8390_CMD) & E8390_TRANS; + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); + + if (ei_debug > 1) + printk(KERN_DEBUG "%s: Receiver overrun.\n", dev->name); + dev->stats.rx_over_errors++; + + /* + * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. + * Early datasheets said to poll the reset bit, but now they say that + * it "is not a reliable indicator and subsequently should be ignored." + * We wait at least 10ms. + */ + + mdelay(10); + + /* + * Reset RBCR[01] back to zero as per magic incantation. + */ + ei_outb_p(0x00, e8390_base+EN0_RCNTLO); + ei_outb_p(0x00, e8390_base+EN0_RCNTHI); + + /* + * See if any Tx was interrupted or not. According to NS, this + * step is vital, and skipping it will cause no end of havoc. + */ + + if (was_txing) + { + unsigned char tx_completed = ei_inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); + if (!tx_completed) + must_resend = 1; + } + + /* + * Have to enter loopback mode and then restart the NIC before + * you are allowed to slurp packets up off the ring. + */ + ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); + ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); + + /* + * Clear the Rx ring of all the debris, and ack the interrupt. + */ + ei_receive(dev); + ei_outb_p(ENISR_OVER, e8390_base+EN0_ISR); + + /* + * Leave loopback mode, and resend any packet that got stopped. + */ + ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); + if (must_resend) + ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); +} + +/* + * Collect the stats. This is called unlocked and from several contexts. + */ + +static struct net_device_stats *__ei_get_stats(struct net_device *dev) +{ + unsigned long ioaddr = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned long flags; + + /* If the card is stopped, just return the present stats. */ + if (!netif_running(dev)) + return &dev->stats; + + spin_lock_irqsave(&ei_local->page_lock,flags); + /* Read the counter registers, assuming we are in page 0. */ + dev->stats.rx_frame_errors += ei_inb_p(ioaddr + EN0_COUNTER0); + dev->stats.rx_crc_errors += ei_inb_p(ioaddr + EN0_COUNTER1); + dev->stats.rx_missed_errors+= ei_inb_p(ioaddr + EN0_COUNTER2); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + return &dev->stats; +} + +/* + * Form the 64 bit 8390 multicast table from the linked list of addresses + * associated with this dev structure. + */ + +static inline void make_mc_bits(u8 *bits, struct net_device *dev) +{ + struct dev_mc_list *dmi; + + for (dmi=dev->mc_list; dmi; dmi=dmi->next) + { + u32 crc; + if (dmi->dmi_addrlen != ETH_ALEN) + { + printk(KERN_INFO "%s: invalid multicast address length given.\n", dev->name); + continue; + } + crc = ether_crc(ETH_ALEN, dmi->dmi_addr); + /* + * The 8390 uses the 6 most significant bits of the + * CRC to index the multicast table. + */ + bits[crc>>29] |= (1<<((crc>>26)&7)); + } +} + +/** + * do_set_multicast_list - set/clear multicast filter + * @dev: net device for which multicast filter is adjusted + * + * Set or clear the multicast filter for this adaptor. May be called + * from a BH in 2.1.x. Must be called with lock held. + */ + +static void do_set_multicast_list(struct net_device *dev) +{ + unsigned long e8390_base = dev->base_addr; + int i; + struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev); + + if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) + { + memset(ei_local->mcfilter, 0, 8); + if (dev->mc_list) + make_mc_bits(ei_local->mcfilter, dev); + } + else + memset(ei_local->mcfilter, 0xFF, 8); /* mcast set to accept-all */ + + /* + * DP8390 manuals don't specify any magic sequence for altering + * the multicast regs on an already running card. To be safe, we + * ensure multicast mode is off prior to loading up the new hash + * table. If this proves to be not enough, we can always resort + * to stopping the NIC, loading the table and then restarting. + * + * Bug Alert! The MC regs on the SMC 83C690 (SMC Elite and SMC + * Elite16) appear to be write-only. The NS 8390 data sheet lists + * them as r/w so this is a bug. The SMC 83C790 (SMC Ultra and + * Ultra32 EISA) appears to have this bug fixed. + */ + + if (netif_running(dev)) + ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); + ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD); + for(i = 0; i < 8; i++) + { + ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i)); +#ifndef BUG_83C690 + if(ei_inb_p(e8390_base + EN1_MULT_SHIFT(i))!=ei_local->mcfilter[i]) + printk(KERN_ERR "Multicast filter read/write mismap %d\n",i); +#endif + } + ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD); + + if(dev->flags&IFF_PROMISC) + ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR); + else if(dev->flags&IFF_ALLMULTI || dev->mc_list) + ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR); + else + ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); + } + +/* + * Called without lock held. This is invoked from user context and may + * be parallel to just about everything else. Its also fairly quick and + * not called too often. Must protect against both bh and irq users + */ + +static void __ei_set_multicast_list(struct net_device *dev) +{ + unsigned long flags; + struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev); + + spin_lock_irqsave(&ei_local->page_lock, flags); + do_set_multicast_list(dev); + spin_unlock_irqrestore(&ei_local->page_lock, flags); +} + +/** + * ethdev_setup - init rest of 8390 device struct + * @dev: network device structure to init + * + * Initialize the rest of the 8390 device structure. Do NOT __init + * this, as it is used by 8390 based modular drivers too. + */ + +static void ethdev_setup(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + if (ei_debug > 1) + printk(version); + + ether_setup(dev); + + spin_lock_init(&ei_local->page_lock); +} + +/** + * alloc_ei_netdev - alloc_etherdev counterpart for 8390 + * @size: extra bytes to allocate + * + * Allocate 8390-specific net_device. + */ +static struct net_device *____alloc_ei_netdev(int size) +{ + return alloc_netdev(sizeof(struct ei_device) + size, "eth%d", + ethdev_setup); +} + + + + +/* This page of functions should be 8390 generic */ +/* Follow National Semi's recommendations for initializing the "NIC". */ + +/** + * NS8390_init - initialize 8390 hardware + * @dev: network device to initialize + * @startp: boolean. non-zero value to initiate chip processing + * + * Must be called with lock held. + */ + +static void __NS8390_init(struct net_device *dev, int startp) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int i; + int endcfg = ei_local->word16 + ? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0)) + : 0x48; + + if(sizeof(struct e8390_pkt_hdr)!=4) + panic("8390.c: header struct mispacked\n"); + /* Follow National Semi's recommendations for initing the DP83902. */ + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */ + ei_outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */ + /* Clear the remote byte count registers. */ + ei_outb_p(0x00, e8390_base + EN0_RCNTLO); + ei_outb_p(0x00, e8390_base + EN0_RCNTHI); + /* Set to monitor and loopback mode -- this is vital!. */ + ei_outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */ + ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ + /* Set the transmit page and receive ring. */ + ei_outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR); + ei_local->tx1 = ei_local->tx2 = 0; + ei_outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG); + ei_outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ + ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */ + ei_outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG); + /* Clear the pending interrupts and mask. */ + ei_outb_p(0xFF, e8390_base + EN0_ISR); + ei_outb_p(0x00, e8390_base + EN0_IMR); + + /* Copy the station address into the DS8390 registers. */ + + ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */ + for(i = 0; i < 6; i++) + { + ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i)); + if (ei_debug > 1 && ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) + printk(KERN_ERR "Hw. address read/write mismap %d\n",i); + } + + ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); + + netif_start_queue(dev); + ei_local->tx1 = ei_local->tx2 = 0; + ei_local->txing = 0; + + if (startp) + { + ei_outb_p(0xff, e8390_base + EN0_ISR); + ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR); + ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); + ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */ + /* 3c503 TechMan says rxconfig only after the NIC is started. */ + ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */ + do_set_multicast_list(dev); /* (re)load the mcast table */ + } +} + +/* Trigger a transmit start, assuming the length is valid. + Always called with the page lock held */ + +static void NS8390_trigger_send(struct net_device *dev, unsigned int length, + int start_page) +{ + unsigned long e8390_base = dev->base_addr; + struct ei_device *ei_local __attribute((unused)) = (struct ei_device *) netdev_priv(dev); + + ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD); + + if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) + { + printk(KERN_WARNING "%s: trigger_send() called with the transmitter busy.\n", + dev->name); + return; + } + ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO); + ei_outb_p(length >> 8, e8390_base + EN0_TCNTHI); + ei_outb_p(start_page, e8390_base + EN0_TPSR); + ei_outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD); +} diff --git a/libdde_linux26/examples/ne2k/main.c b/libdde_linux26/examples/ne2k/main.c new file mode 100644 index 00000000..8dba2f00 --- /dev/null +++ b/libdde_linux26/examples/ne2k/main.c @@ -0,0 +1,115 @@ +#include <l4/dde/linux26/dde26.h> /* l4dde26_*() */ +#include <l4/dde/linux26/dde26_net.h> /* l4dde26 networking */ +#include <l4/sys/types.h> /* l4_threadid_t */ +#include <l4/sys/ipc.h> /* l4_ipc_*() */ + +#include <linux/netdevice.h> /* struct sk_buff */ +#include <linux/pci.h> /* pci_unregister_driver() */ +#include <linux/init.h> // initcall() +#include <linux/delay.h> // msleep() + +#include "arping.h" + +#include "8390.h" /* that's what we are */ + +extern int arping_verbose; +#define VERBOSE_LOG(fmt, ...) \ + do { \ + if (arping_verbose) printk(fmt, ##__VA_ARGS__); \ + } while (0); + +extern struct pci_driver ne2k_driver; +extern int arping(void); + +void open_nw_dev(void); +void open_nw_dev() +{ + struct net_device *dev; + struct net *net; + + read_lock(&dev_base_lock); + for_each_net(net) { + for_each_netdev(net, dev) { + int err = 0; + printk("dev: '%s'\n", dev->name); + printk("MAC: "mac_fmt"\n", mac_str(dev->dev_addr)); + + err = dev_open(dev); + } + } + read_unlock(&dev_base_lock); +} + +void close_nw_dev(void); +void close_nw_dev(void) +{ + struct net_device *dev; + struct net *net; + + read_lock(&dev_base_lock); + for_each_net(net) { + for_each_netdev(net, dev) { + int err = 0; + + err = dev_close(dev); + printk("closed %s\n", dev->name); + } + } + read_unlock(&dev_base_lock); +} + +static int net_rx_handle(struct sk_buff *skb) +{ + skb_push(skb, skb->dev->hard_header_len); + + struct arping_elem *e = kmalloc(sizeof(struct arping_elem), GFP_KERNEL); + e->skb = skb; + skb_get(skb); + e->next = NULL; + + if (arping_list == NULL) + arping_list = e; + else { + struct arping_elem *f = arping_list; + while (f->next) + f = f->next; + f->next = e; + } + + ddekit_sem_up(arping_semaphore); + + kfree_skb(skb); + + VERBOSE_LOG("freed skb, returning from netif_rx\n"); + return NET_RX_SUCCESS; +} + +//subsys_initcall(l4dde26_init_pci); + +int main(int argc, char **argv); +int main(int argc, char **argv) +{ + l4dde26_init(); + l4dde26_process_init(); + l4dde26_softirq_init(); + + printk("Initializing skb subsystem\n"); + skb_init(); + + l4dde26_do_initcalls(); + printk("Setting rx callback @ %p\n", net_rx_handle); + l4dde26_register_rx_callback(net_rx_handle); + + printk("Opening nw devs.\n"); + open_nw_dev(); + printk("dev is up and ready.\n"); + + arping(); + + close_nw_dev(); + + pci_unregister_driver(&ne2k_driver); + printk("shut down driver\n"); + + return 0; +} diff --git a/libdde_linux26/examples/ne2k/ne2k-pci.c b/libdde_linux26/examples/ne2k/ne2k-pci.c new file mode 100644 index 00000000..c9995d36 --- /dev/null +++ b/libdde_linux26/examples/ne2k/ne2k-pci.c @@ -0,0 +1,727 @@ +/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */ +/* + A Linux device driver for PCI NE2000 clones. + + Authors and other copyright holders: + 1992-2000 by Donald Becker, NE2000 core and various modifications. + 1995-1998 by Paul Gortmaker, core modifications and PCI support. + Copyright 1993 assigned to the United States Government as represented + by the Director, National Security Agency. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Issues remaining: + People are making PCI ne2000 clones! Oh the horror, the horror... + Limited full-duplex support. +*/ + +#define DRV_NAME "ne2k-pci" +#define DRV_VERSION "1.03" +#define DRV_RELDATE "9/22/2003" + + +/* The user-configurable values. + These may be modified when a driver module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ + +#define MAX_UNITS 8 /* More are supported, limit only on options */ +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS]; +static int options[MAX_UNITS]; + +/* Force a non std. amount of memory. Units are 256 byte pages. */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ethtool.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include "8390.h" + +/* These identify the driver base version and may not be removed. */ +static char version[] __devinitdata = +KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " D. Becker/P. Gortmaker\n"; + +#if defined(__powerpc__) +#define inl_le(addr) le32_to_cpu(inl(addr)) +#define inw_le(addr) le16_to_cpu(inw(addr)) +#endif + +#define PFX DRV_NAME ": " + +MODULE_AUTHOR("Donald Becker / Paul Gortmaker"); +MODULE_DESCRIPTION("PCI NE2000 clone driver"); +MODULE_LICENSE("GPL"); + +module_param(debug, int, 0); +module_param_array(options, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +MODULE_PARM_DESC(debug, "debug level (1-2)"); +MODULE_PARM_DESC(options, "Bit 5: full duplex"); +MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); + +/* Some defines that people can play with if so inclined. */ + +/* Use 32 bit data-movement operations instead of 16 bit. */ +#define USE_LONGIO + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Flags. We rename an existing ei_status field to store flags! */ +/* Thus only the low 8 bits are usable for non-init-time flags. */ +#define ne2k_flags reg0 +enum { + ONLY_16BIT_IO=8, ONLY_32BIT_IO=4, /* Chip can do only 16/32-bit xfers. */ + FORCE_FDX=0x20, /* User override. */ + REALTEK_FDX=0x40, HOLTEK_FDX=0x80, + STOP_PG_0x60=0x100, +}; + +enum ne2k_pci_chipsets { + CH_RealTek_RTL_8029 = 0, + CH_Winbond_89C940, + CH_Compex_RL2000, + CH_KTI_ET32P2, + CH_NetVin_NV5000SC, + CH_Via_86C926, + CH_SureCom_NE34, + CH_Winbond_W89C940F, + CH_Holtek_HT80232, + CH_Holtek_HT80229, + CH_Winbond_89C940_8c4a, +}; + + +static struct { + char *name; + int flags; +} pci_clone_list[] __devinitdata = { + {"RealTek RTL-8029", REALTEK_FDX}, + {"Winbond 89C940", 0}, + {"Compex RL2000", 0}, + {"KTI ET32P2", 0}, + {"NetVin NV5000SC", 0}, + {"Via 86C926", ONLY_16BIT_IO}, + {"SureCom NE34", 0}, + {"Winbond W89C940F", 0}, + {"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX}, + {"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 }, + {"Winbond W89C940(misprogrammed)", 0}, + {NULL,} +}; + + +static struct pci_device_id ne2k_pci_tbl[] = { + { 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 }, + { 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 }, + { 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 }, + { 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 }, + { 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC }, + { 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 }, + { 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 }, + { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F }, + { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 }, + { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 }, + { 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl); + + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 +#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT 0x20 + +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + + +static int ne2k_pci_open(struct net_device *dev); +static int ne2k_pci_close(struct net_device *dev); + +static void ne2k_pci_reset_8390(struct net_device *dev); +static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ne2k_pci_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ne2k_pci_block_output(struct net_device *dev, const int count, + const unsigned char *buf, const int start_page); +static const struct ethtool_ops ne2k_pci_ethtool_ops; + + + +/* There is no room in the standard 8390 structure for extra info we need, + so we build a meta/outer-wrapper structure.. */ +struct ne2k_pci_card { + struct net_device *dev; + struct pci_dev *pci_dev; +}; + + + +/* + NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet + buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes + 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be + detected by their SA prefix. + + Reading the SAPROM from a word-wide card with the 8390 set in byte-wide + mode results in doubled values, which can be detected and compensated for. + + The probe is also responsible for initializing the card and filling + in the 'dev' and 'ei_status' structures. +*/ + +static const struct net_device_ops ne2k_netdev_ops = { + .ndo_open = ne2k_pci_open, + .ndo_stop = ne2k_pci_close, + .ndo_start_xmit = ei_start_xmit, + .ndo_tx_timeout = ei_tx_timeout, + .ndo_get_stats = ei_get_stats, + .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ei_poll, +#endif +}; + +static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + int i; + unsigned char SA_prom[32]; + int start_page, stop_page; + int irq, reg0, chip_idx = ent->driver_data; + static unsigned int fnd_cnt; + long ioaddr; + int flags = pci_clone_list[chip_idx].flags; + +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + + fnd_cnt++; + + i = pci_enable_device (pdev); + if (i) + return i; + + ioaddr = pci_resource_start (pdev, 0); + irq = pdev->irq; + + if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) { + dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n"); + return -ENODEV; + } + + if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) { + dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n", + NE_IO_EXTENT, ioaddr); + return -EBUSY; + } + + reg0 = inb(ioaddr); + if (reg0 == 0xFF) + goto err_out_free_res; + + /* Do a preliminary verification that we have a 8390. */ + { + int regd; + outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); + regd = inb(ioaddr + 0x0d); + outb(0xff, ioaddr + 0x0d); + outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); + inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ + if (inb(ioaddr + EN0_COUNTER0) != 0) { + outb(reg0, ioaddr); + outb(regd, ioaddr + 0x0d); /* Restore the old values. */ + goto err_out_free_res; + } + } + + /* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */ + dev = alloc_ei_netdev(); + if (!dev) { + dev_err(&pdev->dev, "cannot allocate ethernet device\n"); + goto err_out_free_res; + } + dev->netdev_ops = &ne2k_netdev_ops; + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Reset card. Who knows what dain-bramaged state it was left in. */ + { + unsigned long reset_start_time = jiffies; + + outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); + + /* This looks like a horrible timing loop, but it should never take + more than a few cycles. + */ + while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) + /* Limit wait: '2' avoids jiffy roll-over. */ + if (jiffies - reset_start_time > 2) { + dev_err(&pdev->dev, + "Card failure (no reset ack).\n"); + goto err_out_free_netdev; + } + + outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */ + } + + /* Read the 16 bytes of station address PROM. + We must first initialize registers, similar to NS8390_init(eifdev, 0). + We can't reliably read the SAPROM address without this. + (I learned the hard way!). */ + { + struct {unsigned char value, offset; } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x49, EN0_DCFG}, /* Set word-wide access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + for (i = 0; i < ARRAY_SIZE(program_seq); i++) + outb(program_seq[i].value, ioaddr + program_seq[i].offset); + + } + + /* Note: all PCI cards have at least 16 bit access, so we don't have + to check for 8 bit cards. Most cards permit 32 bit access. */ + if (flags & ONLY_32BIT_IO) { + for (i = 0; i < 4 ; i++) + ((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT)); + } else + for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++) + SA_prom[i] = inb(ioaddr + NE_DATAPORT); + + /* We always set the 8390 registers for word mode. */ + outb(0x49, ioaddr + EN0_DCFG); + start_page = NESM_START_PG; + + stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG; + + /* Set up the rest of the parameters. */ + dev->irq = irq; + dev->base_addr = ioaddr; + pci_set_drvdata(pdev, dev); + + ei_status.name = pci_clone_list[chip_idx].name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; + ei_status.word16 = 1; + ei_status.ne2k_flags = flags; + if (fnd_cnt < MAX_UNITS) { + if (full_duplex[fnd_cnt] > 0 || (options[fnd_cnt] & FORCE_FDX)) + ei_status.ne2k_flags |= FORCE_FDX; + } + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + + ei_status.reset_8390 = &ne2k_pci_reset_8390; + ei_status.block_input = &ne2k_pci_block_input; + ei_status.block_output = &ne2k_pci_block_output; + ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr; + ei_status.priv = (unsigned long) pdev; + + dev->ethtool_ops = &ne2k_pci_ethtool_ops; + NS8390_init(dev, 0); + + i = register_netdev(dev); + if (i) + goto err_out_free_netdev; + + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SA_prom[i]; + printk("%s: %s found at %#lx, IRQ %d, %pM.\n", + dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq, + dev->dev_addr); + + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + + return 0; + +err_out_free_netdev: + free_netdev (dev); +err_out_free_res: + release_region (ioaddr, NE_IO_EXTENT); + pci_set_drvdata (pdev, NULL); + return -ENODEV; + +} + +/* + * Magic incantation sequence for full duplex on the supported cards. + */ +static inline int set_realtek_fdx(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + + outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */ + outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */ + outb(0x40, ioaddr + 0x06); /* Enable full duplex */ + outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */ + outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */ + return 0; +} + +static inline int set_holtek_fdx(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + + outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20); + return 0; +} + +static int ne2k_pci_set_fdx(struct net_device *dev) +{ + if (ei_status.ne2k_flags & REALTEK_FDX) + return set_realtek_fdx(dev); + else if (ei_status.ne2k_flags & HOLTEK_FDX) + return set_holtek_fdx(dev); + + return -EOPNOTSUPP; +} + +static int ne2k_pci_open(struct net_device *dev) +{ + int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev); + if (ret) + return ret; + + if (ei_status.ne2k_flags & FORCE_FDX) + ne2k_pci_set_fdx(dev); + + ei_open(dev); + return 0; +} + +static int ne2k_pci_close(struct net_device *dev) +{ + ei_close(dev); + free_irq(dev->irq, dev); + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ +static void ne2k_pci_reset_8390(struct net_device *dev) +{ + unsigned long reset_start_time = jiffies; + + if (debug > 1) printk("%s: Resetting the 8390 t=%ld...", + dev->name, jiffies); + + outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2) { + printk("%s: ne2k_pci_reset_8390() did not complete.\n", dev->name); + break; + } + outb(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + + long nic_base = dev->base_addr; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne2k_pci_get_8390_hdr " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + + ei_status.dmaing |= 0x01; + outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); + outb(0, nic_base + EN0_RCNTHI); + outb(0, nic_base + EN0_RSARLO); /* On page boundary */ + outb(ring_page, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + + if (ei_status.ne2k_flags & ONLY_16BIT_IO) { + insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); + } else { + *(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT)); + le16_to_cpus(&hdr->count); + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using outb. */ + +static void ne2k_pci_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + long nic_base = dev->base_addr; + char *buf = skb->data; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne2k_pci_block_input " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + if (ei_status.ne2k_flags & ONLY_32BIT_IO) + count = (count + 3) & 0xFFFC; + outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb(count & 0xff, nic_base + EN0_RCNTLO); + outb(count >> 8, nic_base + EN0_RCNTHI); + outb(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb(ring_offset >> 8, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + + if (ei_status.ne2k_flags & ONLY_16BIT_IO) { + insw(NE_BASE + NE_DATAPORT,buf,count>>1); + if (count & 0x01) { + buf[count-1] = inb(NE_BASE + NE_DATAPORT); + } + } else { + insl(NE_BASE + NE_DATAPORT, buf, count>>2); + if (count & 3) { + buf += count & ~3; + if (count & 2) { + __le16 *b = (__le16 *)buf; + + *b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT)); + buf = (char *)b; + } + if (count & 1) + *buf = inb(NE_BASE + NE_DATAPORT); + } + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void ne2k_pci_block_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page) +{ + long nic_base = NE_BASE; + unsigned long dma_start; + + /* On little-endian it's always safe to round the count up for + word writes. */ + if (ei_status.ne2k_flags & ONLY_32BIT_IO) + count = (count + 3) & 0xFFFC; + else + if (count & 0x01) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne2k_pci_block_output." + "[DMAstat:%d][irqlock:%d]\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE8390_RW_BUGFIX + /* Handle the read-before-write bug the same way as the + Crynwr packet driver -- the NatSemi method doesn't work. + Actually this doesn't always work either, but if you have + problems with your NEx000 this is better than nothing! */ + outb(0x42, nic_base + EN0_RCNTLO); + outb(0x00, nic_base + EN0_RCNTHI); + outb(0x42, nic_base + EN0_RSARLO); + outb(0x00, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); +#endif + outb(ENISR_RDC, nic_base + EN0_ISR); + + /* Now the normal output. */ + outb(count & 0xff, nic_base + EN0_RCNTLO); + outb(count >> 8, nic_base + EN0_RCNTHI); + outb(0x00, nic_base + EN0_RSARLO); + outb(start_page, nic_base + EN0_RSARHI); + outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); + if (ei_status.ne2k_flags & ONLY_16BIT_IO) { + outsw(NE_BASE + NE_DATAPORT, buf, count>>1); + } else { + outsl(NE_BASE + NE_DATAPORT, buf, count>>2); + if (count & 3) { + buf += count & ~3; + if (count & 2) { + __le16 *b = (__le16 *)buf; + + outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT); + buf = (char *)b; + } + } + } + + dma_start = jiffies; + + while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */ + printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name); + ne2k_pci_reset_8390(dev); + NS8390_init(dev,1); + break; + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + +static void ne2k_pci_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ei_device *ei = netdev_priv(dev); + struct pci_dev *pci_dev = (struct pci_dev *) ei->priv; + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(pci_dev)); +} + +static const struct ethtool_ops ne2k_pci_ethtool_ops = { + .get_drvinfo = ne2k_pci_get_drvinfo, +}; + +static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + BUG_ON(!dev); + unregister_netdev(dev); + release_region(dev->base_addr, NE_IO_EXTENT); + free_netdev(dev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM +static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + netif_device_detach(dev); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int ne2k_pci_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + int rc; + + pci_set_power_state(pdev, 0); + pci_restore_state(pdev); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + NS8390_init(dev, 1); + netif_device_attach(dev); + + return 0; +} + +#endif /* CONFIG_PM */ + + +struct pci_driver ne2k_driver = { + .name = DRV_NAME, + .probe = ne2k_pci_init_one, + .remove = __devexit_p(ne2k_pci_remove_one), + .id_table = ne2k_pci_tbl, +#ifdef CONFIG_PM + .suspend = ne2k_pci_suspend, + .resume = ne2k_pci_resume, +#endif /* CONFIG_PM */ + +}; + + +int __init ne2k_pci_init(void) +{ +/* when a module, this is printed whether or not devices are found in probe */ +#ifdef MODULE + printk(version); +#endif + return pci_register_driver(&ne2k_driver); +} + + +static void __exit ne2k_pci_cleanup(void) +{ + pci_unregister_driver (&ne2k_driver); +} + +module_init(ne2k_pci_init); +module_exit(ne2k_pci_cleanup); diff --git a/libdde_linux26/examples/unittest/.svn/all-wcprops b/libdde_linux26/examples/unittest/.svn/all-wcprops new file mode 100644 index 00000000..d074c667 --- /dev/null +++ b/libdde_linux26/examples/unittest/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 68 +/repos/tudos/!svn/ver/174/trunk/l4/pkg/dde/linux26/examples/unittest +END +main.c +K 25 +svn:wc:ra_dav:version-url +V 75 +/repos/tudos/!svn/ver/174/trunk/l4/pkg/dde/linux26/examples/unittest/main.c +END +Makefile +K 25 +svn:wc:ra_dav:version-url +V 77 +/repos/tudos/!svn/ver/174/trunk/l4/pkg/dde/linux26/examples/unittest/Makefile +END diff --git a/libdde_linux26/examples/unittest/.svn/entries b/libdde_linux26/examples/unittest/.svn/entries new file mode 100644 index 00000000..b28b8fa2 --- /dev/null +++ b/libdde_linux26/examples/unittest/.svn/entries @@ -0,0 +1,96 @@ +9 + +dir +465 +http://svn.tudos.org/repos/tudos/trunk/l4/pkg/dde/linux26/examples/unittest +http://svn.tudos.org/repos/tudos + + + +2007-09-08T19:44:13.897747Z +174 +l4check + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +a704ac0b-3a55-4d43-a2a9-7be6f07c34fb + +main.c +file + + + + +2009-11-15T17:17:14.000000Z +859924cf0bfc1d9fe646bc55fd063ece +2007-09-08T19:44:13.897747Z +174 +l4check + + + + + + + + + + + + + + + + + + + + + +14854 + +Makefile +file + + + + +2009-11-15T17:17:14.000000Z +0c29d82901ef4377917e235b09237d79 +2007-09-08T19:44:13.897747Z +174 +l4check + + + + + + + + + + + + + + + + + + + + + +416 + diff --git a/libdde_linux26/examples/unittest/.svn/format b/libdde_linux26/examples/unittest/.svn/format new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/libdde_linux26/examples/unittest/.svn/format @@ -0,0 +1 @@ +9 diff --git a/libdde_linux26/examples/unittest/.svn/text-base/Makefile.svn-base b/libdde_linux26/examples/unittest/.svn/text-base/Makefile.svn-base new file mode 100644 index 00000000..1ec4e97d --- /dev/null +++ b/libdde_linux26/examples/unittest/.svn/text-base/Makefile.svn-base @@ -0,0 +1,21 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26_COMMON),y) +TARGET = dde26_unit_test +endif + +SRC_C = main.c + +LIBS += -ldde_linux26 -lddekit -lio -llist_alloc -lparsecmdline -lcunit_dde + +# DDE configuration +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk diff --git a/libdde_linux26/examples/unittest/.svn/text-base/main.c.svn-base b/libdde_linux26/examples/unittest/.svn/text-base/main.c.svn-base new file mode 100644 index 00000000..25e13cc3 --- /dev/null +++ b/libdde_linux26/examples/unittest/.svn/text-base/main.c.svn-base @@ -0,0 +1,611 @@ +/* + * \brief DDE for Linux 2.6 Unit tests + * \author Bjoern Doebel <doebel@os.inf.tu-dresden.de> + * \author Christian Helmuth <ch12@os.inf.tu-dresden.de> + * \date 2007-05-12 + */ + +#include <asm/current.h> + +#include <linux/kernel.h> +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +//#include <linux/kthread.h> + +#include <l4/dde/dde.h> +#include <l4/dde/ddekit/initcall.h> +#include <l4/dde/linux26/dde26.h> + +#include <l4/cunit/CUnit.h> +#include <l4/cunit/Basic.h> +#include <l4/util/parse_cmd.h> +#include <l4/util/util.h> +#include <l4/log/l4log.h> + +/* We define 4 initcalls and see if these are executed + * in the beginning and in the right order. Expecations: + * + * - After running the init calls, _init_count == 4 + * - {_foo, _bar, _bla, _blub}_count carry numbers from 0 through 3 + * giving their execution order. These need to be: + * - _foo_count == 3 + * - _bar_count == 2 + * - _bla_count == 0 + * - _blub_count == 1 + */ +static unsigned _init_count = 0; +static unsigned _foo_count = 0; +static unsigned _bar_count = 0; +static unsigned _bla_count = 0; +static unsigned _blub_count = 0; + +/** Test checking whether the initcalls have been executed correctly. */ +static int test_initcalls(void) +{ + CU_ASSERT(_init_count == 4); + CU_ASSERT(_foo_count == 3); + CU_ASSERT(_bar_count == 2); + CU_ASSERT(_blub_count == 1); + CU_ASSERT(_bla_count == 0); + return 0; +} + + +static void foo(void) +{ + _foo_count = _init_count++; +} +late_initcall(foo); + +static void bar(void) +{ + _bar_count = _init_count++; +} +device_initcall(bar); + +static void bla(void) +{ + _bla_count = _init_count++; +} +arch_initcall(bla); + +static void blub(void) +{ + _blub_count = _init_count++; +} +subsys_initcall(blub); + +/*********************************************************************** + ** Test 1: Check whether the current() macro works. ** + ***********************************************************************/ +static int current_test(void) +{ + struct task_struct *t = current; + CU_ASSERT(t != NULL); + return 0; +} + + +/*********************************************************************** + ** Test 2: Getting complicated. Test startup of some kernel threads ** + ** and wait for them to finish using completions. ** + ***********************************************************************/ +#define NUM_KTHREADS 5 +static struct completion _kthread_completions_[NUM_KTHREADS]; + +static int kernel_thread_func(void *arg) +{ + CU_ASSERT((int)arg >= 0); + CU_ASSERT((int)arg < NUM_KTHREADS); + CU_ASSERT(current != NULL); + + /* do some work */ + msleep(200); + + complete_and_exit( &_kthread_completions_[(int)arg], 0 ); + return 0; +} + + +static int kernel_thread_test(void) +{ + int i; + + for (i=0; i < NUM_KTHREADS; i++) { + int j; + // initialize completion event + init_completion(&_kthread_completions_[i]); + // start kernel thread + j = kernel_thread(kernel_thread_func, (void *)i, 0); + CU_ASSERT(j > 0); + } + + for (i=0; i < NUM_KTHREADS; i++) { + // await completion + wait_for_completion(&_kthread_completions_[i]); + } + CU_ASSERT(i==NUM_KTHREADS); + + return 0; +} + + +/****************************************************************************** + ** Test 3: Test kernel wait queues: start a thread incrementing wait_value, ** + ** and sleep until wait_value is larger than 6 for the first time. ** + ******************************************************************************/ +static DECLARE_WAIT_QUEUE_HEAD(_wq_head); +static unsigned wait_value = 0; +static struct completion wq_completion; + +static int inc_func(void *arg) +{ + int i = 0; + + for (i=0; i<10; i++) + { + ++wait_value; + wake_up(&_wq_head); + msleep(500); + } + CU_ASSERT(wait_value == 10); + CU_ASSERT(i == 10); + complete_and_exit(&wq_completion, 0); +} + + +static int wq_test(void) +{ + int pid; + + init_completion(&wq_completion); + // start a thread incrementing wait_value + pid = kernel_thread(inc_func, 0, 0); + CU_ASSERT(pid > 0); + + // wait until wait_value > 6 + wait_event(_wq_head, wait_value > 6); + CU_ASSERT(wait_value > 6); + + // wait for inc_thread to exit + wait_for_completion(&wq_completion); + CU_ASSERT(wait_value == 10); + + return 0; +} + + +/**************************************************************************** + ** Test 4: Tasklets ** + ****************************************************************************/ +static unsigned _low_count = 0; +static unsigned _high_count = 0; + +static void tasklet_low_func(unsigned long i) +{ + ++_low_count; +} + +static void tasklet_high_func(unsigned long i) +{ + ++_high_count; +} + +static DECLARE_TASKLET(low0, tasklet_low_func, 0); +static DECLARE_TASKLET(low1, tasklet_low_func, 1); +static DECLARE_TASKLET(low2, tasklet_low_func, 2); +static DECLARE_TASKLET_DISABLED(low3, tasklet_low_func, 3); + +static DECLARE_TASKLET(hi0, tasklet_high_func, 10); +static DECLARE_TASKLET(hi1, tasklet_high_func, 11); +static DECLARE_TASKLET_DISABLED(hi2, tasklet_high_func, 12); + + +static int tasklet_test(void) +{ + l4dde26_softirq_init(); + + // schedule tasklets 0-3, 3 disabled + tasklet_schedule(&low0); + tasklet_schedule(&low1); + tasklet_schedule(&low2); + tasklet_schedule(&low3); + msleep(500); + + // 0-2 should have executed by now + CU_ASSERT(_low_count == 3); + tasklet_enable(&low3); + msleep(500); + // 3 should be ready, too + CU_ASSERT(_low_count == 4); + + // schedule hi and low tasklets, hi2 is disabled + tasklet_hi_schedule(&hi0); + tasklet_hi_schedule(&hi1); + tasklet_hi_schedule(&hi2); + tasklet_schedule(&low0); + tasklet_schedule(&low1); + tasklet_schedule(&low2); + msleep(500); + CU_ASSERT(_high_count == 2); + CU_ASSERT(_low_count == 7); + + // enable hi2 + tasklet_enable(&hi2); + // schedule low3 2 times, should only run once + tasklet_schedule(&low3); + tasklet_schedule(&low3); + msleep(500); + CU_ASSERT(_high_count == 3); + CU_ASSERT(_low_count == 8); + + return 0; +} + + +#if 0 +/****************************************************************************** + ** Test 5: Timers ** + ** ** + ** Schedule a periodic timer printing "tick" every second. Additionally, ** + ** schedule timers for 5, 10, 15, 20, and 25 seconds. Timer at 15s will ** + ** deactivate the 20s timer. ** + ******************************************************************************/ + +static struct timer_list _timer; +static struct timer_list _timer5; +static struct timer_list _timer10; +static struct timer_list _timer15; +static struct timer_list _timer20; +static struct timer_list _timer25; + +static void tick_func(unsigned long d) +{ + printk("tick (%ld)\n", jiffies); + _timer.expires = jiffies + HZ; + add_timer(&_timer); +} + + +static void timer_func(unsigned long d) +{ + printk("timer_func: %lu\n", d); + + if (d == 15) { + printk("De-scheduling 20s timer.\n"); + del_timer(&_timer20); + } + + if (timer_pending(&_timer20)) + printk("timer for 20s still pending.\n"); + else + printk("timer for 20s has been disabled.\n"); +} + + +static void timer_test(void) +{ + l4dde26_init_timers(); + + printk("BEGIN TIMER TEST\n"); + printk("jiffies(%p): %ld, HZ(%p): %ld\n", &jiffies, jiffies, &HZ, HZ); + + setup_timer(&_timer, tick_func, 0); + _timer.expires = jiffies + HZ; + add_timer(&_timer); + + setup_timer(&_timer5, timer_func, 5); + _timer5.expires = jiffies + 5*HZ; + setup_timer(&_timer10, timer_func, 10); + _timer10.expires = jiffies + 10*HZ; + setup_timer(&_timer15, timer_func, 15); + _timer15.expires = jiffies + 15*HZ; + setup_timer(&_timer20, timer_func, 20); + _timer20.expires = jiffies + 20*HZ; + setup_timer(&_timer25, timer_func, 25); + _timer25.expires = jiffies + 25*HZ; + + add_timer(&_timer5); + add_timer(&_timer10); + add_timer(&_timer15); + add_timer(&_timer20); + add_timer(&_timer25); + + msleep(30000); + printk("END TIMER TEST\n"); +} +#endif + + +/****************************** + ** Test 6: Memory subsystem ** + ******************************/ + +static void memory_kmem_cache_test(void) +{ + struct kmem_cache *cache0 = NULL; + struct obj0 + { + unsigned foo; + unsigned bar; + }; + static struct obj0 *p0[1024]; + + struct kmem_cache *cache1 = NULL; + struct obj1 + { + char foo[50]; + unsigned *bar; + }; + static struct obj1 *p1[256]; + + CU_ASSERT(cache0 == NULL); + CU_ASSERT(cache1 == NULL); + + cache0 = kmem_cache_create("obj0", sizeof(*p0[0]), 0, 0, 0, 0); + cache1 = kmem_cache_create("obj1", sizeof(*p1[0]), 0, 0, 0, 0); + + CU_ASSERT(cache0 != NULL); + CU_ASSERT(cache1 != NULL); + + unsigned i; + for (i = 0; i < 1024; ++i) { + p0[i] = kmem_cache_alloc(cache0, i); + CU_ASSERT(p0[i] != NULL); + } + + for (i = 0; i < 256; ++i) { + p1[i] = kmem_cache_alloc(cache1, i); + CU_ASSERT(p1[i] != NULL); + } + + for (i = 256; i > 0; --i) + kmem_cache_free(cache1, p1[i-1]); + + for (i = 1024; i > 0; --i) + kmem_cache_free(cache0, p0[i-1]); + + kmem_cache_destroy(cache1); + kmem_cache_destroy(cache0); +} + + +static void memory_page_alloc_test(void) +{ + unsigned long p[4]; + p[0] = __get_free_page(GFP_KERNEL); + p[1] = __get_free_pages(GFP_KERNEL, 1); + p[2] = __get_free_pages(GFP_KERNEL, 2); + p[3] = __get_free_pages(GFP_KERNEL, 3); + + CU_ASSERT(p[0] != 0); + CU_ASSERT(p[1] != 0); + CU_ASSERT(p[2] != 0); + CU_ASSERT(p[3] != 0); + + free_pages(p[0], 0); + free_pages(p[1], 1); + free_pages(p[2], 2); + free_pages(p[3], 3); +} + + +static void memory_kmalloc_test(void) +{ + l4dde26_kmalloc_init(); + + const unsigned count = 33; + char *p[count]; + + unsigned i; + for (i = 0; i < count; ++i) { + p[i] = kmalloc(32 + i*15, GFP_KERNEL); + CU_ASSERT(p[i] != NULL); + *p[i] = i; + CU_ASSERT(*p[i] == i); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + + for (i = 0; i < count; ++i) { + p[i] = kmalloc(3000 + i*20, GFP_KERNEL); + CU_ASSERT(p[i] != NULL); + *p[i] = i; + CU_ASSERT(*p[i] == i); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + +} + + +static int memory_test(void) +{ + if (1) memory_kmem_cache_test(); + if (1) memory_page_alloc_test(); + if (1) memory_kmalloc_test(); + return 0; +} + +#if 0 +/**************************************************************************** + ** Test 7: KThreads ** + ****************************************************************************/ +void kthread_test(void) +{ +} + + +/**************************************************************************** + ** Test 8: Work queues ** + ****************************************************************************/ +static void work_queue_func(void *data); +static void work_queue_func2(void *data); +static struct workqueue_struct *_wq; +static DECLARE_WORK(_wobj, work_queue_func, NULL); +static DECLARE_WORK(_wobj2, work_queue_func2, NULL); +static int wq_cnt = 0; + +static void work_queue_func(void *data) +{ + printk("Work queue function... Do some work here...\n"); + if (++wq_cnt < 5) + queue_work(_wq, &_wobj); +} + + +static void work_queue_func2(void *data) +{ + printk("Work queue function 2... Do some work here...\n"); + if (++wq_cnt < 5) + schedule_work(&_wobj2); +} + + +static void work_queue_test(void) +{ + int i; + printk("BEGIN WQ TEST\n"); + _wq = create_workqueue("HelloWQ"); + BUG_ON(_wq == NULL); + queue_work(_wq, &_wobj); + schedule_work(&_wobj2); + printk("END WQ TEST\n"); +} + + +/**************************************************************************** + ** Test 9: PCI ** + ****************************************************************************/ + +void pci_test(void) +{ + l4dde26_init_pci(); +} + + +/************************************************* + ** Main routine (switch on desired tests here) ** + *************************************************/ + +int main(int argc, const char **argv) +{ + int test_current = 0; + int test_kernel_thread = 0; + int test_wait = 0; + int test_tasklet = 0; + int test_timer = 0; + int test_memory = 0; + int test_kthread = 0; + int test_work = 0; + int test_pci = 0; + + if (parse_cmdline(&argc, &argv, + 'c', "current", "test current() function", + PARSE_CMD_SWITCH, 1, &test_current, + 'k', "kernel-thread", "test startup of kernel threads", + PARSE_CMD_SWITCH, 1, &test_kernel_thread, + 'w', "waitqueue", "test wait queues", + PARSE_CMD_SWITCH, 1, &test_wait, + 't', "tasklet", "test tasklets", + PARSE_CMD_SWITCH, 1, &test_tasklet, + 'T', "timer", "test timers", + PARSE_CMD_SWITCH, 1, &test_timer, + 'm', "memory", "test memory management", + PARSE_CMD_SWITCH, 1, &test_memory, + 'K', "kthread", "test kthreads", + PARSE_CMD_SWITCH, 1, &test_kthread, + 'W', "workqueue", "test work queues", + PARSE_CMD_SWITCH, 1, &test_work, + 'p', "pci", "test PCI stuff", + PARSE_CMD_SWITCH, 1, &test_pci, + 0, 0)) + return 1; + + LOG("DDEKit test. Carrying out tests:"); + LOGd(test_current, "\t* current()"); + LOGd(test_kernel_thread, "\t* kernel_thread()"); + LOGd(test_wait, "\t* wait queues"); + LOGd(test_tasklet, "\t* tasklets"); + LOGd(test_timer, "\t* timers"); + LOGd(test_memory, "\t* memory management"); + LOGd(test_kthread, "\t* kthreads"); + LOGd(test_work, "\t* work queues"); + LOGd(test_pci, "\t* PCI subsystem"); + + l4dde26_init(); + l4dde26_process_init(); + l4dde26_do_initcalls(); + + if (test_current) current_test(); + if (test_kernel_thread) kernel_thread_test(); + if (test_wait) wq_test(); + if (test_tasklet) tasklet_test(); + if (test_timer) timer_test(); + if (test_memory) memory_test(); +/* if (1) kthread_test(); */ + if (test_work) work_queue_test(); + if (test_pci) pci_test(); + + return 0; +} +#endif + +static int dde26_ts_init(void) +{ + l4dde26_init(); + l4dde26_process_init(); + l4dde26_do_initcalls(); + return 0; +} + + +static int dde26_ts_cleanup(void) +{ + return 0; +} + + +int main(int argc, char **argv) +{ + CU_pSuite dde_testsuite = NULL; + + int err = CU_initialize_registry(); + if (err == CUE_SUCCESS) + printk("Initialized CUnit registry.\n"); + else { + printk("Could not initialize CUnit registry.\n"); + return -1; + } + + dde_testsuite = CU_add_suite("DDE2.6 test suite", dde26_ts_init, dde26_ts_cleanup); + if (dde_testsuite) + printk("Added DDE2.6 test suite.\n"); + else { + printk("Could not add DDE2.6 test suite.\n"); + return -2; + } + + CU_ADD_TEST(dde_testsuite, test_initcalls); + CU_ADD_TEST(dde_testsuite, current_test); + CU_ADD_TEST(dde_testsuite, kernel_thread_test); + CU_ADD_TEST(dde_testsuite, wq_test); + CU_ADD_TEST(dde_testsuite, tasklet_test); + CU_ADD_TEST(dde_testsuite, memory_test); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + + CU_cleanup_registry(); + + l4_sleep_forever(); + + return 0; +} diff --git a/libdde_linux26/examples/unittest/Makefile b/libdde_linux26/examples/unittest/Makefile new file mode 100644 index 00000000..1ec4e97d --- /dev/null +++ b/libdde_linux26/examples/unittest/Makefile @@ -0,0 +1,21 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +SYSTEMS = x86-l4v2 + +DEFAULT_RELOC = 0x00a00000 + +-include $(PKGDIR_OBJ)/Makeconf + +ifeq ($(CONFIG_DDE26_COMMON),y) +TARGET = dde26_unit_test +endif + +SRC_C = main.c + +LIBS += -ldde_linux26 -lddekit -lio -llist_alloc -lparsecmdline -lcunit_dde + +# DDE configuration +include $(PKGDIR)/linux26/Makeconf + +include $(L4DIR)/mk/prog.mk diff --git a/libdde_linux26/examples/unittest/main.c b/libdde_linux26/examples/unittest/main.c new file mode 100644 index 00000000..25e13cc3 --- /dev/null +++ b/libdde_linux26/examples/unittest/main.c @@ -0,0 +1,611 @@ +/* + * \brief DDE for Linux 2.6 Unit tests + * \author Bjoern Doebel <doebel@os.inf.tu-dresden.de> + * \author Christian Helmuth <ch12@os.inf.tu-dresden.de> + * \date 2007-05-12 + */ + +#include <asm/current.h> + +#include <linux/kernel.h> +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +//#include <linux/kthread.h> + +#include <l4/dde/dde.h> +#include <l4/dde/ddekit/initcall.h> +#include <l4/dde/linux26/dde26.h> + +#include <l4/cunit/CUnit.h> +#include <l4/cunit/Basic.h> +#include <l4/util/parse_cmd.h> +#include <l4/util/util.h> +#include <l4/log/l4log.h> + +/* We define 4 initcalls and see if these are executed + * in the beginning and in the right order. Expecations: + * + * - After running the init calls, _init_count == 4 + * - {_foo, _bar, _bla, _blub}_count carry numbers from 0 through 3 + * giving their execution order. These need to be: + * - _foo_count == 3 + * - _bar_count == 2 + * - _bla_count == 0 + * - _blub_count == 1 + */ +static unsigned _init_count = 0; +static unsigned _foo_count = 0; +static unsigned _bar_count = 0; +static unsigned _bla_count = 0; +static unsigned _blub_count = 0; + +/** Test checking whether the initcalls have been executed correctly. */ +static int test_initcalls(void) +{ + CU_ASSERT(_init_count == 4); + CU_ASSERT(_foo_count == 3); + CU_ASSERT(_bar_count == 2); + CU_ASSERT(_blub_count == 1); + CU_ASSERT(_bla_count == 0); + return 0; +} + + +static void foo(void) +{ + _foo_count = _init_count++; +} +late_initcall(foo); + +static void bar(void) +{ + _bar_count = _init_count++; +} +device_initcall(bar); + +static void bla(void) +{ + _bla_count = _init_count++; +} +arch_initcall(bla); + +static void blub(void) +{ + _blub_count = _init_count++; +} +subsys_initcall(blub); + +/*********************************************************************** + ** Test 1: Check whether the current() macro works. ** + ***********************************************************************/ +static int current_test(void) +{ + struct task_struct *t = current; + CU_ASSERT(t != NULL); + return 0; +} + + +/*********************************************************************** + ** Test 2: Getting complicated. Test startup of some kernel threads ** + ** and wait for them to finish using completions. ** + ***********************************************************************/ +#define NUM_KTHREADS 5 +static struct completion _kthread_completions_[NUM_KTHREADS]; + +static int kernel_thread_func(void *arg) +{ + CU_ASSERT((int)arg >= 0); + CU_ASSERT((int)arg < NUM_KTHREADS); + CU_ASSERT(current != NULL); + + /* do some work */ + msleep(200); + + complete_and_exit( &_kthread_completions_[(int)arg], 0 ); + return 0; +} + + +static int kernel_thread_test(void) +{ + int i; + + for (i=0; i < NUM_KTHREADS; i++) { + int j; + // initialize completion event + init_completion(&_kthread_completions_[i]); + // start kernel thread + j = kernel_thread(kernel_thread_func, (void *)i, 0); + CU_ASSERT(j > 0); + } + + for (i=0; i < NUM_KTHREADS; i++) { + // await completion + wait_for_completion(&_kthread_completions_[i]); + } + CU_ASSERT(i==NUM_KTHREADS); + + return 0; +} + + +/****************************************************************************** + ** Test 3: Test kernel wait queues: start a thread incrementing wait_value, ** + ** and sleep until wait_value is larger than 6 for the first time. ** + ******************************************************************************/ +static DECLARE_WAIT_QUEUE_HEAD(_wq_head); +static unsigned wait_value = 0; +static struct completion wq_completion; + +static int inc_func(void *arg) +{ + int i = 0; + + for (i=0; i<10; i++) + { + ++wait_value; + wake_up(&_wq_head); + msleep(500); + } + CU_ASSERT(wait_value == 10); + CU_ASSERT(i == 10); + complete_and_exit(&wq_completion, 0); +} + + +static int wq_test(void) +{ + int pid; + + init_completion(&wq_completion); + // start a thread incrementing wait_value + pid = kernel_thread(inc_func, 0, 0); + CU_ASSERT(pid > 0); + + // wait until wait_value > 6 + wait_event(_wq_head, wait_value > 6); + CU_ASSERT(wait_value > 6); + + // wait for inc_thread to exit + wait_for_completion(&wq_completion); + CU_ASSERT(wait_value == 10); + + return 0; +} + + +/**************************************************************************** + ** Test 4: Tasklets ** + ****************************************************************************/ +static unsigned _low_count = 0; +static unsigned _high_count = 0; + +static void tasklet_low_func(unsigned long i) +{ + ++_low_count; +} + +static void tasklet_high_func(unsigned long i) +{ + ++_high_count; +} + +static DECLARE_TASKLET(low0, tasklet_low_func, 0); +static DECLARE_TASKLET(low1, tasklet_low_func, 1); +static DECLARE_TASKLET(low2, tasklet_low_func, 2); +static DECLARE_TASKLET_DISABLED(low3, tasklet_low_func, 3); + +static DECLARE_TASKLET(hi0, tasklet_high_func, 10); +static DECLARE_TASKLET(hi1, tasklet_high_func, 11); +static DECLARE_TASKLET_DISABLED(hi2, tasklet_high_func, 12); + + +static int tasklet_test(void) +{ + l4dde26_softirq_init(); + + // schedule tasklets 0-3, 3 disabled + tasklet_schedule(&low0); + tasklet_schedule(&low1); + tasklet_schedule(&low2); + tasklet_schedule(&low3); + msleep(500); + + // 0-2 should have executed by now + CU_ASSERT(_low_count == 3); + tasklet_enable(&low3); + msleep(500); + // 3 should be ready, too + CU_ASSERT(_low_count == 4); + + // schedule hi and low tasklets, hi2 is disabled + tasklet_hi_schedule(&hi0); + tasklet_hi_schedule(&hi1); + tasklet_hi_schedule(&hi2); + tasklet_schedule(&low0); + tasklet_schedule(&low1); + tasklet_schedule(&low2); + msleep(500); + CU_ASSERT(_high_count == 2); + CU_ASSERT(_low_count == 7); + + // enable hi2 + tasklet_enable(&hi2); + // schedule low3 2 times, should only run once + tasklet_schedule(&low3); + tasklet_schedule(&low3); + msleep(500); + CU_ASSERT(_high_count == 3); + CU_ASSERT(_low_count == 8); + + return 0; +} + + +#if 0 +/****************************************************************************** + ** Test 5: Timers ** + ** ** + ** Schedule a periodic timer printing "tick" every second. Additionally, ** + ** schedule timers for 5, 10, 15, 20, and 25 seconds. Timer at 15s will ** + ** deactivate the 20s timer. ** + ******************************************************************************/ + +static struct timer_list _timer; +static struct timer_list _timer5; +static struct timer_list _timer10; +static struct timer_list _timer15; +static struct timer_list _timer20; +static struct timer_list _timer25; + +static void tick_func(unsigned long d) +{ + printk("tick (%ld)\n", jiffies); + _timer.expires = jiffies + HZ; + add_timer(&_timer); +} + + +static void timer_func(unsigned long d) +{ + printk("timer_func: %lu\n", d); + + if (d == 15) { + printk("De-scheduling 20s timer.\n"); + del_timer(&_timer20); + } + + if (timer_pending(&_timer20)) + printk("timer for 20s still pending.\n"); + else + printk("timer for 20s has been disabled.\n"); +} + + +static void timer_test(void) +{ + l4dde26_init_timers(); + + printk("BEGIN TIMER TEST\n"); + printk("jiffies(%p): %ld, HZ(%p): %ld\n", &jiffies, jiffies, &HZ, HZ); + + setup_timer(&_timer, tick_func, 0); + _timer.expires = jiffies + HZ; + add_timer(&_timer); + + setup_timer(&_timer5, timer_func, 5); + _timer5.expires = jiffies + 5*HZ; + setup_timer(&_timer10, timer_func, 10); + _timer10.expires = jiffies + 10*HZ; + setup_timer(&_timer15, timer_func, 15); + _timer15.expires = jiffies + 15*HZ; + setup_timer(&_timer20, timer_func, 20); + _timer20.expires = jiffies + 20*HZ; + setup_timer(&_timer25, timer_func, 25); + _timer25.expires = jiffies + 25*HZ; + + add_timer(&_timer5); + add_timer(&_timer10); + add_timer(&_timer15); + add_timer(&_timer20); + add_timer(&_timer25); + + msleep(30000); + printk("END TIMER TEST\n"); +} +#endif + + +/****************************** + ** Test 6: Memory subsystem ** + ******************************/ + +static void memory_kmem_cache_test(void) +{ + struct kmem_cache *cache0 = NULL; + struct obj0 + { + unsigned foo; + unsigned bar; + }; + static struct obj0 *p0[1024]; + + struct kmem_cache *cache1 = NULL; + struct obj1 + { + char foo[50]; + unsigned *bar; + }; + static struct obj1 *p1[256]; + + CU_ASSERT(cache0 == NULL); + CU_ASSERT(cache1 == NULL); + + cache0 = kmem_cache_create("obj0", sizeof(*p0[0]), 0, 0, 0, 0); + cache1 = kmem_cache_create("obj1", sizeof(*p1[0]), 0, 0, 0, 0); + + CU_ASSERT(cache0 != NULL); + CU_ASSERT(cache1 != NULL); + + unsigned i; + for (i = 0; i < 1024; ++i) { + p0[i] = kmem_cache_alloc(cache0, i); + CU_ASSERT(p0[i] != NULL); + } + + for (i = 0; i < 256; ++i) { + p1[i] = kmem_cache_alloc(cache1, i); + CU_ASSERT(p1[i] != NULL); + } + + for (i = 256; i > 0; --i) + kmem_cache_free(cache1, p1[i-1]); + + for (i = 1024; i > 0; --i) + kmem_cache_free(cache0, p0[i-1]); + + kmem_cache_destroy(cache1); + kmem_cache_destroy(cache0); +} + + +static void memory_page_alloc_test(void) +{ + unsigned long p[4]; + p[0] = __get_free_page(GFP_KERNEL); + p[1] = __get_free_pages(GFP_KERNEL, 1); + p[2] = __get_free_pages(GFP_KERNEL, 2); + p[3] = __get_free_pages(GFP_KERNEL, 3); + + CU_ASSERT(p[0] != 0); + CU_ASSERT(p[1] != 0); + CU_ASSERT(p[2] != 0); + CU_ASSERT(p[3] != 0); + + free_pages(p[0], 0); + free_pages(p[1], 1); + free_pages(p[2], 2); + free_pages(p[3], 3); +} + + +static void memory_kmalloc_test(void) +{ + l4dde26_kmalloc_init(); + + const unsigned count = 33; + char *p[count]; + + unsigned i; + for (i = 0; i < count; ++i) { + p[i] = kmalloc(32 + i*15, GFP_KERNEL); + CU_ASSERT(p[i] != NULL); + *p[i] = i; + CU_ASSERT(*p[i] == i); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + + for (i = 0; i < count; ++i) { + p[i] = kmalloc(3000 + i*20, GFP_KERNEL); + CU_ASSERT(p[i] != NULL); + *p[i] = i; + CU_ASSERT(*p[i] == i); + } + + for (i = count; i > 0; --i) + if (p[i-1]) kfree(p[i-1]); + +} + + +static int memory_test(void) +{ + if (1) memory_kmem_cache_test(); + if (1) memory_page_alloc_test(); + if (1) memory_kmalloc_test(); + return 0; +} + +#if 0 +/**************************************************************************** + ** Test 7: KThreads ** + ****************************************************************************/ +void kthread_test(void) +{ +} + + +/**************************************************************************** + ** Test 8: Work queues ** + ****************************************************************************/ +static void work_queue_func(void *data); +static void work_queue_func2(void *data); +static struct workqueue_struct *_wq; +static DECLARE_WORK(_wobj, work_queue_func, NULL); +static DECLARE_WORK(_wobj2, work_queue_func2, NULL); +static int wq_cnt = 0; + +static void work_queue_func(void *data) +{ + printk("Work queue function... Do some work here...\n"); + if (++wq_cnt < 5) + queue_work(_wq, &_wobj); +} + + +static void work_queue_func2(void *data) +{ + printk("Work queue function 2... Do some work here...\n"); + if (++wq_cnt < 5) + schedule_work(&_wobj2); +} + + +static void work_queue_test(void) +{ + int i; + printk("BEGIN WQ TEST\n"); + _wq = create_workqueue("HelloWQ"); + BUG_ON(_wq == NULL); + queue_work(_wq, &_wobj); + schedule_work(&_wobj2); + printk("END WQ TEST\n"); +} + + +/**************************************************************************** + ** Test 9: PCI ** + ****************************************************************************/ + +void pci_test(void) +{ + l4dde26_init_pci(); +} + + +/************************************************* + ** Main routine (switch on desired tests here) ** + *************************************************/ + +int main(int argc, const char **argv) +{ + int test_current = 0; + int test_kernel_thread = 0; + int test_wait = 0; + int test_tasklet = 0; + int test_timer = 0; + int test_memory = 0; + int test_kthread = 0; + int test_work = 0; + int test_pci = 0; + + if (parse_cmdline(&argc, &argv, + 'c', "current", "test current() function", + PARSE_CMD_SWITCH, 1, &test_current, + 'k', "kernel-thread", "test startup of kernel threads", + PARSE_CMD_SWITCH, 1, &test_kernel_thread, + 'w', "waitqueue", "test wait queues", + PARSE_CMD_SWITCH, 1, &test_wait, + 't', "tasklet", "test tasklets", + PARSE_CMD_SWITCH, 1, &test_tasklet, + 'T', "timer", "test timers", + PARSE_CMD_SWITCH, 1, &test_timer, + 'm', "memory", "test memory management", + PARSE_CMD_SWITCH, 1, &test_memory, + 'K', "kthread", "test kthreads", + PARSE_CMD_SWITCH, 1, &test_kthread, + 'W', "workqueue", "test work queues", + PARSE_CMD_SWITCH, 1, &test_work, + 'p', "pci", "test PCI stuff", + PARSE_CMD_SWITCH, 1, &test_pci, + 0, 0)) + return 1; + + LOG("DDEKit test. Carrying out tests:"); + LOGd(test_current, "\t* current()"); + LOGd(test_kernel_thread, "\t* kernel_thread()"); + LOGd(test_wait, "\t* wait queues"); + LOGd(test_tasklet, "\t* tasklets"); + LOGd(test_timer, "\t* timers"); + LOGd(test_memory, "\t* memory management"); + LOGd(test_kthread, "\t* kthreads"); + LOGd(test_work, "\t* work queues"); + LOGd(test_pci, "\t* PCI subsystem"); + + l4dde26_init(); + l4dde26_process_init(); + l4dde26_do_initcalls(); + + if (test_current) current_test(); + if (test_kernel_thread) kernel_thread_test(); + if (test_wait) wq_test(); + if (test_tasklet) tasklet_test(); + if (test_timer) timer_test(); + if (test_memory) memory_test(); +/* if (1) kthread_test(); */ + if (test_work) work_queue_test(); + if (test_pci) pci_test(); + + return 0; +} +#endif + +static int dde26_ts_init(void) +{ + l4dde26_init(); + l4dde26_process_init(); + l4dde26_do_initcalls(); + return 0; +} + + +static int dde26_ts_cleanup(void) +{ + return 0; +} + + +int main(int argc, char **argv) +{ + CU_pSuite dde_testsuite = NULL; + + int err = CU_initialize_registry(); + if (err == CUE_SUCCESS) + printk("Initialized CUnit registry.\n"); + else { + printk("Could not initialize CUnit registry.\n"); + return -1; + } + + dde_testsuite = CU_add_suite("DDE2.6 test suite", dde26_ts_init, dde26_ts_cleanup); + if (dde_testsuite) + printk("Added DDE2.6 test suite.\n"); + else { + printk("Could not add DDE2.6 test suite.\n"); + return -2; + } + + CU_ADD_TEST(dde_testsuite, test_initcalls); + CU_ADD_TEST(dde_testsuite, current_test); + CU_ADD_TEST(dde_testsuite, kernel_thread_test); + CU_ADD_TEST(dde_testsuite, wq_test); + CU_ADD_TEST(dde_testsuite, tasklet_test); + CU_ADD_TEST(dde_testsuite, memory_test); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + + CU_cleanup_registry(); + + l4_sleep_forever(); + + return 0; +} |