+# 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
include src/kernel/Makefile
include src/net/Makefile
+include src/sip/Makefile
include src/phone/Makefile
include src/target/Makefile
* 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
+# 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
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...
+
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
================
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
*/
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)))
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);
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;
--- /dev/null
+/* 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
// 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; }
#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'
#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);
+++ /dev/null
-/* 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;
--- /dev/null
+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
--- /dev/null
+/* 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])
+
--- /dev/null
+/* 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;
--- /dev/null
+/* 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;
+
--- /dev/null
+/* 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;
+
--- /dev/null
+/* 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);
+
+# 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
}
//
// 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);
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
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.
}
+/* 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) {
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++) {
} else {
colnm = "UNSET";
}
- bottom_printf ("\t%s", colnm);
+ bottom_printf ("\t%s", (intptr_t) colnm);
}
bottom_printf ("\n");
}
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);
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);
}
}
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;
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)) {
#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) {
+# 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
#
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;
}
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);
} 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) {
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);
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);
}
}
}
#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 ********/
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
#ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
trysend ();
#else
- irqtimer_start (&console_timer, 0, netcons_interval, CPU_PRIO_LOW);
+ ensure_console_timer_runs ();
#endif
}
}
}
consbuf [wpos++] = c;
+ ensure_console_timer_runs ();
}
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;
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)";
}
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;
#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 */
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 */ ;
}
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;
#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, ®ister_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 (®timer, 0, register_phone, CPU_PRIO_LOW);
+ }
for (relpos = 0; relpos < 12; relpos++) {
int actpos = relpos + nibblepos;
uint8_t ip6digit = 0x00;
* 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
}
}
+# 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
* 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);
} else {
//TODO:TEST// ht162x_putchar (0, '8', true);
cur_prio--;
+ bottom_rndseed ();
}
}
//TODO:TEST// ht162x_putchar (0, '9', true);
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);
+ }
}
/* 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 {
exptime = irqtimer_wait_queue->tmr_expiry;
}
}
-bottom_led_set (LED_IDX_BACKLIGHT, 1);
return exptime;
}
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);
#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])
goto netin_LLC_sel;
}
#endif
- return NULL;
+ return (intptr_t) NULL;
}
//
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);
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);
switch (ip4_ptype) {
case 17: goto netin_UDP4_sel;
case 1: goto netin_ICMP4_sel;
- default: return NULL;
+ default: return (intptr_t) NULL;
}
//
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;
}
//
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;
//
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;
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];
//
// 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);
switch (ip6_nxthdr) {
case 17: goto netin_UDP6_sel;
case 58: goto netin_ICMP6_sel;
- default: return NULL;
+ default: return (intptr_t) NULL;
}
//
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;
}
//
} else if (udp6_dst == 546) {
goto netin_DHCP6_sel;
} else {
- return NULL;
+ return (intptr_t) NULL;
}
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);
}
// Finally, a catchall that rejects anything that makes it to here
- return NULL;
+ return (intptr_t) NULL;
/********** OPTIONAL CODE FOR LLC: NETCONSOLE, FIRMWARE UPGRADES **********/
default:
break;
}
- return NULL;
+ return (intptr_t) NULL;
#ifdef CONFIG_FUNCTION_NETCONSOLE
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
#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;
}
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
#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)
} 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) {
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)];
}
// 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);
*/
+#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
+# 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
--- /dev/null
+# 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
+
--- /dev/null
+/* 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 },
+ { ®ister_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);
+}
+
--- /dev/null
+/* 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,
+ ®ister_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);
+ }
+}
+
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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, ®ister_m, ®host_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, ®ister_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);
+}
+
--- /dev/null
+/* 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);
+}
+
+# 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:
#
#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