Network bootstrapping uses timer, and takes the right initiatives (without processing input).
# 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
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
--- /dev/null
+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.
+
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
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.
#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
}
}
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
* 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
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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;
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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,
+};
+
--- /dev/null
+/*
+ * 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
/* 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
+#ifndef HEADER_NETFUN
+#define HEADER_NETFUN
+
+#define MTU 1500
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+=====================================
+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
+
+++ /dev/null
-/* 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;
-}
-
--- /dev/null
+/* 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);
+}
+
--- /dev/null
+
+#include <stdint.h>
+
+typedef uint64_t timing_t;
+
+#define TIMER_NULL 0
+
+#define TIME_MSEC 1
+#define TIME_SEC 1000
+
--- /dev/null
+
+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
--- /dev/null
+==========
+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.
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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()
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
+
--- /dev/null
+/*
+ * 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.
+ */
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
#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
*/
* 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];
//
// 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;
}
*/
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);
}
}
*/
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);
}
#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
*/
*/
// 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];
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]) {
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;
// 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) {
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;
}
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;
/* 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;
/* 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 };
/* 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;
/* 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;
/* 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));
+}
+
#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>
-
/* 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...
* }
*
*/
BPF_BEGIN(netinput)
+ return NULL; //TODO// Early bail-out
+
STX_MEM (MEM_ETHER_HEAD) // Store the ethernet header
LDW_LEN ()
ADD_REX ()
#include <stdint.h>
+#include <stdbool.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/icmp6.h>
#include <netinet/if_ether.h>
+#include <config.h>
+
+#include <0cpm/cpu.h>
#include <0cpm/netfun.h>
#include <0cpm/netdb.h>
/* 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) {
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;
/* 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
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)];
}
/* 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;
/* 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;
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;
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;
}
*
* 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;
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;
}
* 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
* 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];
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;
}
*/
-#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/icmp6.h>
#include <netinet/if_ether.h>
+#include <config.h>
+
+#include <0cpm/cpu.h>
#include <0cpm/netfun.h>
#include <0cpm/netdb.h>
* 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);
}
* 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));
/* 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;
//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
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)];
}
/* 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
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);
}
* 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;
/* 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
/* 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);
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);
}
--- /dev/null
+# mainmenu "Operational settings"
+
+
+# endmenu
--- /dev/null
+
+objs-top-phone-y += src/phone/todo.o
--- /dev/null
+/*
+ * 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
source "src/net/Kconfig"
+source "src/phone/Kconfig"
#
-# 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
--- /dev/null
+/*
+ * 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
+