From 4772348b4f99c81575b8613c0fbe1533bd261b42 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 9 Nov 2011 11:26:15 +0100 Subject: freebsd: Fixes for the compilation of libosmocore on FreeBSD alloca.h is not available on FreeBSD, use the default autoconf function to check for it, there is a complete list[1] of what to do for using alloca but let us see how far we get with this test. Include netinet/in.h for the IPv4 and IPv6 socket address. Check for dlopen in libraries and use this instead of linking -dl. [1] http://www.gnu.org/s/hello/manual/autoconf/Particular-Functions.html --- configure.ac | 4 ++++ src/Makefile.am | 2 +- src/conv.c | 4 +++- src/socket.c | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 52ab850f..7d8e6dda 100644 --- a/configure.ac +++ b/configure.ac @@ -19,6 +19,10 @@ AC_CONFIG_MACRO_DIR([m4]) dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h) +# for src/conv.c +AC_FUNC_ALLOCA +AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) +AC_SUBST(LIBRARY_DL) AC_PATH_PROG(DOXYGEN,doxygen,false) AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false) diff --git a/src/Makefile.am b/src/Makefile.am index 6c0398bc..25da356a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ if ENABLE_PLUGIN libosmocore_la_SOURCES += plugin.c -libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -ldl +libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(LIBRARY_DL) else libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) endif diff --git a/src/conv.c b/src/conv.c index f47d75cd..0416d272 100644 --- a/src/conv.c +++ b/src/conv.c @@ -29,8 +29,10 @@ /*! \file conv.c * \file Osmocom convolutional encoder and decoder */ - +#include "config.h" +#ifdef HAVE_ALLOCA_H #include +#endif #include #include #include diff --git a/src/socket.c b/src/socket.c index 1a1d71dc..8a8829b7 100644 --- a/src/socket.c +++ b/src/socket.c @@ -18,6 +18,8 @@ #include #include +#include + #include #include #include -- cgit v1.2.3 From 3ec5047c6be00729f57da23f6d5de6d5a357ec7a Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 9 Nov 2011 12:18:55 +0100 Subject: freebsd: Provide a dummy backtrace implementation that does nothing --- src/backtrace.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backtrace.c b/src/backtrace.c index 189a3cec..023671c2 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -57,4 +57,8 @@ void osmo_generate_backtrace(void) free(strings); } +#else +void osmo_generate_backtrace(void) +{ +} #endif -- cgit v1.2.3 From ff23d2497286c8d4876009642bf6d35a2b9b2856 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Thu, 10 Nov 2011 23:03:18 +0100 Subject: utils: Fix a bad double osmo_ prefix for osmo_hexdump_nospc Hopefully no project where using them it seems Signed-off-by: Sylvain Munaut --- include/osmocom/core/utils.h | 2 +- src/utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h index 940c25f8..315757c9 100644 --- a/include/osmocom/core/utils.h +++ b/include/osmocom/core/utils.h @@ -34,7 +34,7 @@ int osmo_hexparse(const char *str, uint8_t *b, int max_len); char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); char *osmo_hexdump(const unsigned char *buf, int len); -char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len); +char *osmo_hexdump_nospc(const unsigned char *buf, int len); #define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; diff --git a/src/utils.c b/src/utils.c index a25479f1..50adc314 100644 --- a/src/utils.c +++ b/src/utils.c @@ -173,7 +173,7 @@ char *osmo_hexdump(const unsigned char *buf, int len) * This function will print a sequence of bytes as hexadecimal numbers, * without any space character between each byte (e.g. "1aefd9") */ -char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) +char *osmo_hexdump_nospc(const unsigned char *buf, int len) { return _osmo_hexdump(buf, len, ""); } -- cgit v1.2.3 From e55ae3ae1c631f08714f88f13ae75bfadd47cd51 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 23:06:55 +0100 Subject: core/utils: Add a symbol alias for a previous typo for compatibility Signed-off-by: Sylvain Munaut --- src/utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils.c b/src/utils.c index 50adc314..d32c5c07 100644 --- a/src/utils.c +++ b/src/utils.c @@ -178,6 +178,10 @@ char *osmo_hexdump_nospc(const unsigned char *buf, int len) return _osmo_hexdump(buf, len, ""); } + /* Compat with previous typo to preserve abi */ +char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) + __attribute__((alias("osmo_hexdump_nospc"))); + #include "../config.h" #ifdef HAVE_CTYPE_H #include -- cgit v1.2.3 From 2179f402e062f40a357759ad28717a79df062697 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 15:32:57 +0100 Subject: core/rbtree: add const qualifier to some functions See kernel commit f4b477c47332367d35686bd2b808c2156b96d7c7 ---- The 'rb_first()', 'rb_last()', 'rb_next()' and 'rb_prev()' calls take a pointer to an RB node or RB root. They do not change the pointed objects, so add a 'const' qualifier in order to make life of the users of these functions easier. Indeed, if I have my own constant pointer &const struct my_type *p, and I call 'rb_next(&p->rb)', I get a GCC warning: warning: passing argument 1 of ?~@~Xrb_next?~@~Y discards qualifiers from pointer target type Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse Signed-off-by: Linus Torvalds ---- Signed-off-by: Sylvain Munaut --- include/osmocom/core/linuxrbtree.h | 8 ++++---- src/rbtree.c | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/osmocom/core/linuxrbtree.h b/include/osmocom/core/linuxrbtree.h index ee988918..44e00a16 100644 --- a/include/osmocom/core/linuxrbtree.h +++ b/include/osmocom/core/linuxrbtree.h @@ -139,10 +139,10 @@ extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(struct rb_node *); -extern struct rb_node *rb_prev(struct rb_node *); -extern struct rb_node *rb_first(struct rb_root *); -extern struct rb_node *rb_last(struct rb_root *); +extern struct rb_node *rb_next(const struct rb_node *); +extern struct rb_node *rb_prev(const struct rb_node *); +extern struct rb_node *rb_first(const struct rb_root *); +extern struct rb_node *rb_last(const struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, diff --git a/src/rbtree.c b/src/rbtree.c index 07e1041a..9b7fe83c 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -289,7 +289,7 @@ void rb_erase(struct rb_node *node, struct rb_root *root) /* * This function returns the first node (in sort order) of the tree. */ -struct rb_node *rb_first(struct rb_root *root) +struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n; @@ -301,7 +301,7 @@ struct rb_node *rb_first(struct rb_root *root) return n; } -struct rb_node *rb_last(struct rb_root *root) +struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n; @@ -313,7 +313,7 @@ struct rb_node *rb_last(struct rb_root *root) return n; } -struct rb_node *rb_next(struct rb_node *node) +struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent; @@ -326,7 +326,7 @@ struct rb_node *rb_next(struct rb_node *node) node = node->rb_right; while (node->rb_left) node=node->rb_left; - return node; + return (struct rb_node *)node; } /* No right-hand children. Everything down and left is @@ -341,7 +341,7 @@ struct rb_node *rb_next(struct rb_node *node) return parent; } -struct rb_node *rb_prev(struct rb_node *node) +struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent; @@ -354,7 +354,7 @@ struct rb_node *rb_prev(struct rb_node *node) node = node->rb_left; while (node->rb_right) node=node->rb_right; - return node; + return (struct rb_node *)node; } /* No left-hand children. Go up till we find an ancestor which -- cgit v1.2.3 From 0395c6e7c4baceae4539e4a7ede9a4473c57a350 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 15:25:25 +0100 Subject: core/rbtree: optimize rb_erase() See kernel commit 55a63998b8967615a15e2211ba0ff3a84a565824 ---- Tfour 4 redundant if-conditions in function __rb_erase_color() in lib/rbtree.c are removed. In pseudo-source-code, the structure of the code is as follows: if ((!A || B) && (!C || D)) { . . . } else { if (!C || D) {//if this is true, it implies: (A == true) && (B == false) if (A) {//hence this always evaluates to 'true'... . } . //at this point, C always becomes true, because of: __rb_rotate_right/left(); //and: other = parent->rb_right/left; } . . if (C) {//...and this too ! . } } Signed-off-by: Wolfram Strepp Acked-by: Peter Zijlstra Cc: Andrea Arcangeli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds ---- Signed-off-by: Sylvain Munaut --- src/rbtree.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/rbtree.c b/src/rbtree.c index 9b7fe83c..d1c2f6b1 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -161,17 +161,14 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, { if (!other->rb_right || rb_is_black(other->rb_right)) { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); + rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); + rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; @@ -198,17 +195,14 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, { if (!other->rb_left || rb_is_black(other->rb_left)) { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); + rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); + rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; -- cgit v1.2.3 From b8c5bdefcdb91babbc025e2936702ba19d7946a9 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 15:28:01 +0100 Subject: core/rbtree: reorganize code in rb_erase() for additional changes See kernel commit 16c047add3ceaf0ab882e3e094d1ec904d02312d ---- First, move some code around in order to make the next change more obvious. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Peter Zijlstra Signed-off-by: Wolfram Strepp Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds ---- Signed-off-by: Sylvain Munaut --- src/rbtree.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rbtree.c b/src/rbtree.c index d1c2f6b1..9db0641c 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -229,6 +229,15 @@ void rb_erase(struct rb_node *node, struct rb_root *root) node = node->rb_right; while ((left = node->rb_left) != NULL) node = left; + + if (rb_parent(old)) { + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; + else + rb_parent(old)->rb_right = node; + } else + root->rb_node = node; + child = node->rb_right; parent = rb_parent(node); color = rb_color(node); @@ -245,15 +254,6 @@ void rb_erase(struct rb_node *node, struct rb_root *root) node->rb_right = old->rb_right; node->rb_left = old->rb_left; - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - rb_set_parent(old->rb_left, node); if (old->rb_right) rb_set_parent(old->rb_right, node); -- cgit v1.2.3 From a83cbe12f83d81d4b4167193fe85b1e3d745dc94 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 15:29:59 +0100 Subject: core/rbtree: make clear distinction between two different cases in rb_erase() See kernel commit 4c60117811171d867d4f27f17ea07d7419d45dae ---- There are two cases when a node, having 2 childs, is erased: 'normal case': the successor is not the right-hand-child of the node to be erased 'special case': the successor is the right-hand child of the node to be erased Here some ascii-art, with following symbols (referring to the code): O: node to be deleted N: the successor of O P: parent of N C: child of N L: some other node normal case: O N / \ / \ / \ / \ L \ L \ / \ P ----> / \ P / \ / \ / / N C \ / \ \ C / \ special case: O|P N / \ / \ / \ / \ L \ L \ / \ N ----> / C \ / \ \ C / \ Notice that for the special case we don't have to reconnect C to N. Signed-off-by: Wolfram Strepp Signed-off-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds ---- Signed-off-by: Sylvain Munaut --- src/rbtree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rbtree.c b/src/rbtree.c index 9db0641c..afbdca64 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -242,13 +242,13 @@ void rb_erase(struct rb_node *node, struct rb_root *root) parent = rb_parent(node); color = rb_color(node); - if (child) - rb_set_parent(child, parent); if (parent == old) { - parent->rb_right = child; parent = node; - } else + } else { + if (child) + rb_set_parent(child, parent); parent->rb_left = child; + } node->rb_parent_color = old->rb_parent_color; node->rb_right = old->rb_right; -- cgit v1.2.3 From 54187ba442e16df7346acc8ba68f80ce3239e6b0 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 15:31:01 +0100 Subject: core/rbtree: remove redundant if()-condition in rb_erase() See kernel commit 4b324126e0c6c3a5080ca3ec0981e8766ed6f1ee ---- Furthermore, notice that the initial checks: if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; else { ... } guarantee that old->rb_right is set in the final else branch, therefore we can omit checking that again. Signed-off-by: Wolfram Strepp Signed-off-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds ---- Signed-off-by: Sylvain Munaut --- src/rbtree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rbtree.c b/src/rbtree.c index afbdca64..4e7c0f3a 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -248,15 +248,15 @@ void rb_erase(struct rb_node *node, struct rb_root *root) if (child) rb_set_parent(child, parent); parent->rb_left = child; + + node->rb_right = old->rb_right; + rb_set_parent(old->rb_right, node); } node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; node->rb_left = old->rb_left; - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); + goto color; } -- cgit v1.2.3 From aeeb7070f84437aa608a3d843346b1efa916d175 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 11 Nov 2011 18:57:59 +0100 Subject: core/timer: Allow an already scheduled timer to be re-scheduled at a new time Signed-off-by: Sylvain Munaut --- src/timer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/timer.c b/src/timer.c index 217f6521..77fb0039 100644 --- a/src/timer.c +++ b/src/timer.c @@ -69,8 +69,12 @@ static void __add_timer(struct osmo_timer_list *timer) */ void osmo_timer_add(struct osmo_timer_list *timer) { - timer->active = 1; - INIT_LLIST_HEAD(&timer->list); + if (timer->active) { + rb_erase(&timer->node, &timer_root); + } else { + timer->active = 1; + INIT_LLIST_HEAD(&timer->list); + } __add_timer(timer); } -- cgit v1.2.3 From dc3be0a6533be782c76f23ce653f4fc683b3b4f1 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:09:28 +0100 Subject: vty/telnet_interface: Add telnet_exit function This frees socket and pending connections Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/vty/telnet_interface.h | 2 ++ src/vty/telnet_interface.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/osmocom/vty/telnet_interface.h b/include/osmocom/vty/telnet_interface.h index 9a7c9e52..2de4f192 100644 --- a/include/osmocom/vty/telnet_interface.h +++ b/include/osmocom/vty/telnet_interface.h @@ -48,6 +48,8 @@ struct telnet_connection { int telnet_init(void *tall_ctx, void *priv, int port); +void telnet_exit(void); + /*! }@ */ #endif /* TELNET_INTERFACE_H */ diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c index ed64cdab..167acc18 100644 --- a/src/vty/telnet_interface.c +++ b/src/vty/telnet_interface.c @@ -221,4 +221,16 @@ void vty_event(enum event event, int sock, struct vty *vty) } } +void telnet_exit(void) +{ + struct telnet_connection *tc, *tc2; + + llist_for_each_entry_safe(tc, tc2, &active_connections, entry) + telnet_close_client(&tc->fd); + + osmo_fd_unregister(&server_socket); + close(server_socket.fd); + talloc_free(tall_telnet_ctx); +} + /*! }@ */ -- cgit v1.2.3 From f948dbc442c44c402d6559bdd746f5ef8d01fff2 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Thu, 10 Nov 2011 23:09:35 +0100 Subject: vty: Fixed vty_down_level to move down from config nodes When using ^D at config nodes above the CONFIG_NODE, the go_parent_cb function is used to go down by one node. This is equivalent to "exit" command. Written-by: Andreas.Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/vty/vty.h | 1 + src/vty/vty.c | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h index ffe3c591..83506c5e 100644 --- a/include/osmocom/vty/vty.h +++ b/include/osmocom/vty/vty.h @@ -176,6 +176,7 @@ int vty_shell_serv (struct vty *); void vty_hello (struct vty *); void *vty_current_index(struct vty *); int vty_current_node(struct vty *vty); +enum node_type vty_go_parent(struct vty *vty); extern void *tall_vty_ctx; diff --git a/src/vty/vty.c b/src/vty/vty.c index 5f5e6a4c..da035969 100644 --- a/src/vty/vty.c +++ b/src/vty/vty.c @@ -802,9 +802,11 @@ static void vty_backward_word(struct vty *vty) static void vty_down_level(struct vty *vty) { vty_out(vty, "%s", VTY_NEWLINE); - /* FIXME: we need to call the exit function of the specific node - * in question, not this generic one that doesn't know all nodes */ - (*config_exit_cmd.func) (NULL, vty, 0, NULL); + /* call the exit function of the specific node */ + if (vty->node > CONFIG_NODE) + vty_go_parent(vty); + else + (*config_exit_cmd.func) (NULL, vty, 0, NULL); vty_prompt(vty); vty->cp = 0; } -- cgit v1.2.3 From 5ad4ac800cf26b8cd2f49c1580678a8df4b55f50 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 1 Nov 2011 09:40:21 +0100 Subject: lapd: Fixed possible double free buf in lapd_core.c Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- src/gsm/lapd_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gsm/lapd_core.c b/src/gsm/lapd_core.c index dcc21506..54adbcaa 100644 --- a/src/gsm/lapd_core.c +++ b/src/gsm/lapd_core.c @@ -1920,10 +1920,11 @@ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) if (dl->send_buffer) msgb_free(dl->send_buffer); dl->send_out = 0; - if (msg && msg->len) { + if (msg && msg->len) /* Write data into the send buffer, to be sent first */ dl->send_buffer = msg; - } + else + dl->send_buffer = NULL; /* Discard partly received L3 message */ if (dl->rcv_buffer) { -- cgit v1.2.3 From a42b6995163840edb95667bac0a51d3e801b9a2a Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:31:47 +0100 Subject: gsm/lapdm: Add missing msgb_free in rslms_rx_rll error cases Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- src/gsm/lapdm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c index 82f8b0c1..c77e3132 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -1033,6 +1033,7 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc) if (msgb_l2len(msg) < sizeof(*rllh)) { LOGP(DLLAPD, LOGL_ERROR, "Message too short for RLL hdr!\n"); + msgb_free(msg); return -EINVAL; } @@ -1047,6 +1048,7 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc) dl = datalink_for_sapi(le, sapi); if (!dl) { LOGP(DLLAPD, LOGL_ERROR, "No instance for SAPI %d!\n", sapi); + msgb_free(msg); return -EINVAL; } -- cgit v1.2.3 From 5ac44784b6d725ef34f851d6124190fa848cfe27 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:35:48 +0100 Subject: gsm/lapdm: Make T200 timer depends on the link type (SACCH is slower) This is part of a set of commit to fix LAPDm to handle datalink connection on ACCH (SAPI 3) This is required to transfer SMS on SACCH of TCH/f or SDCCH/8 (4). Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/gsm/lapdm.h | 2 +- src/gsm/lapdm.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/osmocom/gsm/lapdm.h b/include/osmocom/gsm/lapdm.h index cc9c63fe..adffbebb 100644 --- a/include/osmocom/gsm/lapdm.h +++ b/include/osmocom/gsm/lapdm.h @@ -127,7 +127,7 @@ const char *get_rsl_name(int value); extern const char *lapdm_state_names[]; /* initialize a LAPDm entity */ -void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode); +void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200); void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode); /* deinitialize a LAPDm entity */ diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c index c77e3132..f17560ca 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -116,7 +116,7 @@ static int send_rslms_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); static void lapdm_dl_init(struct lapdm_datalink *dl, - struct lapdm_entity *entity) + struct lapdm_entity *entity, int t200) { memset(dl, 0, sizeof(*dl)); dl->entity = entity; @@ -127,18 +127,19 @@ static void lapdm_dl_init(struct lapdm_datalink *dl, dl->dl.n200_est_rel = N200_EST_REL; dl->dl.n200 = N200; dl->dl.t203_sec = 0; dl->dl.t203_usec = 0; + dl->dl.t200_sec = t200; dl->dl.t200_usec = 0; } /*! \brief initialize a LAPDm entity and all datalinks inside * \param[in] le LAPDm entity * \param[in] mode \ref lapdm_mode (BTS/MS) */ -void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode) +void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200) { unsigned int i; for (i = 0; i < ARRAY_SIZE(le->datalink); i++) - lapdm_dl_init(&le->datalink[i], le); + lapdm_dl_init(&le->datalink[i], le, t200); lapdm_entity_set_mode(le, mode); } @@ -152,9 +153,9 @@ void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode) */ void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode) { - lapdm_entity_init(&lc->lapdm_acch, mode); + lapdm_entity_init(&lc->lapdm_acch, mode, 2); /* FIXME: this depends on chan type */ - lapdm_entity_init(&lc->lapdm_dcch, mode); + lapdm_entity_init(&lc->lapdm_dcch, mode, 1); } /*! \brief flush and release all resoures in LAPDm entity */ -- cgit v1.2.3 From cbed32796578db7522c1821bdb06d3c1d596ca40 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:43:08 +0100 Subject: gsm/lapdm: Set N201 depending on the frame type This is part of a set of commit to fix LAPDm to handle datalink connection on ACCH (SAPI 3) This is required to transfer SMS on SACCH of TCH/f or SDCCH/8 (4). Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- src/gsm/lapdm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c index f17560ca..7e4b0380 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -531,7 +531,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, } else { mctx.lapdm_fmt = LAPDm_FMT_B; LOGP(DLLAPD, LOGL_INFO, "fmt=B\n"); - n201 = 20; // FIXME: select correct size by chan. + n201 = N201_AB_SDCCH; sapi = (msg->l2h[0] >> 2) & 7; } } @@ -743,7 +743,7 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) uint8_t sapi = rllh->link_id & 7; struct tlv_parsed tv; uint8_t length; - int n201 = 20; //FIXME + uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH; struct osmo_dlsap_prim dp; /* Set LAPDm context for established connection */ @@ -771,9 +771,9 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) } /* check if the layer3 message length exceeds N201 */ - if (length + 3 > 21) { /* FIXME: do we know the channel N201? */ + if (length > n201) { LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) " - "(discarding)\n", length + 3, 21); + "(discarding)\n", length, n201); msgb_free(msg); return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); } @@ -908,7 +908,7 @@ static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl) uint8_t sapi = rllh->link_id & 7; struct tlv_parsed tv; uint8_t length; - uint8_t n201 = 20; //FIXME + uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH; struct osmo_dlsap_prim dp; /* Set LAPDm context for established connection */ -- cgit v1.2.3 From f1f80de007aeefb7757d469dcc3869b6c5a89a9a Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:45:29 +0100 Subject: gsm/lapdm: Fix TA and power level handling in the ACCH header Timing advance and power level indicated by MS (measurement reports) and BTS (SI 5/6) are now stored for use at ACCH data link connection. This is part of a set of commit to fix LAPDm to handle datalink connection on ACCH (SAPI 3) This is required to transfer SMS on SACCH of TCH/f or SDCCH/8 (4). Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/gsm/lapdm.h | 7 +++++-- src/gsm/lapdm.c | 20 ++++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/include/osmocom/gsm/lapdm.h b/include/osmocom/gsm/lapdm.h index adffbebb..52e8fc52 100644 --- a/include/osmocom/gsm/lapdm.h +++ b/include/osmocom/gsm/lapdm.h @@ -72,8 +72,8 @@ struct lapdm_msg_ctx { int lapdm_fmt; uint8_t chan_nr; uint8_t link_id; - uint8_t ta_ind; - uint8_t tx_power_ind; + uint8_t ta_ind; /* TA indicated by network */ + uint8_t tx_power_ind; /* MS power indicated by network */ }; /*! \brief LAPDm datalink like TS 04.06 / Section 3.5.2 */ @@ -113,6 +113,9 @@ struct lapdm_entity { /*! \brief pointer to \ref lapdm_channel of which we're part */ struct lapdm_channel *lapdm_ch; + + uint8_t ta; /* TA used and indicated to network */ + uint8_t tx_power; /* MS power used and indicated to network */ }; /*! \brief the two lapdm_entities that form a GSM logical channel (ACCH + DCCH) */ diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c index 7e4b0380..aac203e2 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -479,6 +479,15 @@ static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg) if (lctx->more) msg->l2h[2] |= LAPDm_MORE; + /* add ACCH header with last indicated tx-power and TA */ + if ((mctx->link_id & 0x40)) { + struct lapdm_entity *le = mdl->entity; + + msg->l2h = msgb_push(msg, 2); + msg->l2h[0] = le->tx_power; + msg->l2h[1] = le->ta; + } + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, 23); } @@ -799,17 +808,16 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) uint8_t sapi = link_id & 7; struct tlv_parsed tv; int length; - uint8_t ta = 0, tx_power = 0; /* check if the layer3 message length exceeds N201 */ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); if (TLVP_PRESENT(&tv, RSL_IE_TIMING_ADVANCE)) { - ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); + le->ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); } if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) { - tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); + le->tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); } if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { LOGP(DLLAPD, LOGL_ERROR, "unit data request without message " @@ -828,7 +836,7 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) } LOGP(DLLAPD, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n", - tx_power, ta); + le->tx_power, le->ta); /* Remove RLL header from msgb and set length to L3-info */ msgb_pull_l2h(msg); @@ -837,8 +845,8 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) /* Push L1 + LAPDm header on msgb */ msg->l2h = msgb_push(msg, 2 + 3); - msg->l2h[0] = tx_power; - msg->l2h[1] = ta; + msg->l2h[0] = le->tx_power; + msg->l2h[1] = le->ta; msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd); msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0); msg->l2h[4] = LAPDm_LEN(length); -- cgit v1.2.3 From 816e178e99510267475a5f8948677a24e43a2863 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:46:30 +0100 Subject: gsm/lapdm: Fix UI frames from BTS->MS have length (B4 format) This is part of a set of commit to fix LAPDm to handle datalink connection on ACCH (SAPI 3) This is required to transfer SMS on SACCH of TCH/f or SDCCH/8 (4). Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- src/gsm/lapdm.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c index aac203e2..3e33e00f 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -520,8 +520,10 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, if (mctx.link_id & 0x40) { /* It was received from network on SACCH */ - /* If sent by BTS, lapdm_fmt must be B4 */ - if (le->mode == LAPDM_MODE_MS) { + /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */ + if (le->mode == LAPDM_MODE_MS + && LAPDm_CTRL_is_U(msg->l2h[3]) + && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) { mctx.lapdm_fmt = LAPDm_FMT_B4; n201 = N201_B4; LOGP(DLLAPD, LOGL_INFO, "fmt=B4\n"); @@ -802,6 +804,8 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) /* L3 requests transfer of unnumbered information */ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) { + struct lapdm_entity *le = dl->entity; + int ui_bts = (le->mode == LAPDM_MODE_BTS); struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t chan_nr = rllh->chan_nr; uint8_t link_id = rllh->link_id; @@ -828,9 +832,9 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); length = TLVP_LEN(&tv, RSL_IE_L3_INFO); /* check if the layer3 message length exceeds N201 */ - if (length + 5 > 23) { /* FIXME: do we know the channel N201? */ + if (length + 4 + !ui_bts > 23) { LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) " - "(discarding)\n", length + 5, 23); + "(discarding)\n", length, 18 + ui_bts); msgb_free(msg); return -EIO; } @@ -844,13 +848,13 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) msg->tail = msg->data + length; /* Push L1 + LAPDm header on msgb */ - msg->l2h = msgb_push(msg, 2 + 3); + msg->l2h = msgb_push(msg, 4 + !ui_bts); msg->l2h[0] = le->tx_power; msg->l2h[1] = le->ta; msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd); msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0); - msg->l2h[4] = LAPDm_LEN(length); - // FIXME: short L2 header support + if (!ui_bts) + msg->l2h[4] = LAPDm_LEN(length); /* Tramsmit */ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23); -- cgit v1.2.3 From 153903c8cfb07c3fb6e4f37f5b15e052c68e54f0 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:47:00 +0100 Subject: gsm/lapdm: Display SAPI in debug message for easier debug Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- src/gsm/lapdm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c index 3e33e00f..3d2f3d83 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -1065,8 +1065,8 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc) return -EINVAL; } - LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received.\n", - lc->name, rsl_msg_name(msg_type)); + LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n", + lc->name, rsl_msg_name(msg_type), sapi); switch (msg_type) { case RSL_MT_UNIT_DATA_REQ: -- cgit v1.2.3 From c626da9f87a63604f3819b003dca1f797e4ea600 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 28 Oct 2011 03:53:50 +0200 Subject: gsm/sms: Added DLSMS debugging Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/core/logging.h | 3 ++- src/logging.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h index b207c1b4..043a8509 100644 --- a/include/osmocom/core/logging.h +++ b/include/osmocom/core/logging.h @@ -67,7 +67,8 @@ void logp(int subsys, char *file, int line, int cont, const char *format, ...) _ #define DLMUX -4 #define DLMI -5 #define DLMIB -6 -#define OSMO_NUM_DLIB 6 +#define DLSMS -7 +#define OSMO_NUM_DLIB 7 struct log_category { uint8_t loglevel; diff --git a/src/logging.c b/src/logging.c index db00331a..f8ed4cb1 100644 --- a/src/logging.c +++ b/src/logging.c @@ -100,6 +100,12 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = { .description = "A-bis Input Driver for B-Channels (voice)", .enabled = 0, .loglevel = LOGL_NOTICE, }, + [INT2IDX(DLSMS)] = { + .name = "DLSMS", + .description = "Layer3 Short Message Service (SMS)", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\033[1;38m", + }, }; /* You have to keep this in sync with the structure loglevel_strs. */ -- cgit v1.2.3 From d84f47abb3d5571f57bbc751b5e3f429958ee63e Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 6 Nov 2011 20:22:12 +0100 Subject: gsm/sms: Moved utility functions of SMS processing to new gsm0411_utils.c Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/gsm/Makefile.am | 3 +- include/osmocom/gsm/gsm0411_utils.h | 30 ++++ src/gsm/Makefile.am | 2 + src/gsm/gsm0411_utils.c | 306 ++++++++++++++++++++++++++++++++++++ 4 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 include/osmocom/gsm/gsm0411_utils.h create mode 100644 src/gsm/gsm0411_utils.c diff --git a/include/osmocom/gsm/Makefile.am b/include/osmocom/gsm/Makefile.am index 5971d0c5..3dbbad5f 100644 --- a/include/osmocom/gsm/Makefile.am +++ b/include/osmocom/gsm/Makefile.am @@ -1,6 +1,7 @@ osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \ gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \ - sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h + sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \ + gsm0411_utils.h SUBDIRS = protocol diff --git a/include/osmocom/gsm/gsm0411_utils.h b/include/osmocom/gsm/gsm0411_utils.h new file mode 100644 index 00000000..a030f581 --- /dev/null +++ b/include/osmocom/gsm/gsm0411_utils.h @@ -0,0 +1,30 @@ +#ifndef _GSM0411_UTILS_H +#define _GSM0411_UTILS_H + +struct msgb *gsm411_msgb_alloc(void); + +/* Generate 03.40 TP-SCTS */ +void gsm340_gen_scts(uint8_t *scts, time_t time); + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +time_t gsm340_scts(uint8_t *scts); + +/* decode validity period. return minutes */ +unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp); + +/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ +enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs); + +/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ +int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type, + uint8_t plan, const char *number); + +/* Prefix msg with a RP header */ +int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type, + uint8_t rp_msg_ref); + +/* Prefix msg with a 04.08/04.11 CP header */ +int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, + uint8_t msg_type); + +#endif /* _GSM0411_UTILS_H */ diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index f27dff2f..114d20f7 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -10,6 +10,8 @@ lib_LTLIBRARIES = libosmogsm.la libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \ gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \ + gsm0411_utils.c \ lapd_core.c lapdm.c + libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION) libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/gsm0411_utils.c b/src/gsm/gsm0411_utils.c new file mode 100644 index 00000000..21938bf7 --- /dev/null +++ b/src/gsm/gsm0411_utils.c @@ -0,0 +1,306 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann + * (C) 2009 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include + +#include +#include + +#define GSM411_ALLOC_SIZE 1024 +#define GSM411_ALLOC_HEADROOM 128 + +struct msgb *gsm411_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM, + "GSM 04.11"); +} + +/* Turn int into semi-octet representation: 98 => 0x89 */ +static uint8_t bcdify(uint8_t value) +{ + uint8_t ret; + + ret = value / 10; + ret |= (value % 10) << 4; + + return ret; +} + +/* Turn semi-octet representation into int: 0x89 => 98 */ +static uint8_t unbcdify(uint8_t value) +{ + uint8_t ret; + + if ((value & 0x0F) > 9 || (value >> 4) > 9) + LOGP(DLSMS, LOGL_ERROR, + "unbcdify got too big nibble: 0x%02X\n", value); + + ret = (value&0x0F)*10; + ret += value>>4; + + return ret; +} + +/* Generate 03.40 TP-SCTS */ +void gsm340_gen_scts(uint8_t *scts, time_t time) +{ + struct tm *tm = gmtime(&time); + + *scts++ = bcdify(tm->tm_year % 100); + *scts++ = bcdify(tm->tm_mon + 1); + *scts++ = bcdify(tm->tm_mday); + *scts++ = bcdify(tm->tm_hour); + *scts++ = bcdify(tm->tm_min); + *scts++ = bcdify(tm->tm_sec); + *scts++ = bcdify(0); /* GMT */ +} + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +time_t gsm340_scts(uint8_t *scts) +{ + struct tm tm; + uint8_t yr = unbcdify(*scts++); + int ofs; + + memset(&tm, 0x00, sizeof(struct tm)); + + if (yr <= 80) + tm.tm_year = 100 + yr; + else + tm.tm_year = yr; + tm.tm_mon = unbcdify(*scts++) - 1; + tm.tm_mday = unbcdify(*scts++); + tm.tm_hour = unbcdify(*scts++); + tm.tm_min = unbcdify(*scts++); + tm.tm_sec = unbcdify(*scts++); + + /* according to gsm 03.40 time zone is + "expressed in quarters of an hour" */ + ofs = unbcdify(*scts++) * 15*60; + + return mktime(&tm) - ofs; +} + +/* Return the default validity period in minutes */ +static unsigned long gsm340_vp_default(void) +{ + unsigned long minutes; + /* Default validity: two days */ + minutes = 24 * 60 * 2; + return minutes; +} + +/* Decode validity period format 'relative' */ +static unsigned long gsm340_vp_relative(uint8_t *sms_vp) +{ + /* Chapter 9.2.3.12.1 */ + uint8_t vp; + unsigned long minutes; + + vp = *(sms_vp); + if (vp <= 143) + minutes = vp + 1 * 5; + else if (vp <= 167) + minutes = 12*60 + (vp-143) * 30; + else if (vp <= 196) + minutes = vp-166 * 60 * 24; + else + minutes = vp-192 * 60 * 24 * 7; + return minutes; +} + +/* Decode validity period format 'absolute' */ +static unsigned long gsm340_vp_absolute(uint8_t *sms_vp) +{ + /* Chapter 9.2.3.12.2 */ + time_t expires, now; + unsigned long minutes; + + expires = gsm340_scts(sms_vp); + now = time(NULL); + if (expires <= now) + minutes = 0; + else + minutes = (expires-now)/60; + return minutes; +} + +/* Decode validity period format 'relative in integer representation' */ +static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp) +{ + uint8_t vp; + unsigned long minutes; + vp = *(sms_vp); + if (vp == 0) { + LOGP(DLSMS, LOGL_ERROR, + "reserved relative_integer validity period\n"); + return gsm340_vp_default(); + } + minutes = vp/60; + return minutes; +} + +/* Decode validity period format 'relative in semi-octet representation' */ +static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp) +{ + unsigned long minutes; + minutes = unbcdify(*sms_vp++)*60; /* hours */ + minutes += unbcdify(*sms_vp++); /* minutes */ + minutes += unbcdify(*sms_vp++)/60; /* seconds */ + return minutes; +} + +/* decode validity period. return minutes */ +unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp) +{ + uint8_t fi; /* functionality indicator */ + + switch (sms_vpf) { + case GSM340_TP_VPF_RELATIVE: + return gsm340_vp_relative(sms_vp); + case GSM340_TP_VPF_ABSOLUTE: + return gsm340_vp_absolute(sms_vp); + case GSM340_TP_VPF_ENHANCED: + /* Chapter 9.2.3.12.3 */ + fi = *sms_vp++; + /* ignore additional fi */ + if (fi & (1<<7)) sms_vp++; + /* read validity period format */ + switch (fi & 0x7) { + case 0x0: + return gsm340_vp_default(); /* no vpf specified */ + case 0x1: + return gsm340_vp_relative(sms_vp); + case 0x2: + return gsm340_vp_relative_integer(sms_vp); + case 0x3: + return gsm340_vp_relative_semioctet(sms_vp); + default: + /* The GSM spec says that the SC should reject any + unsupported and/or undefined values. FIXME */ + LOGP(DLSMS, LOGL_ERROR, + "Reserved enhanced validity period format\n"); + return gsm340_vp_default(); + } + case GSM340_TP_VPF_NONE: + default: + return gsm340_vp_default(); + } +} + +/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ +enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs) +{ + uint8_t cgbits = dcs >> 4; + enum sms_alphabet alpha = DCS_NONE; + + if ((cgbits & 0xc) == 0) { + if (cgbits & 2) { + LOGP(DLSMS, LOGL_NOTICE, + "Compressed SMS not supported yet\n"); + return 0xffffffff; + } + + switch ((dcs >> 2)&0x03) { + case 0: + alpha = DCS_7BIT_DEFAULT; + break; + case 1: + alpha = DCS_8BIT_DATA; + break; + case 2: + alpha = DCS_UCS2; + break; + } + } else if (cgbits == 0xc || cgbits == 0xd) + alpha = DCS_7BIT_DEFAULT; + else if (cgbits == 0xe) + alpha = DCS_UCS2; + else if (cgbits == 0xf) { + if (dcs & 4) + alpha = DCS_8BIT_DATA; + else + alpha = DCS_7BIT_DEFAULT; + } + + return alpha; +} + +/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ +int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type, + uint8_t plan, const char *number) +{ + int len_in_bytes; + + /* prevent buffer overflows */ + if (strlen(number) > 20) + number = ""; + +// oa[1] = 0xb9; /* networks-specific number, private numbering plan */ + oa[1] = 0x80 | (type << 4) | plan; + + len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number); + + /* GSM 03.40 tells us the length is in 'useful semi-octets' */ + oa[0] = strlen(number) & 0xff; + + return len_in_bytes; +} + +/* Prefix msg with a RP header */ +int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type, + uint8_t rp_msg_ref) +{ + struct gsm411_rp_hdr *rp; + uint8_t len = msg->len; + + /* GSM 04.11 RP-DATA header */ + rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); + rp->len = len + 2; + rp->msg_type = rp_msg_type; + rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ + + return 0; +} + +/* Prefix msg with a 04.08/04.11 CP header */ +int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, + uint8_t msg_type) +{ + struct gsm48_hdr *gh; + + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + /* Outgoing needs the highest bit set */ + gh->proto_discr = proto | (trans << 4); + gh->msg_type = msg_type; + + return 0; +} -- cgit v1.2.3 From bbf9034eaab39ad637ef9a3a1fd935d624dcdba8 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 28 Oct 2011 03:55:37 +0200 Subject: gsm/sms: Rewrite of SMC process, extracted from OpenBSC The SMC process is used to transfer RP frames. It is now extracted from OpenBSC. It includes a real state machine now for easier debugging. Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/gsm/Makefile.am | 2 +- include/osmocom/gsm/gsm0411_smc.h | 62 ++++ include/osmocom/gsm/protocol/gsm_04_11.h | 7 +- src/gsm/Makefile.am | 2 +- src/gsm/gsm0411_smc.c | 539 +++++++++++++++++++++++++++++++ 5 files changed, 607 insertions(+), 5 deletions(-) create mode 100644 include/osmocom/gsm/gsm0411_smc.h create mode 100644 src/gsm/gsm0411_smc.c diff --git a/include/osmocom/gsm/Makefile.am b/include/osmocom/gsm/Makefile.am index 3dbbad5f..49a2ec48 100644 --- a/include/osmocom/gsm/Makefile.am +++ b/include/osmocom/gsm/Makefile.am @@ -1,7 +1,7 @@ osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \ gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \ sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \ - gsm0411_utils.h + gsm0411_utils.h gsm0411_smc.h SUBDIRS = protocol diff --git a/include/osmocom/gsm/gsm0411_smc.h b/include/osmocom/gsm/gsm0411_smc.h new file mode 100644 index 00000000..e1508a2d --- /dev/null +++ b/include/osmocom/gsm/gsm0411_smc.h @@ -0,0 +1,62 @@ +#ifndef _GSM0411_SMC_H +#define _GSM0411_SMC_H + +#include + +#define GSM411_MMSMS_EST_REQ 0x310 +#define GSM411_MMSMS_EST_IND 0x312 +#define GSM411_MMSMS_EST_CNF 0x311 +#define GSM411_MMSMS_REL_REQ 0x320 +#define GSM411_MMSMS_REL_IND 0x322 +#define GSM411_MMSMS_DATA_REQ 0x330 +#define GSM411_MMSMS_DATA_IND 0x332 +#define GSM411_MMSMS_UNIT_DATA_REQ 0x340 +#define GSM411_MMSMS_UNIT_DATA_IND 0x342 +#define GSM411_MMSMS_ERR_IND 0x372 + +#define GSM411_MNSMS_ABORT_REQ 0x101 +#define GSM411_MNSMS_DATA_REQ 0x102 +#define GSM411_MNSMS_DATA_IND 0x103 +#define GSM411_MNSMS_EST_REQ 0x104 +#define GSM411_MNSMS_EST_IND 0x105 +#define GSM411_MNSMS_ERROR_IND 0x106 +#define GSM411_MNSMS_REL_REQ 0x107 + +struct gsm411_smc_inst { + int network; /* is this a MO (0) or MT (1) transfer */ + int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg); + int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type); + + enum gsm411_cp_state cp_state; + struct osmo_timer_list cp_timer; + struct msgb *cp_msg; /* store pending message */ + int cp_rel; /* store pending release */ + int cp_retx; /* retry counter */ + int cp_max_retr; /* maximum retry */ + int cp_tc1; /* timer value TC1* */ + +}; + +extern const struct value_string gsm411_cp_cause_strs[]; + +/* init a new instance */ +void gsm411_smc_init(struct gsm411_smc_inst *inst, int network, + int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg), + int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type)); + +/* clear instance */ +void gsm411_smc_clear(struct gsm411_smc_inst *inst); + +/* message from upper layer */ +int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg); + +/* message from lower layer */ +int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type); + +#endif /* _GSM0411_SMC_H */ diff --git a/include/osmocom/gsm/protocol/gsm_04_11.h b/include/osmocom/gsm/protocol/gsm_04_11.h index c6a2b193..905ea9cb 100644 --- a/include/osmocom/gsm/protocol/gsm_04_11.h +++ b/include/osmocom/gsm/protocol/gsm_04_11.h @@ -5,7 +5,7 @@ /* GSM TS 04.11 definitions */ -/* Chapter 5.2.3: SMC-CS states at the network side */ +/* Chapter 5.2.3: SMC-CS states at the user/network side */ enum gsm411_cp_state { GSM411_CPS_IDLE = 0, GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */ @@ -13,11 +13,12 @@ enum gsm411_cp_state { GSM411_CPS_MM_ESTABLISHED = 3, }; -/* Chapter 6.2.2: SMR states at the network side */ +/* Chapter 6.2.2: SMR states at the user/network side */ enum gsm411_rp_state { GSM411_RPS_IDLE = 0, GSM411_RPS_WAIT_FOR_RP_ACK = 1, GSM411_RPS_WAIT_TO_TX_RP_ACK = 3, + GSM411_RPS_WAIT_FOR_RETRANS_T = 4, }; /* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */ @@ -95,7 +96,7 @@ enum gsm411_rp_cause { #define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */ #define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */ -#define GSM411_TMR_TC1A 30, 0 +#define GSM411_TMR_TC1A 30 /* TR1M - 10 */ /* Chapter 8.2.1 */ struct gsm411_rp_hdr { diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index 114d20f7..bd062226 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = libosmogsm.la libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \ gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \ - gsm0411_utils.c \ + gsm0411_utils.c gsm0411_smc.c \ lapd_core.c lapdm.c libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION) diff --git a/src/gsm/gsm0411_smc.c b/src/gsm/gsm0411_smc.c new file mode 100644 index 00000000..187d6e6d --- /dev/null +++ b/src/gsm/gsm0411_smc.c @@ -0,0 +1,539 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann + * (C) 2009 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* Notes on msg: + * + * Messages from lower layer are freed by lower layer. + * + * Messages to upper layer are freed after upper layer call returns, so upper + * layer cannot use data after returning. Upper layer must not free the msg. + * + * This implies: Lower layer messages can be forwarded to upper layer. + * + * Upper layer messages are freed by lower layer, so they must not be freed + * after calling lower layer. + * + * + * Notes on release: + * + * Whenever the process returns to IDLE, the MM connection is released using + * MMSMS-REL-REQ. It is allowed to destroy this process while processing + * this message. + * + * There is expeption, if MMSMS-REL-IND is received from lower layer, the + * process returns to IDLE without sending MMSMS-REL-REQ. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static void cp_timer_expired(void *data); + +#define MAX_SMS_RETRY 2; + +/* init a new instance */ +void gsm411_smc_init(struct gsm411_smc_inst *inst, int network, + int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg), + int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type)) +{ + memset(inst, 0, sizeof(*inst)); + inst->network = network; + inst->cp_max_retr = MAX_SMS_RETRY; + inst->cp_tc1 = GSM411_TMR_TC1A / (inst->cp_max_retr + 1); + inst->cp_state = GSM411_CPS_IDLE; + inst->mn_recv = mn_recv; + inst->mm_send = mm_send; + + LOGP(DLSMS, LOGL_INFO, "New SMC instance created\n"); +} + +/* clear instance */ +void gsm411_smc_clear(struct gsm411_smc_inst *inst) +{ + LOGP(DLSMS, LOGL_INFO, "Clear SMC instance\n"); + + osmo_timer_del(&inst->cp_timer); + + /* free stored msg */ + if (inst->cp_msg) { + LOGP(DLSMS, LOGL_INFO, "Dropping pending message\n"); + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } +} + +const char *smc_state_names[] = { + "IDLE", + "MM_CONN_PENDING", + "WAIT_CP_ACK", + "MM_ESTABLISHED", +}; + +const struct value_string gsm411_cp_cause_strs[] = { + { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, + { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, + { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" }, + { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" }, + { GSM411_CP_CAUSE_MSG_INCOMP_STATE, + "Message incompatible with protocol state" }, + { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" }, + { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, 0 } +}; + +static void new_cp_state(struct gsm411_smc_inst *inst, + enum gsm411_cp_state state) +{ + LOGP(DLSMS, LOGL_INFO, "New CP state %s -> %s\n", + smc_state_names[inst->cp_state], smc_state_names[state]); + inst->cp_state = state; +} + +static int gsm411_tx_cp_error(struct gsm411_smc_inst *inst, uint8_t cause) +{ + struct msgb *nmsg = gsm411_msgb_alloc(); + uint8_t *causep; + + LOGP(DLSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause, + get_value_string(gsm411_cp_cause_strs, cause)); + + causep = msgb_put(nmsg, 1); + *causep = cause; + + return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, + GSM411_MT_CP_ERROR); +} + +/* etablish SMC connection */ +static int gsm411_mnsms_est_req(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + if (inst->cp_msg) { + LOGP(DLSMS, LOGL_FATAL, "EST REQ, but we already have an " + "cp_msg. This should never happen, please fix!\n"); + msgb_free(inst->cp_msg); + } + + inst->cp_msg = msg; + new_cp_state(inst, GSM411_CPS_MM_CONN_PENDING); + /* clear stored release flag */ + inst->cp_rel = 0; + /* send MMSMS_EST_REQ */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_EST_REQ, nmsg, 0); +} + +static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst) +{ + struct msgb *nmsg; + + LOGP(DLSMS, LOGL_INFO, "Send CP data\n"); + /* reset retry counter */ + if (inst->cp_state != GSM411_CPS_WAIT_CP_ACK) + inst->cp_retx = 0; + /* 5.2.3.1.2: enter MO-wait for CP-ACK */ + /* 5.2.3.2.3: enter MT-wait for CP-ACK */ + new_cp_state(inst, GSM411_CPS_WAIT_CP_ACK); + inst->cp_timer.data = inst; + inst->cp_timer.cb = cp_timer_expired; + /* 5.3.2.1: Set Timer TC1A */ + osmo_timer_schedule(&inst->cp_timer, inst->cp_tc1, 0); + /* clone cp_msg */ + nmsg = gsm411_msgb_alloc(); + memcpy(msgb_put(nmsg, inst->cp_msg->len), inst->cp_msg->data, + inst->cp_msg->len); + /* send MMSMS_DATA_REQ with CP-DATA */ + return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, + GSM411_MT_CP_DATA); +} + +static int gsm411_mmsms_est_cnf(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + if (!inst->cp_msg) { + LOGP(DLSMS, LOGL_FATAL, "EST CNF, but we have no cp_msg. This " + "should never happen, please fix!\n"); + return -EINVAL; + } + + return gsm411_mmsms_send_msg(inst); +} + +/* SMC TC1* is expired */ +static void cp_timer_expired(void *data) +{ + struct gsm411_smc_inst *inst = data; + struct msgb *nmsg; + + if (inst->cp_retx == inst->cp_max_retr) { + + LOGP(DLSMS, LOGL_INFO, "TC1* timeout, no more retries.\n"); + /* 5.3.2.1: enter idle state */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + nmsg = gsm411_msgb_alloc(); + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); + msgb_free(nmsg); + /* free pending stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); + return; + } + + LOGP(DLSMS, LOGL_INFO, "TC1* timeout, retrying...\n"); + inst->cp_retx++; + gsm411_mmsms_est_cnf(inst, NULL); +} + +static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + LOGP(DLSMS, LOGL_INFO, "Received CP-ACK\n"); + /* 5.3.2.1 enter MM Connection established */ + new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED); + /* 5.3.2.1: Reset Timer TC1* */ + osmo_timer_del(&inst->cp_timer); + + /* pending release? */ + if (inst->cp_rel) { + struct msgb *nmsg; + + LOGP(DLSMS, LOGL_INFO, "We have pending release.\n"); + new_cp_state(inst, GSM411_CPS_IDLE); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); + } + + return 0; +} + +static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + int mt = GSM411_MNSMS_DATA_IND; + + LOGP(DLSMS, LOGL_INFO, "Received CP-DATA\n"); + /* 5.3.1 enter MM Connection established (if idle) */ + if (inst->cp_state == GSM411_CPS_IDLE) { + new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED); + mt = GSM411_MNSMS_EST_IND; + /* clear stored release flag */ + inst->cp_rel = 0; + } + /* send MMSMS_DATA_REQ (CP ACK) */ + nmsg = gsm411_msgb_alloc(); + inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_ACK); + /* indicate data */ + inst->mn_recv(inst, mt, msg); + + return 0; +} + +/* send CP DATA */ +static int gsm411_mnsms_data_req(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + if (inst->cp_msg) { + LOGP(DLSMS, LOGL_FATAL, "DATA REQ, but we already have an " + "cp_msg. This should never happen, please fix!\n"); + msgb_free(inst->cp_msg); + } + + /* store and send */ + inst->cp_msg = msg; + return gsm411_mmsms_send_msg(inst); +} + +/* release SMC connection */ +static int gsm411_mnsms_rel_req(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + msgb_free(msg); + + /* discard silently */ + if (inst->cp_state == GSM411_CPS_IDLE) + return 0; + + /* store release, until established or released */ + if (inst->cp_state != GSM411_CPS_MM_ESTABLISHED) { + LOGP(DLSMS, LOGL_NOTICE, "Cannot release yet.\n"); + inst->cp_rel = 1; + return 0; + } + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + new_cp_state(inst, GSM411_CPS_IDLE); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); +} + +static int gsm411_mmsms_cp_error(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + LOGP(DLSMS, LOGL_INFO, "Received CP-ERROR\n"); + /* 5.3.4 enter idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, msg); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); +} + +static int gsm411_mmsms_rel_ind(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + LOGP(DLSMS, LOGL_INFO, "MM layer is released\n"); + /* 5.3.4 enter idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + nmsg = gsm411_msgb_alloc(); + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); + msgb_free(nmsg); + + return 0; +} + +/* abort SMC connection */ +static int gsm411_mnsms_abort_req(struct gsm411_smc_inst *inst, + struct msgb *msg) +{ + struct msgb *nmsg; + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + /* 5.3.4 go idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* send MMSMS_DATA_REQ with CP-ERROR */ + inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, msg, GSM411_MT_CP_ERROR); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); +} + +/* statefull handling for MNSMS SAP messages */ +static struct smcdownstate { + uint32_t states; + int type; + const char *name; + int (*rout) (struct gsm411_smc_inst *inst, + struct msgb *msg); +} smcdownstatelist[] = { + /* establish request */ + {SBIT(GSM411_CPS_IDLE), + GSM411_MNSMS_EST_REQ, + "MNSMS-EST-REQ", gsm411_mnsms_est_req}, + + /* release request */ + {ALL_STATES, + GSM411_MNSMS_REL_REQ, + "MNSMS-REL-REQ", gsm411_mnsms_rel_req}, + + /* data request */ + {SBIT(GSM411_CPS_MM_ESTABLISHED), + GSM411_MNSMS_DATA_REQ, + "MNSMS-DATA-REQ", gsm411_mnsms_data_req}, + + /* abort request */ + {ALL_STATES - SBIT(GSM411_CPS_IDLE), + GSM411_MNSMS_ABORT_REQ, + "MNSMS-ABORT-REQ", gsm411_mnsms_abort_req}, +}; + +#define SMCDOWNSLLEN \ + (sizeof(smcdownstatelist) / sizeof(struct smcdownstate)) + +/* message from upper layer */ +int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMCDOWNSLLEN; i++) { + if ((msg_type == smcdownstatelist[i].type) + && (SBIT(inst->cp_state) & smcdownstatelist[i].states)) + break; + } + if (i == SMCDOWNSLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state " + "%s.\n", msg_type, smc_state_names[inst->cp_state]); + msgb_free(msg); + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smcdownstatelist[i].name, smc_state_names[inst->cp_state]); + + rc = smcdownstatelist[i].rout(inst, msg); + + return rc; +} + +/* statefull handling for MMSMS SAP messages */ +static struct smcdatastate { + uint32_t states; + int type, cp_type; + const char *name; + int (*rout) (struct gsm411_smc_inst *inst, + struct msgb *msg); +} smcdatastatelist[] = { + /* establish confirm */ + {SBIT(GSM411_CPS_MM_CONN_PENDING), + GSM411_MMSMS_EST_CNF, 0, + "MMSMS-EST-CNF", gsm411_mmsms_est_cnf}, + + /* establish indication (CP DATA) */ + {SBIT(GSM411_CPS_IDLE), + GSM411_MMSMS_EST_IND, GSM411_MT_CP_DATA, + "MMSMS-EST-IND (CP DATA)", gsm411_mmsms_cp_data}, + + /* data indication (CP DATA) */ + {SBIT(GSM411_CPS_MM_ESTABLISHED), + GSM411_MMSMS_DATA_IND, GSM411_MT_CP_DATA, + "MMSMS-DATA-IND (CP DATA)", gsm411_mmsms_cp_data}, + + /* data indication (CP ACK) */ + {SBIT(GSM411_CPS_WAIT_CP_ACK), + GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ACK, + "MMSMS-DATA-IND (CP ACK)", gsm411_mmsms_cp_ack}, + + /* data indication (CP ERROR) */ + {ALL_STATES, + GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ERROR, + "MMSMS-DATA-IND (CP_ERROR)", gsm411_mmsms_cp_error}, + + /* release indication */ + {ALL_STATES - SBIT(GSM411_CPS_IDLE), + GSM411_MMSMS_REL_IND, 0, + "MMSMS-REL-IND", gsm411_mmsms_rel_ind}, + +}; + +#define SMCDATASLLEN \ + (sizeof(smcdatastatelist) / sizeof(struct smcdatastate)) + +/* message from lower layer + * WARNING: We must not free msg, since it will be performed by the + * lower layer. */ +int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMCDATASLLEN; i++) { + /* state must machtch, MM message must match + * CP msg must match only in case of MMSMS_DATA_IND + */ + if ((msg_type == smcdatastatelist[i].type) + && (SBIT(inst->cp_state) & smcdatastatelist[i].states) + && (msg_type != GSM411_MMSMS_DATA_IND + || cp_msg_type == smcdatastatelist[i].cp_type)) + break; + } + if (i == SMCDATASLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message 0x%x/%u unhandled at this " + "state %s.\n", msg_type, cp_msg_type, + smc_state_names[inst->cp_state]); + if (msg_type == GSM411_MMSMS_EST_IND + || msg_type == GSM411_MMSMS_DATA_IND) { + struct msgb *nmsg; + + LOGP(DLSMS, LOGL_NOTICE, "RX Unimplemented CP " + "msg_type: 0x%02x\n", msg_type); + /* 5.3.4 enter idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + gsm411_tx_cp_error(inst, + GSM411_CP_CAUSE_MSGTYPE_NOTEXIST); + /* send error indication to upper layer */ + nmsg = gsm411_msgb_alloc(); + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); + msgb_free(nmsg); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, + 0); + } + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smcdatastatelist[i].name, smc_state_names[inst->cp_state]); + + rc = smcdatastatelist[i].rout(inst, msg); + + return rc; +} -- cgit v1.2.3 From c1a91a896faf5ac510daabfe389cfa3129201c9f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 28 Oct 2011 11:05:37 +0200 Subject: gsm/sms: Rewrite of SMR process, extracted from OpenBSC The SMR process is used to transfer SMS TPDUs. It is now extracted from OpenBSC. It includes a real state machine now for easier debugging. Also it implements the TR1M and TR2M timers. The memory notification procedure is missing, but not required for network side. Written-by: Andreas Eversberg Signed-off-by: Sylvain Munaut --- include/osmocom/gsm/Makefile.am | 2 +- include/osmocom/gsm/gsm0411_smr.h | 45 ++++ src/gsm/Makefile.am | 2 +- src/gsm/gsm0411_smr.c | 451 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 498 insertions(+), 2 deletions(-) create mode 100644 include/osmocom/gsm/gsm0411_smr.h create mode 100644 src/gsm/gsm0411_smr.c diff --git a/include/osmocom/gsm/Makefile.am b/include/osmocom/gsm/Makefile.am index 49a2ec48..fc1abfe8 100644 --- a/include/osmocom/gsm/Makefile.am +++ b/include/osmocom/gsm/Makefile.am @@ -1,7 +1,7 @@ osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \ gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \ sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \ - gsm0411_utils.h gsm0411_smc.h + gsm0411_utils.h gsm0411_smc.h gsm0411_smr.h SUBDIRS = protocol diff --git a/include/osmocom/gsm/gsm0411_smr.h b/include/osmocom/gsm/gsm0411_smr.h new file mode 100644 index 00000000..5ea8584d --- /dev/null +++ b/include/osmocom/gsm/gsm0411_smr.h @@ -0,0 +1,45 @@ +#ifndef _GSM0411_SMR_H +#define _GSM0411_SMR_H + +#include + +#define GSM411_SM_RL_DATA_REQ 0x401 +#define GSM411_SM_RL_DATA_IND 0x402 +#define GSM411_SM_RL_MEM_AVAIL_REQ 0x403 +#define GSM411_SM_RL_MEM_AVAIL_IND 0x404 +#define GSM411_SM_RL_REPORT_REQ 0x405 +#define GSM411_SM_RL_REPORT_IND 0x406 + +struct gsm411_smr_inst { + int network; /* is this a MO (0) or MT (1) transfer */ + int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + + enum gsm411_rp_state rp_state; + struct osmo_timer_list rp_timer; +}; + +extern const struct value_string gsm411_rp_cause_strs[]; + +/* init a new instance */ +void gsm411_smr_init(struct gsm411_smr_inst *inst, int network, + int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg), + int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg)); + +/* clear instance */ +void gsm411_smr_clear(struct gsm411_smr_inst *inst); + +/* message from upper layer */ +int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + +/* message from lower layer */ +int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + +#endif /* _GSM0411_SMR_H */ + diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index bd062226..7a49dba9 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = libosmogsm.la libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \ gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \ - gsm0411_utils.c gsm0411_smc.c \ + gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c \ lapd_core.c lapdm.c libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION) diff --git a/src/gsm/gsm0411_smr.c b/src/gsm/gsm0411_smr.c new file mode 100644 index 00000000..d5ca9238 --- /dev/null +++ b/src/gsm/gsm0411_smr.c @@ -0,0 +1,451 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann + * (C) 2009 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* Notes on msg: + * + * Messages from lower layer are freed by lower layer. + * + * Messages to upper layer are freed after upper layer call returns, so upper + * layer cannot use data after returning. Upper layer must not free the msg. + * + * This implies: Lower layer messages can be forwarded to upper layer. + * + * Upper layer messages are freed by lower layer, so they must not be freed + * after calling lower layer. + * + * + * Notes on release: + * + * Sending Abort/Release (MNSMS-ABORT-REQ or MNSMS-REL-REQ) may cause the + * lower layer to become IDLE. Then it is allowed to destroy this instance, + * so sending this this MUST be the last thing that is done. + * + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void rp_timer_expired(void *data); + +/* init a new instance */ +void gsm411_smr_init(struct gsm411_smr_inst *inst, int network, + int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg), + int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg)) +{ + memset(inst, 0, sizeof(*inst)); + inst->network = network; + inst->rp_state = GSM411_RPS_IDLE; + inst->rl_recv = rl_recv; + inst->mn_send = mn_send; + inst->rp_timer.data = inst; + inst->rp_timer.cb = rp_timer_expired; + + LOGP(DLSMS, LOGL_INFO, "New SMR instance created\n"); +} + +/* clear instance */ +void gsm411_smr_clear(struct gsm411_smr_inst *inst) +{ + LOGP(DLSMS, LOGL_INFO, "Clear SMR instance\n"); + + osmo_timer_del(&inst->rp_timer); +} + +const char *smr_state_names[] = { + "IDLE", + "WAIT_FOR_RP_ACK", + "illegal state 2" + "WAIT_TO_TX_RP_ACK", + "WAIT_FOR_RETRANS_T", +}; + +const struct value_string gsm411_rp_cause_strs[] = { + { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" }, + { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" }, + { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" }, + { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" }, + { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" }, + { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" }, + { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" }, + { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" }, + { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" }, + { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" }, + { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" }, + { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" }, + { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" }, + /* valid only for MT */ + { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" }, + /* valid for both directions */ + { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" }, + { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" }, + { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, + { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" }, + { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, NULL } +}; + +static void new_rp_state(struct gsm411_smr_inst *inst, + enum gsm411_rp_state state) +{ + LOGP(DLSMS, LOGL_INFO, "New RP state %s -> %s\n", + smr_state_names[inst->rp_state], smr_state_names[state]); + inst->rp_state = state; + + /* stop timer when going idle */ + if (state == GSM411_RPS_IDLE) + osmo_timer_del(&inst->rp_timer); +} + +/* Prefix msg with a RP-DATA header and send as CP-DATA */ +static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, + uint8_t rp_msg_type, uint8_t rp_msg_ref, + int mnsms_msg_type) +{ + struct gsm411_rp_hdr *rp; + uint8_t len = msg->len; + + /* GSM 04.11 RP-DATA header */ + rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); + rp->len = len + 2; + rp->msg_type = rp_msg_type; + rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ + + return inst->mn_send(inst, mnsms_msg_type, msg); +} + +static int gsm411_send_rp_error(struct gsm411_smr_inst *inst, + uint8_t msg_ref, uint8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, cause); + + LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, + get_value_string(gsm411_rp_cause_strs, cause)); + + return gsm411_rp_sendmsg(inst, msg, + (inst->network) ? GSM411_MT_RP_ERROR_MT : GSM411_MT_RP_ERROR_MO, + msg_ref, GSM411_MNSMS_DATA_REQ); +} + +static int gsm411_send_release(struct gsm411_smr_inst *inst) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + LOGP(DLSMS, LOGL_NOTICE, "TX: MNSMS-REL-REQ\n"); + + return inst->mn_send(inst, GSM411_MNSMS_REL_REQ, msg); +} + +static int gsm411_send_abort(struct gsm411_smr_inst *inst) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, 111); //FIXME: better idea ? */ + LOGP(DLSMS, LOGL_NOTICE, "TX: MNSMS-ABORT-REQ\n"); + + return inst->mn_send(inst, GSM411_MNSMS_ABORT_REQ, msg); +} + +static int gsm411_send_report(struct gsm411_smr_inst *inst) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + LOGP(DLSMS, LOGL_NOTICE, "send empty SM_RL_REPORT_IND\n"); + + return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); +} + +static int gsm411_rl_data_req(struct gsm411_smr_inst *inst, struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "TX SMS RP-DATA\n"); + /* start TR1N and enter 'wait for RP-ACK state' */ + osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR1M); + new_rp_state(inst, GSM411_RPS_WAIT_FOR_RP_ACK); + + return inst->mn_send(inst, GSM411_MNSMS_EST_REQ, msg); +} + +static int gsm411_rl_report_req(struct gsm411_smr_inst *inst, struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "TX SMS REPORT\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + + inst->mn_send(inst, GSM411_MNSMS_DATA_REQ, msg); + gsm411_send_release(inst); + return 0; +} + +static int gsm411_mnsms_est_ind(struct gsm411_smr_inst *inst, struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h; + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + uint8_t msg_type = rp_data->msg_type & 0x07; + int rc; + + /* check direction */ + if (inst->network == (msg_type & 1)) { + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); + new_rp_state(inst, GSM411_RPS_IDLE); + gsm411_send_release(inst); + return -EINVAL; + } + + switch (msg_type) { + case GSM411_MT_RP_DATA_MT: + case GSM411_MT_RP_DATA_MO: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-DATA\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M); + new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK); + rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg); + break; + case GSM411_MT_RP_SMMA_MO: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-SMMA\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M); + new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK); + rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg); + break; + default: + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); + new_rp_state(inst, GSM411_RPS_IDLE); + rc = -EINVAL; + break; + } + + return rc; +} + +static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst, + struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h; + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + uint8_t msg_type = rp_data->msg_type & 0x07; + int rc; + + /* check direction */ + if (inst->network == (msg_type & 1)) { + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); + new_rp_state(inst, GSM411_RPS_IDLE); + gsm411_send_release(inst); + return -EINVAL; + } + + switch (msg_type) { + case GSM411_MT_RP_ACK_MO: + case GSM411_MT_RP_ACK_MT: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ACK\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); + gsm411_send_release(inst); + return 0; + case GSM411_MT_RP_ERROR_MO: + case GSM411_MT_RP_ERROR_MT: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ERROR\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); + gsm411_send_release(inst); + return 0; + default: + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); + new_rp_state(inst, GSM411_RPS_IDLE); + gsm411_send_release(inst); + return -EINVAL; + } + + return rc; +} + +static int gsm411_mnsms_error_ind_tx(struct gsm411_smr_inst *inst, + struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); + gsm411_send_release(inst); + return 0; +} + +static int gsm411_mnsms_error_ind_rx(struct gsm411_smr_inst *inst, + struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); +} + +/* SMR TR1* is expired */ +static void rp_timer_expired(void *data) +{ + struct gsm411_smr_inst *inst = data; + + if (inst->rp_state == GSM411_RPS_WAIT_TO_TX_RP_ACK) + LOGP(DLSMS, LOGL_DEBUG, "TR2N\n"); + else + LOGP(DLSMS, LOGL_DEBUG, "TR1N\n"); + gsm411_send_report(inst); + gsm411_send_abort(inst); +} + +/* statefull handling for SM-RL SAP messages */ +static struct smrdownstate { + uint32_t states; + int type; + const char *name; + int (*rout) (struct gsm411_smr_inst *inst, + struct msgb *msg); +} smrdownstatelist[] = { + /* data request */ + {SBIT(GSM411_RPS_IDLE), + GSM411_SM_RL_DATA_REQ, + "SM-RL-DATA_REQ", gsm411_rl_data_req}, + + /* report request */ + {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK), + GSM411_SM_RL_REPORT_REQ, + "SM-RL-REPORT_REQ", gsm411_rl_report_req}, +}; + +#define SMRDOWNSLLEN \ + (sizeof(smrdownstatelist) / sizeof(struct smrdownstate)) + +/* message from upper layer */ +int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMRDOWNSLLEN; i++) { + if ((msg_type == smrdownstatelist[i].type) + && (SBIT(inst->rp_state) & smrdownstatelist[i].states)) + break; + } + if (i == SMRDOWNSLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state " + "%s.\n", msg_type, smr_state_names[inst->rp_state]); + msgb_free(msg); + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smrdownstatelist[i].name, smr_state_names[inst->rp_state]); + + rc = smrdownstatelist[i].rout(inst, msg); + + return rc; +} + +/* statefull handling for MMSMS SAP messages */ +static struct smrdatastate { + uint32_t states; + int type; + const char *name; + int (*rout) (struct gsm411_smr_inst *inst, + struct msgb *msg); +} smrdatastatelist[] = { + /* establish indication */ + {SBIT(GSM411_RPS_IDLE), + GSM411_MNSMS_EST_IND, + "MNSMS-EST-IND", gsm411_mnsms_est_ind}, + + /* data indication */ + {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK), + GSM411_MNSMS_DATA_IND, + "MNSMS-DATA-IND", gsm411_mnsms_data_ind_tx}, + + /* error indication */ + {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK), + GSM411_MNSMS_ERROR_IND, + "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_tx}, + + /* error indication */ + {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK), + GSM411_MNSMS_ERROR_IND, + "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_rx}, + +}; + +#define SMRDATASLLEN \ + (sizeof(smrdatastatelist) / sizeof(struct smrdatastate)) + +/* message from lower layer + * WARNING: We must not free msg, since it will be performed by the + * lower layer. */ +int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMRDATASLLEN; i++) { + /* state must machtch, MM message must match + * CP msg must match only in case of MMSMS_DATA_IND + */ + if ((msg_type == smrdatastatelist[i].type) + && (SBIT(inst->rp_state) & smrdatastatelist[i].states)) + break; + } + if (i == SMRDATASLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state " + "%s.\n", msg_type, smr_state_names[inst->rp_state]); + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smrdatastatelist[i].name, smr_state_names[inst->rp_state]); + + rc = smrdatastatelist[i].rout(inst, msg); + + return rc; +} -- cgit v1.2.3 From 0d9b8ec9d3f36125b1762dab2e429c3f0405ce9c Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 12 Nov 2011 23:52:20 +0100 Subject: include/gsm_04_11: Fix compatibility issue with GSM411_TMR_TC1A We need to keep the old one for compatibility ! Signed-off-by: Sylvain Munaut --- include/osmocom/gsm/protocol/gsm_04_11.h | 3 ++- src/gsm/gsm0411_smc.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/osmocom/gsm/protocol/gsm_04_11.h b/include/osmocom/gsm/protocol/gsm_04_11.h index 905ea9cb..f37152fe 100644 --- a/include/osmocom/gsm/protocol/gsm_04_11.h +++ b/include/osmocom/gsm/protocol/gsm_04_11.h @@ -96,7 +96,8 @@ enum gsm411_rp_cause { #define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */ #define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */ -#define GSM411_TMR_TC1A 30 /* TR1M - 10 */ +#define GSM411_TMR_TC1A 30, 0 /* TR1M - 10 */ +#define GSM411_TMR_TC1A_SEC 30 /* TR1M - 10 */ /* Chapter 8.2.1 */ struct gsm411_rp_hdr { diff --git a/src/gsm/gsm0411_smc.c b/src/gsm/gsm0411_smc.c index 187d6e6d..2146c9e1 100644 --- a/src/gsm/gsm0411_smc.c +++ b/src/gsm/gsm0411_smc.c @@ -73,7 +73,7 @@ void gsm411_smc_init(struct gsm411_smc_inst *inst, int network, memset(inst, 0, sizeof(*inst)); inst->network = network; inst->cp_max_retr = MAX_SMS_RETRY; - inst->cp_tc1 = GSM411_TMR_TC1A / (inst->cp_max_retr + 1); + inst->cp_tc1 = GSM411_TMR_TC1A_SEC / (inst->cp_max_retr + 1); inst->cp_state = GSM411_CPS_IDLE; inst->mn_recv = mn_recv; inst->mm_send = mm_send; -- cgit v1.2.3 From cc90d494ceaa3b096b8102bc590fcbe9e6671c6f Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 12 Nov 2011 23:52:40 +0100 Subject: gsm/gsm0411_smc: Fix typo Signed-off-by: Sylvain Munaut --- src/gsm/gsm0411_smc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gsm/gsm0411_smc.c b/src/gsm/gsm0411_smc.c index 2146c9e1..54e6129c 100644 --- a/src/gsm/gsm0411_smc.c +++ b/src/gsm/gsm0411_smc.c @@ -61,7 +61,7 @@ static void cp_timer_expired(void *data); -#define MAX_SMS_RETRY 2; +#define MAX_SMS_RETRY 2 /* init a new instance */ void gsm411_smc_init(struct gsm411_smc_inst *inst, int network, -- cgit v1.2.3 From a71b8eaca7eed4dfc96f2cebabfc26430416c2e9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 13 Nov 2011 10:11:31 +0100 Subject: timer: Better fix to the timer re-scheduling situation As it turns out, the previous fix didn't cover the case of a timer X being re-scheduled inside a timer call back expiring at the same time as the previous X instance. Written-by: Pablo Neira Ayuso Signed-off-by: Sylvain Munaut --- src/timer.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/timer.c b/src/timer.c index 77fb0039..79d4ad6d 100644 --- a/src/timer.c +++ b/src/timer.c @@ -69,12 +69,9 @@ static void __add_timer(struct osmo_timer_list *timer) */ void osmo_timer_add(struct osmo_timer_list *timer) { - if (timer->active) { - rb_erase(&timer->node, &timer_root); - } else { - timer->active = 1; - INIT_LLIST_HEAD(&timer->list); - } + osmo_timer_del(timer); + timer->active = 1; + INIT_LLIST_HEAD(&timer->list); __add_timer(timer); } -- cgit v1.2.3