Transactional logic, mostly done outgoing, structured incoming. Split tract and...
authorRick van Rein <vanrein@hwdev.(none)>
Fri, 15 Jul 2011 22:49:40 +0000 (22:49 +0000)
committerRick van Rein <vanrein@hwdev.(none)>
Fri, 15 Jul 2011 22:49:40 +0000 (22:49 +0000)
49 files changed:
Makefile
TODO
bin/i2cp/Makefile
doc/coding.rst
doc/top2bottom.rst
include/0cpm/irq.h
include/0cpm/led.h
include/0cpm/netfun.h
include/0cpm/show.h
include/0cpm/sip.h [new file with mode: 0644]
include/bottom/devel.h
include/bottom/grandstream.h
include/bottom/ksz8842.h
include/stdbool.h [deleted file]
include/tic55x/README [new file with mode: 0644]
include/tic55x/stdarg.h [new file with mode: 0644]
include/tic55x/stdbool.h [new file with mode: 0644]
include/tic55x/stdint.h [new file with mode: 0644]
include/tic55x/stdlib.h [new file with mode: 0644]
include/tic55x/string.h [new file with mode: 0644]
src/driver/Makefile
src/driver/ksz8842.c
src/driver/tic55x/LICENSE-WARNING.TXT
src/driver/tic55x/timer.c
src/driver/util/ledsimu.c
src/driver/util/linuxtuntest.c
src/function/Makefile
src/function/bootloader.c
src/function/develtest/netconsole.c
src/function/netconsole.c
src/function/sip6phone/topmain.c
src/kernel/Makefile
src/kernel/cpu.c
src/kernel/led.c
src/kernel/timer.c
src/net/core.c
src/net/input.c
src/net/llconly.c
src/net/reply.c
src/net/send.c
src/phone/Makefile
src/sip/Makefile [new file with mode: 0644]
src/sip/app.c [new file with mode: 0644]
src/sip/dialog.c [new file with mode: 0644]
src/sip/parse.c [new file with mode: 0644]
src/sip/register.c [new file with mode: 0644]
src/sip/tract.c [new file with mode: 0644]
src/target/Makefile
src/target/linksys_spa962.h

index 09eb50e..b70a523 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,21 @@
+# Makefile for the entire 0cpm project
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+
 
 # TODO: this should be src/Kconfig
 Kconfig := src/target/Kconfig
@@ -30,6 +48,7 @@ include bin/kconfig/Makefile
 
 include src/kernel/Makefile
 include src/net/Makefile
+include src/sip/Makefile
 include src/phone/Makefile
 
 include src/target/Makefile
diff --git a/TODO b/TODO
index a4790ee..fe7528b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -29,7 +29,18 @@ look at:
 * tic55x's int.c driver: work out conditions for hibernation sleep.
   In hibernation, also shutdown the codec.
 
-* tic55x should include from separate directory for <stdbool.h>
-
-* Append network stack with LLC1 (for TFTP) and LLC2 (for console).
-
+* SIP handling:
+   + UAC-tract: move from dialog-function to method-name
+   + replace fixed offsets (sip + 8) with a startline parser routine
+   + match client transactions for a given dialog
+   - correct timing and counts for client's initiation rituals
+   - ensure proper termination of dialogs (always have a timeout)
+   - handle SIP response codes from remote UASs
+   - handle SIP requests from remote UACs
+   - wrap distinction between request & response in a parser function
+   - process incoming requests (such as an INVITE)
+   - route headers
+
+* create & use a irqtimer_stop_completely() that halts prepared fire()
+
+* handle packet fragmentation
index 95a5feb..726f116 100644 (file)
@@ -1,2 +1,20 @@
+# Makefile for the i2cp utility (copies I2C in and out)
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+
 i2cp: i2cp.c
        gcc -o i2cp i2cp.c
index c5e8ae9..5bb7d26 100644 (file)
@@ -123,3 +123,23 @@ Checking in code
   this was first discussed and agreed; the design of the API should stay as
   clean as possible.
 
+
+Small steps at a time
+=====================
+
+Debugging in an embedded environment is difficult, as you will not be able
+to easily detect on what line your code gets stuck.  If you forget to
+increment a counter (yes, you will!) you will have to resort to iterating
+before you know where the problem arises.  The logging facilities don't
+help much either; they need to get a turn running on the CPU, and if your
+code gets stuck the logged information will not actually be sent.  (That
+might be an interesting change though?)
+
+For this reason, make small steps at a time.  The best approach is probably
+to make one conceptual improvement at a time (as half-written code is usually
+not half bad, but double bad).  At times, this will mean writing a lot of
+code and testing it all at once.  Still, its structure will help you to
+find the problem spot more easily.
+
+Just don't forget to increment your counters...
+
index 25ae590..484ccf1 100644 (file)
@@ -929,6 +929,57 @@ played in the future.  A small delay before a packet is actually
 played may be healthy to avoid future sound glitches.
 
 
+Entropy
+-------
+
+Some top applications will require pseudo-random bits.  Although
+not all hardware has a random generator on-board, it is not hard
+to find on a phone:
+
+* A number of low bits from a counter running at the CPU clock,
+  sampled at unpredictable times -- such as network interrupts.
+
+* While coding samples, the amount by which the sample is off
+  in compressed form.
+
+* While the phone is not active, a microphone can still be sampled
+  and its lower bits used.  This would introduce a potential
+  privacy problem though, so it is not something to do without
+  marking it clearly in the phone's specifications!
+
+* When the top half is done doing a certain task, it may invoke
+  a random seeding routine, possible to gather data from the
+  sources above.  The bottom half may assume that the top half
+  will regularly call a random seeding function if it also wants
+  to be able to collect random material.
+
+The bottom half builds an entropy buffer of a prime number of bytes.
+The prime number greatly reduces the chances of cycles occurring;
+the lowest number of bytes that should be supported is 17.  When
+entropy drips in, it is exclusive-ored with the buffer bytes in a
+cyclic fashion.  When random material is needed, the next few
+bytes are taken out and the pointer for such retrieval moves forward
+while doing so.  There is no synchronisation between writing and
+reading, as the service is not truely random, but pseudo-random:
+best-effort suffices for telephony applications.
+
+::
+       void bottom_rndseed (void);
+       void bottom_rnd_pseudo (uint8_t *rnd, uint8_t len);
+       //TBD// void bottom_rnd_strong (uint8_t *rnd, uint8_t len);
+
+The ``bottom_rndseed()`` function is used to tell the bottom that
+now would be a nice time to sample some entropy; this will usually
+be called when the top is done with some job, so that it is as far
+and unpredictably away from a reliable measurement moment as possible.
+
+The ``bottom_rnd_pseudo()`` function fills the first ``len`` bytes
+pointed at by ``rnd`` with random bytes, each consisting of 8 random
+bits.  That is, pseudo-random bits.  The top half should never ask
+for more than 4 bytes at a time, to avoid emptying the entropy from
+the buffer.
+
+
 Special hardware
 ================
 
index 37b7538..1c4638c 100644 (file)
@@ -66,4 +66,9 @@ void bottom_irq_disable (irq_t *irq);
 void bottom_irq_wait (void);
 
 
+/* Bottom-half operations to process pseudo-random information */
+void bottom_rndseed (void);
+void bottom_rnd_pseudo (uint8_t *rnd, uint8_t len);
+
+
 #endif
index bf66139..a6a2ce8 100644 (file)
@@ -121,8 +121,8 @@ typedef uint8_t led_colour_t;
  */
 typedef timing_t led_flashtime_t;
 #define LED_FLASHTIME_NONE     ((led_flashtime_t) (TIME_MSEC(0)   ))
-#define LED_FLASHTIME_FAST     ((led_flashtime_t) (TIME_MSEC(500) ))
-#define LED_FLASHTIME_MEDIUM   ((led_flashtime_t) (TIME_MSEC(1000)))
+#define LED_FLASHTIME_FAST     ((led_flashtime_t) (TIME_MSEC(100 )))
+#define LED_FLASHTIME_MEDIUM   ((led_flashtime_t) (TIME_MSEC(500 )))
 #define LED_FLASHTIME_SLOW     ((led_flashtime_t) (TIME_MSEC(2000)))
 
 
index c0c7552..8394ba9 100644 (file)
@@ -93,20 +93,21 @@ void netcore_bootstrap_success (void);
 void netcore_bootstrap_restart (void);
 void netcore_bootstrap_shutdown (void);
 
-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 *net_dhcp4 (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_rtp (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_rtcp (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_sip (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_mdns_resp_error (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_mdns_resp_dyn (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_mdns_resp_std (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_mdns_query_error (uint8_t *pkt, intptr_t *mem);
+uint8_t *net_mdns_query_ok (uint8_t *pkt, 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_udp6 (uint8_t *pout, 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 *netllc_reply (uint8_t *pout, intptr_t *mem);
index 5307a90..4f3fddc 100644 (file)
@@ -47,6 +47,7 @@ typedef enum {
        FIXMSG_SENDING,         // A call request is being / has been sent
        FIXMSG_RINGING,         // The remote phone is ringing
        FIXMSG_CALL_ENDED,      // The call has ended
+       //
        FIXMSG_COUNT            // The number of fixed messages
 } fixed_msg_t;
 
diff --git a/include/0cpm/sip.h b/include/0cpm/sip.h
new file mode 100644 (file)
index 0000000..b114f00
--- /dev/null
@@ -0,0 +1,313 @@
+/* SIP message handling.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef HEADER_SIP
+#define HEADER_SIP
+
+
+/*
+ * DIALOG, TRANSACTION, LINE
+ *
+ * What is known to a phone user as a call leg, is called a Dialog in
+ * SIP terminology.  To quote RFC 3261,
+ *
+ *    A dialog is a peer-to-peer SIP relationship between two UAs that
+ *    persists for some time.  A dialog is established by SIP messages,
+ *    such as a 2xx response to an INVITE request.  A dialog is
+ *    identified by a call identifier, local tag, and a remote tag.
+ *    A dialog was formerly known as a call leg in RFC 2543.
+ * 
+ * We use the term a bit broader than the RFC, by including early
+ * dialogs.  That means that a dialog may exist before it has been
+ * accepted by both sides.
+ *
+ * A transaction is a message exchange between two SIP speakers, and
+ * and exists solely to describe the reliable exchange of a message.
+ * At any time, a dialog consists of zero or more transactions that
+ * may be in progress.  Zero transactions may apply to a call that
+ * is in progress, and multiple transactions may apply when a few
+ * actions occur in rapid sequence.
+ *
+ * Some dialogs make it all the way to become a call leg, and are
+ * attached to a line.  A line is the user-level concept of a call
+ * leg endpoint, it is the unit that may be answered, put on hold,
+ * pulled into a conference call or kicked out of it, and so on.
+ * This is the end goal of a dialog started with an INVITE.  A
+ * dialog with a line attached is what is termed as a dialog in
+ * RFC 3261.
+ *
+ * Dialogs are identified with a local tag and a call-id and, where
+ * available, a remote tag.  Transactions are attached to a dialog
+ * and are further distinguished as described in section 17.1.3.
+ *
+ * Note that a transaction can linger on for some time after it
+ * appears to have ended; this may happen if repeated sends must
+ * be fended in some way.  Any dialogs under which such transactions
+ * fall will also survive, even after they have been detached from
+ * a phone line.
+ *
+ * The general rule is that a dialog exists as long as it is either
+ * attached to a phone line, or has transactions in progress.  The
+ * creation of a new dialog is done in the layer that uses the
+ * transactions and handles SIP messages; when requests enter the
+ * phone, they are sent up as transactions, for which the SIP-aware
+ * layer may then have to create a dialog.
+ *
+ * Many fields in a dialog occur on two sides.  Sometimes the
+ * traffic's direction helps in determining where information
+ * should go; in those cases, field names are X_send and X_recv.
+ * In other cases, descriptions are tied to the user agent
+ * itself; in those cases, field names are X_local and X_remote.
+ */
+
+
+/* A line is a phone concept.  As this is basically a dialog,
+ * the only type required at the phone level is a line index.
+ * This can be represented in a single byte.  The same index
+ * number may be used in several places to index different
+ * aspects of a line, for instance its LED signalling.  The
+ * range is [0..HAVE_LINES> so the array size to create
+ * in various locations would be ... [HAVE_LINES]
+ */
+typedef uint8_t linenr_t;
+#define LINENR_NULL 0xff
+
+typedef struct transaction tract_t;
+typedef struct dialog dialog_t;
+
+
+struct transaction {
+       irqtimer_t tmr;
+       dialog_t *dialog;
+       void (*userfn) (textptr_t *sip, textptr_t *sdp);
+       bool (*dialogfn) (dialog_t *dia, textptr_t *branch);
+       textptr_t const *method;
+       textptr_t incoming;
+       textptr_t attachmt;
+       timing_t growtime;
+       textptr_t branch;       /* Characters are in txt_branch */
+       textptr_t lastresponse;
+       textptr_t lastsdp;
+       char txt_branch [128];  /* BRANCHPREFIX_RFC3261, bottom_time, rnd32 */
+       char state;             /* See TRS_xxx below */
+};
+
+#define TRS_ALLOCATED 'A'
+#define TRS_CALLING 'C'
+#define TRS_PROCEEDING 'P'
+#define TRS_COMPLETED 'D'
+#define TRS_TERMINATED 0x00
+#define TRS_TRYING 'Y'
+#define TRS_CONFIRMED 'M'
+
+#define BRANCHPREFIX_RFC3261 "z9hG4bK"
+#define BRANCHPREFIXLEN_RFC3261 7
+
+extern const textptr_t branchprefix_rfc3261;
+
+
+struct dialog {
+       uint32_t connflags_send, connflags_recv;        /* See DCF_XXX below */
+       uint32_t sideflags_local, sideflags_remot;      /* See DSF_XXX below */
+       uint32_t cseqnr;                                /* Incremented by one */
+       uint16_t tract_usectr;                          /* How many tract_t point here? */
+       struct sdp *sdp_send, *sdp_recv;                /* TODO:Or_different... */
+       textptr_t tag_local, tag_remot, tag_callid;     /* tag_XXX.str is txt_XXX or NULL */
+       char txt_local [128], txt_remot [128], txt_callid [128];
+       linenr_t linenr;                                /* Line keeping this dialog open */
+};
+
+/* 1. State flags describing call progress */
+#define DSF_CONNECTED          0x00000001      /* Connection wanted */
+#define DSF_DISCONNECTED       0x00000002      /* Connection hung up */
+
+/* 2. Sound choices describing what sounds are played */
+#define DCF_SOUND_MASK         0x000000ff      /* Sound values */
+#define DCF_SOUND_INACTIVE     0x00000000      /* Inactive, no sound */
+#define DCF_SOUND_HOLD         0x00000001      /* Hold music, or silence */
+#define DCF_SOUND_RINGING      0x00000001      /* Ringing sound */
+#define DCF_SOUND_BUSY         0x00000002      /* Busy sound */
+#define DCF_SOUND_RECORDED     0x00000004      /* Playing recorded sound */
+#define DCF_SOUND_EARLYMEDIA   0x00000008      /* Early media is played */
+
+/* 3. SIP behaviour modifications */
+#define DF_100REL              0x00000100      /* RFC 3262 */
+#define DF_199                 0x00000200      /* RFC 6228 */
+
+/* 4. Event packages that have been activated */
+#define DCF_EVENT_MASK         0xffff0000      /* Event package flags */
+#define DCF_EVENT_PRESENCE     0x00010000      /* RFC 3856 */
+#define DCF_EVENT_REFER                0x00020000      /* RFC 3515 */
+#define DCF_EVENT_DIALOG       0x00040000      /* RFC 4235 */
+#define DCF_EVENT_CONFERENCE   0x00080000      /* RFC 4575 */
+#define DCF_EVENT_PROFILE      0x00100000      /* RFC 6080 */
+
+
+/* Method name string constants
+ */
+extern textptr_t const invite_m;
+extern textptr_t const cancel_m;
+extern textptr_t const bye_m;
+extern textptr_t const refer_m;
+extern textptr_t const ack_m;
+extern textptr_t const register_m;
+
+
+/* Core headers are passed around between functions.
+ */
+
+typedef enum coreheadernumber coreheadernum_t;
+typedef struct coreheaders coreheaders_t;
+
+enum coreheadernumber {
+       COREHDR_FROM,
+       COREHDR_TO,
+       COREHDR_CALL_ID,
+       COREHDR_CONTACT,
+       COREHDR_VIA,
+       COREHDR_CSEQ,
+       COREHDR_ROUTE,
+       // The number of core header numbers
+       COREHDR_COUNT,
+       // A tag for other headers
+       COREHDR_OTHER = 31
+};
+
+extern textptr_t const coreheader_name [COREHDR_COUNT];
+
+struct coreheaders {
+       textptr_t headers [COREHDR_COUNT];
+       char *sip;
+       textptr_t from_tag;
+       textptr_t to_tag;
+       textptr_t callid;
+       textptr_t via_branch;
+       textptr_t cseq_method;
+       uint32_t  cseq_serialno;
+};
+
+
+/* Operations to allocate a phone line for a dialog
+ */
+void sipdia_initialise (void);
+dialog_t *sipdia_allocate (coreheaders_t const *coreheaders, bool myrequest);
+void sipdia_deallocate (dialog_t *dia);
+void sipdia_setlinenr (dialog_t *dia, linenr_t ln);
+linenr_t sipdia_getlinenr (dialog_t *dia);
+void sipdia_next_cseq (dialog_t *dia);
+void sipdia_respond (textptr_t const *response, coreheaders_t const *coreheaders, textptr_t const *sip, textptr_t const *sdp_opt);
+dialog_t *sipdia_findmatch (coreheaders_t const *coreheaders, bool const myrequest);
+
+typedef void (*sipdia_climth) (dialog_t *dia);
+sipdia_climth sipdia_client_method (textptr_t const *mth);
+
+
+/* Transaction management dialogs */
+void siptr_initialiase (void);
+tract_t *siptr_findmatch (coreheaders_t const *coreheaders);
+void siptr_client (dialog_t *dia, textptr_t const *mth, void (*truser) (textptr_t const *sip, textptr_t *sdp));
+tract_t *siptr_server (dialog_t *dia, textptr_t const *mth);
+void siptr_respond (tract_t *tr, textptr_t const *response, coreheaders_t const *coreheaders, textptr_t const *sip, textptr_t const *sdp_buffered);
+void siptr_respond_again (tract_t *tr, coreheaders_t *coreheaders, textptr_t const *sip);
+void siptr_response2uac (dialog_t *dia, textptr_t *sip, textptr_t *attachment, textptr_t *branch, textptr_t *cseqmth);
+void siptr_request2uas (dialog_t *dia, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+
+
+/* Operations to manage dialogs
+ */
+bool sipdia_invite (dialog_t *dia, textptr_t *branch);
+bool sipdia_cancel (dialog_t *dia, textptr_t *branch);
+bool sipdia_accept (dialog_t *dia, textptr_t *branch);
+bool sipdia_reject (dialog_t *dia, textptr_t *branch);
+bool sipdia_bye (dialog_t *dia, textptr_t *branch);
+bool sipdia_hold (dialog_t *dia, textptr_t *branch);
+bool sipdia_resume (dialog_t *dia, textptr_t *branch);
+bool sipdia_refer (dialog_t *dia, textptr_t *branch);
+bool sip_ack (dialog_t *dia, textptr_t *branch);
+
+
+/* Upcalls from the dialog/tract layer to the SIP application.
+ * Each of the upcalls is invoked with the SIP message and body
+ * (usually containing SDP) and the return value should be quick
+ * and provide a 3-digit status code, a space and a description.
+ * The return value may not contain line-end markers.
+ * TODO: Additional parameters, like a Contact: header?
+ */
+void sipapp_invite (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+void sipapp_cancel (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+void sipapp_bye (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+void sipapp_hold (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+void sipapp_resume (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+void sipapp_refer (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+void sipapp_register (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+
+/* A {NULL,NULL}-terminated array holding the mapping from
+ * methodname to sipapp_XXX upcall/handler function.
+ */
+typedef struct {
+       textptr_t const *method;
+       void (*sipappfn) (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp);
+} sipappmap_t;
+extern sipappmap_t sipapp_fnmap [];
+
+
+/* Operations to manage registration
+ */
+bool sipreg_start (dialog_t *dia, textptr_t *branch);
+bool sipreg_stop (dialog_t *dia, textptr_t *branch);
+
+
+/* Message parser functions
+ */
+void sip_normalise (textptr_t *sipmsg, textptr_t *attachment);
+bool sip_splitline_request (textptr_t const *sipmsg, textptr_t *method, textptr_t *requri);
+bool sip_splitline_response (textptr_t const *sipmsg, textptr_t *code, textptr_t *descr);
+bool sip_firstheader (textptr_t const *sipmsg, textptr_t *headername, textptr_t *headerval);
+bool sip_nextheader  (textptr_t const *sipmsg, textptr_t *headername, textptr_t *headerval);
+bool sip_firsturi_inheader (textptr_t const *siphdr, textptr_t *uri);
+bool sip_nexturi_inheader  (textptr_t const *siphdr, textptr_t *uri);
+bool sip_firstparm_inheader (textptr_t const *siphdr, textptr_t *parnm, textptr_t *parval);
+bool sip_nextparm_inheader  (textptr_t const *siphdr, textptr_t *parnm, textptr_t *parval);
+bool sip_firstparm_inuri (textptr_t const *uri, textptr_t *parnm, textptr_t *parval);
+bool sip_nextparm_inuri  (textptr_t const *uri, textptr_t *parnm, textptr_t *parval);
+bool sip_components_inuri (textptr_t const *uri, textptr_t *proto, textptr_t *user, textptr_t *pass, textptr_t *dom, uint16_t *port);
+bool sip_split_cseq (textptr_t const *cseq, uint32_t *serial, textptr_t *mth);
+
+
+/* Message generation functionss
+ */
+char *sipgen_request (char *pout, textptr_t const *method, textptr_t const *uri, textptr_t *branch);
+char *sipgen_response (char *pout, textptr_t const *response, coreheaders_t const *coreheaders, textptr_t const *sip, textptr_t const *sdp_opt);
+char *sipgen_max_forwards (char *pout);
+char *sipgen_user_agent (char *pout);
+char *sipgen_from (char *pout, dialog_t *dia);
+char *sipgen_to (char *pout, textptr_t const *callee, dialog_t const *dia);
+char *sipgen_to_register (char *pout, dialog_t const *dia);
+char *sipgen_contact (char *pout, dialog_t const *dia);
+char *sipgen_expires (char *pout, uint32_t const secs);
+char *sipgen_cseq (char *pout, textptr_t const *method, uint32_t cseqnr);
+char *sipgen_call_id (char *pout, dialog_t *dia);
+char *sipgen_sdp_headers (char *pout, textptr_t const *sdp);
+char *sipgen_attach_body (char *pout, textptr_t const *sdp);
+void sipgen_create_branch (textptr_t *branch);
+
+
+
+#endif
index f0385fd..807a752 100644 (file)
 
 // TODO: The following nethandlers have not been implemented yet
 #include <stdlib.h>
-inline uint8_t *net_rtp (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_rtcp (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_sip (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_mdns_resp_error (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_mdns_resp_dyn (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_mdns_resp_std (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_mdns_query_error (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
-inline uint8_t *net_mdns_query_ok (uint8_t *pkt, uint32_t pktlen, intptr_t *mem) { return NULL; }
+inline uint8_t *net_rtp (uint8_t *pkt, intptr_t *mem) { return NULL; }
+inline uint8_t *net_rtcp (uint8_t *pkt, intptr_t *mem) { return NULL; }
+inline uint8_t *net_mdns_resp_error (uint8_t *pkt, intptr_t *mem) { return NULL; }
+inline uint8_t *net_mdns_resp_dyn (uint8_t *pkt, intptr_t *mem) { return NULL; }
+inline uint8_t *net_mdns_resp_std (uint8_t *pkt, intptr_t *mem) { return NULL; }
+inline uint8_t *net_mdns_query_error (uint8_t *pkt, intptr_t *mem) { return NULL; }
+inline uint8_t *net_mdns_query_ok (uint8_t *pkt, intptr_t *mem) { return NULL; }
 
index ea6ac9f..dada9b8 100644 (file)
@@ -29,6 +29,8 @@
 #define HAVE_LED_HANDSET 1
 #define HAVE_LED_SPEAKERPHONE 1
 
+#define HAVE_LINES 2
+
 #define HAVE_BUTTON_MESSAGE    'i'
 #define HAVE_BUTTON_HOLD       'h'
 #define HAVE_BUTTON_TRANSFER   'x'
index c0fb5e3..4af10aa 100644 (file)
@@ -100,6 +100,13 @@ typedef uint32_t kszint32_t;
 #endif
 
 
+/* Data is read 4 bytes at a time, and the buffer
+ * is a multiple of 2 bytes, so reserve 2 bytes
+ * extra for overflow.
+ */
+#define HAVE_NET_TRAILER 2
+
+
 /* Map KSZ8842 network data to or from general purpose memory */
 void ksz_memset16 (kszint16_t ksz, uint8_t *mem);
 kszint16_t ksz_memget16 (uint8_t *mem);
diff --git a/include/stdbool.h b/include/stdbool.h
deleted file mode 100644 (file)
index be5731d..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Backup for unsupportive compiles
- *
- * This file is part of 0cpm Firmerware.
- *
- * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
- *
- * 0cpm Firmerware is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 3.
- *
- * 0cpm Firmerware is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-typedef enum {
-       false = 0,
-       true  = 1
-} bool;
diff --git a/include/tic55x/README b/include/tic55x/README
new file mode 100644 (file)
index 0000000..e5d9b84
--- /dev/null
@@ -0,0 +1,6 @@
+This directory defined standard C headers for tic55x.
+This is done to comply with the compiler conditions,
+which forbid shipping the source code unless it is
+only aimed at TI's processors.
+
+For details, read src/driver/tic55x/LICENSE-WARNING.TXT
diff --git a/include/tic55x/stdarg.h b/include/tic55x/stdarg.h
new file mode 100644 (file)
index 0000000..f18e276
--- /dev/null
@@ -0,0 +1,25 @@
+/* Vararg handling for tic55x processors.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef void *va_list;
+
+#define va_start(list,var) list=((&var)+1)
+#define va_end(list)
+#define va_arg(list,type) (((type *)(list=(((type *)list)+1)))[-1])
+
diff --git a/include/tic55x/stdbool.h b/include/tic55x/stdbool.h
new file mode 100644 (file)
index 0000000..be5731d
--- /dev/null
@@ -0,0 +1,24 @@
+/* Backup for unsupportive compiles
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+typedef enum {
+       false = 0,
+       true  = 1
+} bool;
diff --git a/include/tic55x/stdint.h b/include/tic55x/stdint.h
new file mode 100644 (file)
index 0000000..c7ca802
--- /dev/null
@@ -0,0 +1,29 @@
+/* Standard integer types on tic55x
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+typedef unsigned long int uint32_t;
+
+typedef char int8_t;
+typedef short int int16_t;
+typedef long int int32_t;
+
+typedef uint32_t intptr_t;
+
diff --git a/include/tic55x/stdlib.h b/include/tic55x/stdlib.h
new file mode 100644 (file)
index 0000000..7e4c8e2
--- /dev/null
@@ -0,0 +1,23 @@
+/* Standard C library definitions for tic55x.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define NULL ((void *) 0)
+
+typedef unsigned int size_t;
+
diff --git a/include/tic55x/string.h b/include/tic55x/string.h
new file mode 100644 (file)
index 0000000..66a4910
--- /dev/null
@@ -0,0 +1,24 @@
+/* String and byte array handling for tic55x.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+int memcmp(const void *s1, const void *s2, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+void bzero(void *s, size_t n);
+
index f7e6d7d..4a276d9 100644 (file)
@@ -1,3 +1,20 @@
+# Makefile for 0cpm drivers
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
 
 objs-bottom-$(CONFIG_TARGET_GRANDSTREAM_BT20x) += src/driver/tic55x/grandstream-bt20x.o src/driver/ht162x.o src/driver/ksz8842.o src/driver/tlv320aic2x.o
 
index 78aeaa4..3ee1ec2 100644 (file)
@@ -179,7 +179,7 @@ bool bottom_network_recv (uint8_t *pkt, uint16_t *pktlen) {
        }
        //
        // Check if the frame length does not exceed the available buffer
-       rxlen = (kszmap16 (KSZ8842_BANK18_RXBC) & 0x03ff);
+       rxlen = (kszmap16 (KSZ8842_BANK18_RXBC) & 0x07ff);
        ok = ok && (rxlen >= 6 + 6 + 2);
        ok = ok && (rxlen <= *pktlen);
        KSZ8842_BANK_SET (17);
index 115a2ad..153f13f 100644 (file)
@@ -180,5 +180,6 @@ to the user's ability to exercise the freedom brought to the
 
 As soon as TI permits this redistribution of their TIC55x toolkit,
 we will update or remove this file from the software distribution,
-and the cautioning note in src/target/Kconfig.platform
+and the cautioning notes in src/target/Kconfig.platform and
+include/tic55x/README
 
index c394fb7..083e74e 100644 (file)
 
 static timing_t current_timer = 0;
 
+#define ENTROPY_BUFLEN 17
+static uint8_t entropy_pseudo [ENTROPY_BUFLEN];
+static uint8_t entropy_wpos = 0;
+static uint8_t entropy_rpos = 0;
+
 
 /* Read the current time from timer1's top part.  Be careful about
  * timer increments between the two word reads.
@@ -135,6 +140,29 @@ interrupt void tic55x_tint0_isr (void) {
 }
 
 
+/* Read the lower-half timer for a bit of entropy, at a time
+ * deemed appropriate by the top half.
+ */
+void bottom_rndseed (void) {
+       entropy_pseudo [entropy_wpos++] ^= GPTCNT3_1 & 0xff;
+       if (entropy_wpos >= ENTROPY_BUFLEN) {
+               entropy_wpos = 0;
+       }
+}
+
+/* Retrieve a given number of random bytes from the entropy
+ * buffer.
+ */
+void bottom_rnd_pseudo (uint8_t *rnd, uint8_t len) {
+       while (len-- > 0) {
+               *rnd++ = entropy_pseudo [entropy_rpos += 4];
+               if (entropy_rpos >= ENTROPY_BUFLEN) {
+                       entropy_rpos -= ENTROPY_BUFLEN;
+               }
+       }
+}
+
+
 /* Setup timers and the corresponding interrupts for timer0/timer1
  */
 void tic55x_setup_timers (void) {
index 10978e9..1f7efa8 100644 (file)
@@ -102,11 +102,11 @@ void bottom_led_set (led_idx_t lednum, led_colour_t col) {
        static headctr = 0;
        if (headctr++ % 20 == 0) {
                for (i = 0; i < LED_IDX_COUNT; i++) {
-                       bottom_printf ("\t%s", num2descr [i].name);
+                       bottom_printf ("\t%s", (intptr_t) num2descr [i].name);
                }
                bottom_printf ("\n");
        }
-       bottom_printf ("%3d.%03d", tv.tv_sec, tv.tv_usec / 1000);
+       bottom_printf ("%3d.%03d", (intptr_t) tv.tv_sec, (intptr_t) (tv.tv_usec / 1000));
        char capscol [8];
        char *colnm = num2descr [lednum].states [col];
        for (i = 0; i < 7; i++) {
@@ -124,7 +124,7 @@ void bottom_led_set (led_idx_t lednum, led_colour_t col) {
                } else {
                        colnm = "UNSET";
                }
-               bottom_printf ("\t%s", colnm);
+               bottom_printf ("\t%s", (intptr_t) colnm);
        }
        bottom_printf ("\n");
 }
index d087978..6d34dab 100644 (file)
@@ -138,7 +138,7 @@ timing_t bottom_time (void) {
        struct timeval now;
        timing_t retval;
        if (gettimeofday (&now, NULL) != 0) {
-               bottom_printf ("Failed to get clock time: %s\n", strerror (errno));
+               bottom_printf ("Failed to get clock time: %s\n", (intptr_t) strerror (errno));
                return 0;
        }
        retval = now.tv_sec * 1000 + (now.tv_usec / 1000);
@@ -196,7 +196,7 @@ void bottom_sleep_commit (sleep_depth_t depth) {
                top_timer_expiration (exptm);
                sleepy = false;
        } else {
-               bottom_printf ("select() returned an error: %s\n", strerror (errno));
+               bottom_printf ("select() returned an error: %s\n", (intptr_t) strerror (errno));
                sleep (1);
        }
 }
@@ -210,7 +210,7 @@ bool bottom_network_send (uint8_t *buf, uint16_t buflen) {
                if (errno == EAGAIN) {
                        return false;
                } else {
-                       bottom_printf ("Ignoring error after write to tunnel: %s\n", strerror (errno));
+                       bottom_printf ("Ignoring error after write to tunnel: %s\n", (intptr_t) strerror (errno));
                }
        }
        return true;
@@ -223,7 +223,7 @@ bool bottom_network_recv (uint8_t *buf, uint16_t *buflen) {
                if (errno == EAGAIN) {
                        return false;
                } else {
-                       bottom_printf ("Ignoring error after read from tunnel: %s\n", strerror (errno));
+                       bottom_printf ("Ignoring error after read from tunnel: %s\n", (intptr_t) strerror (errno));
                }
                *buflen = 0;
        } else if (rsz > sizeof (struct tun_pi)) {
@@ -264,8 +264,8 @@ int process_packets (int secs) {
 #if 0
                        int i;
                        for (i=0; i<28; i++) {
-                               bottom_printf ("  M[%d]=0x%08x%s", i, mem [i],
-                                               ((i+1)%4 == 0)? "\n": "");
+                               bottom_printf ("  M[%d]=0x%08x%s", (intptr_t) i, (intptr_t) mem [i],
+                                               (intptr_t) (((i+1)%4 == 0)? "\n": ""));
                        }
 #endif
                        if (rf != NULL) {
index dadb759..d0f021f 100644 (file)
@@ -1,3 +1,22 @@
+# Makefile for 0cpm device functions
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+
+
 # Function-dependent construction of top-objs list
 
 #
index 4774894..e3f5d53 100644 (file)
@@ -70,7 +70,7 @@ uint8_t *netllc_tftp (uint8_t *pkt, intptr_t *mem) {
        memcpy (pkt, llc+6, 6);
        memcpy (pkt+6, ether_mine, 6);
        pktlen2 = (llc [12] << 8) | llc [13];
-bottom_printf ("netllc_tftp, pktlen=%d, pktlen2=%d\n", pktlen, pktlen2);
+bottom_printf ("netllc_tftp, pktlen=%d, pktlen2=%d\n", (intptr_t) pktlen, (intptr_t) pktlen2);
        if (pktlen < 12 + 2 + 3 + 4) {
                return;
        }
@@ -78,12 +78,12 @@ bottom_printf ("netllc_tftp, pktlen=%d, pktlen2=%d\n", pktlen, pktlen2);
                return;
        }
        cmd = (llc [12 + 2 + 3 + 0] << 8) | llc [12 + 2 + 3 + 1];
-bottom_printf ("Processing TFTP command %d\n", (int) cmd);
+bottom_printf ("Processing TFTP command %d\n", (intptr_t) cmd);
        if ((cmd == 1) || (cmd == 2)) {         /* 1==RRQ, 2=WRQ, new setup */
                llc [pktlen - 1] = 0;
                bottom_printf ("TFTP %s for %s\n",
-                       (cmd == 1)? "RRQ": "WRQ",
-                       llc + 12 + 2 + 3 + 2);
+                       (intptr_t) ((cmd == 1)? "RRQ": "WRQ"),
+                       (intptr_t) (llc + 12 + 2 + 3 + 2));
                current = (struct flashpart *) "TODO"; //TODO// current=flash_find_file(...), return if not found
                sending   = (current != NULL) && (cmd == 1);
                receiving = (current != NULL) && (cmd == 2);
@@ -144,8 +144,8 @@ sendblock:
        } else if (cmd == 5) {                  /* 5==ERR */
                pkt [pktlen-1] = 0;
                bottom_printf ("TFTP error %d received: %s\n", 
-                       (pkt [12 + 2 + 3 + 2] << 8) | pkt [12 + 2 + 3 + 3],
-                       pkt + 12 + 2 + 3 + 4);
+                       (intptr_t) ((pkt [12 + 2 + 3 + 2] << 8) | pkt [12 + 2 + 3 + 3]),
+                       (intptr_t) (pkt + 12 + 2 + 3 + 4));
                /* no further error handling */
        }
        if (replylen) {
index 93be551..91ca6b2 100644 (file)
@@ -101,7 +101,6 @@ uint16_t netinputlen;
 void onlinetest_top_main (void) {
        void nethandler_llconly (uint8_t *pkt, uint16_t pktlen);
        bottom_critical_region_end ();
-       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_ON);
        while (true) {
                if (online) {
                        bottom_show_fixed_msg (APP_LEVEL_BACKGROUNDED, FIXMSG_READY);
@@ -109,20 +108,18 @@ void onlinetest_top_main (void) {
                        if (bottom_network_recv (netinput, &netinputlen)) {
                                if (memcmp (netinput, "\xff\xff\xff\xff\xff\xff", 6) == 0) {
                                        bottom_printf ("Broadcast from %2x:%2x:%2x:%2x:%2x:%2x\n",
-                                                       (uint16_t) netinput [ 6],
-                                                       (uint16_t) netinput [ 7],
-                                                       (uint16_t) netinput [ 8],
-                                                       (uint16_t) netinput [ 9],
-                                                       (uint16_t) netinput [10],
-                                                       (uint16_t) netinput [11]);
+                                                       (intptr_t) netinput [ 6],
+                                                       (intptr_t) netinput [ 7],
+                                                       (intptr_t) netinput [ 8],
+                                                       (intptr_t) netinput [ 9],
+                                                       (intptr_t) netinput [10],
+                                                       (intptr_t) netinput [11]);
                                } else {
-                                       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_OFF);
                                        nethandler_llconly (netinput, netinputlen);
                                }
                        }
                } else {
                        bottom_show_fixed_msg (APP_LEVEL_BACKGROUNDED, FIXMSG_OFFLINE);
-                       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_ON);
                }
        }
 }
index 9f04741..db78f28 100644 (file)
@@ -47,6 +47,7 @@
 #include <0cpm/cpu.h>
 #include <0cpm/irq.h>
 #include <0cpm/timer.h>
+#include <0cpm/text.h>
 
 
 /*
 /******** BUFFER STATIC VARIABLES ********/
 
 // TODO: Configuration option would be nice for CONSBUFLEN (max 64k - 1)
-#define CONSBUFLEN 1024
+#define CONSBUFLEN 4096
 
 static char consbuf [CONSBUFLEN];
 
 static uint16_t rpos = 0, wpos = 0;
 
+static bool console_timer_is_running = false;
+
 
 /******** NETWORK INTERFACE ROUTINES ********/
 
@@ -104,9 +107,18 @@ static void trysend (void) {
 static irqtimer_t console_timer;
 static void netcons_interval (irq_t *tmr) {
        trysend ();
-       //TODO:Efficiency// if (rpos != wpos) {
+       if (rpos == wpos) {
+               console_timer_is_running = false;
+       }
+       if (console_timer_is_running) {
                irqtimer_restart ((irqtimer_t *) tmr, TIME_MSEC(20));
-       //TODO// }
+       }
+}
+static void ensure_console_timer_runs (void) {
+       if (netcons_connection && !console_timer_is_running) {
+               console_timer_is_running = true;
+               irqtimer_start (&console_timer, 0, netcons_interval, CPU_PRIO_LOW);
+       }
 }
 #endif
 
@@ -115,7 +127,7 @@ void netcons_connect (struct llc2 *cnx) {
 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
        trysend ();
 #else
-       irqtimer_start (&console_timer, 0, netcons_interval, CPU_PRIO_LOW);
+       ensure_console_timer_runs ();
 #endif
 }
 
@@ -142,6 +154,7 @@ static void cons_putchar (char c) {
                }
        }
        consbuf [wpos++] = c;
+       ensure_console_timer_runs ();
 }
 
 static void cons_putint (unsigned long int val, uint8_t base, uint8_t minpos) {
@@ -159,11 +172,15 @@ static void cons_putint (unsigned long int val, uint8_t base, uint8_t minpos) {
        } while (divisor > 0);
 }
 
+static textptr_t const nulltext = { "(NULL)", 6 };
+
 void bottom_console_vprintf (char *fmt, va_list argh) {
        char *fp = fmt;
        char *str;
        char ch;
        uint32_t intval;
+       textptr_t const *txt;
+       uint16_t idx;
        while (*fp) {
                uint8_t minpos = 0;
                bool val32bit = false;
@@ -193,11 +210,11 @@ void bottom_console_vprintf (char *fmt, va_list argh) {
                                minpos += fp [-1] - '0';
                                goto moremeuk;
                        case 'c':
-                               ch = (char) va_arg (argh, int);
+                               ch = (char) va_arg (argh, intptr_t);
                                cons_putchar (ch);
                                break;
                        case 's':
-                               str = va_arg (argh, char *);
+                               str = (char *) va_arg (argh, intptr_t);
                                if (str == NULL) {
                                        str = "(NULL)";
                                }
@@ -211,23 +228,33 @@ void bottom_console_vprintf (char *fmt, va_list argh) {
                                        cons_putchar (' ');
                                }
                                break;
+                       case 't':
+                               txt = (textptr_t *) va_arg (argh, intptr_t);
+                               idx = 0;
+                               if (textisnull (txt)) {
+                                       txt = &nulltext;
+                               }
+                               while (idx < txt->len) {
+                                       cons_putchar (txt->str [idx++]);
+                               }
+                               break;
                        case 'd':
                                if (val32bit) {
-                                       intval =            va_arg (argh, uint32_t);
+                                       intval = (uint32_t) va_arg (argh, intptr_t);
                                } else {
-                                       intval = (uint32_t) va_arg (argh, int     );
+                                       intval = (uint32_t) va_arg (argh, intptr_t);
                                }
                                cons_putint (intval, 10, minpos);
                                break;
                        case 'p':
-                               intval = (uint32_t) va_arg (argh, void *);
+                               intval = (uint32_t) va_arg (argh, intptr_t);
                                cons_putint (intval, 16, (minpos > 8)? minpos: 8);
                                break;
                        case 'x':
                                if (val32bit) {
-                                       intval =            va_arg (argh, uint32_t);
+                                       intval = (uint32_t) va_arg (argh, intptr_t);
                                } else {
-                                       intval = (uint32_t) va_arg (argh, int     );
+                                       intval = (uint32_t) va_arg (argh, intptr_t);
                                }
                                cons_putint (intval, 16, minpos);
                                break;
index cbeaa4e..c09d8d7 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <stdarg.h>
 
 #include <config.h>
 
 #include <0cpm/kbd.h>
 #include <0cpm/led.h>
 #include <0cpm/irq.h>  //TODO:ONLY-FOR-PATCHWORK-BELOW
-
+#include <0cpm/timer.h>
+#include <0cpm/cons.h>
+#include <0cpm/text.h>
+#include <0cpm/sip.h>
 
 
 /* TODO: shared variables for debugging's sake */
@@ -48,10 +52,6 @@ extern enum netcfgstate boot_state;
 static uint8_t nextround = 0x00;       // Local, only for test purposes
 
 
-void top_hook_update (bool offhook) {
-       /* Keep the linker happy */ ;
-}
-
 void top_can_play (uint16_t samples) {
        /* Keep the linker happy */ ;
 }
@@ -90,10 +90,96 @@ void top_button_release (void) {
        nextround = 0x00;
 }
 
+/* TODO: shared variables for debugging's sake */
+extern uint8_t          boot_retries;
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+#include <0cpm/netdb.h>
+extern struct ip6binding ip6binding [IP6BINDING_COUNT];
+uint8_t bad;
 
-extern priority_t cur_prio;
+irqtimer_t disptimer;
+int nibblepos = 0;
+
+bool registered = false;
+bool am_offhook = false;
+bool connected = false;
+bool talking = false;
+irqtimer_t regtimer;
+dialog_t *regdia = NULL;
+dialog_t *calldia = NULL;
+
+void invite_response (textptr_t *sip, textptr_t *sdp) {
+       textptr_t code, desc;
+       sip_splitline_response (sip, &code, &desc);
+       bottom_printf ("SIP response to INVITE received: %t %t\n", &code, &desc);
+       if (memcmp (code.str, "180", 3)) {
+               ht162x_led_set (11, 1, true); // ringing
+               ht162x_led_set (13, 0, true); // horn.off
+               ht162x_led_set (15, 0, true); // error.off
+       } else if (memcmp (code.str, "200", 3)) {
+               ht162x_led_set (11, 0, true); // ringing.off
+               ht162x_led_set (13, 1, true); // horn
+               ht162x_led_set (15, 0, true); // error.off
+       } else if (*code.str >= '4') {
+               ht162x_led_set (11, 0, true); // ringing.off
+               ht162x_led_set (13, 0, true); // horn.off
+               ht162x_led_set (15, 1, true); // error.on
+       } else {
+               ht162x_led_set (11, 0, true); // ringing.off
+               ht162x_led_set (13, 0, true); // horn.off
+               ht162x_led_set (15, 0, true); // error.off
+       }
+}
+
+void byeresponse (textptr_t *sip, textptr_t *sdp) {
+       textptr_t code, desc;
+       sip_splitline_response (sip, &code, &desc);
+       bottom_printf ("SIP response (TODO:process) to BYE received: %t %t\n", &code, &desc);
+}
+
+void cancelresponse (textptr_t *sip, textptr_t *sdp) {
+       textptr_t code, desc;
+       sip_splitline_response (sip, &code, &desc);
+       bottom_printf ("SIP response (TODO:process) to CANCEL received: %t %t\n", &code, &desc);
+}
+
+void register_response (textptr_t *sip, textptr_t *sdp) {
+       textptr_t code, desc;
+       sip_splitline_response (sip, &code, &desc);
+       bottom_printf ("SIP response to REGISTER: %t %t\n", &code, &desc);
+       if (regdia) {
+               sipdia_deallocate (regdia);
+               regdia = NULL;
+       }
+}
+
+void top_hook_update (bool offhook) {
+       bottom_led_set (LED_IDX_BACKLIGHT, offhook? LED_STABLE_ON: LED_STABLE_OFF);
+       if (am_offhook != offhook) {
+               am_offhook = offhook;
+               if (registered && offhook) {
+                       if (calldia) {
+                               sipdia_deallocate (calldia);
+                               calldia = NULL;
+                       }
+                       calldia = sipdia_allocate (NULL, true);
+                       if (calldia) {
+                               sipdia_setlinenr (calldia, 2);
+                               siptr_client (calldia, &invite_m, invite_response);
+                       }
+               }
+               if (registered && !offhook) {
+                       siptr_client (calldia,
+                                       talking? &bye_m: &cancel_m,
+                                       talking? byeresponse: cancelresponse);
+               }
+       }
+}
 
 
+extern priority_t cur_prio;
 
 /* TODO: bottom definitions shouldn't be copied in the top */
 extern volatile uint16_t IER0, IER1;
@@ -105,30 +191,36 @@ asm ("_IER1 .set 0x0045");
 #define REGBIT_IER1_TINT1       6
 #define REGBIT_IER1_DMAC0       2
 
-/* TODO: shared variables for debugging's sake */
-extern uint8_t          boot_retries;
-#include <0cpm/cpu.h>
-#include <0cpm/irq.h>
-#include <0cpm/timer.h>
-#include <0cpm/netdb.h>
-extern struct ip6binding ip6binding [IP6BINDING_COUNT];
-uint8_t bad;
+void register_phone (irq_t *tmr) {
+       if (regdia) {
+               bottom_printf ("Previous registration still active -- repeating the attempt\n");
+       }
+       if (!regdia) {
+               regdia = sipdia_allocate (NULL, true);
+       }
+       if (regdia) {
+               sipdia_setlinenr (regdia, 1);
+               bottom_printf ("Registering phone\n");
+               siptr_client (regdia, &register_m, register_response);
+       } else {
+               bottom_printf ("No free phone line -- not registering\n");
+       }
+       irqtimer_restart ((irqtimer_t *) tmr, TIME_SEC (300));
+}
 
-irqtimer_t disptimer;
-int nibblepos = 0;
-timing_t nextrot = 0;
 void show_info (irq_t *tmr) {
        int relpos;
-       //TODO:MANUAL_TIMER_AS_REAL_ONE_FAILS://
-#if 0
-       timing_t now = bottom_time ();
-       if (TIME_BEFORE (now, nextrot)) {
-               return;
-       }
-       nextrot += TIME_MSEC (1000);
+#if defined NEED_KBD_SCANNER_BETWEEN_KEYS || defined NEED_KBD_SCANNER_DURING_KEYPRESS
+       bottom_keyboard_scan ();
+#endif
+#if defined NEED_HOOK_SCANNER_WHEN_ONHOOK || defined NEED_HOOK_SCANNER_WHEN_OFFHOOK
+       bottom_hook_scan ();
 #endif
-bottom_keyboard_scan ();
        if ((ip6binding [0].flags & (I6B_DEFEND_ME | I6B_EXPIRED)) == I6B_DEFEND_ME) {
+               if (!registered) {
+                       registered = true;
+                       irqtimer_start (&regtimer, 0, register_phone, CPU_PRIO_LOW);
+               }
                for (relpos = 0; relpos < 12; relpos++) {
                        int actpos = relpos + nibblepos;
                        uint8_t ip6digit = 0x00;
@@ -157,49 +249,18 @@ bottom_keyboard_scan ();
  * nothing remains to be done, stop to wait for interrupts.
  */
 void top_main (void) {
-#if 0
-bool have_ipv4 (void);
-bool have_ipv6 (void);
-#endif
        uint8_t ipish [4] = { 0, 0, 0, 0 };
-       // TODO: init ();
-bottom_led_set (LED_IDX_SPEAKERPHONE, 1);
-bottom_led_set (LED_IDX_BACKLIGHT, 1);
-       // netdb_initialise ();
-       // TODO: cpu_add_closure (X);
-       // TODO: cpu_add_closure (Y);
-       // TODO: cpu_add_closure (Z);
-// IER0 &= ~(1 << REGBIT_IER0_INT0);
-// IER1 &= 0xffff; // ~(1 << REGBIT_IER1_DMAC0);
+       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_OFF);
+       netdb_initialise ();
+       sipdia_initialise ();
        bottom_critical_region_end ();
-       // netcore_bootstrap_initiate ();
-bottom_led_set (LED_IDX_SPEAKERPHONE, 0);
        irqtimer_start (&disptimer, TIME_MSEC (1000), show_info, CPU_PRIO_LOW);
        while (true) {
-#if 0
-               if (have_ipv6 ()) {
-                       bottom_show_ip6 (1, ip6binding [0].ip6addr);
-               } else {
-                       ipish [0] = (uint8_t) boot_state;
-                       ipish [1] = boot_retries;
-                       ipish [2] = bad;
-                       ipish [3] = have_ipv4 ()? 4: 0;
-                       bottom_show_ip4 (1, ipish);
-               }
-#else
-               //TODO:TIMERED// show_info (&disptimer);
-#endif
-bottom_led_set (LED_IDX_BACKLIGHT, bottom_phone_is_offhook ()? 1: 0);
-
                jobhopper ();
-bottom_led_set (LED_IDX_HANDSET, 1);
                //TODO// bottom_sleep_prepare ();
                //TODO// if (cur_prio == CPU_PRIO_ZERO) {
                        //TODO// bottom_sleep_commit (SLEEP_SNOOZE);
                //TODO// }
-#if defined NEED_KBD_SCANNER_BETWEEN_KEYS || defined NEED_KBD_SCANNER_DURING_KEYPRESS
-               //TODO:TIMERED// bottom_keyboard_scan ();
-#endif
        }
 }
 
index 1c1b361..977be9e 100644 (file)
@@ -1,2 +1,19 @@
+# Makefile for the 0cpm kernel (RTOS)
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
 
 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
index 9a35536..8f10644 100644 (file)
@@ -77,6 +77,12 @@ void irq_fire (irq_t *irq) {
  * 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.
+ *
+ * This routine triggers sampling of random seed material after having
+ * lowered the priority level of the CPU.  This ensures that it is
+ * never done while handling anything of high priority.  It also helps
+ * to do this after a series of jobs has been fulfilled, as its timing
+ * is not likely to be very predictable at that point.
  */
 void jobhopper (void) {
 void ht162x_putchar (uint8_t idx, uint8_t ch, bool notify);
@@ -135,6 +141,7 @@ void ht162x_putchar (uint8_t idx, uint8_t ch, bool notify);
                } else {
 //TODO:TEST// ht162x_putchar (0, '8', true);
                        cur_prio--;
+                       bottom_rndseed ();
                }
        }
 //TODO:TEST// ht162x_putchar (0, '9', true);
index 9d76d92..84ce1bd 100644 (file)
@@ -73,7 +73,9 @@ void led_set (led_idx_t ledidx, led_colour_t col, led_flashtime_t ft) {
        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);
+       if (ft != LED_FLASHTIME_NONE) {
+               irqtimer_start (&leds [ledidx].led_timer, ft, led_irq, CPU_PRIO_LOW);
+       }
 }
 
 
index d8fc597..bd04387 100644 (file)
@@ -84,7 +84,6 @@ static bool irqtimer_interrupt_occurred = false;
 /* Process a timer interrupt.
  */
 timing_t top_timer_expiration (timing_t exptime) {
-bottom_led_set (LED_IDX_BACKLIGHT, 0);
        if (irqtimer_interrupt_blocked) {
                irqtimer_interrupt_occurred = true;
        } else {
@@ -98,7 +97,6 @@ bottom_led_set (LED_IDX_BACKLIGHT, 0);
                        exptime = irqtimer_wait_queue->tmr_expiry;
                }
        }
-bottom_led_set (LED_IDX_BACKLIGHT, 1);
        return exptime;
 }
 
index 085f16c..3aaf86a 100644 (file)
@@ -103,7 +103,7 @@ void netcore_checksum_area (uint16_t *checksum, nint16_t *area, uint16_t len) {
                len -= 2;
        }
        if (len > 0) {
-               csum = csum + netget8 (*(nint8_t *)area);
+               csum = csum + (netget8 (*(nint8_t *)area) << 8);
        }
        csum = (csum & 0xffff) + (csum >> 16);
        csum = (csum & 0xffff) + (csum >> 16);
index 5bebd5f..2551d77 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <stdarg.h>            //TODO:TESTONLY//
 
 // #include <netinet/ip.h>
 // #include <netinet/ip6.h>
 #include <0cpm/netinet.h>
 #include <0cpm/netcmd.h>
 #include <0cpm/netfun.h>
-
+#include <0cpm/cons.h>         //TODO:TESTONLY//
 
 /* Utility functions for packet analyses */
 
 #define forward(t) here += sizeof (t); fromhere -= sizeof (t);
-#define store(i, t) if (fromhere < sizeof (t)) return NULL; mem [i] = (intptr_t) (t *) here;
+#define store(i, t) if (fromhere < sizeof (t)) return (intptr_t) NULL; mem [i] = (intptr_t) (t *) here;
 #define store_forward(i, t) store(i,t) forward(t)
 #ifndef get08
 #  define get08(o) ((uint8_t) here [o])
@@ -127,7 +128,7 @@ intptr_t netinput (uint8_t *pkt, uint16_t pktlen, intptr_t *mem) {
                        goto netin_LLC_sel;
                }
 #endif
-               return NULL;
+               return (intptr_t) NULL;
        }
 
        //
@@ -135,7 +136,7 @@ intptr_t netinput (uint8_t *pkt, uint16_t pktlen, intptr_t *mem) {
 netin_ARP_sel:
        arpproto = get16 (2);
 ht162x_led_set (4, 1, true); // Chinese top symbol
-       if (arpproto != 0x0800) return NULL;
+       if (arpproto != 0x0800) return (intptr_t) NULL;
 ht162x_led_set (5, 1, true); // Chinese bottom symbol
        store (MEM_ARP_HEAD, struct ether_arp);
        mem [MEM_IP4_SRC] = get32 (14);
@@ -150,14 +151,14 @@ ht162x_led_set (4, 0, true); // Chinese top symbol (off)
        case 0x06040002:
 ht162x_led_set (5, 0, true); // Chinese bottom symbol (off)
                         return (intptr_t) netdb_arp_reply;
-       default: return NULL;
+       default: return (intptr_t) NULL;
        }
 
        //
        // Decide what IPv4 protocol has come in
 netin_IP4_sel:
        store (MEM_IP4_HEAD, struct iphdr);
-       if (get08 (0) != 0x45) return NULL;
+       if (get08 (0) != 0x45) return (intptr_t) NULL;
        mem [MEM_IP4_SRC] = get32 (12);
        mem [MEM_IP4_DST] = get32 (15);
        ip4_ptype = get08 (9);
@@ -168,7 +169,7 @@ netin_IP4_sel:
        switch (ip4_ptype) {
        case 17: goto netin_UDP4_sel;
        case 1:  goto netin_ICMP4_sel;
-       default: return NULL;
+       default: return (intptr_t) NULL;
        }
 
        //
@@ -177,7 +178,7 @@ netin_ICMP4_sel:
        icmp4_type_code = get16 (0);
        switch (icmp4_type_code) {
        case 8 << 8: return (intptr_t) netreply_icmp4_echo_req;
-       default: return NULL;
+       default: return (intptr_t) NULL;
        }
 
        //
@@ -193,15 +194,15 @@ netin_UDP4_sel:
        if (udp4_src == 3653) goto netin_6BED4_sel;
        if ((udp4_src == 67) && (udp4_dst == 68)) goto netin_DHCP4_sel;
        if (udp4_dst == 53) goto netin_DNS_sel;
-       return NULL;
+       return (intptr_t) NULL;
 
        //
        // Handle incoming DHCP4 traffic
 netin_DHCP4_sel:
        store (MEM_DHCP4_HEAD, uint8_t);
-       if (fromhere < 236 + 4) return NULL;
+       if (fromhere < 236 + 4) return (intptr_t) NULL;
        dhcp4_magic_cookie = get32 (236);
-       if (dhcp4_magic_cookie != 0x63825363) return NULL;      // TODO: BOOTP?
+       if (dhcp4_magic_cookie != 0x63825363) return (intptr_t) NULL;   // TODO: BOOTP?
        here     += 236 + 4;
        fromhere -= 236 + 4;
        //
@@ -210,12 +211,12 @@ netin_DHCP4_sel:
                uint8_t dhcp4_option = get08 (0);
                if (dhcp4_option != 0) {
                        if (2 + get08 (1) > fromhere) {
-                               return NULL;
+                               return (intptr_t) NULL;
                        }
                }
                switch (dhcp4_option) {
                case 255: // Termination, not run into an offer
-                       return NULL;
+                       return (intptr_t) NULL;
                case 0: // Padding
                        here++;
                        continue;
@@ -224,7 +225,7 @@ netin_DHCP4_sel:
                        case 2: return (intptr_t) netreply_dhcp4_offer;
                        case 5: return (intptr_t) netdb_dhcp4_ack;
                        case 6: return (intptr_t) netdb_dhcp4_nak;
-                       default: return NULL;
+                       default: return (intptr_t) NULL;
                        }
                default:
                        here += 2 + here [1];
@@ -241,7 +242,7 @@ netin_6BED4_sel:
        //
        // Handle incoming IP6 traffic
 netin_IP6_sel:
-       if ((get08 (0) & 0xf0) != 0x60) return NULL;
+       if ((get08 (0) & 0xf0) != 0x60) return (intptr_t) NULL;
        store (MEM_IP6_HEAD, struct ip6_hdr);
        ip6_nxthdr = get08 (6);
        forward (struct ip6_hdr);
@@ -251,7 +252,7 @@ netin_IP6_sel:
        switch (ip6_nxthdr) {
        case 17: goto netin_UDP6_sel;
        case 58: goto netin_ICMP6_sel;
-       default: return NULL;
+       default: return (intptr_t) NULL;
        }
 
        //
@@ -260,13 +261,13 @@ netin_ICMP6_sel:
        store (MEM_ICMP6_HEAD, struct icmp6_hdr);
        icmp6_type_code = get16 (0);
        switch (icmp6_type_code) {
-       case ND_ROUTER_ADVERT << 8:     return (fromhere < 16)? NULL: (intptr_t) netdb_router_advertised;
-       case ND_NEIGHBOR_SOLICIT << 8:  return (fromhere < 24)? NULL: (intptr_t) netreply_icmp6_ngb_disc;
-       case ND_NEIGHBOR_ADVERT << 8:   return (fromhere < 24)? NULL: (intptr_t) netdb_neighbour_advertised;
-       case ND_REDIRECT << 8:          return (fromhere < 40)? NULL: NULL; /* TODO */
-       case ICMP6_ECHO_REQUEST << 8:   return (fromhere <  8)? NULL: (intptr_t) netreply_icmp6_echo_req;
-       case ICMP6_ECHO_REPLY << 8:     return (fromhere <  8)? NULL: NULL; /* Future option */
-       default: return NULL;
+       case ND_ROUTER_ADVERT << 8:     return (fromhere < 16)? (intptr_t) NULL: (intptr_t) netdb_router_advertised;
+       case ND_NEIGHBOR_SOLICIT << 8:  return (fromhere < 24)? (intptr_t) NULL: (intptr_t) netreply_icmp6_ngb_disc;
+       case ND_NEIGHBOR_ADVERT << 8:   return (fromhere < 24)? (intptr_t) NULL: (intptr_t) netdb_neighbour_advertised;
+       case ND_REDIRECT << 8:          return (fromhere < 40)? (intptr_t) NULL: (intptr_t) NULL; /* TODO */
+       case ICMP6_ECHO_REQUEST << 8:   return (fromhere <  8)? (intptr_t) NULL: (intptr_t) netreply_icmp6_echo_req;
+       case ICMP6_ECHO_REPLY << 8:     return (fromhere <  8)? (intptr_t) NULL: (intptr_t) NULL; /* Future option */
+       default: return (intptr_t) NULL;
        }
 
        //
@@ -301,7 +302,7 @@ netin_UDP6_sel:
        } else if (udp6_dst == 546) {
                goto netin_DHCP6_sel;
        } else {
-               return NULL;
+               return (intptr_t) NULL;
        }
        
 
@@ -312,12 +313,12 @@ netin_DHCP6_sel:
        case 2: return (intptr_t) netreply_dhcp6_advertise;
        case 7: return (intptr_t) netdb_dhcp6_reply;
        case 10: return (intptr_t) netdb_dhcp6_reconfigure;
-       default: return NULL;
+       default: return (intptr_t) NULL;
        }
 
 netin_DNS_sel:
        store (MEM_DNS_HEAD, uint8_t);
-       return NULL;    // TODO: Actually, split/process DNS
+       return (intptr_t) NULL; // TODO: Actually, split/process DNS
 
 netin_DNSSD_sel:
        store (MEM_DNSSD_HEAD, uint8_t);
@@ -341,7 +342,7 @@ netin_DNSSD_sel:
        }
 
        // Finally, a catchall that rejects anything that makes it to here
-       return NULL;
+       return (intptr_t) NULL;
 
 
 /********** OPTIONAL CODE FOR LLC: NETCONSOLE, FIRMWARE UPGRADES **********/
@@ -389,7 +390,7 @@ netin_LLC1_sel:
        default:
                break;
        }
-       return NULL;
+       return (intptr_t) NULL;
 
 #ifdef CONFIG_FUNCTION_NETCONSOLE
 netin_LLC2_sel:
@@ -399,7 +400,7 @@ netin_LLC2_sel:
        } else if ((mem [MEM_LLC_CMD] & 0x0007) == 0x0001) {
                return (intptr_t) netllc_console_receiverfeedback;
        }
-       return NULL;
+       return (intptr_t) NULL;
 }
 #endif // NETCONSOLE
 
index cc72516..8486413 100644 (file)
@@ -119,20 +119,20 @@ void nethandler_llconly (uint8_t *pkt, uint16_t pktlen) {
 #if 0
        if ((pktlen < 14) || (typelen < 46)) {
 #ifdef CONFIG_DEVEL
-               bottom_printf ("Unpadded packet length %d received\n", pktlen);
+               bottom_printf ("Unpadded packet length %d received\n", (intptr_t) pktlen);
 #endif
                return;
        }
 #endif
        if (typelen > 1500) {
 #ifdef CONFIG_DEVEL
-               bottom_printf ("Traffic is not LLC but protocol 0x%4x\n", typelen);
+               bottom_printf ("Traffic is not LLC but protocol 0x%4x\n", (intptr_t) typelen);
 #endif
                return;
        }
        if ((typelen > 64) && (typelen != pktlen - 14)) {
 #ifdef CONFIG_DEVEL
-               bottom_printf ("Illegal length %d received (pktlen = %d)\n", typelen, pktlen);
+               bottom_printf ("Illegal length %d received (pktlen = %d)\n", (intptr_t) typelen, (intptr_t) pktlen);
 #endif
                return;
        }
@@ -175,7 +175,7 @@ void nethandler_llconly (uint8_t *pkt, uint16_t pktlen) {
                        bottom_network_send (pkt, pktlen);
                } else {
 #ifdef CONFIG_DEVEL
-                       bottom_printf ("LLC1 UA is only used for TFTP, use SAP 68 and not %d\n", pkt [14]);
+                       bottom_printf ("LLC1 UA is only used for TFTP, use SAP 68 and not %d\n", (intptr_t) pkt [14]);
 #endif
                }
 #else
@@ -184,7 +184,7 @@ void nethandler_llconly (uint8_t *pkt, uint16_t pktlen) {
 #endif
        } else if (pkt [14] != 20) {
 #ifdef CONFIG_DEVEL
-               bottom_printf ("To access the network console, use SAP 20 and not %d\n", pkt [14]);
+               bottom_printf ("To access the network console, use SAP 20 and not %d\n", (intptr_t) pkt [14]);
 #endif
 #endif
        } else if (cmd == 0x7f) {                       // SABME (llc.connect)
@@ -213,7 +213,7 @@ void nethandler_llconly (uint8_t *pkt, uint16_t pktlen) {
        } else {
 #ifdef CONFIG_DEVEL
                bottom_printf ("Selfishly ignoring LLC traffic with cmd bytes 0x%2x%2x\n",
-                                       (uint16_t) pkt [16], (uint16_t) pkt [17]);
+                                       (intptr_t) pkt [16], (intptr_t) pkt [17]);
 #endif
        }
        if (ack) {
index d5fafef..1a8e3d8 100644 (file)
@@ -94,7 +94,7 @@ uint8_t *netreply_ip4 (uint8_t *pout, intptr_t *mem) {
        netset16 (ip4out->frag_off, 0x4000);    // Don't fragment
        memcpy (&ip4out->saddr, &ip4in->daddr, 4);
        memcpy (&ip4out->daddr, &ip4in->saddr, 4);
-       mem [MEM_IP4_HEAD] = (uintptr_t) ip4out;
+       mem [MEM_IP4_HEAD] = (intptr_t) ip4out;
        return &pout [sizeof (struct iphdr)];
 }
 
@@ -298,7 +298,7 @@ uint8_t *netreply_dhcp4_offer (uint8_t *pout, intptr_t *mem) {
                                        // mask, router, ntp?, time offset?.
                255                     // End Option
        };
-       bottom_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]);
+       bottom_printf ("DHCPv4 offer for %d.%d.%d.%d received -- requesting its activation\n", (intptr_t) yiaddrptr [0], (intptr_t) yiaddrptr [1], (intptr_t) yiaddrptr [2], (intptr_t) yiaddrptr [3]);
        // TODO: Validate offer to be mine
        mem [MEM_ETHER_DST] = (intptr_t) ether_broadcast;
        pout = netreply_udp4 (pout, mem);
index 5a26bd4..52c6b9e 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 
+#include <stdlib.h>
 #include <stdint.h>
 #include <stdbool.h>
 
index 4d93347..69bc5e2 100644 (file)
@@ -1,2 +1,19 @@
+# Makefile for the 0cpm phone
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
 
 objs-top-phone-y += src/phone/todo.o
diff --git a/src/sip/Makefile b/src/sip/Makefile
new file mode 100644 (file)
index 0000000..20b4659
--- /dev/null
@@ -0,0 +1,20 @@
+# Makefile for the 0cpm SIP stack
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+
+objs-top-kernel-y += src/sip/dialog.o src/sip/gen.o src/sip/parse.o src/sip/register.o src/sip/tract.o src/sip/app.o
+
diff --git a/src/sip/app.c b/src/sip/app.c
new file mode 100644 (file)
index 0000000..e3c76c7
--- /dev/null
@@ -0,0 +1,295 @@
+/* sipapp.c -- SIP application handling.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>    //TODO// TESTONLY
+#include <string.h>
+
+#include <config.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+#include <0cpm/led.h>  //TODO// TESTONLY
+#include <0cpm/netinet.h>
+#include <0cpm/netfun.h>
+#include <0cpm/netdb.h>
+#include <0cpm/text.h>
+#include <0cpm/sip.h>
+#include <0cpm/cons.h> //TODO// TESTONLY
+
+
+/*
+ * This is the application layer of SIP.
+ *
+ * Its main function is to be aware of the
+ * various methods and their meaning in terms
+ * of what they change to call state.  This
+ * knowledge is then communicated to the phone
+ * level, where user-understood things happen,
+ * such as ringing and exchanging sound.
+ *
+ * The main task of this layer could be said to
+ * state the desired state of the phone layer,
+ * triggered by the changes that are requested
+ * by way of SIP methods.  The phone has an
+ * idempotent API, meaning that it never adds
+ * anything if the same request is repeated.
+ * The reason for that is that the phone is
+ * simply asked by the application layer to set 
+ * its state so-and-so, instead of undergoing
+ * a change.
+ *
+ * It could be said that a better designed SIP
+ * protocol would have exchanged precisely such
+ * desired-remote-state messages, instead of
+ * changes to state.  Differential protocols
+ * always lead to requirements of checking that
+ * no changes have been lost, and none are
+ * applied twice.  This would have saved a lot
+ * of work in both transactional and application
+ * layers.  Alas, that is not how influential
+ * standards are made, not even in the IETF.
+ */
+
+
+/********** Global variables **********/
+
+
+sipappmap_t sipapp_fnmap [] = {
+       { &invite_m, sipapp_invite },
+       { &cancel_m, sipapp_cancel },
+       { &bye_m, sipapp_bye },
+       // { &hold_m, sipapp_hold },
+       // { &resume_m, sipapp_resume },
+       { &refer_m, sipapp_refer },
+       { &register_m, sipapp_register },
+       { NULL, NULL }
+};
+
+static textptr_t const report_notallowed = { "405 Not Allowed", 15 };
+static textptr_t const report_notimpl = { "501 Not Implemented", 19 };
+static textptr_t const report_busyhere = { "486 Busy Here", 13 };
+static textptr_t const report_trying = { "100 Trying", 10 };
+static textptr_t const report_ringing = { "180 Ringing", 11 };
+static textptr_t const report_notexist = { "481 Call/Transaction Does Not Exist", 35 };
+static textptr_t const report_notfound = { "404 Not Found", 13 };
+static textptr_t const report_notacceptable = { "488 Not Acceptable Here", 23 };
+static textptr_t const report_urischeme = { "416 Unsupported URI Scheme", 26 };
+static textptr_t const report_ok = { "200 OK", 6 };
+
+static textptr_t const proto_sip = { "sip", 3 };
+
+
+/*
+ * The SIP application layer handles the various SIP methods
+ * with knowledge about their individual meaning.  It runs on
+ * top of the dialog and tract layers, and represents the UAS
+ * interface to the phone.
+ *
+ * This is not the phone application; rather, it is the glue
+ * logic between the generic message exchange facilities in
+ * the siptr_XXX/sipdia_XXX modules and the user interface
+ * aspects covered by the phoneXXX modules.
+ *
+ * What this layer does is receive requests over SIP, interact
+ * with them as the phone's internals require.  For instance,
+ * upon receiving an INVITE, it will validate the incoming
+ * number and try to grab a free phone line, and fire off what
+ * it takes to make it ring.
+ *
+ * The task of the sipapp_XXX routines is to handle upcalls in
+ * response to SIP requests arriving over the network, in as
+ * short a time as possible.
+ */
+
+
+/* An incoming INVITE is a request to setup a dialog with the
+ * caller.  Before this may be done, the called number will be
+ * verified against the number configured for each line.  If
+ * the number exists, a line will be allocated and the phone
+ * will be made to ring.
+ */
+void sipapp_invite (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+       dialog_t *dia;
+       linenr_t line;
+       textptr_t proto, user, pass, dom;
+       uint16_t port;
+       bool stxok;
+       bool newcall;
+bottom_printf ("UAS received INVITE for %t\n", requri);
+       //
+       // If a transaction exists, then this is a resend
+       // TODO: transactions should never exist when receiving a method!!!
+       if (tr) {
+               siptr_respond_again (tr, coreheaders, sip);
+               return;
+       }
+       //
+       // Send a provisional response to the sender
+       //TODO:memhack-only-works-with-one-response-so-nothing-provisional// sipdia_respond (&report_trying, coreheaders, sip, NULL);
+       //
+       // Verify if the called party exists here
+       stxok = sip_components_inuri (requri, &proto, &user, &pass, &dom, &port);
+bottom_printf ("URI parser: retval=%d, proto=%t user=%t, pass=%t, dom=%t port=%d\n", (intptr_t) stxok, (intptr_t) &proto, (intptr_t) &user, (intptr_t) &pass, (intptr_t) &dom, (intptr_t) port);
+       stxok = stxok && !textisnull (&user);
+       stxok = stxok &&  textisnull (&pass);
+       stxok = stxok && (port == 5060);
+       if (!stxok) {
+               sipdia_respond (&report_notacceptable, coreheaders, sip, NULL);
+               return;
+       }
+       if (!texteq (&proto, &proto_sip)) {
+               sipdia_respond (&report_urischeme, coreheaders, sip, NULL);
+               return;
+       }
+{ textptr_t localuser = { "666*880", 7 };  // TODO:FIXED//
+       if (!texteq (&user, &localuser)) {
+               sipdia_respond (&report_notfound, coreheaders, sip, NULL);
+               return;
+       }
+}
+       //
+       // Assign a line to this call
+       newcall = (dia == NULL);
+       if (newcall) {
+               line = LINENR_NULL;
+       } else {
+               line = sipdia_getlinenr (dia);
+       }
+       if (line == LINENR_NULL) {
+               if (true) {     //TODO// Allocate a line number
+                       line = 3;
+               } else {
+                       sipdia_respond (&report_busyhere, coreheaders, sip, NULL);
+                       return;
+               }
+       }
+       //
+       // For new calls, create a new dialog
+       // For all calls, create a new transaction
+       if (newcall) {
+               dia = sipdia_allocate (coreheaders, false);
+               // dia==NULL result caught as tr==NULL below
+       }
+       tr = siptr_server (dia, &invite_m);
+       if (!tr) {
+               //TODO// Deallocate the line number
+               sipdia_respond (&report_busyhere, coreheaders, sip, NULL);
+               return;
+       }
+       //
+       // Connect the phone line and dialog to each other
+       sipdia_setlinenr (dia, line);
+       // TODO: phoneline_setdialog (line, dia);
+       //
+       // Setup for ringing and fire up the ringing process
+       // TODO: phoneline_setstate (line, PLS_RINGING);
+       // TODO: phoneringer_update (line);
+led_set (LED_IDX_MESSAGE, LED_FLASH (0,1), LED_FLASHTIME_FAST);
+       //
+       // Report "180 Ringing"
+       sipdia_respond (&report_ringing, coreheaders, sip, NULL);
+}
+
+/* An incoming call is cancelled.  The phone should stop
+ * ringing on any line that it might have made to ring.
+ */
+void sipapp_cancel (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+       linenr_t line;
+bottom_printf ("UAS received CANCEL for %t\n", requri);
+       //
+       // Ignore the request if this is not a dialog
+       if (!tr->dialog) {
+               sipdia_respond (&report_notexist, coreheaders, sip, NULL);
+               return;
+       }
+       line = tr->dialog->linenr;
+       //
+       // Stop ringing the phone line for this dialog
+       // TODO: phoneline_setstate (line, PLS_INACTIVE);
+       // TODO: phoneringer_update (line);
+bottom_led_set (LED_IDX_MESSAGE, LED_STABLE_ON);
+       //
+       // Disconnect the phone line and dialog from each other
+       //SAME_AS_BELOW// sipdia_setlinenr (tr->dialog, LINENR_NULL);
+       sipdia_deallocate (tr->dialog);
+       // TODO: phoneline_setdialog (line, NULL);
+       sipdia_respond (&report_ok, coreheaders, sip, NULL);
+}
+
+/* An active dialog is terminated.
+ */
+void sipapp_bye (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+       linenr_t line;
+bottom_printf ("UAS received BYE for %t\n", requri);
+       //
+       // Ignore the request if this is not a dialog
+       if (!tr->dialog) {
+               sipdia_respond (&report_notexist, coreheaders, sip, NULL);
+               return;
+       }
+       line = tr->dialog->linenr;
+       //
+       // Stop using the phone line for this dialog
+       // TODO: phoneline_setstate (line, PLS_HANGUP);
+       // TODO: phonetone_update (line);
+bottom_led_set (LED_IDX_MESSAGE, LED_STABLE_OFF);
+       //
+       // Disconnect the phone line and dialog from each other
+       //SAME_AS_BELOW// sipdia_setlinenr (tr->dialog, LINENR_NULL);
+       sipdia_deallocate (tr->dialog);
+       // TODO: phoneline_setdialog (line, NULL);
+       sipdia_respond (&report_ok, coreheaders, sip, NULL);
+}
+
+/* An active dialog is placed on hold.
+ * TODO - Is "on hold" any different from a re-INVITE?
+ */
+void sipapp_hold (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+       sipdia_respond (&report_notimpl, coreheaders, sip, NULL);
+}
+
+/* An active dialog that was placed on hold is being resumed.
+ * TODO - Is "resume" any different from a re-INVITE?
+ */
+void sipapp_resume (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+       sipdia_respond (&report_notimpl, coreheaders, sip, NULL);
+}
+
+/* A proposal to refer an active (possibly on-hold) dialog to another
+ * location is being proposed.
+ */
+void sipapp_refer (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+bottom_printf ("UAS received REFER for %t\n", requri);
+       sipdia_respond (&report_notimpl, coreheaders, sip, NULL);
+}
+
+/* An external entity is trying to register with this phone.  This may
+ * be a satellite that circles around a home base, possibly using a
+ * redirection mechanism like mobile IPv6.
+ */
+void sipapp_register (tract_t *tr, textptr_t const *requri, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *sdp) {
+bottom_printf ("UAS received REGISTER for %t\n", requri);
+       sipdia_respond (&report_notallowed, coreheaders, sip, NULL);
+}
+
diff --git a/src/sip/dialog.c b/src/sip/dialog.c
new file mode 100644 (file)
index 0000000..9225873
--- /dev/null
@@ -0,0 +1,761 @@
+/* sipdialog.c -- Manage dialogs (call legs).
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>    //TODO// TESTONLY
+#include <string.h>
+
+#include <config.h>
+
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+#include <0cpm/netinet.h>
+#include <0cpm/netfun.h>
+#include <0cpm/netdb.h>
+#include <0cpm/text.h>
+#include <0cpm/sip.h>
+#include <0cpm/cons.h> //TODO// TESTONLY
+
+
+
+/********** Global variables **********/
+
+
+#ifndef HAVE_DIALOGS
+#  define HAVE_DIALOGS (HAVE_LINES*8)
+#endif
+
+dialog_t dialog [HAVE_DIALOGS];
+
+extern packet_t wbuf, rbuf;
+extern struct ip6binding ip6binding [IP6BINDING_COUNT];
+
+textptr_t const coreheader_name [COREHDR_COUNT] = {
+       { "From", 4 },
+       { "To", 2 },
+       { "Call-id", 7 },
+       { "Contact", 7 },
+       { "Via", 3 },
+       { "Cseq", 4 },
+       { "Route", 5 }
+};
+
+
+/********** Fixed strings and other settings **********/
+
+
+textptr_t const invite_m = { "INVITE", 6 };
+textptr_t const cancel_m = { "CANCEL", 6 };
+textptr_t const bye_m    = { "BYE", 3 };
+textptr_t const refer_m  = { "REFER", 5 };
+textptr_t const ack_m    = { "ACK", 3 };
+extern textptr_t const register_m;
+
+static struct { textptr_t const *name; sipdia_climth const constructor; } climth_mapping [] = {
+       &invite_m,      sipdia_invite,
+       &cancel_m,      sipdia_cancel,
+       &bye_m,         sipdia_bye,
+       &refer_m,       sipdia_refer,
+       // &ack_m,      sipdia_ack,
+       &register_m,    sipreg_start,
+       NULL, NULL
+};
+
+static const textptr_t at = { "@", 1 };
+static const textptr_t colon = { ":", 1 };
+
+
+/********** GENERAL FUNCTIONS **********/
+
+
+void sipdia_initialise (void) {        //TODO:DEPRECATE:but_leave:siptr_init//
+       uint16_t dianr;
+       for (dianr = 0; dianr < HAVE_DIALOGS; dianr++) {
+               dialog [dianr].tract_usectr = 0;
+               dialog [dianr].linenr = LINENR_NULL;
+       }
+       siptr_initialise ();
+}
+
+
+/* Allocate a free dialog structure.  The dialog is kept alive
+ * until it has no active transactions running against it and
+ * it does not have an assigned phoneline.
+ * The coreheaders and myrequest values are used to initiate the
+ * tag_local, tag_remot and tag_callid identifying values.
+ * If the coreheaders are not provided, tag_local and tag_callid
+ * will be generated at random. (TODO)
+ */
+dialog_t *sipdia_allocate (coreheaders_t const *coreheaders, bool myrequest) {
+       uint16_t dianr = 0;
+       dialog_t *dia;
+       uint16_t strlen;
+       textptr_t const *lsrc;
+       textptr_t const *rsrc;
+       while (dianr < HAVE_DIALOGS) {
+               if ((dialog [dianr].tract_usectr == 0)
+               && (dialog [dianr].linenr == LINENR_NULL)) {
+                       dia = &dialog [dianr];
+                       break;
+               }
+               dianr++;
+       }
+       if (!dia) {
+               return NULL;
+       }
+       //
+       // Basic initialisation of the dialog
+       bzero (dia, sizeof (dialog_t));
+       dia->cseqnr = 101;      /* Some think this is fun */
+       //
+       // If no coreheaders are provided, fill in random values
+       if (!coreheaders) {
+               char *tagstr;
+               uint8_t random [4];
+               int ctr;
+               tagstr = dia->txt_callid;
+               dia->tag_callid.str = tagstr;
+               dia->tag_callid.len = 48;
+               bottom_rnd_pseudo (random, 4);
+               for (ctr = 0; ctr < 4; ctr++) {
+                       tagstr = hexcat (tagstr, random [ctr], 2);
+               }
+               tagstr = txtcat (tagstr, &at);
+               for (ctr = 0; ctr < 16; ctr += 2) {
+                       if (ctr > 0) {
+                               tagstr = txtcat (tagstr, &colon);
+                       }
+                       tagstr = hexcat (tagstr, ip6binding [0].ip6addr [ctr+0], 2);
+                       tagstr = hexcat (tagstr, ip6binding [0].ip6addr [ctr+1], 2);
+               }
+               tagstr = dia->txt_local;
+               dia->tag_local.str = tagstr;
+               dia->tag_local.len = 16;
+               tagstr = hexcat (tagstr, (uint32_t) bottom_time (), 8);
+               bottom_rnd_pseudo (random, 4);
+               for (ctr = 0; ctr < 4; ctr++) {
+                       tagstr = hexcat (tagstr, random [ctr], 2);
+               }
+               return dia;
+       }
+       //
+       // Clone local and remote values, and the callid
+       if (myrequest) {
+               lsrc = &coreheaders->from_tag;
+               rsrc = &coreheaders->to_tag;
+       } else {
+               lsrc = &coreheaders->to_tag;
+               rsrc = &coreheaders->from_tag;
+       }
+       if (!textisnull (lsrc)) {
+               strlen = lsrc->len;
+               if (strlen > sizeof (dia->txt_local)) {
+                       strlen = sizeof (dia->txt_local);
+               }
+               memcpy (dia->txt_local, lsrc->str, strlen);
+               dia->tag_local.str = dia->txt_local;
+               dia->tag_local.len = strlen;
+       }
+       if (!textisnull (rsrc)) {
+               strlen = rsrc->len;
+               if (strlen > sizeof (dia->txt_remot)) {
+                       strlen = sizeof (dia->txt_remot);
+               }
+               memcpy (dia->txt_remot, rsrc->str, strlen);
+               dia->tag_remot.str = dia->txt_remot;
+               dia->tag_remot.len = strlen;
+       }
+       if (!textisnull (&coreheaders->callid)) {
+               strlen = coreheaders->callid.len;
+               if (strlen > sizeof (dia->txt_callid)) {
+                       strlen = sizeof (dia->txt_callid);
+               }
+               memcpy (dia->txt_callid, coreheaders->callid.str, strlen);
+               dia->tag_callid.str = dia->txt_callid;
+               dia->tag_callid.len = strlen;
+       }
+       return dia;
+}
+
+
+/* Deallocate a dialog structure or, more accurately, prepare
+ * it for disbanding as soon as all transactions have ended.
+ * The core act here is to remove any setting of line number
+ * in this dialog. 
+ */
+void sipdia_deallocate (dialog_t *dia) {
+       dia->linenr = LINENR_NULL;
+}
+
+
+/* Move to the next Cseq: serial number in a dialog.
+ */
+void sipdia_next_cseq (dialog_t *dia) {
+       dia->cseqnr++;
+}
+
+/* Assign a phone line to a given dialog.  This may also be used
+ * to remove a phone line, by setting it to LINENR_NULL.  While
+ * the phone line is set to a value unequal to LINENR_NULL, the
+ * dialog will not be deallocated for reuse.
+ */
+void sipdia_setlinenr (dialog_t *dia, linenr_t ln) {
+       dia->linenr = ln;
+}
+
+/* Retrieve the phone line assigned to a given dialog. */
+linenr_t sipdia_getlinenr (dialog_t *dia) {
+       return dia->linenr;
+}
+
+
+/* Map a methodname to a dialog creating function.
+ */
+sipdia_climth sipdia_client_method (textptr_t const *mth) {
+       uint8_t mapidx = 0;
+       while (climth_mapping [mapidx].name) {
+               if (texteq (climth_mapping [mapidx].name, mth)) {
+                       return climth_mapping [mapidx].constructor;
+               }
+               mapidx++;
+       }
+       return NULL;
+}
+
+
+/********** DIALOG MANAGEMENT CLIENT FUNCTIONS **********/
+
+
+/* Start an outgoing dialog on a given dialog */
+bool sipdia_invite (dialog_t *dia, textptr_t *branch) {
+       intptr_t mem [MEM_NETVAR_COUNT];
+       char *msg;
+       uint16_t len;
+       //
+       // Create the UDP/IPv6 headers
+       bzero (mem, sizeof (mem));
+       mem [MEM_BINDING6] = (intptr_t) &ip6binding [0];
+       mem [MEM_ETHER_DST] = /* TODO:perhaps_in:netsend_udp6 */
+               (intptr_t) "\xbc\x05\x43\x70\x4f\xd3";  //FIXED: 10.0.0.1 Fritz!Box
+       mem [MEM_IP6_DST] = /* TODO:proxy.0cpm.net */
+               (intptr_t) "\x20\x01\x16\xf8\x00\x16\x00\x00\x00\x00\x05\x19\x10\x75\x33\x33";  //FIXED: welcome.0cpm.nl
+       mem [MEM_UDP6_DST_PORT] = 5060;
+       mem [MEM_UDP6_SRC_PORT] = 5060;
+       msg = (char *) netsend_udp6 (wbuf.data, mem);
+       //
+       // StartLine: INVITE to_uri SIP/2.0
+{textptr_t to_uri = { "sip:2020*880@welcome.0cpm.nl:5060", 33 };       //FIXED//
+       msg = sipgen_request (msg, &invite_m, &to_uri, branch);
+}
+       //
+       // Further headers
+       msg = sipgen_from (msg, dia);
+{textptr_t callee = { "2020*880", 8 }; //FIXED//
+       msg = sipgen_to (msg, &callee, dia);
+}
+msg = sipgen_contact (msg, dia);       //TODO// Only needed for SIPproxy64?
+       msg = sipgen_cseq (msg, &invite_m, dia->cseqnr);
+       msg = sipgen_call_id (msg, dia);
+       msg = sipgen_max_forwards (msg);
+       msg = sipgen_user_agent (msg);
+{textptr_t sdp = { "v=0\r\n"
+"o=- 111416154 111416154 IN IP4 10.0.0.186\r\n"
+"s=-\r\n"
+"c=IN IP6 2001:610:66f:5060::1\r\n"
+"t=0 0\r\n"
+"m=audio 14000 RTP/AVP 0 8 101\r\n"
+"a=rtpmap:0 PCMU/8000\r\n"
+"a=rtpmap:8 PCMA/8000\r\n"
+"a=rtpmap:101 telephone-event/8000\r\n"
+"a=fmtp:101 0-15\r\n"
+"a=ptime:20\r\n"
+"a=sendrecv\r\n" , 242 };
+       msg = sipgen_sdp_headers (msg, &sdp);
+       msg = sipgen_attach_body (msg, &sdp);
+}
+       //
+       // Send to upstream server for this dialog
+       mem [MEM_ALL_DONE] = (intptr_t) msg;
+       len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
+       if ((len > 0) && (len < 1500 + 18)) {
+               netcore_send_buffer (mem, wbuf.data);
+               return true;
+       } else {
+               return false;
+       }
+}
+
+/* Stop an outgoing dialog because nobody seems to be responding */
+bool sipdia_cancel (dialog_t *dia, textptr_t *branch) {
+       intptr_t mem [MEM_NETVAR_COUNT];
+       char *msg;
+       uint16_t len;
+       //
+       // Create the UDP/IPv6 headers
+       bzero (mem, sizeof (mem));
+       mem [MEM_BINDING6] = (intptr_t) &ip6binding [0];
+       mem [MEM_ETHER_DST] = /* TODO:perhaps_in:netsend_udp6 */
+               (intptr_t) "\xbc\x05\x43\x70\x4f\xd3";  //FIXED: 10.0.0.1 Fritz!Box
+       mem [MEM_IP6_DST] = /* TODO:proxy.0cpm.net */
+               (intptr_t) "\x20\x01\x16\xf8\x00\x16\x00\x00\x00\x00\x05\x19\x10\x75\x33\x33";  //FIXED: welcome.0cpm.nl
+       mem [MEM_UDP6_DST_PORT] = 5060;
+       mem [MEM_UDP6_SRC_PORT] = 5060;
+       msg = (char *) netsend_udp6 (wbuf.data, mem);
+       //
+       // StartLine: CANCEL to_uri SIP/2.0
+{textptr_t to_uri = { "sip:2020*880@welcome.0cpm.nl:5060", 33 };       //FIXED//
+       msg = sipgen_request (msg, &cancel_m, &to_uri, branch);
+}
+       //
+       // Further headers 
+       msg = sipgen_from (msg, dia);
+{textptr_t callee = { "2020*880", 8 }; //FIXED//
+       msg = sipgen_to (msg, &callee, dia);
+}
+msg = sipgen_contact (msg, dia);       //TODO// Only needed for SIPproxy64?
+       msg = sipgen_cseq (msg, &cancel_m, dia->cseqnr);
+       msg = sipgen_call_id (msg, dia);
+       msg = sipgen_max_forwards (msg);
+       msg = sipgen_user_agent (msg);
+       msg = sipgen_sdp_headers (msg, NULL);
+       msg = sipgen_attach_body (msg, NULL);
+       //
+       // Send to upstream server for this dialog
+       mem [MEM_ALL_DONE] = (intptr_t) msg;
+       len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
+       if ((len > 0) && (len < 1500 + 18)) {
+               netcore_send_buffer (mem, wbuf.data);
+               return true;
+       } else {
+               return false;
+       }
+}
+
+/* Accept an incoming dialog on a given dialog */
+bool sipdia_accept (dialog_t *dia, textptr_t *branch) {
+       // StartLine: SIP/2.0 200 OK
+       // Headers: 
+}
+
+/* Reject an incoming dialog on a given dialog */
+bool sipdia_reject (dialog_t *dia, textptr_t *branch) {
+       // StartLine: SIP/2.0 486 Busy Here
+       // Headers:
+}
+
+/* Enqueue an incoming dialog for later acceptance */
+bool sipdia_enqueue (dialog_t *dia, textptr_t *branch) {
+       // StartLine: SIP/2.0 182 Queued
+       // Headers:
+}
+
+/* Terminate a dialog on a given dialog */
+bool sipdia_bye (dialog_t *dia, textptr_t *branch) {
+       intptr_t mem [MEM_NETVAR_COUNT];
+       char *msg;
+       uint16_t len;
+       //
+       // Create the UDP/IPv6 headers
+       bzero (mem, sizeof (mem));
+       mem [MEM_BINDING6] = (intptr_t) &ip6binding [0];
+       mem [MEM_ETHER_DST] = /* TODO:perhaps_in:netsend_udp6 */
+               (intptr_t) "\xbc\x05\x43\x70\x4f\xd3";  //FIXED: 10.0.0.1 Fritz!Box
+       mem [MEM_IP6_DST] = /* TODO:proxy.0cpm.net */
+               (intptr_t) "\x20\x01\x16\xf8\x00\x16\x00\x00\x00\x00\x05\x19\x10\x75\x33\x33";  //FIXED: welcome.0cpm.nl
+       mem [MEM_UDP6_DST_PORT] = 5060;
+       mem [MEM_UDP6_SRC_PORT] = 5060;
+       msg = (char *) netsend_udp6 (wbuf.data, mem);
+       // StartLine: BYE to_uri SIP/2.0
+{textptr_t to_uri = { "sip:2020*880@welcome.0cpm.nl:5060", 33 };       //FIXED//
+       msg = sipgen_request (msg, &bye_m, &to_uri, branch);
+}
+       //
+       // Further headers 
+       msg = sipgen_from (msg, dia);
+{textptr_t callee = { "2020*880", 8 }; //FIXED//
+       msg = sipgen_to (msg, &callee, dia);
+}
+msg = sipgen_contact (msg, dia);       //TODO// Only needed for SIPproxy64?
+       msg = sipgen_cseq (msg, &bye_m, dia->cseqnr);
+       msg = sipgen_call_id (msg, dia);
+       msg = sipgen_max_forwards (msg);
+       msg = sipgen_user_agent (msg);
+       msg = sipgen_sdp_headers (msg, NULL);
+       msg = sipgen_attach_body (msg, NULL);
+       //
+       // Send to upstream server for this dialog
+       mem [MEM_ALL_DONE] = (intptr_t) msg;
+       len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
+       if ((len > 0) && (len < 1500 + 18)) {
+               netcore_send_buffer (mem, wbuf.data);
+               return true;
+       } else {
+               return false;
+       }
+}
+
+/* Place a dialog on hold */
+bool sipdia_hold (dialog_t *dia, textptr_t *branch) {
+}
+
+/* Take a dialog off hold */
+bool sipdia_resume (dialog_t *dia, textptr_t *branch) {
+}
+
+/* Redirect a call to another number */
+bool sipdia_refer (dialog_t *dia, textptr_t *branch) {
+       // StartLine: REFER to_uri SIP/2.0
+       // Headers:
+}
+
+
+/* Send a SIP ACK message */
+bool sip_ack (dialog_t *dia, textptr_t *branch) {
+       intptr_t mem [MEM_NETVAR_COUNT];
+       char *msg;
+       uint16_t len;
+bottom_printf ("Constructing ACK\n");
+       // Headers: 
+       //
+       // Create the UDP/IPv6 headers
+       bzero (mem, sizeof (mem));
+       mem [MEM_BINDING6] = (intptr_t) &ip6binding [0];
+       mem [MEM_ETHER_DST] = /* TODO:perhaps_in:netsend_udp6 */
+               (intptr_t) "\xbc\x05\x43\x70\x4f\xd3";  //FIXED: 10.0.0.1 Fritz!Box
+       mem [MEM_IP6_DST] = /* TODO:proxy.0cpm.net */
+               (intptr_t) "\x20\x01\x16\xf8\x00\x16\x00\x00\x00\x00\x05\x19\x10\x75\x33\x33";  //FIXED: welcome.0cpm.nl
+       mem [MEM_UDP6_DST_PORT] = 5060;
+       mem [MEM_UDP6_SRC_PORT] = 5060;
+       msg = (char *) netsend_udp6 (wbuf.data, mem);
+       //
+       // StartLine: ACK to_uri SIP/2.0
+{textptr_t to_uri = { "sip:2020*880@welcome.0cpm.nl", 28 };    //FIXED//
+       msg = sipgen_request (msg, &ack_m, &to_uri, branch);
+}
+       //
+       // Further headers
+       msg = sipgen_from (msg, dia);
+{textptr_t callee = { "2020*880", 8 }; //FIXED//
+       msg = sipgen_to (msg, &callee, dia);
+}
+       msg = sipgen_cseq (msg, &ack_m, dia->cseqnr);
+       msg = sipgen_call_id (msg, dia);
+       msg = sipgen_max_forwards (msg);
+       msg = sipgen_user_agent (msg);
+       msg = sipgen_sdp_headers (msg, NULL);
+       msg = sipgen_attach_body (msg, NULL);
+       //
+       // Send to upstream server for this dialog
+       mem [MEM_ALL_DONE] = (intptr_t) msg;
+       len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
+bottom_printf ("Length of ACK is %d\n", (intptr_t) len);
+       if ((len > 0) && (len < 1500 + 18)) {
+bottom_printf ("Sending ACK\n");
+               netcore_send_buffer (mem, wbuf.data);
+               return true;
+       } else {
+               return false;
+       }
+}
+
+
+/********** DIALOG MANAGEMENT SERVER FUNCTIONS **********/
+
+
+static const textptr_t parname_tag = { "tag", 3 };
+static const textptr_t parname_branch = { "branch", 6 };
+
+
+static intptr_t *uasmem; //TODO:FINDBETTERWAY//
+void sipdia_respond (textptr_t const *response, coreheaders_t const *coreheaders, textptr_t const *sip, textptr_t const *sdp_opt) {
+       uint8_t *pout;
+       //
+       // Construct the response
+       pout = wbuf.data;
+       pout = netreply_udp6 (pout, uasmem);
+       pout = (uint8_t *) sipgen_response ((char *) pout, response, coreheaders, sip, sdp_opt);
+       //
+       // Send the response
+       uasmem [MEM_ALL_DONE] = (intptr_t) pout;
+       netcore_send_buffer (uasmem, wbuf.data);
+}
+
+
+/* Extract the value of a named parameter from a SIP header */
+void extract_named_parameter (textptr_t const *hdr, textptr_t const *parname, textptr_t *parvalue) {
+       textptr_t p_nm, p_val;
+       textnullify (parvalue);
+       if ((!textisnull (hdr)) && sip_firstparm_inheader (hdr, &p_nm, &p_val)) {
+               do {
+bottom_printf ("Found parameter \"%t\" with value \"%t\"\n", (intptr_t) &p_nm, (intptr_t) &p_val);
+                       if (texteq (&p_nm, parname)) {
+                               memcpy (parvalue, &p_val, sizeof (textptr_t));
+                       }
+               } while (sip_nextparm_inheader (hdr, &p_nm, &p_val));
+       }
+}
+
+
+/* Parse the customary headers in a SIP message and return
+ * the headers and parts of them in coreheaders.  If something
+ * is not found or does not parse, it will be a null text.
+ */
+static void net_sip_h_parser (textptr_t const *sip, coreheaders_t *coreheaders) {
+       textptr_t h_nm, h_val;
+       //
+       // Initialise the header strings
+       bzero (coreheaders, sizeof (coreheaders_t));
+       coreheaders->sip = sip->str;
+       if (sip_firstheader (sip, &h_nm, &h_val)) {
+               do {
+                       int idx;
+                       for (idx = 0; idx < COREHDR_COUNT; idx++) {
+                               if (textisnull (&coreheaders->headers [idx]) && texteq (&h_nm, &coreheader_name [idx])) {
+                                       memcpy (&coreheaders->headers [idx], &h_val, sizeof (textptr_t));
+                               }
+                       }
+               } while (sip_nextheader (sip, &h_nm, &h_val));
+       }
+       //
+       // Extract tags and branch (or set them to NULL strings)
+       extract_named_parameter (&coreheaders->headers [COREHDR_FROM], &parname_tag,    &coreheaders->from_tag  );
+       extract_named_parameter (&coreheaders->headers [COREHDR_TO  ], &parname_tag,    &coreheaders->to_tag    );
+       extract_named_parameter (&coreheaders->headers [COREHDR_VIA ], &parname_branch, &coreheaders->via_branch);
+bottom_printf ("Received top From: header as %t\n", (intptr_t) &coreheaders->headers [COREHDR_FROM]);
+bottom_printf ("Received top Via:  header as %t\n", (intptr_t) &coreheaders->headers [COREHDR_VIA]);
+bottom_printf ("Received top Via: branch is %t\n", (intptr_t) &coreheaders->via_branch);
+       //
+       // Extract the callid value from its header (or set it to a NULL string)
+       memcpy (&coreheaders->callid, &coreheaders->headers [COREHDR_CALL_ID], sizeof (coreheaders->callid));
+       //
+       // Extract the cseq method from its header (or set it to a NULL string)
+       sip_split_cseq (&coreheaders->headers [COREHDR_CSEQ], &coreheaders->cseq_serialno, &coreheaders->cseq_method);
+bottom_printf ("Received Cseq: method is %t\n", (intptr_t) &coreheaders->cseq_method);
+}
+
+
+/* Try to find a matching dialog for the core headers.
+ * The values compared are the following:
+ *  - Call-id: header value
+ *  - From:tag
+ * The parameters influence the operation as follows:
+ *  - coreheaders provides the core headers and inferred info in the current request
+ *  - myrequest   determines if From:to and To:tag are local or remote
+ * This function returns NULL if no matching dialog could be found.
+ */
+dialog_t *sipdia_findmatch (coreheaders_t const *coreheaders, bool const myrequest) {
+       uint16_t dianr;
+       bool have_to_tag;
+       have_to_tag = !textisnull (&coreheaders->to_tag);
+       for (dianr = 0; dianr < HAVE_DIALOGS; dianr++) {
+               //
+               // Only work on allocated/used dialogs
+               if ((dialog [dianr].tract_usectr == 0) && (dialog [dianr].linenr == LINENR_NULL)) {
+                       continue;
+               }
+               //
+               // Ensure a matching Call-id:
+               if (!texteq (&dialog [dianr].tag_callid,   &coreheaders->callid  )) {
+bottom_printf ("Call-id: mismatch with \"%t\"\n", (intptr_t) &dialog [dianr].tag_callid);
+                       continue;
+               }
+               //
+               // Ensure a matching From:tag (which is always present)
+               if (!texteq (&coreheaders->from_tag, myrequest? &dialog [dianr].tag_local: &dialog [dianr].tag_remot)) {
+bottom_printf ("From:tag mismatch with \"%t\"\n", (intptr_t) &dialog [dianr].tag_local);
+                       continue;
+               }
+               //
+               // Ensure a matching To:tag if one is present
+               if (have_to_tag) {
+                       textptr_t *internal_to = myrequest? &dialog [dianr].tag_remot: &dialog [dianr].tag_local;
+                       if (!textisnull (internal_to)) {
+                               if (!texteq (&coreheaders->to_tag, internal_to)) {
+bottom_printf ("To:tag mismatch with \"%t\"\n", (intptr_t) internal_to);
+                                       continue;
+                               }
+                       } else {
+                               //
+                               // Possibly learn the To:tag from an incoming response message
+                               if (myrequest && texteq (&coreheaders->cseq_method, &invite_m) && (memcmp (coreheaders->sip, "SIP/2.0 2", 9) == 0)) {
+                                       uint16_t len = coreheaders->to_tag.len;
+                                       if (len > 128) {
+                                               len = 128;
+                                       }
+                                       memcpy (dialog [dianr].txt_remot, coreheaders->to_tag.str, len);
+                                       dialog [dianr].tag_remot.str = dialog [dianr].txt_remot;
+                                       dialog [dianr].tag_remot.len = len;
+bottom_printf ("To:tag learnt as \"%t\"\n", (intptr_t) &dialog [dianr].tag_remot);
+                               }
+                       }
+               }
+#if TODO_OLD_TIMES_REVIVE
+               if (!textisnull (&dialog [dianr].tag_remot)) {
+                       if (!texteq (&dialog [dianr].tag_remot, &coreheaders.to_tag  )) {
+bottom_printf ("To:tag mismatch with \"%t\"\n", (intptr_t) &dialog [dianr].tag_remot);
+                               continue;
+                       }
+               } else if (texteq (&coreheaders.cseq_method, &invite_m) && (coreheaders->sip [8] == '2')) {
+                       uint16_t len = coreheaders.to_tag.len;
+                       if (len > 128) {
+                               len = 128;
+                       }
+                       memcpy (dialog [dianr].txt_remot, coreheaders.to_tag.str, len);
+                       dialog [dianr].tag_remot.str = dialog [dianr].txt_remot;
+                       dialog [dianr].tag_remot.len = len;
+bottom_printf ("To:tag learnt as \"%t\"\n", (intptr_t) &dialog [dianr].tag_remot);
+               }
+#endif
+               //
+               // Found a match!  Now proudly return it
+bottom_printf ("sipdia_findmatch() Returning dialog found\n");
+               return &dialog [dianr];
+       }
+bottom_printf ("sipdia_findmatch(): No matching dialog found\n");
+       return NULL;
+}
+
+
+/* An incoming SIP request from the network.
+ * Depending on the to Via:branch, it may not be necessary to find the dialog.
+ * Instead, it may be possible to directly locate the transaction (which
+ * references a dialog) if the Via:branch starts with "z9hG4bK".  If this is
+ * the case, the dialog will be sent up to the transaction layer as NULL.
+ * Another case in which the dialog is sent up as NULL is when there is no
+ * dialog yet; a new one will be established only if the application layer
+ * wants to.  The transaction layer can easily distinguish between the
+ * two cases of a NULL dialog by looking at the Via:branch.
+ */
+static uint8_t *net_sip_request2uas (textptr_t *sip, textptr_t *attachment) {
+       bool rfc3261branch;
+       dialog_t *dia_opt = NULL;
+       coreheaders_t coreheaders;
+bottom_printf ("Called net_sip_request2uas()\n");
+       //
+       // Parse standard headers and extract tags: Call-id, From
+       net_sip_h_parser (sip, &coreheaders);
+       if (textisnull (&coreheaders.callid) || textisnull (&coreheaders.from_tag)) {
+               return NULL;
+       }
+       //
+       // Try to find the matching dialog, and trigger it;
+       // but first use the Via: tag to locate a transaction
+       rfc3261branch = (memcmp (coreheaders.via_branch.str, branchprefix_rfc3261.str, branchprefix_rfc3261.len) == 0);
+       if (!rfc3261branch) {
+               dia_opt = sipdia_findmatch (&coreheaders, false);
+       }
+       //
+       // If no match is found, disapprove all but INVITE methods
+       // TODO: This is an application layer check -- move it there
+       if ((!rfc3261branch) && (!dia_opt) && (memcmp (sip->str, "INVITE ", 7) != 0)) {
+bottom_printf ("Ignoring non-INVITE without pre-existing dialog\n");
+               return NULL;    // Out-of-dialog non-INVITE     //TODO: Send "481 Call/Transaction Does Not Exist"
+       }
+       //
+       // Approved, whether dialog is found or not
+       siptr_request2uas (dia_opt, &coreheaders, sip, attachment);
+       //
+       // Do not return a packet to send
+       return NULL;
+}
+
+/* An incoming SIP response from the network */
+static uint8_t *net_sip_response2uac (textptr_t *sip, textptr_t *attachment) {
+       dialog_t *dia;
+       coreheaders_t coreheaders;
+       //
+       // Parse standard headers and extract tags: Call-id, From, To
+       net_sip_h_parser (sip, &coreheaders);
+       if (textisnull (&coreheaders.callid) || textisnull (&coreheaders.from_tag) || textisnull (&coreheaders.via_branch) || textisnull (&coreheaders.cseq_method)) {
+               return NULL;
+       }
+       //
+       // Try to find the matching dialog, and trigger it
+       //TODO// First use the Via: tag to locate a transaction?
+       dia = sipdia_findmatch (&coreheaders, true);
+       if (dia) {
+               siptr_response2uac (dia, sip, attachment, &coreheaders.via_branch, &coreheaders.cseq_method);
+               return NULL;
+       }
+       //
+       // No match found -- so this response is invalid and will be ignored
+       return NULL;
+}
+
+
+/* An incoming SIP message from the network */
+uint8_t *net_sip (uint8_t *msg, intptr_t *mem) {
+       textptr_t sip, attachmt;
+       //
+       // Prepare SIP content for further parsing
+       sip.str = (char *) mem [MEM_SIP_HEAD];
+       sip.len = mem [MEM_ALL_DONE] - mem [MEM_SIP_HEAD];
+bottom_printf ("sip.len prior to scrubbing: %d\n", (intptr_t) sip.len);
+       sip_normalise (&sip, &attachmt);
+bottom_printf ("sip.len after    scrubbing: %d\n", (intptr_t) sip.len);
+bottom_printf ("SIP contents \"\"\"%t\"\"\"\n", &sip);
+       //
+       // Take notes of the most interesting headers
+       //
+       // Decide how to further handle the SIP content
+       if (memcmp (sip.str, "SIP/2.0 ", 8) == 0) {
+               //
+               // Verify the response header, then process it
+               if ((sip.str [ 8] < '0') || (sip.str [ 8] > '9')) {
+                       return NULL;
+               }
+               if ((sip.str [ 9] < '0') || (sip.str [ 9] > '9')) {
+                       return NULL;
+               }
+               if ((sip.str [10] < '0') || (sip.str [10] > '9')) {
+                       return NULL;
+               }
+               if (sip.str [11] != ' ') {
+                       return NULL;
+               }
+{int code = (sip.str [8] << 8) + (sip.str [9] << 4) + (sip.str [10]) - 0x3330;
+bottom_printf ("Received response: %x\n", (intptr_t) code);
+bottom_printf ("Response code in characters: %c%c%c\n", (intptr_t) sip.str [8] & 0xff, (intptr_t) sip.str [9] & 0xff, (intptr_t) sip.str [10] & 0xff);
+}
+               return net_sip_response2uac (&sip, &attachmt);
+       } else {
+               //
+               // Verify the request header, then process it
+               int sp = 0, idx = 0;
+               while ((idx < sip.len) && (sip.str [idx] != '\r') && (sip.str [idx] != '\n')) {
+                       if (sip.str [idx++] == ' ') {
+                               sp++;
+                       }
+               }
+               if ((sp != 2) || (idx < 10) || (idx == sip.len)) {
+                       return NULL;
+               }
+               if (memcmp (sip.str + idx - 7, "SIP/2.0", 7) != 0) {
+                       return NULL;
+               }
+{textptr_t x; x.str = sip.str; x.len = idx; bottom_printf ("Received request: %t\n", &x); }
+uasmem = mem; //TODO:FINDBETTERWAY//
+               return net_sip_request2uas (&sip, &attachmt);
+       }
+}
+
diff --git a/src/sip/parse.c b/src/sip/parse.c
new file mode 100644 (file)
index 0000000..7257348
--- /dev/null
@@ -0,0 +1,680 @@
+/* sipparse.c -- Normalisation and parsing of SIP messages.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <config.h>
+
+#include <0cpm/text.h>
+
+
+/*
+ * These are parser routines that process incoming SIP messages.
+ *
+ * The first thing to do with a new SIP message is to run the
+ * sip_normalise() routine on it.  This may make the message
+ * smaller, by wiping away extravagant spaces and so on.  The
+ * resulting message may have very long lines, as broken lines
+ * are also turned into single long lines.
+ *
+ * Note on UTF8:
+ *
+ * Note that UTF8 is not treated special in any way; the new
+ * characters introduced are all >= 0x80 and will not match the
+ * checks for specific ASCII characters below.  Also note that
+ * the SIP standard does not require knowledge of UTF8 in the
+ * message handling part of a phone.
+ */
+
+
+
+/* A mapping from short header names to long ones.  This is used
+ * to avoid ever showing short names to parser users, thus
+ * simplifying the application.
+ *
+ * Source: http://www.iana.org/assignments/sip-parameters
+ * (The mapping below incorporates RFCs until RFC 6228.)
+ */
+static textptr_t shortheadermap [26] = {
+       /* a */         { "Accept-contact", 14 },
+       /* b */         { "Referred-by", 11 },
+       /* c */         { "Content-type", 12 },
+       /* d */         { "Request-disposition", 19 },
+       /* e */         { "Content-encoding", 16 },
+       /* f */         { "From", 4 },
+       /* g */         { NULL, 0 },
+       /* h */         { NULL, 0 },
+       /* i */         { "Call-id", 7 },
+       /* j */         { "Reject-contact", 14 },
+       /* k */         { "Supported", 9 },
+       /* l */         { "Content-length", 14 },
+       /* m */         { "Contact", 7 },
+       /* n */         { "Identity-info", 13 },
+       /* o */         { "Event", 5 },
+       /* p */         { NULL, 0 },
+       /* q */         { NULL, 0 },
+       /* r */         { "Refer-to", 8 },
+       /* s */         { "Subject", 7 },
+       /* t */         { "To", 2 },
+       /* u */         { "Allow-events", 12 },
+       /* v */         { "Via", 3 },
+       /* w */         { NULL, 0 },
+       /* x */         { "Session-expires", 15 },
+       /* y */         { "Identity", 8 },
+       /* z */         { NULL, 0 }
+};
+
+
+/* Normalise a SIP message, as a prerequisite to further parsing:
+ *  - Replace combined whitespace by its canonical or simplest form
+ *  - Reunite split header lines
+ *  - Standardise header capitalisation
+ * An explicit pointer to the attachment will follow, if any.
+ */
+void sip_normalise (textptr_t *sipmsg, textptr_t *attachment) {
+       register uint16_t togo = sipmsg->len;
+       register uint16_t skip = 0;
+       register char *here = sipmsg->str;
+       register char *there = here;
+       //
+       // Initialise for empty attachment
+       textnullify (attachment);
+       //
+       // First, skip any leading CRLF content
+       while ((togo > 0) && ((*here == '\r') || (*here == '\n'))) {
+               here++;
+               togo--;
+       }
+       //
+       // Copy the start-line
+       while ((togo > 0) && (*here != '\r')) {
+               *there++ = *here++;
+               togo--;
+       }
+       //
+       // Copy the CRLF terminating the start-line
+       while ((togo > 0) && ((*here == '\r') || (*here == '\n'))) {
+               *there++ = *here++;
+               togo--;
+       }
+       //
+       // Then, process any additional content
+       // Do this by cloning from *here to *there, and
+       // at any step decreasing togo.  When skipping
+       // characters, only lower togo and increase skip
+       // instead of cloning.
+       while (togo > 0) {
+               //
+               // Recognise the end of headers, start of body
+               if ((*here == '\r') || (*here == '\n')) {
+                       sipmsg->len -= skip + togo;
+sipmsg->len += 2; //TODO:WHAT_IS_MISSING?!?//
+                       do {
+                               here++;
+                               togo--;
+                       } while ((togo > 0) && (*here == '\r') || (*here == '\n'));
+                       attachment->str = here;
+                       attachment->len = togo;
+                       break;
+               }
+               //
+               // Skip a headername, changing to "Its-name" or "i" capitalisation.
+               while ((togo > 0) && ((*here == ' ') || (*here == '\t'))) {
+                       here++;
+                       togo--;
+                       skip++;
+               }
+               //
+               // Clone the first of at least two letters in uppercase form
+               if ((togo > 1) && ((*here & 0xdf) >= 'A') && (((*here & 0xdf) <= 'Z'))) {
+                       if (((here [1] & 0xdf) >= 'A') && ((here [1] & 0xdf) <= 'Z')) {
+                               *there++ = *here++ & 0xdf;
+                               togo--;
+                       }
+               }
+               //
+               // Clone remaining letters and other non-whitespace in lowercase form
+               while ((togo > 0) && (*here != ' ') && (*here != '\t') && (*here != ':')) {
+                       if ((*here >= 'A') && ((*here <= 'Z'))) {
+                               *there++ = *here++ | 0x20;
+                       } else {
+                               *there++ = *here++;
+                       }
+                       togo--;
+               }
+               //
+               // After whitespace starts, chase for the colon 
+               while ((togo-- > 0) && (*here++ != ':')) {
+                       skip++;
+               }
+               *there++ = ':';
+               //
+               // Reduce whitespace after the colon, if any to one space
+               if ((togo > 0) && ((*here == ' ') || (*here == '\t'))) {
+                       *there++ = ' ';
+                       here++;
+                       togo--;
+                       while ((togo > 0) && ((*here == ' ') || (*here == '\t'))) {
+                               here++;
+                               skip++;
+                               togo--;
+                       }
+               }
+               //
+               // Clone header contents -- possibly spanning multiple lines
+               while (togo > 0) {
+                       //
+                       // Clone the remainder of the current line
+                       while ((togo > 0) && (*here != '\r') && (*here != '\n')) {
+                               *there++ = *here++;
+                               togo--;
+                       }
+                       //
+                       // See if the current line is continued on the next
+                       if ((togo >= 3) && (here [0] == '\r') && (here [1] == '\n')
+                                       && ((here [2] == ' ') || (here [2] == '\t'))) {
+                               //
+                               // Replace CRLF + whitespace by a single space
+                               *there++ = ' ';
+                               skip += 2;
+                               togo -= 3;
+                               while ((togo > 0) && (*here == ' ') || (*here == '\t')) {
+                                       here++;
+                                       togo--;
+                                       skip++;
+                               }
+                       } else {
+                               //
+                               // Clone CRLF and after that end cloning this header
+                               if ((togo > 0) && ((*here == '\r') || (*here == '\n'))) {
+                                       *there++ = *here++;
+                                       togo--;
+                                       if ((togo > 0) && (*here != here [-1]) && ((*here == '\r') || (*here == '\n'))) {
+                                               *there++ = *here++;
+                                               togo--;
+                                       }
+                               }
+                               break;  /* Done with this header */
+                       }
+               }
+       }
+       //
+       // Remove skipped chars from the SIP message
+       sipmsg->len -= skip;
+}
+
+/* Extract the components from a request's start-line.
+ */
+bool sip_splitline_request (textptr_t const *sipmsg, textptr_t *method, textptr_t *requri) {
+       uint16_t idx = 0;
+       // Note: Parsing upon reception ensured that the folowing will work
+       method->str = sipmsg->str;
+       while (method->str [idx] != ' ') {
+               idx++;
+       }
+       method->len = idx;
+       requri->str = sipmsg->str + idx + 1;
+       idx = 0;
+       while (requri->str [idx] != ' ') {
+               idx++;
+       }
+       requri->len = idx;
+       return (method->len > 0) && (requri->len > 0);
+}
+
+/* Extract the components from a response's start-line.
+ */
+bool sip_splitline_response (textptr_t const *sipmsg, textptr_t *code, textptr_t *descr) {
+       // Note: Parsing upon reception ensured that the following will work
+       uint16_t idx;
+       code->str = sipmsg->str + 8;
+       code->len = 3;
+       descr->str = sipmsg->str + 12;
+       idx = 12;
+       while ((idx < sipmsg->len) && (sipmsg->str [idx] != '\r') && (sipmsg->str [idx] != '\n')) {
+               idx++;
+       }
+       descr->len = idx - 12;
+       return true;
+}
+
+/* Find the first header in a given SIP message.  The result consists
+ * of the full name in "Standard-capitalisation" stored in headername,
+ * and the contents as a single line without termination stored in
+ * headerval.  The application should pickup headers in their order
+ * of appearance, and process them accordingly.
+ * Return true if the header was found.
+ */
+bool sip_firstheader (textptr_t const *sipmsg, textptr_t *headername, textptr_t *headerval) {
+       char *here   = sipmsg->str;
+       uint16_t len = sipmsg->len;
+       //
+       // First find the end of the start-line
+       while ((len > 0) && (*here != '\r') && (*here != '\n')) {
+               here++;
+               len--;
+       }
+       //
+       // Then share the sip_nextheader() algorithm
+       headerval->str = sipmsg->str;
+       headerval->len = sipmsg->len - len;
+       return sip_nextheader (sipmsg, headername, headerval);
+}
+
+/* Find the next header in a given SIP message.  The result consists
+ * of the full name in "Standard-capitalisation" stored in headername,
+ * and the contents as a single line without termination stored in
+ * headerval.  When called, these values contain the previous header,
+ * the one to find the following for.  The application should pickup
+ * headers in their order of appearance, and process them accordingly.
+ * Return true if the header was found.
+ */
+bool sip_nextheader (textptr_t const *sipmsg, textptr_t *headername, textptr_t *headerval) {
+       uint16_t togo = sipmsg->len - (((intptr_t) headerval->str) - ((intptr_t) sipmsg->str));
+       char *here = headerval->str + headerval->len;
+       //
+       // Skip the CRLF after the current headerval
+       while ((togo > 0) && ((*here == '\r') || (*here == '\n'))) {
+               here++;
+               togo--;
+       }
+       //
+       // Take note of the next header name
+       headername->str = here;
+       headername->len = togo;
+       while ((togo > 0) && (*here != ':')) {
+               here++;
+               togo--;
+       }
+       headername->len -= togo;
+       //
+       // Expand single-letter header names to their full form
+       if (headername->len == 1) {
+               char hnam = headername->str [0];
+               if ((hnam >= 'a') && (hnam <= 'z') && shortheadermap [hnam - 'a'].len) {
+                       headername->str = shortheadermap [hnam].str;
+                       headername->len = shortheadermap [hnam].len;
+               }
+       }
+       //
+       // Skip past ": " or else ":"
+       if (togo > 0) {
+               here++;
+               togo--;
+       }
+       if ((togo > 0) && (*here == ' ')) {
+               here++;
+               togo--;
+       }
+       //
+       // Take note of the next header value
+       headerval->str = here;
+       headerval->len = togo;
+       while ((togo > 0) && (*here != '\r') && (*here != '\n')) {
+               here++;
+               togo--;
+       }
+       headerval->len -= togo;
+       //
+       // There should always be a CRLF left to skip; if not, fail
+       return (togo > 0);
+}
+
+
+/* Find the first SIP URI in a header.  The result is stored in uri.
+ * The URI is assumed to be enclosed in angular brackets of, if that
+ * is not the case, it is assumed to be a list separated by commas,
+ * or semicolons.
+ * Return true if the URI was found.
+ */
+bool sip_firsturi_inheader (textptr_t const *siphdr, textptr_t *uri) {
+       register char *hdr    = siphdr->str;
+       register uint16_t max = siphdr->len;
+       register uint16_t len = 0;
+       //
+       // Find if angular brackets occur in the header
+       while (len < max) {
+               if (hdr [len++] == '<') {
+                       //
+                       // Mark the area between < and >
+                       uri->str = hdr + len;
+                       uri->len = ~len; // Remember to add 1 later
+                       //
+                       // Count the length between < and >
+                       while (len < max) {
+                               if (hdr [len++] == '>') {
+                                       uri->len += len; // 1 too high
+                                       return true;
+                               }
+                       }
+                       //
+                       // Did not find closing angular bracket
+                       return false;
+               }
+       }
+       //
+       // Since angular brackets do not occur, find the first URI
+       len = 0;
+       if (hdr [len] == ' ') {
+               len++;
+               uri->str = hdr + 1;
+               uri->len = ~0;  // offset -1
+       } else {
+               uri->str = hdr;
+               uri->len = 0;
+       }
+       while ((len < max) &&
+                       (hdr [len] != ' ' ) && (hdr [len] != '\t') &&
+                       (hdr [len] != '\r') && (hdr [len] != '\n') &&
+                       (hdr [len] != ';' ) && (hdr [len] != ',' )) {
+               len++;
+       }
+       uri->len += len;
+       //
+       // Return success if a URI was found
+       return (uri->len != 0);
+}
+
+/* Find the next SIP URI in a header.  The result is stored in uri.
+ * The URI is assumed to be enclosed in angular brackets of, if that
+ * is not the case, it is assumed to be a list separated by commas,
+ * or semicolons.
+ * Return true if the URI was found.
+ */
+bool sip_nexturi_inheader (textptr_t const *siphdr, textptr_t *uri) {
+       //
+       // Construct a fake SIP header to cover the remainder
+       textptr_t remainder;
+       remainder.str = uri->str + uri->len;
+       remainder.len = (uint16_t) ((intptr_t) siphdr->str - (intptr_t) siphdr->str);
+       //
+       // Find the next URI as it were the first URI in the remainder
+       return sip_firsturi_inheader (&remainder, uri);
+}
+
+/* Find the first header parameter.  The name of the parameter is stored
+ * in parnm, the value in parval.  Return true if the parameter was found.
+ */
+bool sip_firstparm_inheader (textptr_t const *siphdr, textptr_t *parnm, textptr_t *parval) {
+       bool sip_firstparm_inuri (textptr_t const *uri, textptr_t *parnm, textptr_t *parval); //TODO//
+       //TODO// Should first skip any <enclosed_URIs> in the header
+       return sip_firstparm_inuri (siphdr, parnm, parval);
+}
+
+/* Find the next header parameter.  The name of the parameter is stored
+ * in parnm, the value in parval.  Return true if the parameter was found.
+ */
+bool sip_nextparm_inheader (textptr_t const *siphdr, textptr_t *parnm, textptr_t *parval) {
+       bool sip_nextparm_inuri (textptr_t const *uri, textptr_t *parnm, textptr_t *parval); //TODO//
+       return sip_nextparm_inuri (siphdr, parnm, parval);
+}
+
+
+/* Common code for finding a parameter in a URI, used by the functions
+ * sip_firstparm_inuri() and sip_nextparm_inuri ().  These routines
+ * invoke this final part after having located the next parameter.
+ */
+static bool sip_foundparm_inuri (char *here, uint16_t togo, textptr_t *parnm, textptr_t *parval) {
+       //
+       // First, store the found location for a parameter name
+       parnm->str = here;
+       parnm->len = togo;
+       //
+       // Second, chase for an equals sign or semicolon
+       while ((togo > 0) && (*here != ';') && (*here != '=')) {
+               here++;
+               togo--;
+       }
+       parnm->len -= togo;
+       if (parnm->len == 0) {
+               textnullify (parnm);
+               return false;
+       }
+       //
+       // End now if there is no equals sign
+       if ((togo == 0) || (*here != '=')) {
+               parval->str = here;
+               parval->len = 0;
+               return (parnm->len != 0);
+       }
+       //
+       // Skip the equals sign and find the value
+       here++;
+       togo--;
+       parval->str = here;
+       parval->len = togo;
+       // 
+       // Chase the value of this parameter
+       while ((togo > 0) && (*here != ';')) {
+               here++;
+               togo--;
+       }
+       parval->len -= togo;
+       if (parval->len == 0) {
+               textnullify (parval);
+       }
+       //
+       // End in success, but with varying parval contents
+       return true;
+}
+
+/* Find the first URI parameter.  The name of the parameter is stored
+ * in parnm, the value in parval.  Return true if the parameter was found.
+ */
+bool sip_firstparm_inuri (textptr_t const *uri, textptr_t *parnm, textptr_t *parval) {
+       char *here    = uri->str;
+       uint16_t togo = uri->len;
+       //
+       // First chase for a semicolon
+       while ((togo > 0) && (*here != ';')) {
+               here++;
+               togo--;
+       }
+       //
+       // See if the end has been reached
+       if (togo == 0) {
+               return false;
+       }
+       //
+       // Skip past the semicolon
+       here++;
+       togo--;
+       //
+       // Found the parameter name, process in general routine
+       return sip_foundparm_inuri (here, togo, parnm, parval);
+}
+
+/* Find the next URI parameter.  The name of the parameter is stored
+ * in parnm, the value in parval.  Return true if the parameter was found.
+ */
+bool sip_nextparm_inuri (textptr_t const *uri, textptr_t *parnm, textptr_t *parval) {
+       char *here = parval->str + parval->len;
+       int16_t togo = uri->len - ((intptr_t) here - (intptr_t) uri->str);
+       //
+       // Skip the current semicolon if there is one (so, before the end)
+       if ((togo == 0) || (*here != ';')) {
+               return false;
+       }
+       togo--;
+       here++;
+       //
+       // Found the parameter name, process in general routine
+       return sip_foundparm_inuri (here, togo, parnm, parval);
+}
+
+/* Take a SIP URI apart into its constituent components.
+ * Parameters are stripped off, but their analysis should
+ * be done with the sip_first/nextparm_inuri() functions.
+ * Return false if the URI has a bad format.  Note that a
+ * SIP URI need not have a user, so check it after this
+ * call if you need the username part.
+ */
+bool sip_components_inuri (textptr_t const *uri,
+                               textptr_t *proto,
+                               textptr_t *user,
+                               textptr_t *pass,
+                               textptr_t *dom,
+                               uint16_t *port) {
+       char *here = uri->str;
+       uint16_t togo = uri->len;
+       //
+       // Parse out the protocol
+       proto->str = here;
+       proto->len = togo;
+       while ((togo > 0) && (*here != ':')) {
+               togo--;
+               here++;
+       }
+       if ((proto->len -= togo) == 0) {
+               // Fatal if no proto present
+               // textnullify (proto);
+               return false;
+       }
+       //
+       // Skip the ':' after the protocol
+       if (togo > 0) {
+               togo--;
+               here++;
+       }
+       //
+       // Parse out the uesrname
+       user->str = here;
+       user->len = togo;
+       while ((togo > 0) && (*here != '@') && (*here != ':')) {
+               togo--;
+               here++;
+       }
+       if ((user->len -= togo) == 0) {
+               // Non-fatal if no user present -- so check!
+               textnullify (user);
+       }
+       if ((togo > 0) && (*here == ':')) {
+               here++;
+               togo--;
+               //
+               // Parse out the password
+               pass->str = here;
+               pass->len = togo;
+               while ((togo > 0) && (*here != '@')) {
+                       here++;
+                       togo--;
+               }
+               // Zero length can be meaningful, so allow it
+               pass->len -= togo;
+       } else {
+               //
+               // Set a null password -- which is non-fatal
+               textnullify (pass);
+       }
+       //
+       // Skip the '@' between userpart and domainname
+       if ((togo > 0) && (*here == '@')) {
+               here++;
+               togo--;
+       }
+       //
+       // Parse out the domain name (treat '?' as an end also)
+       dom->str = here;
+       dom->len = togo;
+       if ((togo > 0) && (*here != '[')) {
+               while ((togo > 0) && (*here != ':') && (*here != ';') && (*here != '?')) {
+                       here++;
+                       togo--;
+               }
+       } else {
+               while ((togo > 0) && (*here != ']')) {
+                       here++;
+                       togo--;
+               }
+               if (togo > 0) {
+                       here++;
+                       togo--;
+               }
+       }
+       if ((dom->len -= togo) == 0) {
+               // An empty domain is fatal
+               return false;
+       }
+       //
+       // Parse out an optional port number
+       if ((togo > 0) && (*here == ':')) {
+               here++;
+               togo--;
+               *port = 0;
+               while ((togo > 0) && (*here >= '0') && (*here <= '9')) {
+                       *port *= 10;
+                       *port += *here - '0';
+                       here++;
+                       togo--;
+               }
+               if (*port == 0) {
+                       // UDP does not allow port 0 to occur
+                       return false;
+               }
+               if ((togo > 0) && (*here != ';') && (*here != '?')) {
+                       // Any remaining text must hold options
+                       return false;
+               }
+       } else {
+               // Fall back to default port 5060 for SIP
+               *port = 5060;
+       }
+       //
+       // No fatal component failures occurred, so report success
+       return true;
+}
+
+/* Split a Cseq: header into its two parts: a serial number and a method name.
+ * Return false (and set NULL string and 0 value) on error.
+ */
+bool sip_split_cseq (textptr_t const *cseq, uint32_t *serial, textptr_t *mth) {
+       char *here = cseq->str;
+       uint16_t togo = cseq->len;
+       textnullify (mth);
+       //
+       // First parse the numeric part
+       *serial = 0;
+       if (!here) {
+               return false;
+       }
+       while ((togo > 0) && (*here >= '0') && (*here <= '9')) {
+               *serial *= 10;
+               *serial += *here++ - '0';
+               togo--;
+       }
+       if ((togo == 0) || (*here != ' ')) {
+               *serial = 0;
+               return false;
+       }
+       //
+       // Then skip the space
+       here++;
+       togo--;
+       //
+       // Take in the method name (which is assumed to cover the rest of the line)
+       mth->str = here;
+       mth->len = togo;
+       //
+       // Succeeded -- return the two components and signal success
+       return true;
+}
+
diff --git a/src/sip/register.c b/src/sip/register.c
new file mode 100644 (file)
index 0000000..e83fb81
--- /dev/null
@@ -0,0 +1,112 @@
+/* sipregister.c -- Register phone lines with location services.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>  //TODO// ONLY_FOR_TESTING
+
+#include <config.h>
+
+#include <0cpm/text.h>
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+#include <0cpm/netinet.h>
+#include <0cpm/netfun.h>
+#include <0cpm/netdb.h>
+#include <0cpm/sip.h>
+#include <0cpm/cons.h>  //TODO// ONLY_FOR_TESTING
+
+
+/********** Fixed strings and other settings **********/
+
+
+textptr_t const register_m = { "REGISTER", 8 };
+
+
+/********** Global variables **********/
+
+
+extern struct ip6binding ip6binding [IP6BINDING_COUNT];
+extern packet_t wbuf, rbuf;
+
+
+/********** Registration support functions **********/
+
+
+/* Parameterised registration of a phone line with an account */
+static bool sipreg_generic (dialog_t *dia, uint32_t expires, textptr_t *branch) {
+       intptr_t mem [MEM_NETVAR_COUNT];
+       char *msg;
+       uint16_t len;
+       //
+       // Create the UDP/IPv6 headers
+       bzero (mem, sizeof (mem));
+       mem [MEM_BINDING6] = (intptr_t) &ip6binding [0];
+       mem [MEM_ETHER_DST] = /* TODO:perhaps_in:netsend_udp6 */
+               (intptr_t) "\xbc\x05\x43\x70\x4f\xd3";  //FIXED: 10.0.0.1 Fritz!Box
+       mem [MEM_IP6_DST] = /* TODO:proxy.0cpm.net */
+               (intptr_t) "\x20\x01\x16\xf8\x00\x16\x00\x00\x00\x00\x05\x19\x10\x75\x33\x33";  //FIXED: welcome.0cpm.nl
+       mem [MEM_UDP6_DST_PORT] = 5060;
+       mem [MEM_UDP6_SRC_PORT] = 5060;
+       msg = (char *) netsend_udp6 (wbuf.data, mem);
+       //
+       // StartLine: REGISTER sip:lineservice SIP/2.0
+{textptr_t reghost_uri = { "sip:welcome.0cpm.nl", 19 };        //FIXED//
+       msg = sipgen_request (msg, &register_m, &reghost_uri, branch);
+}
+       //
+       // Further headers
+       msg = sipgen_from (msg, dia);
+       msg = sipgen_to_register (msg, dia);
+       msg = sipgen_contact (msg, dia);
+       msg = sipgen_expires (msg, expires);
+       msg = sipgen_cseq (msg, &register_m, dia->cseqnr);
+       msg = sipgen_call_id (msg, dia);
+       msg = sipgen_max_forwards (msg);
+       msg = sipgen_user_agent (msg);
+       msg = sipgen_sdp_headers (msg, NULL);
+       msg = sipgen_attach_body (msg, NULL);
+       //
+       // Send to upstream server for this dialog
+       mem [MEM_ALL_DONE] = (intptr_t) msg;
+       len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
+       if ((len > 0) && (len < 1500 + 18)) {
+               netcore_send_buffer (mem, wbuf.data);
+// wbuf.data [len] = 0;
+// bottom_printf ("Sent SIP message: %t\n", wbuf);
+bottom_printf ("Sent SIP REGISTER message\n");
+               return true;
+       } else {
+               return false;
+       }
+}
+
+/* Start registration of a dialog with an account */
+bool sipreg_start (dialog_t *dia, textptr_t *branch) {
+       return sipreg_generic (dia, 3600, branch);
+}
+
+/* Stop registering a dialog with an account */
+bool sipreg_stop (dialog_t *dia, textptr_t *branch) {
+       return sipreg_generic (dia, 0, branch);
+}
+
diff --git a/src/sip/tract.c b/src/sip/tract.c
new file mode 100644 (file)
index 0000000..3f9aff2
--- /dev/null
@@ -0,0 +1,741 @@
+/* siptract.c -- Transaction layer for SIP.
+ *
+ * This file is part of 0cpm Firmerware.
+ *
+ * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+ *
+ * 0cpm Firmerware is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * 0cpm Firmerware is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>    //TODO// TESTING_ONLY
+
+#include <config.h>
+
+#include <0cpm/text.h>
+#include <0cpm/cpu.h>
+#include <0cpm/irq.h>
+#include <0cpm/timer.h>
+#include <0cpm/sip.h>
+#include <0cpm/cons.h> //TODO// TESTING_ONLY
+
+
+/* Timer values, defaults applied according to page 265 of RFC 3261 */
+
+#define T1 TIME_MSEC(500)
+#define T2 TIME_MSEC(4000)
+#define T4 TIME_MSEC(5000)
+
+#define TA_init T1
+#define TB (64*T1)
+#define TC TIME_SEC(200)
+#define TD TIME_SEC (45)
+#define TE_init T1
+#define TF (64*T1)
+#define TG_init T1
+#define TH (64*T1)
+#define TI T4
+#define TJ (64*T1)
+#define TK T4
+
+
+/*
+ * SIP transactions are defined in RFC 3261.  There are four variants,
+ * due to two variation factors: UAC or UAS role, and INIVITE or other.
+ *
+ * The procedures below initiate the desired flavour of SIP transaction
+ * based on the state diagram.  They initiate a chain of interrupts,
+ * several of which may be timer interrupts, and aim to eventually
+ * report back to the upper layer that wants to assume transactional
+ * semantics for the delivery of a message.
+ *
+ * The actual message to send is generated as part of the transaction,
+ * so all the information required to construct it must have been
+ * setup for the line addressed.  As a result, this module will only
+ * need the line number to be able to act.
+ *
+ * There should not be multiple processes running at the same time
+ * on the same dialog/line.  Also, before initiating a dialog for
+ * submission of an INVITE in the UAC role, the dialog must have been
+ * allocated.  Upon reception of an INVITE in the UAS role, the dialog
+ * will be allocated on the fly.
+ */
+
+
+extern dialog_t dialog [HAVE_LINES];
+
+
+#ifndef HAVE_TRACTS
+#  define HAVE_TRACTS (HAVE_LINES*16)
+#endif
+
+static tract_t trs [HAVE_TRACTS];
+static uint32_t trnr_next = 0;
+
+/* A few simple reports to relay back to the transaction user */
+//TODO// Change form by removing "SIP/2.0 " prefix
+static textptr_t const report_timeout = { "SIP/2.0 408 Request Timeout\r\n\r\n", 31 };
+static textptr_t const report_badreq  = { "SIP/2.0 400 Bad Request\r\n\r\n", 27 };
+static textptr_t const report_tmpunavail = { "SIP/2.0 480 Temporarily Unavailable\r\n\r\n", 39 };
+static textptr_t const report_mthnotallow = { "SIP/2.0 405 Method Not Allowed\r\n\r\n", 34 };
+static textptr_t const report_calltrnotexist = { "SIP/2.0 481 Call/Transaction Does Not Exist", 43 };
+
+/* A definition of a textptr_t constant with the branch prefix from RFC 3261 */
+const textptr_t branchprefix_rfc3261 = { BRANCHPREFIX_RFC3261, BRANCHPREFIXLEN_RFC3261 };
+
+
+
+/********** GENERAL FUNCTIONS **********/
+
+
+/* Setup the transaction layer */
+void siptr_initialise (void) {
+       uint16_t i;
+       for (i=0; i<HAVE_TRACTS; i++) {
+               trs [i].state = TRS_TERMINATED;
+       }
+}
+
+/* Find a free transaction (looking for state == TRS_TERMINATED) */
+static tract_t *tract_find_free (void) {
+       uint16_t trnr = trnr_next;
+       uint16_t ctr;
+       for (ctr = HAVE_TRACTS; ctr > 0; ctr--) {
+               if (trs [trnr].state == TRS_TERMINATED) {
+                       trnr_next = trnr + 1;
+                       if (trnr_next >= HAVE_TRACTS) {
+                               trnr_next = 0;
+                       }
+                       return &trs [trnr];
+               } else {
+                       trnr++;
+                       if (trnr >= HAVE_TRACTS) {
+                               trnr = 0;
+                       }
+               }
+       }
+       return NULL;
+}
+
+/* Allocate a new transaction  */
+static tract_t *tract_allocate (dialog_t *dia) {
+       tract_t *retval;
+       if (!dia) {
+               return NULL;
+       }
+       retval = tract_find_free ();
+       if (!retval) {
+               return NULL;
+       }
+       bzero (retval, sizeof (tract_t));
+       retval->state = TRS_ALLOCATED;
+       retval->dialog = dia;
+       dia->tract_usectr++;
+       retval->branch.str = retval->txt_branch;
+       retval->branch.len = 0;
+       sipgen_create_branch (&retval->branch);
+       return retval;
+}
+
+/* Clone a transaction as a new one with shared identities.
+ * This is used for CANCEL and, at least in theory, for ACK.
+ */
+static tract_t *tract_clone (tract_t *tr) {
+       tract_t *retval = tract_find_free ();
+       if (!retval) {
+               return NULL;
+       }
+       memcpy (retval, tr, sizeof (tract_t));
+       retval->state = TRS_ALLOCATED;
+       tr->dialog->tract_usectr++;
+       retval->branch.str = retval->txt_branch;
+       return retval;
+}
+
+/* Deallocate a transaction */
+static inline void tract_deallocate (tract_t *tr) {
+       tr->dialog->tract_usectr--;
+       tr->dialog = NULL;
+       tr->state = TRS_TERMINATED;
+}
+
+/* Find a transaction that matches the core headers.
+ * This implements the RFC 3261 form of transaction
+ * identification, based on a special Via:branch form.
+ * This is usually requested by the UAS functions, so
+ * in response to an incoming request.
+ */
+tract_t *siptr_findmatch (coreheaders_t const *coreheaders) {
+       uint16_t trnr;
+       //
+       // No match if the Via:branch is not in RFC3261 format
+       if (textisnull (&coreheaders->via_branch) || (memcmp (&coreheaders->via_branch, branchprefix_rfc3261.str, branchprefix_rfc3261.len) != 0)) {
+               return NULL;
+       }
+       //
+       // Iterate over transactions to find a match
+       for (trnr = 0; trnr < HAVE_TRACTS; trnr++) {
+               //
+               // Skip if transaction is not allocated/used
+               if (trs [trnr].state == TRS_TERMINATED) {
+                       continue;
+               }
+               //
+               // Return transaction if its branch equals Via:branch
+               if (texteq (&trs [trnr].branch, &coreheaders->via_branch)) {
+                       return &trs [trnr];
+               }
+       }
+       //
+       // No transaction found (yet) -- return NULL
+       return NULL;
+}
+
+/* Report back to the user, when transactions need it */
+static inline void siptr_report (tract_t *st, const textptr_t *msg) {
+       memcpy (&st->incoming, msg, sizeof (textptr_t));
+       (*st->userfn) (&st->incoming, &st->attachmt);
+}
+
+
+/********** TRANSACTION HANDLING FUNCTIONS **********/
+
+
+/*
+\f
+ * SIP INVITE in a UAC role, figure 5 on page 128 of RFC 3261:
+
+                               |INVITE from TU
+             Timer A fires     |INVITE sent
+             Reset A,          V                      Timer B fires
+             INVITE sent +-----------+                or Transport Err.
+               +---------|           |---------------+inform TU
+               |         |  Calling  |               |
+               +-------->|           |-------------->|
+                         +-----------+ 2xx           |
+                            |  |       2xx to TU     |
+                            |  |1xx                  |
+    300-699 +---------------+  |1xx to TU            |
+   ACK sent |                  |                     |
+resp. to TU |  1xx             V                     |
+            |  1xx to TU  -----------+               |
+            |  +---------|           |               |
+            |  |         |Proceeding |-------------->|
+            |  +-------->|           | 2xx           |
+            |            +-----------+ 2xx to TU     |
+            |       300-699    |                     |
+            |       ACK sent,  |                     |
+            |       resp. to TU|                     |
+            |                  |                     |      NOTE:
+            |  300-699         V                     |
+            |  ACK sent  +-----------+Transport Err. |  transitions
+            |  +---------|           |Inform TU      |  labeled with
+            |  |         | Completed |-------------->|  the event
+            |  +-------->|           |               |  over the action
+            |            +-----------+               |  to take
+            |              ^   |                     |
+            |              |   | Timer D fires       |
+            +--------------+   | -                   |
+                               |                     |
+                               V                     |
+                         +-----------+               |
+                         |           |               |
+                         | Terminated|<--------------+
+                         |           |
+                         +-----------+
+
+                 Figure 5: INVITE client transaction
+
+ */
+
+static void isr_client_invite (irq_t *irq) {
+       tract_t *st = (tract_t *) irq;
+       textptr_t retval, descr;
+       //
+       // See if a response came in
+       if (!textisnull (&st->incoming)) {
+               sip_splitline_request (&st->incoming, &retval, &descr);
+       } else {
+               retval.str = NULL;
+       }
+       //
+       // Decide on the next state
+       switch (st->state) {
+       case TRS_CALLING:
+               if (retval.str) {
+                       (*st->userfn) (&st->incoming, &st->attachmt);   /* Pass back retval */
+                       if (*retval.str == '1') {
+                               st->state = TRS_PROCEEDING;
+                               irqtimer_stop (&st->tmr);
+                       } else if (*retval.str == '2') {
+                               // st->state = TRS_TERMINATED;
+                               irqtimer_stop (&st->tmr);
+                               tract_deallocate ((tract_t *) irq);
+                       } else {
+                               sip_ack (st->dialog, &st->branch);
+                               st->state = TRS_COMPLETED;
+                               irqtimer_stop (&st->tmr);
+                               irqtimer_start (&st->tmr, TD, isr_client_invite, CPU_PRIO_LOW);
+                       }
+               } else {
+                       st->growtime <<= 1;
+                       if (TIME_BEFORE (T4, st->growtime)) {
+                               siptr_report (st, &report_timeout);
+                       } else if (!sipdia_invite (st->dialog, &st->branch)) {
+                               siptr_report (st, &report_badreq);
+                       } else {
+                               irqtimer_start (&st->tmr, st->growtime, isr_client_invite, CPU_PRIO_LOW);
+                       }
+               }
+               break;
+       case TRS_PROCEEDING:
+               if (retval.str) {
+                       (*st->userfn) (&st->incoming, &st->attachmt);   /* Pass back retval */
+                       if (*retval.str == '1') {
+                               ;
+                       } else if (*retval.str == '2') {
+                               // st->state = TRS_TERMINATED;
+                               tract_deallocate ((tract_t *) irq);
+                       } else {
+                               st->state = TRS_COMPLETED;
+                               sip_ack (st->dialog, &st->branch);
+                       }
+               }
+               break;
+       case TRS_COMPLETED:
+               if (retval.str) {
+                       sip_ack (st->dialog, &st->branch);
+               } else {
+                       // st->state = TRS_TERMINATED;
+                       tract_deallocate ((tract_t *) irq);
+               }
+               break;
+       case TRS_TERMINATED:
+       default:
+               break;
+       }
+       textnullify (&st->incoming);
+}
+
+
+/*
+\f
+ * SIP non-INVITE in a UAC role, figure 6 on page 133 of RFC 3261:
+
+                                   |Request from TU
+                                   |send request
+               Timer E             V
+               send request  +-----------+
+                   +---------|           |-------------------+
+                   |         |  Trying   |  Timer F          |
+                   +-------->|           |  or Transport Err.|
+                             +-----------+  inform TU        |
+                200-699         |  |                         |
+                resp. to TU     |  |1xx                      |
+                +---------------+  |resp. to TU              |
+                |                  |                         |
+                |   Timer E        V       Timer F           |
+                |   send req +-----------+ or Transport Err. |
+                |  +---------|           | inform TU         |
+                |  |         |Proceeding |------------------>|
+                |  +-------->|           |-----+             |
+                |            +-----------+     |1xx          |
+                |              |      ^        |resp to TU   |
+                | 200-699      |      +--------+             |
+                | resp. to TU  |                             |
+                |              |                             |
+                |              V                             |
+                |            +-----------+                   |
+                |            |           |                   |
+                |            | Completed |                   |
+                |            |           |                   |
+                |            +-----------+                   |
+                |              ^   |                         |
+                |              |   | Timer K                 |
+                +--------------+   | -                       |
+                                   |                         |
+                                   V                         |
+             NOTE:           +-----------+                   |
+                             |           |                   |
+         transitions         | Terminated|<------------------+
+         labeled with        |           |
+         the event           +-----------+
+         over the action
+         to take
+                 Figure 6: non-INVITE client transaction
+
+ */
+
+static void isr_client_other (irq_t *irq) {
+       tract_t *st = (tract_t *) irq;
+       textptr_t retval, descr;
+       //
+       // See if a response came in
+       if (!textisnull (&st->incoming)) {
+               sip_splitline_request (&st->incoming, &retval, &descr);
+       } else {
+               textnullify (&retval);
+               textnullify (&descr);
+       }
+       //
+       // Decide on the next state
+bottom_printf ("isr_client_other @ %c\n", (intptr_t) st->state);
+       switch (st->state) {
+       case TRS_TRYING:
+               if (retval.str) {
+                       (*st->userfn) (&st->incoming, &st->attachmt);   /* Pass back retval */
+                       if (*retval.str == '1') {
+                               st->state = TRS_PROCEEDING;
+                               // Continue with current timer setup
+                       } else {
+                               st->state = TRS_COMPLETED;
+                               irqtimer_stop (&st->tmr);
+                               irqtimer_start (&st->tmr, TK, isr_client_other, CPU_PRIO_LOW);
+                       }
+               } else {
+                       st->growtime <<= 1;
+                       if (TIME_BEFORE (TF, st->growtime)) {
+                               siptr_report (st, &report_timeout);
+                       } else if (!(*st->dialogfn) (st->dialog, &st->branch)) {
+                               siptr_report (st, &report_badreq);
+                       } else {
+                               irqtimer_start (&st->tmr, st->growtime, isr_client_other, CPU_PRIO_LOW);
+                       }
+               }
+               break;
+       case TRS_PROCEEDING:
+               if (retval.str) {
+                       (*st->userfn) (&st->incoming, &st->attachmt);   /* Pass back retval */
+                       if (*retval.str == '1') {
+                       } else {
+                               st->state = TRS_COMPLETED;
+                               irqtimer_stop (&st->tmr);
+                               irqtimer_start (&st->tmr, TK, isr_client_other, CPU_PRIO_LOW);
+                       }
+               } else {
+                       st->growtime <<= 1;
+                       if (TIME_BEFORE (TF, st->growtime)) {
+                               siptr_report (st, &report_timeout);
+                       } else if (!(*st->dialogfn) (st->dialog, &st->branch)) {
+                               siptr_report (st, &report_badreq);
+                       } else {
+                               irqtimer_start (&st->tmr, st->growtime, isr_client_other, CPU_PRIO_LOW);
+                       }
+               }
+               break;
+       case TRS_COMPLETED:
+               if (retval.str) {
+                       ;
+               } else {
+                       // st->state = TRS_TERMINATED;
+                       tract_deallocate ((tract_t *) irq);
+               }
+       case TRS_TERMINATED:
+       default:
+               break;
+       }
+       textnullify (&st->incoming);
+}
+
+void siptr_client (dialog_t *dia, textptr_t const *mth, void (*truser) (textptr_t const *sip, textptr_t *sdp)) {
+       bool (*dialogfunction) (void);
+       tract_t *tr = NULL;
+       dialogfunction = sipdia_client_method (mth);
+       if (!dialogfunction) {
+               truser (&report_mthnotallow, NULL);
+               return;
+       }
+       //
+       // The behaviour for CANCEL is exceptional:
+       // Send it in a transaction cloned from INVITE
+       if (dialogfunction == sipdia_cancel) {
+               uint16_t trnr;
+               for (trnr = 0; trnr < HAVE_TRACTS; trnr++) {
+                       if (trs [trnr].dialog != dia) {
+                               continue;
+                       }
+                       if (texteq (trs [trnr].method, &invite_m)) {
+                               tr = tract_clone (&trs [trnr]);
+                               break;
+                       }
+               }
+       //
+       // For normal methods, create a new transaction:
+       } else {
+               tr = tract_allocate (dia);
+       }
+       if (tr == NULL) {
+               truser (&report_tmpunavail, NULL);
+               return;
+       }
+       tr->userfn = truser;
+       tr->dialogfn = dialogfunction;
+       tr->method = mth;
+       textnullify (&tr->incoming);
+       textnullify (&tr->attachmt);
+       if (dialogfunction == sipdia_invite) {
+               tr->state = TRS_CALLING;
+               tr->growtime = TA_init;
+               tr->tmr.tmr_irq.irq_handler = isr_client_invite;
+       } else {
+               tr->state = TRS_TRYING;
+               tr->growtime = TE_init;
+               tr->tmr.tmr_irq.irq_handler = isr_client_other;
+       }
+       (*tr->tmr.tmr_irq.irq_handler) (&tr->tmr.tmr_irq);
+}
+
+
+/* Network-entry of a response ends up at a dialog for further processing */
+
+void siptr_response2uac (dialog_t *dia, textptr_t *sip, textptr_t *attachment, textptr_t *branch, textptr_t *cseqmth) {
+       uint16_t trnr;
+       for (trnr = 0; trnr < HAVE_TRACTS; trnr++) {
+               //
+               // Find the transaction that matches the incoming response
+               if (trs [trnr].dialog != dia) {
+                       continue;
+               }
+               if (trs [trnr].dialog->tract_usectr == 0) {
+                       continue;
+               }
+               if (!texteq (&trs [trnr].branch, branch)) {
+bottom_printf ("Branch mismatch: %t != %t\n", &trs [trnr].branch, branch);
+                       continue;
+               }
+               if (!texteq (trs [trnr].method, cseqmth)) {
+bottom_printf ("Method mismatch: %t != %t\n", trs [trnr].method, cseqmth);
+                       continue;
+               }
+               //
+               // Instead of transaction timeout, call the handler
+bottom_printf ("Processing upcall for response\n");
+               irqtimer_stop (&trs [trnr].tmr);
+               memcpy (&trs [trnr].incoming, sip,        sizeof (textptr_t));
+               memcpy (&trs [trnr].attachmt, attachment, sizeof (textptr_t));
+#if 0
+               if (memcmp (sip, "INVITE ", 7)) {
+                       isr_client_other  (&trs [trnr].tmr.tmr_irq);
+               } else {
+                       isr_client_invite (&trs [trnr].tmr.tmr_irq);
+               }
+#endif
+               (*trs [trnr].tmr.tmr_irq.irq_handler) (&trs [trnr].tmr.tmr_irq);
+               return;
+       }
+}
+
+
+/*
+\f
+ * SIP INVITE in a UAC role, figure 7 on page 136 of RFC 3261:
+
+                               |INVITE
+                               |pass INV to TU
+            INVITE             V send 100 if TU won't in 200ms
+            send response+-----------+
+                +--------|           |--------+101-199 from TU
+                |        | Proceeding|        |send response
+                +------->|           |<-------+
+                         |           |          Transport Err.
+                         |           |          Inform TU
+                         |           |--------------->+
+                         +-----------+                |
+            300-699 from TU |     |2xx from TU        |
+            send response   |     |send response      |
+                            |     +------------------>+
+                            |                         |
+            INVITE          V          Timer G fires  |
+            send response+-----------+ send response  |
+                +--------|           |--------+       |
+                |        | Completed |        |       |
+                +------->|           |<-------+       |
+                         +-----------+                |
+                            |     |                   |
+                        ACK |     |                   |
+                        -   |     +------------------>+
+                            |        Timer H fires    |
+                            V        or Transport Err.|
+                         +-----------+  Inform TU     |
+                         |           |                |
+                         | Confirmed |                |
+                         |           |                |
+                         +-----------+                |
+                               |                      |
+                               |Timer I fires         |
+                               |-                     |
+                               |                      |
+                               V                      |
+                         +-----------+                |
+                         |           |                |
+                         | Terminated|<---------------+
+                         |           |
+                         +-----------+
+
+              Figure 7: INVITE server transaction
+
+ */
+static void isr_server_invite (irq_t *irq) {
+       //TODO// CREATE THIS ROUTINE
+}
+
+
+/*
+\f
+ * SIP non-INVITE in a UAC role, figure 8 on page 140 of RFC 3261:
+
+                                  |Request received
+                                  |pass to TU
+                                  V
+                            +-----------+
+                            |           |
+                            | Trying    |-------------+
+                            |           |             |
+                            +-----------+             |200-699 from TU
+                                  |                   |send response
+                                  |1xx from TU        |
+                                  |send response      |
+                                  |                   |
+               Request            V      1xx from TU  |
+               send response+-----------+send response|
+                   +--------|           |--------+    |
+                   |        | Proceeding|        |    |
+                   +------->|           |<-------+    |
+            +<--------------|           |             |
+            |Trnsprt Err    +-----------+             |
+            |Inform TU            |                   |
+            |                     |                   |
+            |                     |200-699 from TU    |
+            |                     |send response      |
+            |  Request            V                   |
+            |  send response+-----------+             |
+            |      +--------|           |             |
+            |      |        | Completed |<------------+
+            |      +------->|           |
+            +<--------------|           |
+            |Trnsprt Err    +-----------+
+            |Inform TU            |
+            |                     |Timer J fires
+            |                     |-
+            |                     |
+            |                     V
+            |               +-----------+
+            |               |           |
+            +-------------->| Terminated|
+                            |           |
+                            +-----------+
+
+                Figure 8: non-INVITE server transaction
+
+ */
+static void isr_server_other (irq_t *irq) {
+       //TODO// CREATE THIS ROUTINE
+}
+
+/* A downcall from the SIP application code, to create
+ * a server transaction -- which is of course a reaction
+ * to an incoming SIP message.
+ */
+tract_t *siptr_server (dialog_t *dia, textptr_t const *mth) {
+       tract_t *tr = tract_allocate (dia);
+       if (tr) {
+               //TODO// CREATE THIS ROUTINE
+       }
+       return tr;
+}
+
+/* A downcall from the SIP application code, to send
+ * a SIP response to a current SIP request.  Note that
+ * the SDP body, if any, is assumed to be buffered by
+ * the application layer.
+ */
+void siptr_respond (tract_t *tr, textptr_t const *response, coreheaders_t const *coreheaders, textptr_t const *sip, textptr_t const *sdp_buffered) {
+       //TODO// PROCESS RESPONSE IN TRANSACTION STATE
+       memcpy (&tr->lastresponse, response, sizeof (textptr_t));
+       if (sdp_buffered) {
+               memcpy (&tr->lastsdp, sdp_buffered, sizeof (textptr_t));
+       } else {
+               bzero  (&tr->lastsdp,               sizeof (textptr_t));
+       }
+       sipdia_respond (response, coreheaders, sip, sdp_buffered);
+}
+
+/* A downcall from the SIP application code, to resend
+ * the last SIP response.
+ */
+void siptr_respond_again (tract_t *tr, coreheaders_t *coreheaders, textptr_t const *sip) {
+       if (!textisnull (&tr->lastresponse)) {
+               sipdia_respond (&tr->lastresponse, coreheaders, sip, &tr->lastsdp);
+       }
+}
+
+/* An upcall from the network stack, to handle an
+ * incoming SIP request.  This is basically passed
+ * up to SIP application code, depending on the
+ * method.
+ * The dialog in dia_opt is optional, and if it is
+ * not supplied then either a transaction may be
+ * found directly through the Via:branch, or else
+ * there is no current dialog.
+ */
+void siptr_request2uas (dialog_t *dia_opt, coreheaders_t *coreheaders, textptr_t const *sip, textptr_t const *attachment) {
+       tract_t *tr = NULL;
+       textptr_t method;
+       textptr_t requri;
+       sipappmap_t *fnmap = sipapp_fnmap;
+       textptr_t const *retval = NULL;
+bottom_printf ("Invoked siptr_request2uas() -- TODO: Integrate with state diagrams\n");
+       //
+       // Split start-line into method and request-uri
+       if (!sip_splitline_request (sip, &method, &requri)) {
+               // Send "400 Bad Request"
+               sipdia_respond (&report_badreq, coreheaders, sip, NULL);
+               return;
+       }
+bottom_printf ("Method is %t, URI is %t\n", &method, &requri);
+       //
+       // Iterate over available method names to find an upcall
+       while (fnmap->method) {
+               if (texteq (fnmap->method, &method)) {
+                       break;
+               }
+               fnmap++;
+       }
+       if (!fnmap->method) {
+               // Send "405 Method Not Allowed"
+               // TODO: ACK should be handled -- in the app or here?
+               sipdia_respond (&report_mthnotallow, coreheaders, sip, NULL);
+               return;
+       }
+       //
+       // Find the transaction, if it already exists.
+       // DO NOT be so kind to always create a transaction.
+       // This choice for overhead is made by the application,
+       // if it cannot avoid doing so.
+       if (!dia_opt) {
+               tr = siptr_findmatch (coreheaders);
+               if (tr) {
+                       dia_opt = tr->dialog;
+               }
+       }
+       //
+       // TODO -- integrate transaction state diagram here
+       //
+       // Make an upcall to the corresponding sipapp_XXX handler
+       (*fnmap->sipappfn) (tr, &requri, coreheaders, sip, attachment);
+}
+
index df20062..0d1d976 100644 (file)
@@ -1,3 +1,22 @@
+# Makefile for 0cpm target platform picking
+#
+# This file is part of 0cpm Firmerware.
+#
+# 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
+#
+# 0cpm Firmerware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 3.
+#
+# 0cpm Firmerware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
+
+
 #
 # Sort each target's object files into objs-bottom-y or objs-bottom-n:
 #
index db0c4a6..65012bf 100644 (file)
@@ -27,6 +27,7 @@
 #define HAVE_KBD_ABCD          0
 #define HAVE_KBD_FLASH         0
 #define HAVE_KBD_LINES         6
+#define HAVE_LINES             6
 #define HAVE_KBD_SOFT_FUNCTION 4
 #ifndef HAVE_KBD_SIDECARS
 #   define HAVE_KBD_SIDECARS   0