Inserted tickless RTOS code. Network input code dysfunctional, and bound to be replaced.
authorroot <root@hwdev.(none)>
Tue, 8 Mar 2011 22:31:49 +0000 (22:31 +0000)
committerroot <root@hwdev.(none)>
Tue, 8 Mar 2011 22:31:49 +0000 (22:31 +0000)
Network bootstrapping uses timer, and takes the right initiatives (without processing input).

44 files changed:
Makefile
bin/kconfig/BIG.FAT.WARNING [new file with mode: 0644]
doc/top2bottom.rst
include/0cpm/app.h [new file with mode: 0644]
include/0cpm/cpu.h [new file with mode: 0644]
include/0cpm/defaults.h [new file with mode: 0644]
include/0cpm/event.h [new file with mode: 0644]
include/0cpm/irq.h [new file with mode: 0644]
include/0cpm/kbd.h [new file with mode: 0644]
include/0cpm/led.h [new file with mode: 0644]
include/0cpm/netdb.h
include/0cpm/netfun.h
include/0cpm/resource.h [new file with mode: 0644]
include/0cpm/timer.h [new file with mode: 0644]
src/driver/README [new file with mode: 0644]
src/driver/ata/si3210.c [new file with mode: 0644]
src/driver/net/rtl8019as.c [new file with mode: 0644]
src/driver/soc/tic54x.c [new file with mode: 0644]
src/driver/soc/tic55x.c [new file with mode: 0644]
src/driver/soc/visba3.c [new file with mode: 0644]
src/driver/tuntest.c [deleted file]
src/driver/util/linuxtuntest.c [new file with mode: 0644]
src/driver/util/linuxtuntest.h [new file with mode: 0644]
src/kernel/Makefile [new file with mode: 0644]
src/kernel/README [new file with mode: 0644]
src/kernel/app.c [new file with mode: 0644]
src/kernel/cpu.c [new file with mode: 0644]
src/kernel/led.c [new file with mode: 0644]
src/kernel/net.c [new file with mode: 0644]
src/kernel/resource.c [new file with mode: 0644]
src/kernel/timer.c [new file with mode: 0644]
src/net/Makefile
src/net/core.c
src/net/db.c
src/net/input.c
src/net/reply.c
src/net/send.c
src/phone/Kconfig [new file with mode: 0644]
src/phone/Makefile [new file with mode: 0644]
src/phone/app_zero.c [new file with mode: 0644]
src/phone/todo.c [new file with mode: 0644]
src/target/Kconfig
src/target/Makefile
src/target/linksys_spa962.h [new file with mode: 0644]

index 5b9be2d..45dda2d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,24 @@
 # TODO: this should be src/Kconfig
 Kconfig := src/target/Kconfig
 
+#
+# Default target "all"
+#
 .PHONY += all
-all: bin/firmerware.bin
+all: configincludes tags bin/firmerware.bin
+
+#
+# Builder helping hand
+#
+.PHONY += help
+help:
+       @echo
+       @echo "*** Try 'make menuconfig' followed by 'make'"
+       @echo "    Learn about object sizes at any stage with 'make size'"
+       @echo
+       @echo Platform-specific notes are part of the target help descriptions.
+       @echo
+
 
 #
 # Load configuration file and various Makefiles
@@ -12,29 +28,117 @@ all: bin/firmerware.bin
 
 include bin/kconfig/Makefile
 
+include src/kernel/Makefile
+include src/net/Makefile
+include src/phone/Makefile
+
 include src/target/Makefile
 include src/driver/Makefile
-include src/net/Makefile
 
 #
-# The main building target
+# Dependencies for generated include files
+# TODO: Solve cyclic dependencies on non-generated file
 #
-bin/firmerware.bin: $(objs-y)
-       gcc -o $@ $^
+include/config.h: .config
+       ( cat $< ; for m in $(metavars-y) ; do echo $$m ; done ) | sed -e '/^#/d' -e 's/^\([A-Z0-9_]*\)=\(.*\)/#define \1 \2/'  > $@
+       for i in $(includes-y) ; do echo "#include \"../$$i\"" >> $@ ; done
 
-.PHONY += clean
-clean:
-       find . -name \*\.o -exec rm {} \;
-       rm -f src/target/tuntest/firmly0cpm.bin
+.PHONY += configincludes
+configincludes: include/config.h
+
+#
+# Include dependency files
+#
+-include $(objs-top-kernel-y:.o=.d)
+-include $(objs-top-net-y:.o=.d)
+-include $(objs-top-phone-y:.o=.d)
+-include $(objs-top-bottom-y:.o=.d)
+
+#
+# The main building targets
+#
+# Building is done in two phases; first top.o and bottom.o
+# each hold their respective halves of the process, then they
+# are composed to form the target binary.  This separation
+# enables a thoroughness check on the API separation.  The
+# rule is that all API calls between the two halves must be
+# documented in doc/top2bottom.*
+#
+bin/firmerware.bin: bin/firmerware.elf
+       strip -s -o $@ $<
+
+bin/firmerware.elf: bin/top.o bin/bottom.o
+       gcc -MD -o $@ bin/top.o bin/bottom.o
+       @echo
+       @echo "*** Compilation succesful"
+       @echo
+       @size bin/firmerware.elf
+
+bin/top-kernel.o: $(objs-top-kernel-y)
+       ld -r -o $@ $(objs-top-kernel-y)
+
+bin/top-net.o: $(objs-top-net-y)
+       ld -r -o $@ $(objs-top-net-y)
 
+bin/top-phone.o: $(objs-top-phone-y)
+       ld -r -o $@ $(objs-top-phone-y)
 
+bin/top.o: bin/top-kernel.o bin/top-net.o bin/top-phone.o
+       ld -r -o $@ bin/top-kernel.o bin/top-net.o bin/top-phone.o
+
+bin/bottom.o: $(objs-bottom-y)
+       ld -r -o $@ $(objs-bottom-y)
+
+#
+# General compiler rule
+# TODO: Use -Wall and remove silencing-up overrides
+# gcc -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -fno-builtin -MD -c -Iinclude -ggdb3 -o $@ $<
+#
 %.o: %.c
-       gcc -c -Iinclude -ggdb3 -o $@ $^
+       gcc -fno-builtin -MD -c -Iinclude -ggdb3 -o $@ $<
 
-.PHONY += help
-help:
+#
+# Create a "tags" file for easy Vim navigation
+#
+tags:
+       ctags $(objs-top-kernel-y:.o=.c) $(objs-top-net-y:.o=.c) $(objs-top-phone-y:.o=.c) $(objs-bottom-y:.o=.c) include/0cpm/*.h include/config.h
+
+.PHONY += clean
+clean:
+       rm -f $(objs-top-kernel-y)
+       rm -f $(objs-top-net-y)
+       rm -f $(objs-top-phone-y)
+       rm -f $(objs-bottom-y)
+       rm -f $(objs-top-kernel-y:.o=.d)
+       rm -f $(objs-top-net-y:.o=.d)
+       rm -f $(objs-top-phone-y:.o=.d)
+       rm -f $(objs-bottom-y:.o=.d)
+       rm -f bin/top-kernel.o bin/top-net.o bin/top-phone.o
+       rm -f bin/top.o bin/bottom.o
+       rm -f include/config.h
+       rm -f tags src/net/6bed4.c
+       rm -f bin/firmerware.exe bin/firmerware.bin
+
+.PHONY += size
+size:
        @echo
-       @echo "*** Try 'make menuconfig' followed by 'make'"
+       @echo '*** Top half kernel sizes:'
        @echo
-       @echo Platform-specific notes are part of the target help descriptions.
+       @size $(objs-top-kernel-y) || true
+       @echo
+       @echo '*** Top half network sizes:'
+       @echo
+       @size $(objs-top-net-y) || true
+       @echo
+       @echo '*** Top half phone sizes:'
+       @echo
+       @size $(objs-top-phone-y) || true
+       @echo
+       @echo '*** Bottom half sizes:'
+       @echo
+       @size $(objs-bottom-y) || true
+       @echo
+       @echo '*** Major component sizes:'
+       @echo
+       @size bin/top-kernel.o bin/top-net.o bin/top-phone.o bin/bottom.o || true
        @echo
diff --git a/bin/kconfig/BIG.FAT.WARNING b/bin/kconfig/BIG.FAT.WARNING
new file mode 100644 (file)
index 0000000..f656db9
--- /dev/null
@@ -0,0 +1,13 @@
+Configuration Utility from the Linux Kernel
+===========================================
+
+This interactive configuration utilty was kindly taken from the
+Linux kernel, as there seemed no point in rebuilding such a fancy
+tool.  Everything that was broken should not be reported to the
+Linux maintainers, but to the 0cpm project.  Or better even, send
+a patch containing your wisdom to share.
+
+The utility is withdrawn from the Linux build infrastructure and
+has been setup with a crude facility for making at least the
+"make menuconfig" target function well.
+
index 4a58405..efd2180 100644 (file)
@@ -101,10 +101,10 @@ package; you should not incllude it in the source tree.
 In additon, there are a few include files that define the
 upcall and downcall interface:
 
-* ``src/include/downcall.h`` contains the functions that must
+* ``include/0cpm/downcall.h`` contains the functions that must
   be implemented in all bottom halves.
 
-* ``src/include/upcall.h`` contains the only functions that a
+* ``include/0cpm/upcall.h`` contains the only functions that a
   bottom half may invoke on the top half.
 
 The data types exchanged between bottom and top halves should be
@@ -222,8 +222,8 @@ during an upcall.  The top half should never block in a
 critical region, nor should it do complex things.  The
 functions supporting asynchronous upcall blocking are::
 
-       void bottom_critical_begin (void);
-       void bottom_critical_end   (void);
+       void bottom_critical_region_begin (void);
+       void bottom_critical_region_end   (void);
 
 The definition of these functions may well be an
 assembler inline function to disable and enable interrupts.
@@ -235,21 +235,22 @@ The code structure for a critical region is::
        #include <bottom.h>
 
        // non-critical code
-       bottom_criticital_begin ();
+       bottom_criticital_region_begin ();
        // critical region
-       bottom_criticital_end ();
+       bottom_criticital_region_end ();
        // non-critical code
 
 When the bottom invokes ``top_main()``, it has not yet
 enabled asynchronous upcalls, so after some setup this
 function must start by releasing the critical region::
 
+       #include <stdbool.h>
        #include <bottom.h>
 
        void top_main (void) {
                // top-half setup code
                bottom_critical_region_end ();
-               while (1) {
+               while (true) {
                        // main loop, normal operation
                }
        }
@@ -523,7 +524,7 @@ should select such a precision that times of up to a day in the
 past or future can be represented.  The function definition for
 setting the next timer interval is::
 
-       uint32_t bottom_timer_set (uint32_t timeout);
+       timing_t bottom_timer_set (timing_t timeout);
 
 The value returned is the previous value in the timer.  The special
 value ``TIMER_NULL`` is used to represent no timer setting.  By
@@ -565,6 +566,7 @@ Network events relate to a few events:
 
 * Network connectivity going offline and coming online
 * The arrival of a network packet
+* The ability to send another network packet
 
 When booted, the network connectivity is assumed to be
 down.  Upon activation of upcalls, a check is made to
@@ -580,26 +582,118 @@ calls apply to the uplink, and the downlink is
 ignored.  Switching between downlink and uplink
 is part of the bottom half responsibilities.
 
-The bottom half is also responsible for selecting
-packets destined for the phone.  It will make this
-distinction at the ethernet level, so based on the
-MAC address of incoming packets.  Unicast messages
-as well as broadcast packets are welcomed.  For each
-of those, an upcall exists::
+The bottom half is also the interface that permits or
+stops network reads and writes, as a result of available
+buffer space and arrival of traffic.
+
+The following function reports arrival of a packet
+over the network::
+
+       void top_network_can_recv (void);
+
+This function must be called when a packet arrives
+while there were none available before that.  The
+function may also be called for any subsequent
+arrives.  This optimally reflects the variations in
+available hardware, and it should not be a problem
+if the implementation of this top function only does
+a few simple administrative things.  The subsequent
+handling of network packets is (of course) done in a
+polling loop, to handle the uncertainty of the number
+of available packets.
+
+Mirrorring this function, the following reports the
+ability to send a packet over the network::
+
+       void top_network_can_send (void);
+
+The buffer space available should be at least the MTU
+for the network, so 1500 plus ethernet headers.  The
+optimal position may however be different.  The function
+need not be called when the network interface comes up.
+
+Any high-priority traffic is always immediately offered
+for sending, and if that fails it is queued along with
+the lower-priority traffic until this interrupt function
+is called.  If the network card has internal buffer
+space, this is good to use.  However, it makes no sense
+to simulate it in main memory at the driver level, as
+the top half can do that instead.
+
+To receive packets from the network interface, the following
+function is called::
+
+       bool bottom_network_recv (uint8_t *pkt, uint16_t *pktlen);
+
+This function only returns ``true`` if a packet was
+available.  A good cause for failure is not having any
+packets available for reception.  The ``pkt`` will
+be filled with at most ``pktlen`` bytes of data,
+and the latter variable will be update upon return to
+reflect the length used.  When reporting failure, the
+value returned in ``pktlen`` is unchanged and the
+``pkt`` may have changed over that length; the results
+of that however, are not reliable for processing.
+
+In case of an error, for example a false checksum,
+the function returns ``true`` but sets ``pktlen``
+to 0.  This means that further polling is sensible.
+Some code to poll and process network packets would
+be::
+
+       uint16_t buflen;
+       uint8_t buf [MTU + 16];
+       bool more = true;
+       while (more) {
+               buflen = sizeof (buf);
+               more = bottom_network_recv (buf, &buflen);
+               if (buflen > 0) {
+                       process_packet (buf, buflen);
+               }
+       }
+
+After the ``bottom_network_recv`` has returned ``false``,
+the top layer will await invocation of ``top_network_can_recv``
+before it tries again.
+
+The function to send data out to the network card is
+the mirror image of receiving::
 
-       top_network_unicast   (uint8_t *pkt, uint16_t pktlen, uint16_t ether_t);
-       top_network_broadcast (uint8_t *pkt, uint16_t pktlen, uint16_t ether_t);
+       bool bottom_network_send (uint8_t *pkt, uint16_t pktlen);
 
-The ``pkt`` in both functions points to the part of the
-network packet after ethernet headers; only the
-ethernet type field is added as a 16-bit integer (after
-changing from network to host-local byte order) to guide
-further demultiplexing.
+This function only returns ``true`` if the packet could
+be sent to a point where the network takes over.  The
+place where this would be is decided by the bottom layer,
+but the more certainty this can give about internal
+processing, the more reliable the entire application gets.
 
-At the ethernet level, there is no separate addressing
-mode for multicast traffic, so any multicast traffic
-results in a broadcasting upcall.
+It is possible to submit multiple packets, until no more
+could be delivered.  After the ``bottom_network_send``
+has returned ``false``, the top layer will await
+invocation of ``top_network_can_send`` before it tries again.
 
+Some example code, not taking priorities into account, that
+would write data out to the network would be::
+
+       qitem = netsendqueue;
+       bool more = true;
+       while (more) {
+               if (!qitem) {
+                       break;
+               }
+               more = bottom_network_send (qitem->buf, qitem->buflen);
+               if (more) {
+                       qitem = qitem->next;
+               }
+       }
+       //...free netsendqueue items up to but excluding qitem...
+       netsendqueue = cursend;
+
+The mechanisms for dealing with the upcalls are simple
+enough; a flag can be used that is reset just before
+making the networking downcall, and that is set by the
+corresponding upcall.  This way, no signalling is ever
+lost.  This is not shown in the examples above.
 
 
 Display
diff --git a/include/0cpm/app.h b/include/0cpm/app.h
new file mode 100644 (file)
index 0000000..0f6d062
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Resource contention handlers.
+ */
+
+
+#ifndef HEADER_APP
+#define HEADER_APP
+
+
+/* Hardware phones vary wildy in their configurations of buttons, screens and LEDs.
+ * To handle all phones in the same way would imply that the simplest phone dictated
+ * the facilities on all others.  Instead, it is more desirable to have the best
+ * possible phone dictate what facilities are possible.  This is indeed possible by
+ * exploitation of context sensitivity, generic buttons that can be dynamically
+ * mapped to a function and of course the menu that is present on virtually all
+ * phones.
+ *
+ * In cases where resources are really scarce, such as on an analog phone attached
+ * to an ATA or a phone with a one-line display of 7-segment digits, another
+ * tactic is applied.  Resources then line up according to their priority, which
+ * is pretty much the same as their level of urgency.  An incoming call counts as
+ * more urgent than a call being setup, and that in turn outranks a clock.  If
+ * resources are tight, the resource access priority determines what is shown;
+ * on more elaborate displays a layering setup may be shown, and so on.  Much of
+ * this is generic resource contention handling, within constraints that depend
+ * on the hardware platform.
+ *
+ * To make this even more interesting, we desire to do most of the work at
+ * compile-time, in macros that expand to the best possible approach for a
+ * given hardware phone.
+ */
+
+
+/* The app_level_t type defines the various priority levels at which an end user
+ * can experience a phone.  Note that this is totally unrelated to realtime
+ * priorities, even if both are a sort of prioirity level with override.
+ */
+typedef enum {
+       APP_LEVEL_ZERO,         /* Nothing to do; this is a sleep mode */
+       APP_LEVEL_BACKGROUNDED, /* Calls in progress, but the user detached from them */
+       APP_LEVEL_REINVITE,     /* The user is transferring a call, or creating a conference */
+       APP_LEVEL_DIALING,      /* The user is dialing, is entering the digits of a number */
+       APP_LEVEL_PHONEBOOK,    /* The user is looking through a phonebook */
+       APP_LEVEL_CONNECTING,   /* The phone is setting up a connection to another phone */
+       APP_LEVEL_TALKING,      /* The user is on the phone, interacting with others */
+       APP_LEVEL_ZRTPCHECK,    /* The user is verifying a ZRTP key hash */
+       APP_LEVEL_SOFTBUTTON,   /* The user is engaged in a softbutton-initiated dialog */
+       APP_LEVEL_MENU,         /* The user is looking through a menu */
+       APP_LEVEL_RINGING,      /* A new call is coming into the system */
+       APP_LEVEL_COUNT         /* The number of distinguished resource levels */
+} app_level_t;
+
+
+
+/* Applications exist at each resource level.  Applications are aware
+ * when they are being overruled by another application, and they
+ * will make a choice whether to stop or wait for later continuation.
+ *
+ * Applications will lay claims on resources, such as keys and (partial)
+ * display area, or the ability to output sound to the user's handset.
+ * These claims may cause applications to be put aside, while in other
+ * cases the claims can co-exist.  The more I/O facilities one has, the
+ * more power one can deploy, basically.
+ *
+ * It is possible for a number of applications to share the same resource
+ * level.  In that case, the one that prevails is the last one started.
+ * An application can decide for itself it is will ever resume, or just
+ * quit as soon as it is suspended (or decide on it later, when resumed).
+ *
+ * Resources may be wished or demanded by an application.  If they are
+ * wished, then the removal of these keys by higher layers will not cause
+ * suspending the application, like with demanded keys.  If a higher layer
+ * wishes for resources that are demanded by the lower layer, then the
+ * higher layer will not be given these resources.  If higher and lower
+ * layer wish a resource, then the higher layer wins.
+ */
+
+typedef void (*app_action_t) (void);
+typedef void (*app_event_processor_t) (uint16_t);
+
+typedef struct application app_t;
+struct application {
+       app_t *app_stackdown;
+       app_level_t app_resourcelevel;
+       struct resource *res_wish;      /* NULL means: No wish for resources */
+       struct resource *res_demand;    /* NULL means: No demanded resources */
+
+       app_action_t app_start;
+       app_action_t app_stop;
+       app_action_t app_suspend;       /* NULL means: use app_stop  */
+       app_action_t app_resume;        /* NULL means: use app_start */
+
+       app_event_processor_t app_event;
+
+       app_level_t app_level;
+
+};
+
+
+/* A routine to register an application structure.  This may only be
+ * called once on an application structure.
+ */
+void app_register (app_t *app);
+
+
+/* A routine to focus on an application, provided that nothing at a
+ * higher resource level wins any demanded resources from this one.
+ * This is used to compete with other applications at the same
+ * resource level.
+ */
+void app_focus (app_t *app);
+
+
+// TODO: API?  Unfocus, unregister?  When to start, how long to claim, and so on?
+
+
+#endif
diff --git a/include/0cpm/cpu.h b/include/0cpm/cpu.h
new file mode 100644 (file)
index 0000000..1666b35
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * CPU scheduling
+ */
+
+
+#ifndef HEADER_CPU
+#define HEADER_CPU
+
+
+/* Depth of sleep is used to make the hardware dive as deeply
+ * as practical into a sleep state.  Hardware usually offers
+ * a choice of such states, with varying wake-up times.
+ * For a phone, two levels seem to matter: one for sleeping
+ * lightly, and being able to process realtime traffic as part
+ * of an active phone call (SLEEP_SNOOZE); the other for deep
+ * sleep, spending as little energy as possible because nothing
+ * appears to be going on and an interrupt would be a serious
+ * change as it is a sudden call for action (SLEEP_HIBERNATE).
+ */
+typedef enum sleep_depth_type sleep_depth_t;
+enum sleep_depth_type {
+       SLEEP_SNOOZE = 0,
+       SLEEP_HIBERNATE = 1,
+};
+
+
+/* The priority levels of work (to be) done by the processor
+ * varies, and is steered by application levels, realtime
+ * requirements and so on.  A number of levels is defined and
+ * scheduling of tasks and interrupts is done with a preference
+ * for higher levels.
+ * TODO: Actually define something sensible.  event, irq levels?
+ */
+typedef enum priority_type priority_t;
+enum priority_type {
+       CPU_PRIO_ZERO,          // Never used for closures
+       CPU_PRIO_LOW,           // Low    priority closure
+       CPU_PRIO_MEDIUM,        // Medium priority closure
+       CPU_PRIO_HIGH,          // High   priority closure
+       CPU_PRIO_COUNT,         // Number of priority levels
+       // Aliases:
+       CPU_PRIO_NONE       = CPU_PRIO_ZERO,
+       CPU_PRIO_BACKGROUND = CPU_PRIO_LOW,
+       CPU_PRIO_UNKNOWN    = CPU_PRIO_MEDIUM,
+       CPU_PRIO_NORMAL     = CPU_PRIO_MEDIUM,
+       CPU_PRIO_REALTIME   = CPU_PRIO_HIGH,
+};
+
+
+/* Closures are self-contained units of work.  In light of the
+ * stateless nature of the phone user experience, these closures
+ * do not include a stack but only global state.  They will
+ * normally be statically allocated, to further avoid dynamic
+ * behaviour that would be too complicated for the user.  This
+ * is a serious choice but it seems to make sense for a phone,
+ * which is a sort of stimulus-response device.  If it was not
+ * for the realtime character and the need to wait for remote
+ * actors to respond, the whole phone would have been a single
+ * thread without even the need of closures.
+ *
+ * A closure is usually contained in a local structure holding
+ * variables (possibly pointers to shared data structures)
+ * which can be accessed by the handling function after
+ * invocation.  For the sake of convenience, some common
+ * data forms is included with every closure.  Where these
+ * suffice, they can save work.
+ *
+ * Closures contain facilities to queue them for orderly
+ * handling; the _next field and _priority field are used for
+ * control as a prioritised step in a larger whole of actions.
+ *
+ * Among others, closures are used as a deferred response to
+ * interrupts and timeouts, but they can just as easily be
+ * used to respond to certain kinds of incoming traffic on a
+ * network port.
+ *
+ * When the handler function in the closure is called, it is
+ * removed from the queue.  Upon return, a new closure may
+ * be provided, which will then be enqueued.  This simplifies
+ * many practical uses of closures; if forking is needed, a
+ * closure may be explicitly registered.
+ */
+typedef struct closure_type closure_t;
+typedef closure_t * (*closure_handler) (closure_t *);
+struct closure_type {
+       closure_t *cls_next;
+       priority_t cls_prio;
+       closure_handler cls_handler;
+       uint32_t cls_int;       // Utility data
+       void *cls_ptr;          // Utility data
+};
+
+
+/* Management functions for dealing with closures */
+
+void cpu_add_closure (closure_t *cls);
+void cpu_rm_closure  (closure_t *cls);
+
+
+/* Top half functions to support driving the CPU */
+void top_main (void);
+
+/* Bottom half drivers for processor control */
+void bottom_critical_begin (void);
+void bottom_critical_end (void);
+void bottom_sleep_prepare (void);
+void bottom_sleep_commit (sleep_depth_t depth);
+
+#endif
diff --git a/include/0cpm/defaults.h b/include/0cpm/defaults.h
new file mode 100644 (file)
index 0000000..ec70433
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * http://devel.0cpm.org/ -- Open source SIP telephony firmware.
+ *
+ * Device-generic default settings for undefined phone variables.
+ */
+
+
+/* The maximum number of supported handset */
+#ifndef HAS_HANDSETS
+#  define HAS_HANDSETS                         1
+#endif
+
+/* Keyboard settings */
+#ifdef DETECTS_DTMF
+#   error "Unsure how to set the defaults for a DETECTS_DTMF phone, sorry..."
+#endif
+#ifndef HAS_KBD_STARHASH
+#   define HAS_KBD_STARHASH            1
+#endif
+#ifndef HAS_KBD_ABCD
+#   define HAS_KBD_ABCD                        0
+#endif
+#ifndef HAS_KBD_FLASH
+#   define HAS_KBD_FLASH               1
+#endif
+#ifndef HAS_KBD_LINES
+#   define HAS_KBD_LINES               0
+#endif
+#ifndef HAS_KBD_SOFT_FUNCTION
+#   define HAS_KBD_SOFT_FUNCTION       0
+#endif
+#ifndef HAS_KBD_GENERIC
+#   define HAS_KBD_GENERIC             0
+#endif
+#ifndef HAS_KBD_VOICEMAIL
+#   define HAS_KBD_VOICEMAIL           1
+#endif
+#ifndef HAS_KBD_MENU
+#   define HAS_KBD_MENU                        0
+#endif
+#ifndef HAS_KBD_UPDOWN
+#   define HAS_KBD_UPDOWN              0
+#endif
+#ifndef HAS_KBD_LEFTRIGHT
+#   define HAS_KBD_LEFTRIGHT           0
+#endif
+#ifndef HAS_KBD_MUTE
+#   define HAS_KBD_MUTE                        0
+#endif
+#ifndef HAS_KBD_CONFERENCE
+#   define HAS_KBD_CONFERENCE          0
+#endif
+#ifndef HAS_KBD_HOLD
+#   define HAS_KBD_HOLD                        0
+#endif
+#ifndef HAS_KBD_TRANSFER
+#   define HAS_KBD_TRANSFER            0
+#endif
+#ifndef HAS_KBD_PHONEBOOK
+#   define HAS_KBD_PHONEBOOK           0
+#endif
+#ifndef HAS_KBD_VOLUPDOWN
+#   define HAS_KBD_VOLUPDOWN           0
+#endif
+#ifndef HAS_KBD_HADSET
+#   define HAS_KBD_HADSET              1
+#endif
+#ifndef HAS_KBD_SPEAKER
+#   define HAS_KBD_SPEAKER             0
+#endif
+#ifndef HAS_KBD_HEADSET
+#   define HAS_KBD_HEADSET             0
+#endif
+#ifndef HAS_KBD_MISSEDCALLS
+#   define HAS_KBD_MISSEDCALLS         0
+#endif
+#ifndef HAS_KBD_REDIAL
+#   define HAS_KBD_REDIAL              0
+#endif
+
+/* Light settings */
+#ifndef HAS_LED_MESSAGE
+#   define HAS_LED_MESSAGE             1
+#endif
+#ifndef HAS_LED_LINEKEYS
+#   define HAS_LED_LINEKEYS            0
+#endif
+#ifndef HAS_LED_GENERIC
+#   define HAS_LED_GENERIC             0
+#endif
+#ifndef HAS_LED_MUTE
+#   define HAS_LED_MUTE                        0
+#endif
+#ifndef HAS_LED_HANDSET
+#   define HAS_LED_HANDSET             0
+#endif
+#ifndef HAS_LED_HEADSET
+#   define HAS_LED_HEADSET             0
+#endif
+#ifndef HAS_LED_SPEAKERPHONE
+#   define HAS_LED_SPEAKERPHONE                0
+#endif
+#ifndef HAS_LED_BACKLIGHT
+#   define HAS_LED_BACKLIGHT           1
+#endif
+
+/* Display settings */
+
+/* Sound settings */
+#ifndef HAS_HANDSET
+#   define HAS_HANDSET                 1
+#endif
+#ifndef HAS_HEADSET
+#   define HAS_HEADSET                 0
+#endif
+#ifndef HAS_SPEAKER_MIKE
+#   define HAS_SPEAKER_MIKE            0
+#endif
+
+/* Line settings */
+#ifndef NUM_LINES
+#   if HAS_KBD_FLASH
+#      define NUM_LINES                        2
+#   else
+#      define NUM_LINES                        1
+#   endif
+#endif
+
+/* Account settings */
+#ifndef NUM_ACCOUNTS
+#   define NUM_ACCOUNTS                        NUM_LINES
+#endif
+
+/* Call settings */
+#define NUM_CALLS                      NUM_LINES
+
+/* Media settings */
+#ifndef HAS_MEDIA_VOICE
+#   define HAS_MEDIA_VOICE             1
+#endif
+#ifndef HAS_MEDIA_VIDEO
+#   define HAS_MEDIA_VIDEO             0
+#endif
+#ifndef HAS_MEDIA_URI
+#   define HAS_MEDIA_URI               0
+#endif
+#ifndef HAS_MEDIA_IMAGE
+#   define HAS_MEDIA_IMAGE             0
+#endif
+
diff --git a/include/0cpm/event.h b/include/0cpm/event.h
new file mode 100644 (file)
index 0000000..2253178
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Event handling
+ */
+
+
+DEPRECATED FILE
+
+
+/* Events are situations that normally trigger responses from applications.
+ *
+ * Examples of events are timers expiring, network packets arriving, buttons being
+ * pressed, and anything else that triggers an application to perform actions.  An
+ * application under this framework is written to respond to events, much like a
+ * modern GUI application.
+ *
+ * As a result of this event-driven application structure, applications have no
+ * stack, so as a result, they have no state other than what they store in data
+ * structures declared globally, or as static globals inside their bodies.  This
+ * does not seem restrictive to a phone design, regardless of the types of media
+ * it services.
+ *
+ * Not all events are offered to all applications.  Firstly, the applications are
+ * tried in stack order until one accepts the event.  Secondly, an application
+ * will not even be tried if it is not expressing an interest in the event.  To
+ * enable filtering events, they are classified, and each application has a mask
+ * to indicate which events are interesting to it.
+ *
+ * In terms of scheduling, events are stored in queues at different CPU priority
+ * levels.  When a new event is raised while running another, it will be entered
+ * in the queue that matches its priority.  It is possible for any event handler
+ * to schedule events at any level, even higher than their own.
+ *
+ * Events that are scheduled at a higher priority can be assumed to have finished
+ * when the next event at a lower priority is run; events at a lower priority will
+ * only run when the system finds time for it, and must therefore be tested for
+ * having completed.  Given enough pressure on the device, such an event may never
+ * actually schedule.  TODO: In support of knowing about the completion of an event,
+ * the firing application will be notified of the completion of each event it sends.
+ */
+
+
+/* The class of an event is its group, and determines in part how it can be handled.
+ * The keyboard for example, is split into the numeric pad, soft function keys, line
+ * keys, a number of dedicated keys, and so on.
+ */
+
+typedef enum {
+       /*
+        * A number of different key classes
+        */
+       EVT_CLASS_KEY_HOOK,     /* Going on-hook or off-hook */
+       EVT_CLASS_KEY_DIALING,  /* Digits and * and # but not DTMF-codes A, B, C, D */
+       EVT_CLASS_KEY_ALPHA,    /* Alphanumeric entry mode for strings etc. */
+       EVT_CLASS_KEY_LINE,     /* Line keys */
+       EVT_CLASS_KEY_GENERIC,  /* Programmable keys */
+       EVT_CLASS_KEY_MENU,     /* Operate menu, go up/down */
+       EVT_CLASS_KEY_FIXEDFUN, /* Fixed functions, such as speaker on/off, mute, xfer */
+       /*
+        * Sound events
+        */
+       EVT_CLASS_MIKE,         /* The microphone has picked up an number of bytes */
+       EVT_CLASS_SPEAKER,      /* The speaker is done playing the sound sent to it */
+       /*
+        * TODO:Network events
+        */
+       TODO_EVT_CLASS_NET_INCOMING,    /* Received a network packet */
+       TODO_EVT_CLASS_NET_WENTOUT,     /* Outgoing traffic has been sent as requested */
+       /*
+        * Timer events
+        */
+       EVT_CLASS_TIMEOUT,      /* Timer expired */
+} event_class_t;
+
+
+/* Event details depend on the class.  For a line key, this would hold the actual key,
+ * but for a fixed function key it would use the general key code.  For a timeout,
+ * a different thing would happen -- the timer object would be in evt_data.  It all
+ * depends on the type of event, really.
+ */
+typedef uint16_t event_detail_t;
+
+
+/* An event handler is a plain C function that is called when the scheduler gets to it.
+ * It returns true when done handling the event, or false if others should get a chance.
+ */
+typedef event_t;
+bool (*event_handler_t) (event_t *);
+
+/* An event_t structure represents an event and, optionally, the application it is
+ * destined for.  In no application is hard-wired, it will be sent down the stack.
+ */
+typedef struct event_type {
+       app_t evt_destination;
+       app_t evt_origin;
+       event_class_t evt_class;
+       event_detail_t evt_detail;
+       void *evt_data;
+       event_type *evt_next;
+} event_t;
+
diff --git a/include/0cpm/irq.h b/include/0cpm/irq.h
new file mode 100644 (file)
index 0000000..725573f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Interrupt drivers
+ */
+
+
+#ifndef HEADER_IRQ
+#define HEADER_IRQ
+
+
+/* Interrupt drivers are split in a top half and a bottom half.  The top half is generic,
+ * the bottom half is device-specific.  The bottom half routines start with bottom_irq_
+ * while the top half functions start with irq_
+ *
+ * The bottom system is only aware of the hardware interrupts, and whether they have
+ * occurred.  The top half is responsible for accepting interrupts from the bottom
+ * half, and using them to notify top handlers.
+ */
+
+
+/* A top-half interrupt handler is a plain C function that is called shortly after
+ * a bottom-half interrupt has occurred.
+ */
+
+typedef struct irq_type irq_t;
+typedef void (*irq_handler_t) (irq_t *);
+
+/* An irq_t structure represents an interrupt and how it invokes top-half handlers.
+ */
+struct irq_type {
+       irq_handler_t irq_handler;
+       irq_t *irq_next;
+       priority_t irq_priority;
+};
+
+
+/* Manage interrupts; fire one */
+void irq_fire (irq_t *irq);
+
+/* Top-half operations to manipulate interrupts */
+
+
+/* Bottom-half operations to manipulate interrupts */
+void bottom_irq_start (void);
+void bottom_irq_stop (void);
+void bottom_irq_enable  (irq_t *irq, irq_handler_t hdl);
+void bottom_irq_disable (irq_t *irq);
+void bottom_irq_wait (void);
+
+
+#endif
diff --git a/include/0cpm/kbd.h b/include/0cpm/kbd.h
new file mode 100644 (file)
index 0000000..718f617
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Keyboard drivers
+ */
+
+
+#include <resource.h>
+#include <kbd.h>
+
+
+/* The keyboard can be used and reused in many different ways.  Which way applies when is
+ * dependent on the state of the phone.  At each resource level, the application may
+ * request the use of certain keys and other resources.  When not all resources are
+ * available because higher resource levels overtake them, the application will be
+ * suspended.  At a later moment, when the higher resource level application ends, the
+ * application at the lower level may be resumed.
+ */
+
+
+resource_t resource_keyboard = {
+       /* res_claims */        NULL,
+       /* res_eventprio */     EVT_PRIO_USER,
+};
+
diff --git a/include/0cpm/led.h b/include/0cpm/led.h
new file mode 100644 (file)
index 0000000..fd596bd
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * LED drivers
+ */
+
+
+#ifndef HEADER_LED
+#define HEADER_LED
+
+
+/* LED drivers are split in a top half and a bottom half.  The top half is generic, the
+ * bottom half is device-specific.  The bottom half routines start with bottom_led_
+ * while the top half functions start with led_
+ *
+ * Both systems number LEDs sequentially.  The colour of a LED is numbered from 0 up,
+ * where 0 represents the least intrusive colour (which means off) and the highest
+ * value represents the most intrusive colour.
+ *
+ * In addition, the colour can be combined to flash with LED_FLASHING(col1,col2).
+ */
+
+
+/* Define a LED_IDX for all available LED functions.
+ * End with LED_IDX_COUNT to define the number of LEDs defined
+ */
+
+#define LED_IDX_LINEKEYS 0
+
+#if HAS_LED_LINEKEYS
+#   define LED_IDX_GENERIC (LED_IDX_LINEKEYS+NUM_LINEKEYS)
+#else
+#   define LED_IDX_GENERIC LED_IDX_LINEKEYS
+#endif
+
+#if HAS_LED_GENERIC
+#   define LED_IDX_MESSAGE (LED_IDX_GENERIC+HAS_KBD_GENERIC)
+#else
+#   define LED_IDX_MESSAGE LED_IDX_GENERIC
+#endif
+
+#if HAS_LED_MESSAGE
+#   define LED_IDX_MUTE (LED_IDX_GENERIC+1)
+#else
+#   define LED_IDX_MUTE LED_IDX_GENERIC
+#endif
+
+#if HAS_LED_MUTE
+#   define LED_IDX_HANDSET (LED_IDX_MUTE+1)
+#else
+#   define LED_IDX_HANDSET LED_IDX_MUTE
+#endif
+
+#if HAS_LED_HANDSET
+#   define LED_IDX_HEADSET (LED_IDX_HANDSET+1)
+#else
+#   define LED_IDX_HEADSET LED_IDX_HANDSET
+#endif
+
+#if HAS_LED_HEADSET
+#   define LED_IDX_SPEAKERPHONE (LED_IDX_HEADSET+1)
+#else
+#   define LED_IDX_SPEAKERPHONE LED_IDX_HEADSET
+#endif
+
+#if HAS_LED_SPEAKERPHONE
+#   define LED_IDX_BACKLIGHT (LED_IDX_SPEAKERPHONE+1)
+#else
+#   define LED_IDX_BACKLIGHT LED_IDX_SPEAKERPHONE
+#endif
+
+#if HAS_LED_BACKLIGHT
+#   define LED_IDX_COUNT (LED_IDX_BACKLIGHT+1)
+#else
+#   define LED_IDX_COUNT LED_IDX_BACKLIGHT
+#endif
+
+/* Define led_idx_t as a value that can hold the index number
+ * of an existing LED in the system.
+ */
+#if LED_IDX_COUNT > 256
+       typedef uint16_t led_idx_t;
+#else
+       typedef uint8_t  led_idx_t;
+#endif
+
+/*
+ * Define led_colour_t as the colour type for a LED, including flashing.
+ * The flashing definition consists of an XOR pattern in the top half.
+ * Combinations of colours can be composed to a flashing pattern with
+ * the LED_FLASH(col1,col2) macro.
+ */
+typedef uint8_t led_colour_t;
+
+#define LED_STABLE(col) ((col) & 0x0f)
+#define LED_STABLE_OFF 0x00
+#define LED_STABLE_ON  0x01
+
+#define LED_FLASH(col1,col2) ((col1) | ((col1)^(col2)) << 4)
+#define LED_FLASHING_CURRENT(col) ((col) & 0x0f)
+#define LED_FLASHING_OTHER(col) (((col) ^ (col >> 4)) & 0x0f)
+#define LED_FLASH_FLIP(col) ((col) ^ ((col) >> 4))
+
+/*
+ * Define led_flashtime_t as the time for half a delay of a flashing LED.
+ * Define values LED_FLASHTIME_XXX for various flashing speeds, with
+ * NONE for no flashing at all.
+ */
+typedef timing_t led_flashtime_t;
+#define LED_FLASHTIME_NONE     ((led_flashtime_t) (   0 * TIME_MSEC))
+#define LED_FLASHTIME_FAST     ((led_flashtime_t) ( 500 * TIME_MSEC))
+#define LED_FLASHTIME_MEDIUM   ((led_flashtime_t) (1000 * TIME_MSEC))
+#define LED_FLASHTIME_SLOW     ((led_flashtime_t) (2000 * TIME_MSEC))
+
+
+/* Top-half operations to manipulate LED states */
+void led_set (led_idx_t ledidx, led_colour_t col, led_flashtime_t ft);
+led_colour_t led_getcolour (led_idx_t ledidx);
+led_flashtime_t led_getflashtime (led_idx_t ledidx);
+static inline void led_set_off (led_idx_t ledidx) { led_set (ledidx, LED_STABLE_OFF, LED_FLASHTIME_NONE); }
+static inline void led_set_on  (led_idx_t ledidx) { led_set (ledidx, LED_STABLE_ON,  LED_FLASHTIME_NONE); }
+
+
+/* Bottom-half operations to manipulate LED states */
+void bottom_led_set (led_idx_t ledidx, led_colour_t col);
+
+#endif
index 81e1fa4..a2acb40 100644 (file)
@@ -53,7 +53,6 @@ struct ip6binding {
 
 /* An autoconfig ip6binding has a preference; tunnels are always lower */
 #define I6B_PREFERENCE_MASK            0x000f
-// #define I6B_PREFERENCE_TUNNEL               TODO
 
 /* An ip6binding can be obtained from one of a few sources */
 #define I6B_ROUTE_SOURCE_MASK          0x00f0
index cd1f4a9..0d9652a 100644 (file)
@@ -1,3 +1,7 @@
+#ifndef HEADER_NETFUN
+#define HEADER_NETFUN
+
+#define MTU 1500
 
 enum mem_netinput {
        //
@@ -42,45 +46,63 @@ enum mem_netinput {
        MEM_NETVAR_COUNT
 };
 
-#include <linux/if_tun.h>
-struct tunbuf {
-       struct tun_pi prefix;
+typedef struct packet packet_t;
+struct packet {
+#ifdef HAVE_NET_LEADER
+       uint8_t preamble [HAVE_NET_LEADER];
+#endif
        uint8_t data [1500 + 18];
+#ifdef HAVE_NET_TRAILER
+       uint8_t postfix [HAVE_NET_TRAILER];
+#endif
 };
 
 
 
 uint16_t netcore_checksum_areas (void *area0, ...);
-int netcore_send_buffer (int tunsox, uint32_t *mem, struct tunbuf *wbuf);
+void netcore_send_buffer (intptr_t *mem, uint8_t *wbuf);
+void netcore_bootstrap_initiate (void);
+void netcore_bootstrap_shutdown (void);
+
+uint8_t *net_arp_reply (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_dhcp4 (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_rtp (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_rtcp (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_sip (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_mdns_resp_error (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_mdns_resp_dyn (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_mdns_resp_std (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_mdns_query_error (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+uint8_t *net_mdns_query_ok (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
+
+uint8_t *netreply_arp_query (uint8_t *pkt, intptr_t *mem);
+uint8_t *netreply_icmp4_echo_req (uint8_t *pkt, intptr_t *mem);
+uint8_t *netreply_icmp6_ngb_disc (uint8_t *pkt, intptr_t *mem);
+uint8_t *netreply_icmp6_echo_req (uint8_t *pkt, intptr_t *mem);
+uint8_t *netreply_dhcp4_offer (uint8_t *pkt, intptr_t *mem);
+uint8_t *netreply_dhcp6_advertise (uint8_t *pout, intptr_t *mem);
+
+uint8_t *netsend_icmp6_router_solicit (uint8_t *pout, intptr_t *mem);
+uint8_t *netsend_icmp6_ngb_sol (uint8_t *pout, intptr_t *mem);
+uint8_t *netsend_dhcp4_discover (uint8_t *pout, intptr_t *mem);
+uint8_t *netsend_dhcp6_solicit (uint8_t *pout, intptr_t *mem);
 
-uint8_t *net_arp_reply (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_dhcp4 (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_rtp (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_rtcp (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_sip (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_mdns_resp_error (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_mdns_resp_dyn (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_mdns_resp_std (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_mdns_query_error (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
-uint8_t *net_mdns_query_ok (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
+void netdb_initialise (void);
+uint8_t *netdb_router_advertised (uint8_t *pout, intptr_t *mem);
+uint8_t *netdb_neighbour_advertised (uint8_t *pout, intptr_t *mem);
+uint8_t *netdb_dhcp4_ack (uint8_t *pout, intptr_t *mem);
+uint8_t *netdb_dhcp4_nak (uint8_t *pout, intptr_t *mem);
+uint8_t *netdb_dhcp6_reply (uint8_t *pout, intptr_t *mem);
+uint8_t *netdb_dhcp6_reconfigure (uint8_t *pout, intptr_t *mem);
 
-uint8_t *netreply_arp_query (uint8_t *pkt, uint32_t *mem);
-uint8_t *netreply_icmp4_echo_req (uint8_t *pkt, uint32_t *mem);
-uint8_t *netreply_icmp6_ngb_disc (uint8_t *pkt, uint32_t *mem);
-uint8_t *netreply_icmp6_echo_req (uint8_t *pkt, uint32_t *mem);
-uint8_t *netreply_dhcp4_offer (uint8_t *pkt, uint32_t *mem);
-uint8_t *netreply_dhcp6_advertise (uint8_t *pout, uint32_t *mem);
+uint8_t *netout (uint8_t *pkt, uint32_t pktlen, intptr_t *mem);
 
-uint8_t *netsend_icmp6_router_solicit (uint8_t *pout, uint32_t *mem);
-uint8_t *netsend_icmp6_ngb_sol (uint8_t *pout, uint32_t *mem);
-uint8_t *netsend_dhcp4_discover (uint8_t *pout, uint32_t *mem);
-uint8_t *netsend_dhcp6_solicit (uint8_t *pout, uint32_t *mem);
+/* Bottom layer functions for reading and writing packets */
+bool bottom_network_recv (uint8_t *pktbuf, uint16_t *pktbuflen);
+bool bottom_network_send (uint8_t *pktbuf, uint16_t  pktbuflen);
 
-uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem);
-uint8_t *netdb_neighbour_advertised (uint8_t *pout, uint32_t *mem);
-uint8_t *netdb_dhcp4_ack (uint8_t *pout, uint32_t *mem);
-uint8_t *netdb_dhcp4_nak (uint8_t *pout, uint32_t *mem);
-uint8_t *netdb_dhcp6_reply (uint8_t *pout, uint32_t *mem);
-uint8_t *netdb_dhcp6_reconfigure (uint8_t *pout, uint32_t *mem);
+/* Top layer functions for processing network interrupts */
+void top_network_can_recv (void);
+void top_network_can_send (void);
 
-uint8_t *netout (uint8_t *pkt, uint32_t pktlen, uint32_t *mem);
+#endif
diff --git a/include/0cpm/resource.h b/include/0cpm/resource.h
new file mode 100644 (file)
index 0000000..6959576
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Resource framework
+ */
+
+
+#ifndef HEADER_RESOURCE
+#define HEADER_RESOURCE
+
+/*
+ * Phone applications may run in some situations, and not in others.
+ * This results in dynamicity of interactions, especially towards humans.
+ * The resource framework is setup to support resource claims that depend
+ * on the priority of an application.  Applications at a higher priority
+ * will be offered a first chance to process events.
+ * 
+ * A resource forms a queue of resource claims, and each of these claims
+ * ties to an application.  The first active application is in charge
+ * of a resource.  Only if the first chooses to share the events with
+ * lower applications will an event be made visible downstream.  This
+ * can be used, for instance, to have a high-priority application shutdown
+ * if certain buttons are pressed, the hook is placed back, and so on.
+ */
+
+
+#include <0cpm/app.h>
+
+
+/* Event priorities define levels of importance for events.  They are
+ * defined in resource_t structures, and applied to all events that
+ * emenate from that resource.  The lower the class, the later it will
+ * be handled.
+ */
+
+typedef enum eventpriority {
+       EVT_BACKGROUND,
+       EVT_USER,
+       EVT_NEGOTIATION,
+       EVT_REALTIME,
+       EVT_IMMEDIATE,
+} evtprio_t;
+
+
+/* Resource claims will be ordered by application priority, and only
+ * actually be used if an application is set to running.
+ * Input resources send events to clm_eventhandler() and output
+ * resources may invoke clm_outputrefresh() when assigned to another
+ * application.
+ */
+typedef struct claim claim_t;
+struct claim {
+       claim_t *clm_next;
+       struct application *clm_app;
+       //TODO:event// bool (*clm_eventhandler)  (claim_t *clm, struct event *evt);
+       bool (*clm_eventhandler)  (claim_t *clm, void *evt);
+       void (*clm_outputrefresh) (claim_t *clm);
+};
+
+
+/* Resources are global structures that can be claimed.  They are known
+ * by their name, or their position in an array, and so on.  In general,
+ * anything the resource offering party and resource claiming party can
+ * agree on will work.
+ */
+typedef struct resource resource_t;
+struct resource {
+       struct claim *res_claims;
+       evtprio_t res_eventprio;
+};
+
+
+/* Events report the occurrence of resource-related situations to an
+ * application.  Liberally included are timer events, which are not
+ * quite the same.
+ *
+ * The description of events is split into event class, instance and
+ * value.  For further information an event trigger function is always
+ * called with the matching resource claim structure, which may be
+ * embedded in a larger structure holding addtional state of use to
+ * the event processing function.
+ *
+ * Event handlers are defined as part of a resource claim, where they
+ * are known as clm_eventhandler functions.  Multiple events may
+ * occur in relation to a single resource claim.  Events are queued by
+ * the scheduler, ordered by the event priority defined in the original
+ * resource that triggered it.
+ */
+
+typedef struct event {
+       struct claim *evt_claim;
+       uint8_t evt_class; //TODO// eventclass_t evt_class;
+       uint8_t evt_instance;
+       uint16_t evt_value;
+} event_t;
+
+
+#endif
diff --git a/include/0cpm/timer.h b/include/0cpm/timer.h
new file mode 100644 (file)
index 0000000..673ce87
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Timer drivers
+ */
+
+
+#ifndef HEADER_TIMER
+#define HEADER_TIMER
+
+#include <0cpm/irq.h>
+
+
+/* The bottom defines a few useful things:
+ *   typedef uint..._t timing_t;
+ *   #define TIME_MSEC ...
+ *   #define TIME_SEC  ...
+ * The former can be used as a parameter without pointers;
+ * the TIME_xxx define such periods in terms of that time.
+ */
+
+
+/* Timer drivers are split in a top half and a bottom half.  The top half is generic, the
+ * bottom half is device-specific.  The bottom half routines start with bottom_irqtimer_
+ * while the top half functions start with irqtimer_
+ *
+ * The bottom system is only aware of the time of the next hardware-raised interrupt.
+ * The top system maintains a timeout-ordered queue of interrupts to raise.
+ */
+
+typedef struct irqtimer_type irqtimer_t;
+struct irqtimer_type {
+       irq_t tmr_irq;
+       timing_t tmr_expiry;
+};
+
+
+/* Managing functions for kernel-defined timers */
+void irqtimer_start   (irqtimer_t *tmr, timing_t delay, irq_handler_t hdl, priority_t prio);
+void irqtimer_restart (irqtimer_t *tmr, timing_t intval);
+void irqtimer_stop    (irqtimer_t *tmr);
+
+
+/* Top-half operations to manipulate timers */
+void top_timer_expiration (timing_t exptime);
+
+
+/* Bottom-half operations to manipulate the next timer interrupt */
+timing_t bottom_time (void);
+timing_t bottom_timer_set (timing_t nextirq);
+
+
+#endif
diff --git a/src/driver/README b/src/driver/README
new file mode 100644 (file)
index 0000000..7fd776e
--- /dev/null
@@ -0,0 +1,12 @@
+=====================================
+README for 0cpm firmerware driver dir
+=====================================
+
+net    Network chip drivers
+
+soc    System-on-chip drivers
+
+ata    SLIC/Codec chips for analog telephony
+
+util   Special utilities, such as testing on Linux
+
diff --git a/src/driver/ata/si3210.c b/src/driver/ata/si3210.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/driver/net/rtl8019as.c b/src/driver/net/rtl8019as.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/driver/soc/tic54x.c b/src/driver/soc/tic54x.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/driver/soc/tic55x.c b/src/driver/soc/tic55x.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/driver/soc/visba3.c b/src/driver/soc/visba3.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/driver/tuntest.c b/src/driver/tuntest.c
deleted file mode 100644 (file)
index d0170d3..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/* tuntest.c -- Tunnel-based testing of the network code.
- *
- * From: Rick van Rein <rick@openfortress.nl>
- */
-
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <string.h>
-#include <fcntl.h>
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/udp.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/icmp6.h>
-
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_ether.h>
-
-#include <0cpm/netfun.h>
-#include <0cpm/netdb.h>
-
-
-
-int tunsox = -1;
-
-struct tunbuf rbuf, wbuf;
-
-
-
-/*
- * TEST OUTPUT FUNCTIONS
- */
-
-uint8_t *net_arp_reply (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       printf ("Received: ARP Reply\n");
-       return NULL;
-}
-
-uint8_t *net_rtp (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       printf ("Received: RTP\n");
-       return NULL;
-}
-
-uint8_t *net_rtcp (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       printf ("Received: RTCP\n");
-       return NULL;
-}
-
-uint8_t *net_sip (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       printf ("Received: SIP\n");
-       return NULL;
-}
-
-uint8_t *net_mdns_resp_error (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       //TODO// printf ("Received: mDNS error response\n");
-       return NULL;
-}
-
-uint8_t *net_mdns_resp_dyn (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       //TODO// printf ("Received: mDNS dynamic response\n");
-       return NULL;
-}
-
-uint8_t *net_mdns_resp_std (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       //TODO// printf ("Received: mDNS standard response\n");
-       return NULL;
-}
-
-uint8_t *net_mdns_query_error (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       //TODO// printf ("Received: mDNS error query\n");
-       return NULL;
-}
-
-uint8_t *net_mdns_query_ok (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
-       //TODO// printf ("Received: mDNS ok query\n");
-       return NULL;
-}
-
-
-
-/* A collection of initialisation steps.
- */
-void init (void) {
-       int i;
-       for (i=0; i<IP6BINDING_COUNT; i++) {
-               ip6binding [i].flags = I6B_EXPIRED;
-       }
-}
-
-/*
- * TEST SETUP FUNCTIONS
- */
-
-typedef void *retfn (uint8_t *pout, uint32_t *mem);
-
-int process_packets (int sox, int secs) {
-       int ok = 1;
-       time_t maxtm = secs + time (NULL);
-       while (ok) {
-               fd_set seln;
-               FD_ZERO (&seln);
-               FD_SET (sox, &seln);
-               struct timeval tout;
-               tout.tv_sec = maxtm - time (NULL);
-               tout.tv_usec = 500000;
-               if ((tout.tv_sec < 0) || (select (sox+1, &seln, NULL, NULL, &tout) <= 0)) {
-                       return 1;
-               }
-               size_t len = read (sox, &rbuf, sizeof (struct tun_pi) + sizeof (rbuf.data));
-               uint8_t *pkt = rbuf.data;
-               uint32_t pktlen = len - sizeof (struct tun_pi);
-               if (pktlen > 18) {
-                       uint32_t mem [28];
-                       bzero (mem, sizeof (mem));
-                       retfn *rf = (retfn *) netinput (rbuf.data, len - sizeof (struct tun_pi), mem);
-#if 0
-                       int i;
-                       for (i=0; i<28; i++) {
-                               printf ("  M[%d]=0x%08x%s", i, mem [i],
-                                               ((i+1)%4 == 0)? "\n": "");
-                       }
-#endif
-                       if (rf != NULL) {
-                               uint8_t *start = wbuf.data;
-                               uint8_t *stop = (*rf) (start, mem);
-                               if (stop) {
-                                       mem [MEM_ALL_DONE] = (uint32_t) stop;
-                                       uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
-                                       if (netcore_send_buffer (tunsox, mem, &wbuf) < 0) {
-                                               printf ("Failed while sending reply data\n");
-                                       }
-                               }
-                       }
-               }
-       }
-       return 0;
-}
-
-
-int setup_tunnel (void) {
-       tunsox = -1;
-       int ok = 1;
-       //
-       // Open the tunnel
-       tunsox = open ("/dev/net/tun", O_RDWR);
-       if (tunsox < 0) {
-               ok = 0;
-       }
-       //
-       // Set the tunnel name to "test0cpm"
-       struct ifreq ifreq;
-       bzero (&ifreq, sizeof (ifreq));
-       strncpy (ifreq.ifr_name, "test0cpm", IFNAMSIZ);
-       ifreq.ifr_flags = IFF_TAP;
-       if (ok)
-       if (ioctl (tunsox, TUNSETIFF, &ifreq) == -1) {
-               ok = 0;
-       }
-       //
-       // Set to promiscuous mode (meaningless for a tunnel)
-#if 0
-       if (ok)
-       if (ioctl(tunsox, SIOCGIFFLAGS, &ifreq) == -1) {
-               ok = 0;
-       }
-       ifreq.ifr_flags |= IFF_PROMISC | IFF_UP;
-       if (ok)
-       if (ioctl(tunsox, SIOCSIFFLAGS, &ifreq) == -1) {
-               ok = 0;
-       }
-#endif
-       //
-       // Cleanup and report success or failure
-       if (!ok) {
-               if (tunsox >= 0) {
-                       close (tunsox);
-                       tunsox = -1;
-               }
-               return -1;
-       }
-       return tunsox;
-}
-
-
-
-int main (int argc, char *argv []) {
-       init ();
-       int tunsox = setup_tunnel ();
-       if (tunsox == -1) {
-               fprintf (stderr, "Failed to setup tunnel\n");
-               exit (1);
-       }
-       system ("/sbin/ifconfig test0cpm up");
-       system ("/sbin/ifconfig test0cpm");
-       sleep (10);
-       if (!netcore_bootstrap ()) {
-               fprintf (stderr, "Failed to bootstrap the network\n");
-               exit (1);
-       }
-       process_packets (tunsox, 60);
-       printf ("Done.\n");
-       close (tunsox);
-       return 0;
-}
-
diff --git a/src/driver/util/linuxtuntest.c b/src/driver/util/linuxtuntest.c
new file mode 100644 (file)
index 0000000..d8d0ced
--- /dev/null
@@ -0,0 +1,332 @@
+/* tuntest.c -- Tunnel-based testing of the network code.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_ether.h>
+
+#include <config.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/netfun.h>
+#include <0cpm/netdb.h>
+
+#include <0cpm/timer.h>
+#include <0cpm/led.h>
+
+
+int tunsox = -1;
+
+packet_t rbuf, wbuf;
+
+timing_t timeout = TIMER_NULL;
+
+bool sleepy = false;
+
+
+/*
+ * TEST OUTPUT FUNCTIONS
+ */
+
+uint8_t *net_arp_reply (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       printf ("Received: ARP Reply\n");
+       return NULL;
+}
+
+uint8_t *net_rtp (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       printf ("Received: RTP\n");
+       return NULL;
+}
+
+uint8_t *net_rtcp (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       printf ("Received: RTCP\n");
+       return NULL;
+}
+
+uint8_t *net_sip (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       printf ("Received: SIP\n");
+       return NULL;
+}
+
+uint8_t *net_mdns_resp_error (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       //TODO// printf ("Received: mDNS error response\n");
+       return NULL;
+}
+
+uint8_t *net_mdns_resp_dyn (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       //TODO// printf ("Received: mDNS dynamic response\n");
+       return NULL;
+}
+
+uint8_t *net_mdns_resp_std (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       //TODO// printf ("Received: mDNS standard response\n");
+       return NULL;
+}
+
+uint8_t *net_mdns_query_error (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       //TODO// printf ("Received: mDNS error query\n");
+       return NULL;
+}
+
+uint8_t *net_mdns_query_ok (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
+       //TODO// printf ("Received: mDNS ok query\n");
+       return NULL;
+}
+
+
+
+/* A collection of initialisation steps.
+ */
+void init (void) {
+       int i;
+       for (i=0; i<IP6BINDING_COUNT; i++) {
+               ip6binding [i].flags = I6B_EXPIRED;
+       }
+}
+
+
+/*
+ * Environment simulation functions
+ */
+
+void bottom_led_set (led_idx_t ledidx, led_colour_t col) {
+       char *colmap [] = { "off", "green", "red" };
+       printf ("LED #%d switches to colour #%d (%s)\n",
+                       ledidx, col, (col < 3)? colmap [col]: "out-of-range");
+}
+
+timing_t bottom_time (void) {
+       struct timeval now;
+       timing_t retval;
+       if (gettimeofday (&now, NULL) != 0) {
+               fprintf (stderr, "Failed to get clock time: %s\n", strerror (errno));
+               return 0;
+       }
+       retval = now.tv_sec * 1000 + (now.tv_usec / 1000);
+       return retval;
+}
+
+timing_t bottom_timer_set (timing_t nextirq) {
+       timing_t retval = timeout;
+       timeout = nextirq;
+       return timeout;
+}
+
+void bottom_critical_region_begin (void) {
+       // No real action, as this is already software
+}
+
+void bottom_critical_region_end (void) {
+       // No real action, as this is already software
+}
+
+void bottom_sleep_prepare (void) {
+       sleepy = true;
+}
+
+void bottom_sleep_commit (sleep_depth_t depth) {
+       if (!sleepy) {
+               return;
+       }
+       fd_set seln;
+       FD_ZERO (&seln);
+       FD_SET (tunsox, &seln);
+       struct timeval seltimeout;
+       timing_t tm = 0;
+       if (timeout != TIMER_NULL) {
+               gettimeofday (&seltimeout, NULL);
+               tm = seltimeout.tv_sec * 1000 + seltimeout.tv_usec / 1000;
+               if (timeout < tm) {
+                       timeout = TIMER_NULL;
+                       top_timer_expiration (tm);
+                       sleepy = false;
+                       return;
+               }
+               tm = timeout - tm;
+               seltimeout.tv_sec  =  tm / 1000        ;
+               seltimeout.tv_usec = (tm % 1000) * 1000;
+       }
+       int sel = select (tunsox+1, &seln, NULL, NULL, &seltimeout);
+       if (sel >= 1) {
+               top_network_can_recv ();
+               sleepy = false;
+       } else if (sel == 0) {
+               gettimeofday (&seltimeout, NULL);
+               timing_t exptm = (seltimeout.tv_sec * 1000) * (seltimeout.tv_usec / 1000);
+               timeout = TIMER_NULL;
+               top_timer_expiration (exptm);
+               sleepy = false;
+       } else {
+               fprintf (stderr, "select() returned an error: %s\n", strerror (errno));
+               sleep (1);
+       }
+}
+
+bool bottom_network_send (uint8_t *buf, uint16_t buflen) {
+       struct tun_pi *tunpi = (struct tun_pi *) &buf [-sizeof (struct tun_pi)];
+       tunpi->flags = 0;
+       tunpi->proto = ((struct ethhdr *) buf)->h_proto;
+       size_t wsz = write (tunsox, buf - sizeof (struct tun_pi), buflen + sizeof (struct tun_pi));
+       if (wsz == -1) {
+               if (errno == EAGAIN) {
+                       return false;
+               } else {
+                       fprintf (stderr, "Ignoring error after write to tunnel: %s\n", strerror (errno));
+               }
+       }
+       return true;
+}
+
+bool bottom_network_recv (uint8_t *buf, uint16_t *buflen) {
+       uint16_t bl = (*buflen) + sizeof (struct tun_pi);
+       size_t rsz = read (tunsox, buf - sizeof (struct tun_pi), bl);
+       if (rsz == -1) {
+               if (errno == EAGAIN) {
+                       return false;
+               } else {
+                       fprintf (stderr, "Ignoring error after read from tunnel: %s\n", strerror (errno));
+               }
+               *buflen = 0;
+       } else if (rsz > sizeof (struct tun_pi)) {
+               *buflen = rsz - sizeof (struct tun_pi);
+       } else {
+               *buflen = 0;
+       }
+       return true;
+}
+
+
+/*
+ * TEST SETUP FUNCTIONS
+ */
+
+typedef void *retfn (uint8_t *pout, intptr_t *mem);
+
+int process_packets (int secs) {
+       int ok = 1;
+       time_t maxtm = secs + time (NULL);
+       while (ok) {
+               fd_set seln;
+               FD_ZERO (&seln);
+               FD_SET (tunsox, &seln);
+               struct timeval tout;
+               tout.tv_sec = maxtm - time (NULL);
+               tout.tv_usec = 500000;
+               if ((tout.tv_sec < 0) || (select (tunsox+1, &seln, NULL, NULL, &tout) <= 0)) {
+                       return 1;
+               }
+               size_t len = read (tunsox, &rbuf.data [-sizeof (struct tun_pi)], sizeof (struct tun_pi) + sizeof (rbuf.data));
+               uint8_t *pkt = rbuf.data;
+               uint32_t pktlen = len - sizeof (struct tun_pi);
+               if (pktlen > 18) {
+                       intptr_t mem [28];
+                       bzero (mem, sizeof (mem));
+                       retfn *rf = (retfn *) netinput (rbuf.data, pktlen, mem);
+#if 0
+                       int i;
+                       for (i=0; i<28; i++) {
+                               printf ("  M[%d]=0x%08x%s", i, mem [i],
+                                               ((i+1)%4 == 0)? "\n": "");
+                       }
+#endif
+                       if (rf != NULL) {
+                               uint8_t *start = wbuf.data;
+                               uint8_t *stop = (*rf) (start, mem);
+                               if (stop) {
+                                       mem [MEM_ALL_DONE] = (intptr_t) stop;
+                                       netcore_send_buffer (mem, wbuf.data);
+                               }
+                       }
+               }
+       }
+       return 0;
+}
+
+
+int setup_tunnel (void) {
+       tunsox = -1;
+       int ok = 1;
+       //
+       // Open the tunnel
+       tunsox = open ("/dev/net/tun", O_RDWR | O_NONBLOCK);
+       if (tunsox < 0) {
+               ok = 0;
+       }
+       //
+       // Set the tunnel name to "test0cpm"
+       struct ifreq ifreq;
+       bzero (&ifreq, sizeof (ifreq));
+       strncpy (ifreq.ifr_name, "test0cpm", IFNAMSIZ);
+       ifreq.ifr_flags = IFF_TAP;
+       if (ok)
+       if (ioctl (tunsox, TUNSETIFF, &ifreq) == -1) {
+               ok = 0;
+       }
+       //
+       // Set to promiscuous mode (meaningless for a tunnel)
+#if 0
+       if (ok)
+       if (ioctl(tunsox, SIOCGIFFLAGS, &ifreq) == -1) {
+               ok = 0;
+       }
+       ifreq.ifr_flags |= IFF_PROMISC | IFF_UP;
+       if (ok)
+       if (ioctl(tunsox, SIOCSIFFLAGS, &ifreq) == -1) {
+               ok = 0;
+       }
+#endif
+       //
+       // Cleanup and report success or failure
+       if (!ok) {
+               if (tunsox >= 0) {
+                       close (tunsox);
+                       tunsox = -1;
+               }
+               return -1;
+       }
+       return tunsox;
+}
+
+
+
+int main (int argc, char *argv []) {
+       init ();
+       int tunsox = setup_tunnel ();
+       if (tunsox == -1) {
+               fprintf (stderr, "Failed to setup tunnel\n");
+               exit (1);
+       }
+       system ("/sbin/ifconfig test0cpm up");
+       system ("/sbin/ifconfig test0cpm");
+       sleep (10);
+       top_main ();
+       fprintf (stderr, "top_main() returned -- which MUST NOT happen!\n");
+       close (tunsox);
+       exit (1);
+}
+
diff --git a/src/driver/util/linuxtuntest.h b/src/driver/util/linuxtuntest.h
new file mode 100644 (file)
index 0000000..f8a604a
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include <stdint.h>
+
+typedef uint64_t timing_t;
+
+#define TIMER_NULL 0
+
+#define TIME_MSEC 1
+#define TIME_SEC  1000
+
diff --git a/src/kernel/Makefile b/src/kernel/Makefile
new file mode 100644 (file)
index 0000000..1c1b361
--- /dev/null
@@ -0,0 +1,2 @@
+
+objs-top-kernel-y += src/kernel/led.o src/kernel/timer.o src/kernel/resource.o src/kernel/app.o src/kernel/cpu.o src/kernel/net.o
diff --git a/src/kernel/README b/src/kernel/README
new file mode 100644 (file)
index 0000000..6126ddd
--- /dev/null
@@ -0,0 +1,27 @@
+==========
+src/kernel
+==========
+
+This directory contains generic kernel code, written in pure C.
+
+Kernel components provide basic functions such as control over LEDs or
+buttons of the telephone device, but also timers and sound control.
+The part in the kernel directory is the generic top-half of these
+drivers, while the src/drive/ directory holds the corresponding,
+device-specific bottom-half.
+
+The task of the kernel source files together with macro definitions
+in include files is to provide a programmer-friendly level of access
+to hardware devices.  This includes resource control and handling of
+resource contention -- for example, if a device lacks certain keys
+or buttons, the kernel may try to replace it with a menu entry.  And
+if a phone has no dedicated line buttons, a flash button or a short
+hangup is accepted as a flash command, to switch between lines.
+
+Where resources are shared, this is either done by scrolling through
+them, or by assigning priorities to the claims on resources.  For
+instance, if a call comes in, then its display requests take
+precedence over information shown for the current call, and certainly
+over a background clock.  Such priorities help to determine what is
+shown at each time, and how resource struggles are resolved.
+
diff --git a/src/kernel/app.c b/src/kernel/app.c
new file mode 100644 (file)
index 0000000..20c793a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Application drivers
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <0cpm/app.h>
+
+
+/* Applications are the parties that scream for resources.
+ * They may wish or demand resources.  If they ask for it at
+ * the same strength, then the application with the highest
+ * resource level wins; if two applications reside at the
+ * same resource level, then the latest one started wins.
+ * If however, one application has a wish and another has a
+ * demand for a resource, and no other resources compete,
+ * then the demand wins, regardless of its higher or lower
+ * position in the resource ranking.
+ */
+
+
+/* The stack pointer shows the active application; that is,
+ * the one that has the highest resource level and/or was
+ * started last.  The stack is ordered strictly on these
+ * values, so the question who wins a battle over equally
+ * desired resources is resolved by finding the first to
+ * occur on the stack.
+ */
+
+app_t *stackpointer = NULL;
+
+
+/* Register an application, placing it as high on the stack
+ * as possible, given its resource level.
+ */
+void app_register (app_t *app) {
+       app_level_t lvl = app->app_resourcelevel;
+       app_t **spp = &stackpointer;
+       while ((*spp) && ((*spp)->app_resourcelevel > lvl)) {
+               spp = &(*spp)->app_stackdown;
+       }
+       // TODO: Is this what we want?  Or is this "app_focus"?
+       app->app_stackdown = *spp;
+       *spp = app;
+}
+
diff --git a/src/kernel/cpu.c b/src/kernel/cpu.c
new file mode 100644 (file)
index 0000000..6423d3f
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * CPU drivers
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+
+
+/* Round-robin queues with interrupts and closures, ordered by priority.
+ * To be fair, the elements pointed at have already been completed
+ * and are awaiting roll-over or removal.
+ */
+irq_t     *irqs     [CPU_PRIO_COUNT];
+closure_t *closures [CPU_PRIO_COUNT];
+
+/* The current priority is an optimisation; it tells what the highest
+ * active priority is, so as to avoid too much searching.  The priority
+ * starts at the low value CPU_PRIO_ZERO at which nothing runs; as soon
+ * as anything is scheduled, the value is increased to signal work is to
+ * be done.
+ */
+priority_t cur_prio = CPU_PRIO_ZERO;
+
+
+/* Add an IRQ to the round-robin task queue to be scheduled */
+void irq_fire (irq_t *irq) {
+       priority_t p = irq->irq_priority;
+       if (p > cur_prio) {
+               cur_prio = p;
+       }
+       irq->irq_next = irqs [p]? irqs [p]: irq;
+       irqs [p] = irq;
+       irqs [p] = irqs [p]->irq_next;
+}
+
+
+/* Hop jobs until there is nothing left to do.  This is actually the main
+ * procedure for the scheduler.  If it ends, then all the work has been
+ * enqueued somewhere, and is awaiting a response.  In other words, it
+ * is then time to sleep.  This assumes that no task or IRQ will ever
+ * be set to CPU_PRIO_ZERO.
+ */
+void jobhopper (void) {
+       printf ("Jobhopper starts.\n");
+       while (cur_prio > CPU_PRIO_ZERO) {
+               while (irqs [cur_prio]) {
+                       irq_t *this;
+                       irqs [cur_prio] = irqs [cur_prio]->irq_next;
+                       while (this = irqs [cur_prio], this != NULL) {
+                               if (this != this->irq_next) {
+                                       irqs [cur_prio] = this->irq_next;
+                               } else {
+                                       irqs [cur_prio] = NULL;
+                               }
+                               this->irq_next = NULL;
+                               printf ("Jobhopper calls interrupt handler.\n");
+                               (*this->irq_handler) (this);
+                       }
+               }
+               closure_t *here = closures [cur_prio];
+               if (here) {
+                       closures [cur_prio] = here->cls_next;
+                       here->cls_next = NULL;
+                       here = (*here->cls_handler) (here);
+                       if (here) {
+                               closure_t **last = &closures [cur_prio];
+                               while (*last != NULL) {
+                                       last = &(*last)->cls_next;
+                               }
+                               printf ("Jobhopper found closure -- TODO: call it!\n");
+                               here->cls_next = NULL;
+                               *last = here;
+                       }
+               } else {
+                       cur_prio--;
+               }
+       }
+       printf ("Jobhopper ends.\n");
+}
+
+
+/* The main operational loop for the CPU.  Try to run code, and when
+ * nothing remains to be done, stop to wait for interrupts.
+ */
+void top_main (void) {
+       // TODO: init ();
+       netdb_initialise ();
+       // TODO: cpu_add_closure (X);
+       // TODO: cpu_add_closure (Y);
+       // TODO: cpu_add_closure (Z);
+       bottom_critical_region_end ();
+       top_network_online ();  // TODO: Should be called from bottom!
+       while (true) {
+               jobhopper ();
+               bottom_sleep_prepare ();
+               if (cur_prio == CPU_PRIO_ZERO) {
+                       bottom_sleep_commit (SLEEP_SNOOZE);
+               }
+       }
+}
+
diff --git a/src/kernel/led.c b/src/kernel/led.c
new file mode 100644 (file)
index 0000000..f28229d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * LED drivers
+ */
+
+
+#include <config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+#include <0cpm/led.h>
+
+
+/* Refer to the top handler state storage structures for LED information.
+ * The size of the array is LED_IDX_COUNT as derived above.
+ */
+struct led_state {
+       irqtimer_t      led_timer;
+        led_colour_t    led_colour;
+        led_flashtime_t led_flashtime;
+};
+
+static struct led_state leds [LED_IDX_COUNT];
+
+
+/* LED interrupts will change the state of a LED.
+ */
+static bool led_irq (irq_t *irq) {
+       struct led_state *this = (struct led_state *) irq;
+       led_idx_t ledidx = (this - leds) / sizeof (struct led_state);   // TODO: CALC IS GENERAL?
+       this->led_colour = LED_FLASH_FLIP (this->led_colour);
+       bottom_led_set (ledidx, LED_FLASHING_CURRENT (this->led_colour));
+       irqtimer_restart (&this->led_timer, this->led_flashtime);
+       return true;
+}
+
+
+
+/* Set the state of a LED.  If the current colour matches the initial colour of a
+ * flashing LED, that a flash operation is immediately performed.
+ */
+void led_set (led_idx_t ledidx, led_colour_t col, led_flashtime_t ft) {
+       if (leds [ledidx].led_flashtime != LED_FLASHTIME_NONE) {
+               irqtimer_stop (&leds [ledidx].led_timer);
+       }
+       if (ft == LED_FLASHTIME_NONE) {
+               col = LED_STABLE (col);
+       } else if (LED_FLASHING_CURRENT (leds [ledidx].led_colour) == LED_FLASHING_CURRENT (col)) {
+               col = LED_FLASH_FLIP (col);
+       }
+       leds [ledidx].led_colour = col;
+       leds [ledidx].led_flashtime = ft;
+       bottom_led_set (ledidx, col);
+       irqtimer_start (&leds [ledidx].led_timer, ft, led_irq, CPU_PRIO_LOW);
+}
+
+
+/* Get the colour currently shown on the LED.
+ */
+led_colour_t led_getcolour (led_idx_t ledidx) {
+       return LED_FLASHING_CURRENT (leds [ledidx].led_colour);
+}
+
+/* Get the flashing time for a given LED.
+ */
+led_flashtime_t led_getflashtime (led_idx_t ledidx) {
+       return leds [ledidx].led_flashtime;
+}
+
diff --git a/src/kernel/net.c b/src/kernel/net.c
new file mode 100644 (file)
index 0000000..9dd479e
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Network multiplexing for incoming traffic.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/netfun.h>
+
+
+/* TODO: Current return function from netinput
+ */
+typedef void *retfn (uint8_t *pout, intptr_t *mem);
+
+
+/* The can_recv and can_send flags store whether
+ * the network is in a state to permit either of
+ * these operations on the network interface.
+ */
+static bool can_send = false;
+static bool can_recv = false;
+
+
+/* IRQ handling structures dealing with reads and
+ * writes on the network card.
+ */
+static void network_recv_handler (irq_t *irq);
+static void network_send_handler (irq_t *irq);
+
+static irq_t recv_irq = {
+       .irq_handler = network_recv_handler,
+       .irq_next = NULL,
+       .irq_priority = CPU_PRIO_UNKNOWN,
+};
+static irq_t send_irq = {
+       .irq_handler = network_send_handler,
+       .irq_next = NULL,
+       .irq_priority = CPU_PRIO_UNKNOWN,
+};
+
+
+
+/* This upcall is used to report that the network
+ * has new data available.  The bottom half will
+ * call this for the first packet to arrive after
+ * having failed an attempt to read input.
+ * More calls are possible for later arrivals of
+ * even more packets, and will be silently ignored
+ * below.
+ */
+void top_network_can_recv (void) {
+       if (!can_recv) {
+               can_recv = true;
+               irq_fire (&recv_irq);
+       }
+}
+
+/* This upcall is used to report that the network
+ * is certain to accept a full-size network packet.
+ * It is called by the bottom half after having
+ * failed an attempt to send output.
+ * Additional calls on the same matter are silently
+ * ignored.
+ */
+void top_network_can_send (void) {
+       if (!can_send) {
+               can_send = true;
+               irq_fire (&send_irq);  // Polling, so safe
+       }
+}
+
+
+/* This upcall is made to report that the network
+ * has gone offline.  This may be detected at the
+ * hardware level, as a result of unplugging the
+ * cable and/or loosing sync.
+ * The state resulting from this is implied at the
+ * time of booting the device.
+ */
+void top_network_offline (void) {
+       can_send = false;
+       netcore_bootstrap_shutdown (); // TODO: wait 5 min, then call the user
+}
+
+
+/* This upcall is made to report that the network
+ * has come online.  This is also called when the
+ * network card has been initialised properly.
+ */
+void top_network_online (void) {
+       top_network_can_send (); // Polling, so safe
+       netcore_bootstrap_initiate ();
+}
+
+/* As the processor now has time for it, process
+ * the reception of network packets.
+ */
+static void network_recv_handler (irq_t *irq) {
+       bool go = can_recv;
+       can_recv = false;
+       // TODO: rescheduling would be better than looping
+       while (go) {
+               packet_t rbuf;
+               uint16_t rbuflen = sizeof (rbuf.data);
+               go = bottom_network_recv (rbuf.data, &rbuflen);
+               if (!go) {
+                       break;
+               }
+               printf ("Received a network packet\n");
+               intptr_t mem [MEM_NETVAR_COUNT];
+               bzero (mem, sizeof (mem));
+               retfn *rf = (retfn *) netinput (rbuf.data, rbuflen, mem);
+               if (rf != NULL) {
+                       packet_t wbuf; // TODO: Allocate
+                       uint8_t *stop = (*rf) (wbuf.data, mem);
+                       if (stop) {
+                               mem [MEM_ALL_DONE] = (intptr_t) stop;
+                               netcore_send_buffer (mem, wbuf.data);
+                       }
+               }
+       }
+       if (go) {
+               can_recv = true;  // TODO: Future IRQ trigger?
+       }
+}
+
+
+/* As the processor now has time for it, process
+ * the sending of network packets.
+ */
+static void network_send_handler (irq_t *irq) {
+       bool go = can_send;
+       can_send = false;
+       // TODO: Go over priority queues, bottom_network_send()
+}
diff --git a/src/kernel/resource.c b/src/kernel/resource.c
new file mode 100644 (file)
index 0000000..4a50db0
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Resource management.
+ */
+
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <0cpm/resource.h>
+
+
+/* Refresh a resource, meaning if it has any output aspects, ask it to
+ * refresh that output.  This is used if re-ordering resources may have
+ * caused another party to be assigned control over the resource.
+ *
+ * The refreshed state ought to be idempotent, meaning that it should
+ * not change anything if this routine is called more than once.
+ *
+ * TODO: Flashing LEDs might show clumsy timing if not done properly.
+ *       Is idempotence ever really used?
+ *
+ * Among others, this is done when a resource is claimed, so it is best
+ * to initialise state before claiming a resource.  When a resource is
+ * released, it (TODO:doc)
+ */
+static void res_refresh (resource_t *res) {
+       claim_t *clm = res->res_claims;
+       while (clm && !clm->clm_outputrefresh) {
+               clm = clm->clm_next;
+       }
+       if (clm) {
+               (*clm->clm_outputrefresh) (clm);
+       }
+}
+
+/* Notify a resource about an event.  This is used for input resources,
+ * for example upon a key being pressed or a packet arriving over the
+ * network link.
+ */
+void res_notify (resource_t *res, event_t *evt) {
+       claim_t *clm = res->res_claims;
+       bool todo = (clm != NULL);
+       while (todo) {
+               if (clm->clm_eventhandler) {
+                       todo = (*clm->clm_eventhandler) (clm, evt);
+               }
+       }
+}
+
+/* Claim a resource for the application named in it.
+ */
+void res_claim (resource_t *res, claim_t *clm) {
+       app_level_t lvl = clm->clm_app->app_level;
+       claim_t **clmptr = &res->res_claims;
+       bool refresh = 1;
+       while (*clmptr && ((*clmptr)->clm_app->app_level > lvl)) {
+               if ((*clmptr)->clm_outputrefresh) {
+                       refresh = 0;
+               }
+               clmptr = &(*clmptr)->clm_next;
+       }
+       clm->clm_next = *clmptr;
+       *clmptr = clm;
+       if (refresh) {
+               res_refresh (res);
+       }
+}
+
+/* Free a resource.
+ */
+void res_free (resource_t *res, claim_t *clm) {
+       claim_t **clmptr = &res->res_claims;
+       bool refresh = 1;
+       while (*clmptr && (*clmptr != clm)) {
+               if ((*clmptr)->clm_outputrefresh) {
+                       refresh = 0;
+               }
+               clmptr = &(*clmptr)->clm_next;
+       }
+       if (*clmptr == clm) {
+               *clmptr = clm->clm_next;
+               clm->clm_next = NULL;
+       }
+       if (refresh) {
+               res_refresh (res);
+       }
+}
+
diff --git a/src/kernel/timer.c b/src/kernel/timer.c
new file mode 100644 (file)
index 0000000..fd85ac4
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Timer drivers
+ */
+
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+
+
+/* The queue for waiting timer events.  There is no queue for ready
+ * timer events; those are sent to general IRQ queues.
+ */
+static irqtimer_t *irqtimer_wait_queue = NULL;
+
+#define tmr_next tmr_irq.irq_next
+
+
+static bool irqtimer_interrupt_blocked = true;
+static bool irqtimer_interrupt_occurred = false; // TODO: highest priority_t
+
+static bool irqtimer_fire_blocked = false;
+
+
+/* Process a timer interrupt.
+ */
+void top_timer_expiration (timing_t exptime) {
+       if (irqtimer_interrupt_blocked) {
+               printf ("Deferring timer interrupt\n");
+               irqtimer_interrupt_occurred = true;
+       } else {
+               while (irqtimer_wait_queue && (irqtimer_wait_queue->tmr_expiry < exptime)) {
+                       irqtimer_t *here = irqtimer_wait_queue;
+                       irqtimer_wait_queue = (irqtimer_t *) here->tmr_next;
+                       printf ("Firing IRQ for timer interrupt\n");
+                       irq_fire (&here->tmr_irq);
+               }
+       }
+}
+
+
+/* Enable or disable a timer interrupt.
+ */
+void irqtimer_enable (void) {
+#if 0
+       timing_t now = bottom_time ();
+       while (irqtimer_interrupt_occurred) {
+               irqtimer_interrupt_occurred = false;
+               top_timer_expiration (now);
+               irqtimer_interrupt_blocked = false;
+       }
+#endif
+       irqtimer_interrupt_blocked = false;
+       if (irqtimer_interrupt_occurred) {
+               printf ("Activating deferred timer interrupt\n");
+               timing_t now = bottom_time ();
+               top_timer_expiration (now);
+       }
+}
+inline void irqtimer_disable (void) {
+       irqtimer_interrupt_blocked = true;
+}
+
+
+/* Enqueue an initialised timer structure to the timer wait queue.
+ */
+static void irqtimer_enqueue (irqtimer_t *tmr) {
+       irqtimer_t **here;
+       irqtimer_disable ();
+       here = &irqtimer_wait_queue;
+       while ((*here) && ((*here)->tmr_expiry < tmr->tmr_expiry)) {
+               here = (irqtimer_t **) & (*here)->tmr_next;
+       }
+       tmr->tmr_next = (void *) *here;
+       (*here) = tmr;
+       if (irqtimer_wait_queue == tmr) {
+               bottom_timer_set ((*here)->tmr_expiry);
+       }
+       irqtimer_enable ();
+}
+
+/* Setup a new timer by enqueueing its structure in the timer list.
+ */
+void irqtimer_start (irqtimer_t *tmr, timing_t delay, irq_handler_t hdl, priority_t prio) {
+       timing_t new_expiry = bottom_time () + delay;
+       tmr->tmr_irq.irq_handler = hdl;
+       tmr->tmr_irq.irq_priority = prio;
+       tmr->tmr_expiry = new_expiry;
+       irqtimer_enqueue (tmr);
+}
+
+
+/* Continue a repeating timer by adding the interval delay to the
+ * last expiry time.  If more than the timer interval has passed
+ * since the last timer expiration, the timer will fire immediately.
+ */
+void irqtimer_restart (irqtimer_t *tmr, timing_t intval) {
+       tmr->tmr_expiry += intval;
+       irqtimer_enqueue (tmr);
+}
+
+
+/* Prematurely remove a timer from the timer queue.  If it is not
+ * there, ignore that fact silently.  Note that there may still
+ * be interrupts that were caused by the timer that is now being
+ * stopped.  That may actually revive an interval timer.  TODO...
+ */
+void irqtimer_stop (irqtimer_t *tmr) {
+       irqtimer_t **here;
+       irqtimer_disable ();
+       here = &irqtimer_wait_queue;
+       while ((*here)) {
+               if (*here == tmr) {
+                       *here = (irqtimer_t *) tmr->tmr_next;
+                       break;
+               }
+               here = (irqtimer_t **) & (*here)->tmr_next;
+       }
+       irqtimer_enable ();
+}
+
+
+/* TODO: Initialise this module by registering the top-half timer interrupt handler.
+ */
index df3a20b..b772eef 100644 (file)
@@ -1,4 +1,4 @@
 src/net/6bed4.c:
        printf > $@ '#include <stdint.h>\nuint32_t ip4_6bed4 = ( %d << 24 | %d << 16 | %d << 8 | %d );\n' `echo $(CONFIG_NET_6BED4_SERVER) | sed 's/\./ /g'`
 
-objs-y += src/net/core.o src/net/input.o src/net/reply.o src/net/send.o src/net/db.o src/net/6bed4.o
+objs-top-net-y += src/net/core.o src/net/input.o src/net/reply.o src/net/send.o src/net/db.o src/net/6bed4.o
index 100d5e0..3980338 100644 (file)
@@ -6,8 +6,9 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
 #include <string.h>
 #include <fcntl.h>
 
 #include <netinet/icmp6.h>
 #include <net/ethernet.h>
 
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_ether.h>
+#include <config.h>
 
+#include <0cpm/cpu.h>
 #include <0cpm/netfun.h>
 #include <0cpm/netdb.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+
+
+/* Network configuration states */
+
+enum netcfgstate {
+       NCS_OFFLINE,    // Await top_network_online()
+       NCS_AUTOCONF,   // A few LAN autoconfiguration attempts
+       NCS_DHCPV6,     // A few LAN DHCPv6 attempts
+       NCS_DHCPV4,     // A few LAN DHCPv4 attempts
+       NCS_6BED4,      // A few 6BED4 attempts over IPv4
+       NCS_ONLINE,     // Stable online performance
+       NCS_RENEW,      // Stable online, but renewing DHCPv6/v4
+       NCS_PANIC,      // Not able to bootstrap anything
+       NCS_HAVE_ALT = NCS_6BED4,
+};
 
 
 /* Global variables
  */
 
-extern struct tunbuf rbuf, wbuf;       // TODO: Testing setup
-extern int tunsox;                     // TODO: Testing setup
+static enum netcfgstate boot_state   = NCS_OFFLINE;
+static uint8_t          boot_retries = 1;
+
+static irqtimer_t netboottimer;
+
+extern packet_t rbuf, wbuf;    // TODO: Testing setup
 
 extern uint8_t ether_mine [ETHER_ADDR_LEN];
 
+
 /* netcore_checksum_areas (void *area0, uint16_t len0, ..., NULL)
  * Calculate the Internet checksum over the given areas
  */
@@ -69,7 +91,7 @@ uint16_t netcore_checksum_areas (void *area0, ...) {
  * This calculates length and checksum fields, then sends the
  * message into the world.
  */
-int netcore_send_buffer (int tunsox, uint32_t *mem, struct tunbuf *wbuf) {
+void netcore_send_buffer (intptr_t *mem, uint8_t *wbuf) {
        struct icmp6_hdr *icmp6 = (void *) mem [MEM_ICMP6_HEAD];
        struct icmp      *icmp4 = (void *) mem [MEM_ICMP4_HEAD];
        struct udphdr *udp4 = (void *) mem [MEM_UDP4_HEAD];
@@ -147,32 +169,21 @@ int netcore_send_buffer (int tunsox, uint32_t *mem, struct tunbuf *wbuf) {
        //
        // Determine the tunnel prefix info and ethernet protocol
        if (ip4) {
-               wbuf->prefix.proto = htons (ETH_P_IP);
+               eth->h_proto = htons (ETH_P_IP);
        } else if (ip6) {
-               wbuf->prefix.proto = htons (ETH_P_IPV6);
+               eth->h_proto = htons (ETH_P_IPV6);
        } else if (arp) {
-               wbuf->prefix.proto = htons (ETH_P_ARP);
+               eth->h_proto = htons (ETH_P_ARP);
        } else {
-               return -1;
+               return;
        }
-       eth->h_proto = wbuf->prefix.proto;
-       wbuf->prefix.flags = 0;
        //
-       // Actually send the packet
-       int alen = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD] + sizeof (struct tun_pi);
-       if ((alen > 0) && (alen < 1500+12)) {
-               int wlen = write (tunsox, wbuf, alen);
-printf ("Written %d out of %d:\n", wlen, alen); int i; for (i=0; i<alen; i++) { printf ("%02x%s", ((uint8_t *) wbuf) [i], (i%16 == 0? "\n": " ")); }; printf ("\n");
-               if (wlen != alen) {
-                       if (wlen < 0) {
-                               return -1;
-                       } else {
-                               // No guaranteed delivery -- swallow problem
-                               return 0;
-                       }
-               }
+       // Actually have the packet sent
+       // TODO: Defer if priority lower than current CPU priority level
+       uint16_t wlen = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
+       if (!bottom_network_send (wbuf, wlen)) {
+               // TODO: Queue packet for future delivery
        }
-       return 0;
 }
 
 
@@ -180,14 +191,14 @@ printf ("Written %d out of %d:\n", wlen, alen); int i; for (i=0; i<alen; i++) {
  */
 static void solicit_router (void) {
        uint8_t *start = wbuf.data;
-       uint32_t mem [MEM_NETVAR_COUNT];
+       intptr_t mem [MEM_NETVAR_COUNT];
        bzero (mem, sizeof (mem));
-       mem [MEM_BINDING6] = (uint32_t) &ip6binding [0];
+       mem [MEM_BINDING6] = (intptr_t) &ip6binding [0];
        uint8_t *stop = netsend_icmp6_router_solicit (start, mem);
-       mem [MEM_ALL_DONE] = (uint32_t) stop;
+       mem [MEM_ALL_DONE] = (intptr_t) stop;
        uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
        if ((len > 0) && (len < 1500 + 18)) {
-               netcore_send_buffer (tunsox, mem, &wbuf);
+               netcore_send_buffer (mem, wbuf.data);
        }
 }
 
@@ -196,90 +207,145 @@ static void solicit_router (void) {
  */
 static void get_dhcp4_lease (void) {
        uint8_t *start = wbuf.data;
-       uint32_t mem [MEM_NETVAR_COUNT];
+       intptr_t mem [MEM_NETVAR_COUNT];
        bzero (mem, sizeof (mem));
        uint8_t *stop = netsend_dhcp4_discover (start, mem);
-       mem [MEM_ALL_DONE] = (uint32_t) stop;
+       mem [MEM_ALL_DONE] = (intptr_t) stop;
        uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
        if ((len > 0) && (len < 1500 + 18)) {
-               netcore_send_buffer (tunsox, mem, &wbuf);
+               netcore_send_buffer (mem, wbuf.data);
        }
 }
 
+
 /* Start to obtain an IPv6 address.
  */
 static void get_dhcp6_lease (void) {
        uint8_t *start = wbuf.data;
-       uint32_t mem [MEM_NETVAR_COUNT];
+       intptr_t mem [MEM_NETVAR_COUNT];
        bzero (mem, sizeof (mem));
        uint8_t *stop = netsend_dhcp6_solicit (start, mem);
-       mem [MEM_ALL_DONE] = (uint32_t) stop;
+       mem [MEM_ALL_DONE] = (intptr_t) stop;
        uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
        if ((len > 0) && (len < 1500 + 18)) {
-               netcore_send_buffer (tunsox, mem, &wbuf);
+               netcore_send_buffer (mem, wbuf.data);
+       }
+}
+
+
+/* Is an IPv6 address available?
+ */
+static bool have_ipv6 (void) {
+       int i;
+       for (i=0; i < IP6BINDING_COUNT; i++) {
+               if (ip6binding [i].flags & (I6B_TENTATIVE | I6B_EXPIRED) == 0) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/* Is an IPv4 address available?
+ */
+static bool have_ipv4 (void) {
+       int i;
+       for (i=0; i < IP4BINDING_COUNT; i++) {
+               if (ip4binding [i].ip4addr != 0) {
+                       return true;
+               }
        }
+       return false;
 }
 
 
 /* Boot the network: Obtain an IPv6 address, possibly based on an IPv4
- * and a 6bed4 tunnel.  The routine returns success as nonzero.
+ * and a 6bed4 tunnel.  This is implemented as a state diagram made
+ * with closures.
  *
  * The booting procedure works through the following 1-second steps:
  *  - ensure that the interface is connected to a network
- *  - try IPv6 autoconfiguration a few times (random number)
- *     - first try to reuse an older IPv6 address, even if it was DHCPv6
+ *  - try IPv6 autoconfiguration a few times
+ *     - first try to reuse an older IPv6 address, even if it was DHCPv6 (TODO)
  *  - try DHCPv6 a few times
- *     - first try to reuse an older IPv6 address, even if it was autoconfig
+ *     - first try to reuse an older IPv6 address, even if it was autoconfig (TODO)
  *  - try DHCPv4 a few times
- *     - first try to reuse an older IPv4 address
- *     - when an IPv4 address is acquired, autoconfigure 6bed4 over it
+ *     - first try to reuse an older IPv4 address (TODO)
+ *     - when an IPv4 address was acquired, autoconfigure 6bed4 over it
  */
-int netcore_bootstrap (void) {
-       int process_packets (int sox, int secs); //TODO:NOTHERE
-       int i;
-       int rnd1 = 2 + ((ether_mine [5] & 0x1c) >> 2);
-       int rnd2 =       ether_mine [5] & 0x03;
-       //
-       // TODO: better wipe bindings at physical boot
-       bzero (ip4binding, sizeof (ip4binding));
-       bzero (ip6binding, sizeof (ip6binding));
-       //
-       // TODO: ensure physical network connectivity
-       //
-       // Obtain an IPv6 address through stateless autoconfiguration
-       i = rnd1;
-       while (i-- > 0) {
-               solicit_router ();
-               process_packets (tunsox, 2);
-               // TODO: return 1 if bound
+bool netcore_bootstrap_step (irq_t *tmrirq) {
+       printf ("Taking a step in network bootstrapping\n");
+       timing_t delay = 0;
+       if (boot_retries > 0) {
+               boot_retries--;
+       } else {
+               boot_retries = 3;
+               if (boot_state <= NCS_HAVE_ALT) {
+                       boot_state++;
+               } else {
+                       boot_state = NCS_PANIC;
+               }
        }
-       //
-       // Obtain an IPv6 address through DHCPv6
-       i = 3;
-       while (i-- > 0) {
+       switch (boot_state) {
+       case NCS_6BED4:         // A few 6BED4 attempts over IPv4
+               if (!have_ipv6 ()) {
+                       ip6binding [0].ip4binding = &ip4binding [0];
+                       ip6binding [0].flags |= I6B_ROUTE_SOURCE_6BED4_FLAG;
+               }
+               // Continue into NCS_AUTOCONF for Router Solicitation
+       case NCS_AUTOCONF:      // A few LAN autoconfiguration attempts
+               if (have_ipv6 ()) {
+                       return true;
+               }
+               solicit_router ();
+               delay = 500 * TIME_MSEC;
+               break;
+       case NCS_DHCPV6:        // A few LAN DHCPv6 attempts
+               if (have_ipv6 ()) {
+                       return true;
+               }
                get_dhcp6_lease ();
-               process_packets (tunsox, 3);
-               // TODO: return 1 if bound
-       }
-       //
-       // Obtain an IPv4 address through DHCPv4
-       i = 3;
-       while (i-- > 0) {
+               delay = 1000 * TIME_MSEC;
+               break;
+       case NCS_DHCPV4:        // A few LAN DHCPv4 attempts
+               if (have_ipv4 ()) {
+                       return true;
+               }
                get_dhcp4_lease ();
-               process_packets (tunsox, rnd2);
-               // TODO: break if bound
+               delay = 5000 * TIME_MSEC;
+               break;
+       case NCS_ONLINE:        // Stable online performance
+               return true;
+       case NCS_RENEW:         // Stable online, but renewing DHCPv6/v4
+               // TODO: Inspect timers or resend renewal request
+               return true;
+       case NCS_OFFLINE:       // Await top_network_online()
+               break;
+       case NCS_PANIC:         // Not able to bootstrap anything
+               // delay = 15 * 60 * TIME_MSEC;
+               // break;
+               return true;
+       default:
+               break;
        }
-       // TODO: fail (return 0) if no IPv4 connection
-       //
-       // Obtain an IPv6 address through 6bed4 over IPv4
-       i = rnd1;
-       while (i-- > 0) {
-               ip6binding [0].ip4binding = &ip4binding [0];
-               ip6binding [0].flags |= I6B_ROUTE_SOURCE_6BED4_FLAG;
-               solicit_router ();      //TODO: Over 6bed4
-               sleep (1);
-               // TODO:return 1 if bound
-       }
-       return 0;
+       irqtimer_restart ((irqtimer_t *) tmrirq, delay);
+       return true;
+}
+
+/* Initiate the bootstrapping process, returning its closure
+ */
+void netcore_bootstrap_initiate (void) {
+       bzero (&netboottimer, sizeof (netboottimer));
+       boot_retries = 3 + (ether_mine [5] & 0x03);     // 3..7
+       boot_state = NCS_AUTOCONF;
+       printf ("Initiating network bootstrapping procedures\n");
+       irqtimer_start (&netboottimer, 0, netcore_bootstrap_step, CPU_PRIO_LOW);
+}
+
+/* Shutdown the bootstrapping process, if any
+ */
+void netcore_bootstrap_shutdown (void) {
+       boot_retries = 1;
+       boot_state   = NCS_OFFLINE;
+       irqtimer_stop (&netboottimer);
 }
 
index 6e2c491..3190ca4 100644 (file)
@@ -5,13 +5,18 @@
 
 
 #include <stdint.h>
+#include <stdbool.h>
+
 #include <time.h>
 
+#include <netinet/icmp6.h>
+
+#include <config.h>
+
+#include <0cpm/cpu.h>
 #include <0cpm/netfun.h>
 #include <0cpm/netdb.h>
 
-#include <netinet/icmp6.h>
-
 
 /* A few useful global variables
  */
@@ -55,7 +60,7 @@ void util_mac2ifid (uint8_t *ip6address, const uint8_t *mac) {
  */
 // TODO: Process retractions (life set to 0)
 // TODO: Generate ND packet for DAD and start timeout
-uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
+uint8_t *netdb_router_advertised (uint8_t *pout, intptr_t *mem) {
        printf ("Received & Processing: Router Advertisement\n");
        struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) mem [MEM_ICMP6_HEAD];
        uint32_t routerlife = (uint32_t) icmp6->icmp6_data16 [1];
@@ -65,11 +70,11 @@ uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
        uint8_t *ip6prefix = NULL;
        int ip6prefixlen = -1;
        int best_pref = -1, cur_pref = -1;
-       int ok = 1;
-       int needDHCP = 0;
-       while (ok && (mem [MEM_ALL_DONE] > (uint32_t) options)) {
-               if (((uint32_t) options) + 8 * options [1] > mem [MEM_ALL_DONE]) {
-                       ok = 0;
+       bool ok = true;
+       bool needDHCP = true;
+       while (ok && (mem [MEM_ALL_DONE] > (intptr_t) options)) {
+               if (((intptr_t) options) + 8 * options [1] > mem [MEM_ALL_DONE]) {
+                       ok = false;
                        break;
                }
                switch (options [0] + 256 * options [1]) {
@@ -79,7 +84,7 @@ uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
                case ND_OPT_MTU + 256:
                        if (ntohl (*(uint32_t *)(options + 4)) < 1280) {
                                // MTU under 1280 is illegal, higher ignored
-                               ok = 0;
+                               ok = false;
                                break;
                        }
                        break;
@@ -88,14 +93,14 @@ uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
                        // best one continues.
                        // Autoconfiguration: 8 + 2 * prefix_pref_if_any
                        // DHCPv6: 0
-                       if (options [3] & 0x40) {
-                               cur_pref = 8;
-                               needDHCP = 0;
-                       } else {
+                       if (options [3] & 0x80) {
+                               // we prefer autoconfiguration, so DHCPv6 ranks low
                                cur_pref = 0;
-                               if (best_pref == -1) {
-                                       needDHCP = 1;
-                               }
+                       } else {
+                               // preferences according to RFC 4191
+                               static int preferencemap [4] = { 8, 10, /*reserved:*/ 8, 6 };
+                               cur_pref = preferencemap [(options [3] >> 3) & 0x03];
+                               needDHCP = false;
                        }
                        // Accept only /64 or /112 prefixes, prefer /64
                        if (options [2] == 64) {
@@ -114,12 +119,12 @@ uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
                        best_pref = cur_pref;
                        break;
                default:
-                       ok = 0;
+                       ok = false;
                        break;
                }
                options = options + 8 * options [1];
        }
-       ok = ok && (mem [MEM_ALL_DONE] == (uint32_t) options);
+       ok = ok && (mem [MEM_ALL_DONE] == (intptr_t) options);
        ok = ok && (best_pref >= 0);
        if (!ok) {
                return NULL;
@@ -174,16 +179,12 @@ uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
        }
        ip6binding [bindidx].ip6timeout = 0xffffffff;           //TODO
        if (bindflags & I6B_ROUTE_SOURCE_6BED4_FLAG) {
-               //TODO:OLD// ip6binding [bindidx].ip4timeout = 0xffffffff;      //TODO
-               //TODO:OLD// ip6binding [bindidx].ip4uplink = ip4gateway;
-               //TODO:OLD// ip6binding [bindidx].ip4addr = htonl (mem [MEM_IP4_DST]);
-               //TODO:OLD// ip6binding [bindidx].ip4port = htons (mem [MEM_UDP4_PORTS] & 0xffff);
                ip6binding [bindidx].ip4binding = &ip4binding[0]; //TODO:mk_dyn
        }
        ip6binding [bindidx].flags = bindflags;
        // Finally, if needed, send a packet for duplicate address detection
        if (ip6binding [bindidx].flags & I6B_TENTATIVE) {
-               mem [MEM_BINDING6] = (uint32_t) &ip6binding [bindidx];
+               mem [MEM_BINDING6] = (intptr_t) &ip6binding [bindidx];
                return netsend_icmp6_ngb_sol (pout, mem);
        } else {
                return NULL;
@@ -192,7 +193,7 @@ uint8_t *netdb_router_advertised (uint8_t *pout, uint32_t *mem) {
 
 /* A neighbour advertisement was received.  Store it for future reference.
  */
-uint8_t *netdb_neighbour_advertised (uint8_t *pout, uint32_t *mem) {
+uint8_t *netdb_neighbour_advertised (uint8_t *pout, intptr_t *mem) {
        printf ("Received: Neighbour Advertisement\n");
        //TODO -- embodyment of this function//
        return NULL;
@@ -200,7 +201,7 @@ uint8_t *netdb_neighbour_advertised (uint8_t *pout, uint32_t *mem) {
 
 /* DHCPv4 sends an acknowledgement.  Create a ip6binding for that.
  */
-uint8_t *netdb_dhcp4_ack (uint8_t *pout, uint32_t *mem) {
+uint8_t *netdb_dhcp4_ack (uint8_t *pout, intptr_t *mem) {
        printf ("Received: DHCP4 ACK\n");
        uint8_t *opt = (uint8_t *) (mem [MEM_DHCP4_HEAD] + 240);
        static const uint8_t cookie [4] = { 99, 130, 83, 99 };
@@ -250,7 +251,7 @@ uint8_t *netdb_dhcp4_ack (uint8_t *pout, uint32_t *mem) {
 
 /* DHCPv4 sends a negative acknowledgement.  Whatever :)
  */
-uint8_t *netdb_dhcp4_nak (uint8_t *pout, uint32_t *mem) {
+uint8_t *netdb_dhcp4_nak (uint8_t *pout, intptr_t *mem) {
        printf ("Received: DHCP4 NAK\n");
        // Drop this, it is non-information to us
        return NULL;
@@ -258,7 +259,7 @@ uint8_t *netdb_dhcp4_nak (uint8_t *pout, uint32_t *mem) {
 
 /* DHCPv6 sends a reply to confirm allocation of an IPv6 address
  */
-uint8_t *netdb_dhcp6_reply (uint8_t *pout, uint32_t *mem) {
+uint8_t *netdb_dhcp6_reply (uint8_t *pout, intptr_t *mem) {
        // TODO: Validate offer to be mine + store configuration data
        printf ("Received: DHCP6 REPLY\n");
        return NULL;
@@ -266,9 +267,17 @@ uint8_t *netdb_dhcp6_reply (uint8_t *pout, uint32_t *mem) {
 
 /* DHCPv6 wants to reconfigure our settings
  */
-uint8_t *netdb_dhcp6_reconfigure (uint8_t *pout, uint32_t *mem) {
+uint8_t *netdb_dhcp6_reconfigure (uint8_t *pout, intptr_t *mem) {
        // TODO: Validate offer to be mine + update configuration data
        printf ("Received: DHCP6 RECONFIGURE\n");
        return NULL;
 }
 
+
+/* Initialise the networking database after a restart
+ */
+void netdb_initialise (void) {
+       bzero (ip6binding, sizeof (ip6binding));
+       bzero (ip4binding, sizeof (ip4binding));
+}
+
index 1ef6e4c..88a29cc 100644 (file)
 
 
 #include <stdint.h>
+#include <stdbool.h>
 
+#include <netinet/icmp6.h>
+
+#include <config.h>
+
+#include <0cpm/cpu.h>
 #include <0cpm/netcmd.h>
 #include <0cpm/netfun.h>
 
-#include <netinet/icmp6.h>
-
 
 
 
@@ -70,7 +74,7 @@
 
 /* Declare an efficient network packet analysis function:
  *
- * uint32_t netinput (uint8_t *pkt, uint32_t pktlen, uint32_t *mem) {
+ * intptr_t netinput (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) {
  *     ...possibly assembly...
  * }
  *
@@ -79,6 +83,8 @@
  */
 BPF_BEGIN(netinput)
 
+       return NULL;            //TODO// Early bail-out
+
        STX_MEM (MEM_ETHER_HEAD)                // Store the ethernet header
        LDW_LEN ()
        ADD_REX ()
index 3296c31..3db4bcb 100644 (file)
@@ -5,6 +5,7 @@
 
 
 #include <stdint.h>
+#include <stdbool.h>
 
 #include <netinet/ip.h>
 #include <netinet/udp.h>
@@ -13,6 +14,9 @@
 #include <netinet/icmp6.h>
 #include <netinet/if_ether.h>
 
+#include <config.h>
+
+#include <0cpm/cpu.h>
 #include <0cpm/netfun.h>
 #include <0cpm/netdb.h>
 
@@ -39,7 +43,7 @@ uint16_t bootsecs = 0;
 /* Create an Ethernet header.
  * Some fields are filled later: h_proto
  */
-uint8_t *netreply_ether (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_ether (uint8_t *pout, intptr_t *mem) {
        struct ethhdr *ethin  = (struct ethhdr *) mem [MEM_ETHER_HEAD];
        struct ethhdr *ethout = (struct ethhdr *) pout;
        if (mem [MEM_ETHER_DST] != 0) {
@@ -50,14 +54,14 @@ uint8_t *netreply_ether (uint8_t *pout, uint32_t *mem) {
                memset (ethout->h_dest, 0xff, ETHER_ADDR_LEN);  // Broadcast
        }
        memcpy (ethout->h_source, ether_mine, ETHER_ADDR_LEN);
-       mem [MEM_ETHER_HEAD] = (uint32_t) ethout;
+       mem [MEM_ETHER_HEAD] = (intptr_t) ethout;
        return &pout [sizeof (struct ethhdr)];
 }
 
 /* Create an IPv4 header.
  * Some IPv4 fields are filled later: protocol, tot_len, check.
  */
-uint8_t *netreply_ip4 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_ip4 (uint8_t *pout, intptr_t *mem) {
        pout = netreply_ether (pout, mem);
        struct iphdr *ip4in  = (struct iphdr *) mem [MEM_IP4_HEAD];
        struct iphdr *ip4out = (struct iphdr *) pout;
@@ -75,33 +79,33 @@ uint8_t *netreply_ip4 (uint8_t *pout, uint32_t *mem) {
 /* Create an UDPv4 header.
  * Some fields are filled later: len, check.
  */
-uint8_t *netreply_udp4 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_udp4 (uint8_t *pout, intptr_t *mem) {
        pout = netreply_ip4 (pout, mem);
        struct udphdr *udp = (struct udphdr *) pout;
        udp->source = htons (mem [MEM_UDP4_PORTS] & 0xffff);
        udp->dest   = htons (mem [MEM_UDP4_PORTS] >> 16);
-       mem [MEM_UDP4_HEAD] = (uint32_t) udp;
+       mem [MEM_UDP4_HEAD] = (intptr_t) udp;
        return &pout [sizeof (struct udphdr)];
 }
 
 /* Create an UDPv4 header for 6bed4.
  * Some fields are filled later: len, check.
  */
-uint8_t *netreply_udp4_6bed4 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_udp4_6bed4 (uint8_t *pout, intptr_t *mem) {
        mem [MEM_IP4_SRC] = htonl (ip4_6bed4);
        pout = netreply_ip4 (pout, mem);
        struct udphdr *udp = (struct udphdr *) pout;
        udp->source = htons (mem [MEM_UDP4_PORTS] & 0xffff);
        udp->dest   = htons (3653);
        mem [MEM_6BED4_PLOAD] =
-       mem [MEM_UDP4_HEAD] = (uint32_t) udp;
+       mem [MEM_UDP4_HEAD] = (intptr_t) udp;
        return &pout [sizeof (struct udphdr)];
 }
 
 /* Create an IPv6 header.
  * Some fields are filled later: plen, nxt.
  */
-uint8_t *netreply_ip6 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_ip6 (uint8_t *pout, intptr_t *mem) {
        struct ip6_hdr *in6 = (struct ip6_hdr *) mem [MEM_IP6_HEAD];
        if (mem [MEM_6BED4_PLOAD] != 0) {
                // Use 6bed4 for destination address
@@ -115,19 +119,19 @@ uint8_t *netreply_ip6 (uint8_t *pout, uint32_t *mem) {
        ip6->ip6_hlim = 64;
        memcpy (&ip6->ip6_src, &in6->ip6_dst, 16);
        memcpy (&ip6->ip6_dst, &in6->ip6_src, 16);
-       mem [MEM_IP6_HEAD] = (uint32_t) ip6;
+       mem [MEM_IP6_HEAD] = (intptr_t) ip6;
        return &pout [sizeof (struct ip6_hdr)];
 }
 
 /* Create an UDPv6 header.
  * Some fields are filled later: len, check.
  */
-uint8_t *netreply_udp6 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_udp6 (uint8_t *pout, intptr_t *mem) {
        pout = netreply_ip6 (pout, mem);
        struct udphdr *udp = (struct udphdr *) pout;
        udp->source = htons (mem [MEM_UDP6_PORTS] & 0xffff);
        udp->dest   = htons (mem [MEM_UDP6_PORTS] >> 16);
-       mem [MEM_UDP6_HEAD] = (uint32_t) udp;
+       mem [MEM_UDP6_HEAD] = (intptr_t) udp;
        return &pout [sizeof (struct udphdr)];
 }
 
@@ -138,7 +142,7 @@ uint8_t *netreply_udp6 (uint8_t *pout, uint32_t *mem) {
 /* Create an ARP Reply packet to respond to an ARP Query
  * There are no checksum or length fields in ARP.
  */
-uint8_t *netreply_arp_query (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_arp_query (uint8_t *pout, intptr_t *mem) {
        printf ("Received an ARP Request; replying\n");
        pout = netreply_ether (pout, mem);
        struct ether_arp *arpout  = (struct ether_arp *) pout;
@@ -163,7 +167,7 @@ uint8_t *netreply_arp_query (uint8_t *pout, uint32_t *mem) {
 /* Create an ICMPv4 Echo Reply packet to respond to Echo Request
  * Some fields are filled later: icmp_cksum.
  */
-uint8_t *netreply_icmp4_echo_req (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_icmp4_echo_req (uint8_t *pout, intptr_t *mem) {
        printf ("Received an ICMPv4 Echo Request; replying\n");
        pout = netreply_ip4 (pout, mem);
        struct icmphdr *icmp4out = (struct icmphdr *) pout;
@@ -178,14 +182,14 @@ uint8_t *netreply_icmp4_echo_req (uint8_t *pout, uint32_t *mem) {
                memcpy (&icmp4out [1], &icmp4in [1], alen);
                pout += alen;
        }
-       mem [MEM_ICMP4_HEAD] = (uint32_t) icmp4out;
+       mem [MEM_ICMP4_HEAD] = (intptr_t) icmp4out;
        return pout;
 }
 
 /* Create an ICMPv6 Echo Reply packet to respond to Echo Request
  * Some fields are filled later: icmp6_cksum.
  */
-uint8_t *netreply_icmp6_echo_req (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_icmp6_echo_req (uint8_t *pout, intptr_t *mem) {
        printf ("Received an ICMPv6 Echo Request; replying\n");
        pout = netreply_ip6 (pout, mem);
        struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) pout;
@@ -195,7 +199,7 @@ uint8_t *netreply_icmp6_echo_req (uint8_t *pout, uint32_t *mem) {
        memcpy (&icmp6->icmp6_data8,
                        (void *) (mem [MEM_ICMP6_HEAD] + 4),
                        len - 4);
-       mem [MEM_ICMP6_HEAD] = (uint32_t) icmp6;
+       mem [MEM_ICMP6_HEAD] = (intptr_t) icmp6;
        return pout + len;
 }
 
@@ -205,7 +209,7 @@ uint8_t *netreply_icmp6_echo_req (uint8_t *pout, uint32_t *mem) {
  *
  * Some fields are filled later: icmp6_cksum.
  */
-uint8_t *netreply_icmp6_ngb_disc (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_icmp6_ngb_disc (uint8_t *pout, intptr_t *mem) {
        printf ("Received an ICMPv6 Neighbour Discovery; replying\n");
        int bndidx = IP6BINDING_COUNT;
        uint8_t *addr = linklocal_mine;
@@ -234,7 +238,7 @@ uint8_t *netreply_icmp6_ngb_disc (uint8_t *pout, uint32_t *mem) {
        icmp6->icmp6_data8 [20] = ND_OPT_TARGET_LINKADDR;
        icmp6->icmp6_data8 [21] = 1;    // 1x 8 bytes
        memcpy (icmp6->icmp6_data8 + 22, ether_mine, ETHER_ADDR_LEN);
-       mem [MEM_ICMP6_HEAD] = (uint32_t) icmp6;
+       mem [MEM_ICMP6_HEAD] = (intptr_t) icmp6;
        return pout + 8 + 16 + 8;
 }
 
@@ -242,11 +246,11 @@ uint8_t *netreply_icmp6_ngb_disc (uint8_t *pout, uint32_t *mem) {
  * offered in the yiaddr field (offset 16) of the DHCP packet, but
  * that will also be repeated in the future DHCP ACK.
  */
-uint8_t *netreply_dhcp4_offer (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_dhcp4_offer (uint8_t *pout, intptr_t *mem) {
        uint8_t *yiaddrptr = (uint8_t *) (mem [MEM_DHCP4_HEAD] + 16);
        printf ("DHCPv4 offer for %d.%d.%d.%d received -- requesting its activation\n", (int) yiaddrptr [0], (int) yiaddrptr [1], (int) yiaddrptr [2], (int) yiaddrptr [3]);
        // TODO: Validate offer to be mine
-       mem [MEM_ETHER_DST] = (uint32_t) ether_broadcast;
+       mem [MEM_ETHER_DST] = (intptr_t) ether_broadcast;
        pout = netreply_udp4 (pout, mem);
        ((struct iphdr *) mem [MEM_IP4_HEAD])->daddr = 0xffffffff;
        bzero (pout, 576);      // erase the acceptable package size
@@ -287,7 +291,7 @@ uint8_t *netreply_dhcp4_offer (uint8_t *pout, uint32_t *mem) {
  * be reachable over as many IPv6 addresses as are available, so it
  * will actually take hold of all the IPv6 space that it can get.
  */
-uint8_t *netreply_dhcp6_advertise (uint8_t *pout, uint32_t *mem) {
+uint8_t *netreply_dhcp6_advertise (uint8_t *pout, intptr_t *mem) {
        printf ("DHCPv6 advertisement\n");
        // TODO: Validate offer to be mine
        uint32_t adlen = mem [MEM_ALL_DONE] - mem [MEM_DHCP6_HEAD];
@@ -299,7 +303,7 @@ uint8_t *netreply_dhcp6_advertise (uint8_t *pout, uint32_t *mem) {
        udp->dest = htons (547);
        memcpy (pout, (void *) mem [MEM_DHCP6_HEAD], adlen);
        pout [0] = 3;
-       mem [MEM_DHCP6_HEAD] = (uint32_t) pout;
+       mem [MEM_DHCP6_HEAD] = (intptr_t) pout;
        return pout + adlen;
 }
 
index c554ccb..465375e 100644 (file)
@@ -8,8 +8,10 @@
  */
 
 
-#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
 
+#include <string.h>
 
 #include <netinet/ip.h>
 #include <netinet/udp.h>
@@ -17,6 +19,9 @@
 #include <netinet/icmp6.h>
 #include <netinet/if_ether.h>
 
+#include <config.h>
+
+#include <0cpm/cpu.h>
 #include <0cpm/netfun.h>
 #include <0cpm/netdb.h>
 
@@ -88,14 +93,14 @@ uint8_t ipv6_router_solicitation [] = {
  * Some fields are filled later: h_proto
  * TODO: When to use the broadcast address?
  */
-uint8_t *netsend_ether (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_ether (uint8_t *pout, intptr_t *mem) {
        struct ethhdr *eth = (struct ethhdr *) pout;
        memcpy (eth->h_source, ether_mine, ETHER_ADDR_LEN);
        memcpy (eth->h_dest, (void *) mem [MEM_ETHER_DST], ETHER_ADDR_LEN);
        if (mem [MEM_VLAN_ID] != 0) {
                // TODO: Setup VLAN with mem [MEM_VLAN] number
        }
-       mem [MEM_ETHER_HEAD] = (uint32_t) eth;
+       mem [MEM_ETHER_HEAD] = (intptr_t) eth;
        return pout + sizeof (struct ethhdr);
 }
 
@@ -103,7 +108,7 @@ uint8_t *netsend_ether (uint8_t *pout, uint32_t *mem) {
  * This may be used for local protocols (DNS, DHCPv4) or as a 6bed4 carrier.
  * Some IPv4 fields are filled later: protocol, tot_len, check.
  */
-uint8_t *netsend_ip4 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_ip4 (uint8_t *pout, intptr_t *mem) {
        pout = netsend_ether (pout, mem);
        struct iphdr *ip4 = (struct iphdr *) pout;
        bzero (ip4, sizeof (struct iphdr));
@@ -120,20 +125,20 @@ uint8_t *netsend_ip4 (uint8_t *pout, uint32_t *mem) {
 /* Send an UDPv4 header.
  * Some fields are filled later: len, check.
  */
-uint8_t *netsend_udp4 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_udp4 (uint8_t *pout, intptr_t *mem) {
        // TODO: Setup MEM_ETHER_DST with router's address
        pout = netsend_ip4 (pout, mem);
        struct udphdr *udp = (struct udphdr *) pout;
        udp->source = htons (mem [MEM_UDP4_PORTS] & 0xffff);
        udp->dest   = htons (mem [MEM_UDP4_PORTS] >> 16);
-       mem [MEM_UDP4_HEAD] = (uint32_t) udp;
+       mem [MEM_UDP4_HEAD] = (intptr_t) udp;
        return &pout [sizeof (struct udphdr)];
 }
 
 /* Send an UDPv4 header for 6bed4.
  * Some fields are filled later: len, check.
  */
-uint8_t *netsend_udp4_6bed4 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_udp4_6bed4 (uint8_t *pout, intptr_t *mem) {
        struct ip6binding *bnd = (struct ip6binding *) mem [MEM_BINDING6];
        mem [MEM_IP4_SRC] = bnd->ip4binding->ip4addr;
        mem [MEM_IP4_DST] = ip4_6bed4;
@@ -143,15 +148,15 @@ uint8_t *netsend_udp4_6bed4 (uint8_t *pout, uint32_t *mem) {
        //TODO:dyn.ports?// udp->source = htons (bnd->ip4binding->ip4port);
        udp->source = htons (3653);
        udp->dest   = htons (3653);
-       mem [MEM_UDP4_HEAD] = (uint32_t) udp;
-       mem [MEM_6BED4_PLOAD] = (uint32_t) (pout + sizeof (struct udphdr));
+       mem [MEM_UDP4_HEAD] = (intptr_t) udp;
+       mem [MEM_6BED4_PLOAD] = (intptr_t) (pout + sizeof (struct udphdr));
        return (uint8_t *) mem [MEM_6BED4_PLOAD];
 }
 
 /* Send an IPv6 header, and wrap it in 6bed4 if needed.
  * Some fields are filled later: plen, nxt.
  */
-uint8_t *netsend_ip6 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_ip6 (uint8_t *pout, intptr_t *mem) {
        struct ip6binding *bnd = (struct ip6binding *) mem [MEM_BINDING6];
        if (bnd->flags & I6B_ROUTE_SOURCE_6BED4_FLAG) {
                // Use 6bed4 for destination address
@@ -166,20 +171,20 @@ uint8_t *netsend_ip6 (uint8_t *pout, uint32_t *mem) {
        ip6->ip6_hlim = 64;
        memcpy (&ip6->ip6_src, bnd->ip6addr, 16);
        memcpy (&ip6->ip6_dst, (uint8_t *) mem [MEM_IP6_DST], 16);
-       mem [MEM_IP6_HEAD] = (uint32_t) ip6;
+       mem [MEM_IP6_HEAD] = (intptr_t) ip6;
        return &pout [sizeof (struct ip6_hdr)];
 }
 
 /* Send an UDP6 header over IPv6.
  * Some fields are filled later: len, check.
  */
-uint8_t *netsend_udp6 (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_udp6 (uint8_t *pout, intptr_t *mem) {
        // TODO: Setup MEM_ETHER_DST with router's address
        pout = netsend_ip6 (pout, mem);
        struct udphdr *udp = (struct udphdr *) pout;
        udp->source = htons (mem [MEM_UDP6_PORTS] & 0xffff);
        udp->dest   = htons (mem [MEM_UDP6_PORTS] >> 16);
-       mem [MEM_UDP6_HEAD] = (uint32_t) udp;
+       mem [MEM_UDP6_HEAD] = (intptr_t) udp;
        return &pout [sizeof (struct udphdr)];
 }
 
@@ -190,8 +195,8 @@ uint8_t *netsend_udp6 (uint8_t *pout, uint32_t *mem) {
 
 /* Create an ICMPv6 Router Solicitation.
  */
-uint8_t *netsend_icmp6_router_solicit (uint8_t *pout, uint32_t *mem) {
-       mem [MEM_ETHER_DST] = (uint32_t) ether_broadcast;
+uint8_t *netsend_icmp6_router_solicit (uint8_t *pout, intptr_t *mem) {
+       mem [MEM_ETHER_DST] = (intptr_t) ether_broadcast;
        struct ip6binding *bnd = (struct ip6binding *) mem [MEM_BINDING6];
        if (bnd->flags & I6B_ROUTE_SOURCE_6BED4_FLAG) {
                // Use 6bed4 for destination address
@@ -204,8 +209,8 @@ uint8_t *netsend_icmp6_router_solicit (uint8_t *pout, uint32_t *mem) {
        memcpy (pout + 40 + 8 + 2, ether_mine, ETHER_ADDR_LEN);
        memcpy (pout + 8, linklocal_mine, 16);
        //ADDS_NOTHING// memcpy (pout + 8, linklocal_mine, 16);
-       mem [MEM_IP6_HEAD] = (uint32_t) pout;
-       mem [MEM_ICMP6_HEAD] = (uint32_t) pout + 40;
+       mem [MEM_IP6_HEAD] = (intptr_t) pout;
+       mem [MEM_ICMP6_HEAD] = (intptr_t) pout + 40;
        return pout + sizeof (ipv6_router_solicitation);
 }
 
@@ -213,13 +218,13 @@ uint8_t *netsend_icmp6_router_solicit (uint8_t *pout, uint32_t *mem) {
  * Some fields are filled later: icmp6_cksum
  * TODO: Set multicast target address for IPv6
  */
-uint8_t *netsend_icmp6_ngb_sol (uint8_t *pout, uint32_t *mem) {
-       mem [MEM_ETHER_DST] = (uint32_t) ether_broadcast;
-       mem [MEM_IP6_DST] = (uint32_t) ip6_multicast_all_hosts;
+uint8_t *netsend_icmp6_ngb_sol (uint8_t *pout, intptr_t *mem) {
+       mem [MEM_ETHER_DST] = (intptr_t) ether_broadcast;
+       mem [MEM_IP6_DST] = (intptr_t) ip6_multicast_all_hosts;
        pout = netsend_ip6 (pout, mem);
        struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) pout;
        struct ip6binding *bnd = (struct ip6binding *) mem [MEM_BINDING6];
-       mem [MEM_ICMP6_HEAD] = (uint32_t) pout;
+       mem [MEM_ICMP6_HEAD] = (intptr_t) pout;
        icmp6->icmp6_type = ND_NEIGHBOR_SOLICIT;
        icmp6->icmp6_code = 0;
        icmp6->icmp6_data32 [0] = 0;
@@ -232,10 +237,10 @@ uint8_t *netsend_icmp6_ngb_sol (uint8_t *pout, uint32_t *mem) {
 
 /* Send a DHCPv4 DISCOVER packet to initiatie IPv4 LAN configuration.
  */
-uint8_t *netsend_dhcp4_discover (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_dhcp4_discover (uint8_t *pout, intptr_t *mem) {
        mem [MEM_UDP4_PORTS] = 0x00430044;
        mem [MEM_IP4_DST] = 0xffffffff;
-       mem [MEM_ETHER_DST] = (uint32_t) ether_broadcast;
+       mem [MEM_ETHER_DST] = (intptr_t) ether_broadcast;
        pout = netsend_udp4 (pout, mem);
        bzero (pout, 576);      // erase the acceptable package size
        pout [0] = 1;           // bootrequest
@@ -267,11 +272,11 @@ uint8_t *netsend_dhcp4_discover (uint8_t *pout, uint32_t *mem) {
 
 /* Send a DHCPv6 SOLICIT packet to initiatie native IPv6 configuration.
  */
-uint8_t *netsend_dhcp6_solicit (uint8_t *pout, uint32_t *mem) {
+uint8_t *netsend_dhcp6_solicit (uint8_t *pout, intptr_t *mem) {
        mem [MEM_UDP6_PORTS] = 0x02230222;
-       mem [MEM_BINDING6] = (uint32_t) binding_linklocal;
-       mem [MEM_IP6_DST] = (uint32_t) ip6_multicast_all_dhcp6_servers;
-       mem [MEM_ETHER_DST] = (uint32_t) ether_multicast_all_dhcp6_servers;
+       mem [MEM_BINDING6] = (intptr_t) binding_linklocal;
+       mem [MEM_IP6_DST] = (intptr_t) ip6_multicast_all_dhcp6_servers;
+       mem [MEM_ETHER_DST] = (intptr_t) ether_multicast_all_dhcp6_servers;
        pout = netsend_udp6 (pout, mem);
        pout [0] = 1;
        memcpy (pout + 1, ether_mine + 3, 3);
@@ -296,7 +301,7 @@ uint8_t *netsend_dhcp6_solicit (uint8_t *pout, uint32_t *mem) {
        memcpy (pout + 4          , dhcp6_options, sizeof (dhcp6_options));
        memcpy (pout + 4 + 4+4    , ether_mine, ETHER_ADDR_LEN);
        memcpy (pout + 4 + 4+4+6+4, ether_mine, ETHER_ADDR_LEN);
-       mem [MEM_DHCP6_HEAD] = (uint32_t) pout;
+       mem [MEM_DHCP6_HEAD] = (intptr_t) pout;
        return pout + 4 + sizeof (dhcp6_options);
 }
 
diff --git a/src/phone/Kconfig b/src/phone/Kconfig
new file mode 100644 (file)
index 0000000..b02173b
--- /dev/null
@@ -0,0 +1,4 @@
+# mainmenu "Operational settings"
+
+
+# endmenu
diff --git a/src/phone/Makefile b/src/phone/Makefile
new file mode 100644 (file)
index 0000000..4d93347
--- /dev/null
@@ -0,0 +1,2 @@
+
+objs-top-phone-y += src/phone/todo.o
diff --git a/src/phone/app_zero.c b/src/phone/app_zero.c
new file mode 100644 (file)
index 0000000..b0e84a4
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * http://devel.0cpm.org/ -- Open source firmware for SIP phones.
+ *
+ * Application zero -- the one that makes the phone doze off to sleep.
+ */
+
+
+#include <resource.h>
+#include <timer.h>
+#include <irq.h>
+#include <led.h>
+
+
+/* The timer that awaits the end of the phone's activity display.
+ * When this timer fires, everything will be brought back to normal
+ * and further power-saving facilities may be effected, such as
+ * switching off backlights and other LEDs.
+ */
+static timer_t zero_sleeptimer;
+
+
+/* Process a timeout of the zero_sleeptimer.  When this happens, the phone
+ * can go into a deep power saving mode.  Given, that is, that it can
+ * wakeup as soon as some explicit action is taken by the user or over the
+ * network.
+ */
+static bool zero_timeout (irq_t irq) {
+#if HAS_LED_BACKLIGHT
+       led_set_off (LED_IDX_BACKLIGHT);
+#endif
+       // TODO: Fall back to a trivial do-nothing mode
+}
+
+
+/* Start and initialise the zero application.  This will make the phone wait for some
+ * time and then fall asleep.
+ */
+void zero_start (void) {
+       timer_start (&zero_sleeptimer, 60 * TIME_SEC, zero_timeout);
+}
+
+
+/* Stop any attempts to make the phone fall asleep.
+ */
+void zero_stop (void) {
+       timer_stop (&zero_sleeptimer);
+#if HAS_LED_BACKLIGHT
+       led_set (LED_IDX_BACKLIGHT, HAS_LED_BACKLIGHT);
+#endif
+}
+
+
+/* Describe the application interace.  The zero application has no use for suspend/resume,
+ * because it is stateless.  As soon as another application takes over, the zero app
+ * forgets any state it may have, and will make a fresh start when resumed next time.
+ * TODO: Claim the display; only when nobody is interested can it switch off.
+ */
+static application_t app_zero = {
+
+       /* Operations */
+       .app_start = zero_start,
+       .app_stop = zero_stop,
+       .app_suspend = NULL,
+       .app_resume = NULL,
+       .app_event = NULL,
+
+       /* Resource specifications */
+       .app_res_wish = NULL,
+       .app_res_demand = NULL,
+
+};
+
+
+// TODO: init this module by registering at RES_LEVEL_ZERO the application structure for app_zero
diff --git a/src/phone/todo.c b/src/phone/todo.c
new file mode 100644 (file)
index 0000000..e69de29
index 72d5265..f81a0ac 100644 (file)
@@ -102,3 +102,4 @@ endchoice
 
 
 source "src/net/Kconfig"
+source "src/phone/Kconfig"
index 75eff3d..297b3db 100644 (file)
@@ -1,6 +1,9 @@
 #
-# Sort each target's object files into objs-y or objs-n:
+# Sort each target's object files into objs-bottom-y or objs-bottom-n:
 #
 
-objs-$(CONFIG_TARGET_LINUX_TUNTEST) += src/driver/tuntest.o
+objs-bottom-$(CONFIG_TARGET_LINUX_TUNTEST) += src/driver/util/linuxtuntest.o
 
+metavars-$(CONFIG_TARGET_LINUX_TUNTEST) += HAVE_NET_LEADER=4
+
+includes-$(CONFIG_TARGET_LINUX_TUNTEST) += src/driver/util/linuxtuntest.h
diff --git a/src/target/linksys_spa962.h b/src/target/linksys_spa962.h
new file mode 100644 (file)
index 0000000..ce3d01e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * http://devel.0cpm.org/ -- Open source SIP telephony firmware.
+ *
+ * Device-specific includes for Linksys SPA962
+ */
+
+
+/* The maximum number of supported handset */
+#define HAS_HANDSETS 1
+
+/* Keyboard settings */
+#undef DETECTS_DTMF
+#define HAS_KBD_STARHASH       1
+#define HAS_KBD_ABCD           0
+#define HAS_KBD_FLASH          0
+#define HAS_KBD_LINES          6
+#define HAS_KBD_SOFT_FUNCTION  4
+#ifndef HAS_KBD_SIDECARS
+#   define HAS_KBD_SIDECARS    0
+#endif
+#define HAS_KBD_GENERIC                ((HAS_KBD_SIDECARS)*32)
+#define HAS_KBD_VOICEMAIL      1
+#define HAS_KBD_MENU           1
+#define HAS_KBD_UPDOWN         1
+#define HAS_KBD_LEFTRIGHT      1
+#define HAS_KBD_MUTE           1
+#define HAS_KBD_CONFERENCE     0
+#define HAS_KBD_HOLD           1
+#define HAS_KBD_TRANSFER       0
+#define HAS_KBD_PHONEBOOK      0
+#define HAS_KBD_VOLUPDOWN      1
+#define HAS_KBD_HEADSET                1
+#define HAS_KBD_SPEAKER                1
+#define HAS_KBD_HEADSET                1
+#define HAS_KBD_MISSEDCALLS    0
+#define HAS_KBD_REDIAL         0
+
+/* Light settings */
+#define HAS_LED_MESSAGE                1
+#define HAS_LED_LINEKEYS       1
+#define HAS_LED_GENERIC                1
+#define HAS_LED_MUTE           1
+#define HAS_LED_HANDSET                1
+#define HAS_LED_HEADSET                1
+#define HAS_LED_SPEAKERPHONE   1
+#define HAS_LED_BACKLIGHT      1
+
+/* Display settings */
+
+/* Sound settings */
+#define HAS_HANDSET            1
+#define HAS_HEADSET            1
+#define HAS_SPEAKER_MIKE       1
+
+/* Line settings */
+
+/* Account settings */
+
+/* Media settings */
+#define HAS_MEDIA_VOICE                1
+#define HAS_MEDIA_VIDEO                0
+#define HAS_MEDIA_URI          0
+#define HAS_MEDIA_IMAGE                1
+