blob: 75ddf9dd9d56c864c00dea274cc2c2576f6c13f3 [file] [log] [blame]
Daniel Willmann8b3390e2008-12-28 00:31:09 +00001/* Point-to-Point (PP) Short Message Service (SMS)
2 * Support on Mobile Radio Interface
3 * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
4
5/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
Harald Welte7e310b12009-03-30 20:56:32 +00006 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
Daniel Willmann8b3390e2008-12-28 00:31:09 +00007 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
Harald Weltef3efc592009-07-27 20:11:35 +020031#include <time.h>
Daniel Willmann8b3390e2008-12-28 00:31:09 +000032#include <netinet/in.h>
33
34#include <openbsc/msgb.h>
Harald Welte7e310b12009-03-30 20:56:32 +000035#include <openbsc/tlv.h>
Daniel Willmann8b3390e2008-12-28 00:31:09 +000036#include <openbsc/debug.h>
Daniel Willmann471712b2008-12-29 01:54:02 +000037#include <openbsc/gsm_data.h>
38#include <openbsc/gsm_subscriber.h>
Daniel Willmann8b3390e2008-12-28 00:31:09 +000039#include <openbsc/gsm_04_11.h>
40#include <openbsc/gsm_04_08.h>
Holger Freyther76c95692009-02-17 20:31:30 +000041#include <openbsc/gsm_utils.h>
Daniel Willmann8b3390e2008-12-28 00:31:09 +000042#include <openbsc/abis_rsl.h>
Holger Freyther9b177762009-02-16 19:07:18 +000043#include <openbsc/signal.h>
Harald Welte7e310b12009-03-30 20:56:32 +000044#include <openbsc/db.h>
Harald Welte2cf161b2009-06-20 22:36:41 +020045#include <openbsc/talloc.h>
Harald Weltef3efc592009-07-27 20:11:35 +020046#include <openbsc/transaction.h>
Harald Welte76042182009-08-08 16:03:15 +020047#include <openbsc/paging.h>
Harald Weltecb8f4432009-08-09 14:59:02 +020048#include <openbsc/bsc_rll.h>
Holger Hans Peter Freyther34e97492009-08-10 07:54:02 +020049#include <openbsc/chan_alloc.h>
Daniel Willmann8b3390e2008-12-28 00:31:09 +000050
Daniel Willmann471712b2008-12-29 01:54:02 +000051#define GSM411_ALLOC_SIZE 1024
52#define GSM411_ALLOC_HEADROOM 128
53
Harald Weltecb8f4432009-08-09 14:59:02 +020054#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */
55
Harald Welte (local)d19e58b2009-08-15 02:30:58 +020056void *tall_gsms_ctx;
Harald Weltef3efc592009-07-27 20:11:35 +020057static u_int32_t new_callref = 0x40000001;
58
Harald Welte (local)c89a5112009-08-14 10:42:43 +020059static const struct value_string cp_cause_strs[] = {
60 { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" },
61 { GSM411_CP_CAUSE_CONGESTION, "Congestion" },
62 { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" },
63 { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
64 { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
65 { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" },
66 { GSM411_CP_CAUSE_MSG_INCOMP_STATE,
67 "Message incompatible with protocol state" },
68 { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" },
69 { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
70 { 0, 0 }
71};
72
73static const struct value_string rp_cause_strs[] = {
74 { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" },
75 { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" },
76 { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" },
77 { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" },
78 { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" },
79 { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" },
80 { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" },
81 { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" },
82 { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" },
83 { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" },
84 { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" },
85 { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" },
86 { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" },
87 { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" },
88 { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" },
89 /* valid only for MT */
90 { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" },
91 /* valid for both directions */
92 { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" },
93 { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
94 { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
95 { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" },
96 { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" },
97 { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" },
98 { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
99 { 0, NULL }
100};
101
Harald Welte76042182009-08-08 16:03:15 +0200102struct gsm_sms *sms_alloc(void)
103{
104 return talloc_zero(tall_gsms_ctx, struct gsm_sms);
105}
106
107void sms_free(struct gsm_sms *sms)
108{
109 /* drop references to subscriber structure */
110 if (sms->sender)
111 subscr_put(sms->sender);
112 if (sms->receiver)
113 subscr_put(sms->receiver);
114
115 talloc_free(sms);
116}
117
Holger Freythera553d092009-01-04 20:16:25 +0000118struct msgb *gsm411_msgb_alloc(void)
Daniel Willmann471712b2008-12-29 01:54:02 +0000119{
Harald Welte966636f2009-06-26 19:39:35 +0200120 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
121 "GSM 04.11");
Daniel Willmann471712b2008-12-29 01:54:02 +0000122}
123
Harald Welte (local)daef6062009-08-14 11:41:12 +0200124static int gsm411_sendmsg(struct msgb *msg, u_int8_t link_id)
Daniel Willmann471712b2008-12-29 01:54:02 +0000125{
126 if (msg->lchan)
127 msg->trx = msg->lchan->ts->trx;
128
129 msg->l3h = msg->data;
130
Harald Welte76042182009-08-08 16:03:15 +0200131 DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
132
Harald Welte (local)daef6062009-08-14 11:41:12 +0200133 return rsl_data_request(msg, link_id);
Daniel Willmann471712b2008-12-29 01:54:02 +0000134}
135
Harald Welte41985612009-08-10 00:24:32 +0200136/* SMC TC1* is expired */
137static void cp_timer_expired(void *data)
138{
139 struct gsm_trans *trans = data;
140
141 DEBUGP(DSMS, "SMC Timer TC1* is expired, calling trans_free()\n");
142 /* FIXME: we need to re-transmit the last CP-DATA 1..3 times */
143 trans_free(trans);
144}
145
Harald Welte87f5d632009-07-04 17:39:00 +0200146/* Prefix msg with a 04.08/04.11 CP header */
Harald Weltef3efc592009-07-27 20:11:35 +0200147static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
148 u_int8_t msg_type)
Harald Welte87f5d632009-07-04 17:39:00 +0200149{
150 struct gsm48_hdr *gh;
151
152 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
153 /* Outgoing needs the highest bit set */
Harald Weltef3efc592009-07-27 20:11:35 +0200154 gh->proto_discr = trans->protocol | (trans->transaction_id<<4);
Harald Welte87f5d632009-07-04 17:39:00 +0200155 gh->msg_type = msg_type;
156
Harald Weltef3efc592009-07-27 20:11:35 +0200157 /* assign the outgoing lchan */
158 msg->lchan = trans->lchan;
159
160 /* mobile originating */
161 switch (gh->msg_type) {
162 case GSM411_MT_CP_DATA:
163 /* 5.2.3.1.2: enter MO-wait for CP-ack */
Harald Weltecb8f4432009-08-09 14:59:02 +0200164 /* 5.2.3.2.3: enter MT-wait for CP-ACK */
Harald Weltef3efc592009-07-27 20:11:35 +0200165 trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK;
Harald Welte41985612009-08-10 00:24:32 +0200166 trans->sms.cp_timer.data = trans;
167 trans->sms.cp_timer.cb = cp_timer_expired;
168 /* 5.3.2.1: Set Timer TC1A */
169 bsc_schedule_timer(&trans->sms.cp_timer, GSM411_TMR_TC1A);
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200170 DEBUGP(DSMS, "TX: CP-DATA ");
171 break;
172 case GSM411_MT_CP_ACK:
173 DEBUGP(DSMS, "TX: CP-ACK ");
174 break;
175 case GSM411_MT_CP_ERROR:
176 DEBUGP(DSMS, "TX: CP-ACK ");
Harald Weltef3efc592009-07-27 20:11:35 +0200177 break;
178 }
179
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200180 DEBUGPC(DSMS, "trans=%x\n", trans->transaction_id);
181
Harald Welte (local)daef6062009-08-14 11:41:12 +0200182 return gsm411_sendmsg(msg, trans->sms.link_id);
Harald Welte87f5d632009-07-04 17:39:00 +0200183}
184
185/* Prefix msg with a RP-DATA header and send as CP-DATA */
Harald Weltef3efc592009-07-27 20:11:35 +0200186static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
187 u_int8_t rp_msg_type, u_int8_t rp_msg_ref)
Harald Welte87f5d632009-07-04 17:39:00 +0200188{
189 struct gsm411_rp_hdr *rp;
Harald Welte0d544e72009-08-10 00:22:19 +0200190 u_int8_t len = msg->len;
Harald Welte87f5d632009-07-04 17:39:00 +0200191
192 /* GSM 04.11 RP-DATA header */
193 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
Harald Welte0d544e72009-08-10 00:22:19 +0200194 rp->len = len + 2;
Harald Welte87f5d632009-07-04 17:39:00 +0200195 rp->msg_type = rp_msg_type;
196 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
197
Harald Weltef3efc592009-07-27 20:11:35 +0200198 return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
Harald Welte87f5d632009-07-04 17:39:00 +0200199}
200
Steffen Neubauer5727cf42009-11-11 23:02:07 +0900201static time_t gsm340_scts(u_int8_t *scts);
202
Harald Welte76042182009-08-08 16:03:15 +0200203static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
Harald Welte7e310b12009-03-30 20:56:32 +0000204{
205 u_int8_t vp;
206 unsigned long minutes;
Steffen Neubauer5727cf42009-11-11 23:02:07 +0900207 time_t expires;
208 time_t now;
Harald Welte7e310b12009-03-30 20:56:32 +0000209
Harald Welte76042182009-08-08 16:03:15 +0200210 switch (sms_vpf) {
Harald Welte7e310b12009-03-30 20:56:32 +0000211 case GSM340_TP_VPF_RELATIVE:
212 /* Chapter 9.2.3.12.1 */
Harald Welte76042182009-08-08 16:03:15 +0200213 vp = *(sms_vp);
Harald Welte7e310b12009-03-30 20:56:32 +0000214 if (vp <= 143)
215 minutes = vp + 1 * 5;
216 else if (vp <= 167)
217 minutes = 12*60 + (vp-143) * 30;
218 else if (vp <= 196)
219 minutes = vp-166 * 60 * 24;
220 else
221 minutes = vp-192 * 60 * 24 * 7;
222 break;
223 case GSM340_TP_VPF_ABSOLUTE:
224 /* Chapter 9.2.3.12.2 */
Steffen Neubauer5727cf42009-11-11 23:02:07 +0900225 expires = gsm340_scts(sms_vp);
226 now = mktime(gmtime(NULL));
227 if (expires <= now)
228 minutes = 0;
229 else
230 minutes = (expires-now)/60;
Harald Welte7e310b12009-03-30 20:56:32 +0000231 break;
232 case GSM340_TP_VPF_ENHANCED:
233 /* Chapter 9.2.3.12.3 */
234 /* FIXME: implementation */
235 DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
236 break;
Daniel Willmann58c83d82009-08-13 03:40:49 +0200237 case GSM340_TP_VPF_NONE:
238 /* Default validity: two days */
239 minutes = 24 * 60 * 2;
240 break;
Harald Welte7e310b12009-03-30 20:56:32 +0000241 }
242 return minutes;
243}
244
245/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
246enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
247{
248 u_int8_t cgbits = dcs >> 4;
249 enum sms_alphabet alpha = DCS_NONE;
250
251 if ((cgbits & 0xc) == 0) {
252 if (cgbits & 2)
253 DEBUGP(DSMS, "Compressed SMS not supported yet\n");
254
Daniel Willmannd5d5e1d2009-08-15 03:01:17 +0200255 switch ((dcs >> 2)&0x03) {
Harald Welte7e310b12009-03-30 20:56:32 +0000256 case 0:
257 alpha = DCS_7BIT_DEFAULT;
258 break;
259 case 1:
260 alpha = DCS_8BIT_DATA;
261 break;
262 case 2:
263 alpha = DCS_UCS2;
264 break;
265 }
266 } else if (cgbits == 0xc || cgbits == 0xd)
267 alpha = DCS_7BIT_DEFAULT;
268 else if (cgbits == 0xe)
269 alpha = DCS_UCS2;
270 else if (cgbits == 0xf) {
271 if (dcs & 4)
272 alpha = DCS_8BIT_DATA;
273 else
274 alpha = DCS_7BIT_DEFAULT;
275 }
276
277 return alpha;
278}
279
Harald Welte76042182009-08-08 16:03:15 +0200280static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
Harald Welte7e310b12009-03-30 20:56:32 +0000281{
282 if (db_sms_store(gsms) != 0) {
283 DEBUGP(DSMS, "Failed to store SMS in Database\n");
Harald Welteb9c758b2009-07-05 14:02:46 +0200284 return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
Harald Welte7e310b12009-03-30 20:56:32 +0000285 }
Harald Welte76042182009-08-08 16:03:15 +0200286 /* dispatch a signal to tell higher level about it */
287 dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
Harald Welte (local)ee4410a2009-08-17 09:39:55 +0200288 /* try delivering the SMS right now */
289 //gsm411_send_sms_subscr(gsms->receiver, gsms);
290
Harald Welte7e310b12009-03-30 20:56:32 +0000291 return 0;
292}
293
Harald Welte76042182009-08-08 16:03:15 +0200294/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
295static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
296 struct gsm_subscriber *subscr)
Harald Weltef3efc592009-07-27 20:11:35 +0200297{
Harald Welte76042182009-08-08 16:03:15 +0200298 int len_in_bytes;
Harald Weltef3efc592009-07-27 20:11:35 +0200299
Harald Welte76042182009-08-08 16:03:15 +0200300 oa[1] = 0xb9; /* networks-specific number, private numbering plan */
301
302 len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
303
304 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
305 oa[0] = strlen(subscr->extension) & 0xff;
306
307 return len_in_bytes;
Harald Weltef3efc592009-07-27 20:11:35 +0200308}
309
Steffen Neubauer5727cf42009-11-11 23:02:07 +0900310/* Turn int into semi-octet representation: 98 => 0x89 */
Harald Weltef3efc592009-07-27 20:11:35 +0200311static u_int8_t bcdify(u_int8_t value)
312{
313 u_int8_t ret;
314
Harald Welte76042182009-08-08 16:03:15 +0200315 ret = value / 10;
316 ret |= (value % 10) << 4;
Harald Weltef3efc592009-07-27 20:11:35 +0200317
318 return ret;
319}
320
Steffen Neubauer5727cf42009-11-11 23:02:07 +0900321/* Turn semi-octet representation into int: 0x89 => 98 */
322static u_int8_t unbcdify(u_int8_t value)
323{
324 u_int8_t ret;
325
326 if ((value & 0x0F) > 9 || (value >> 4) > 9)
327 DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
328
329 ret = (value&0x0F)*10;
330 if (ret > 90)
331 ret += value>>4;
332
333 return ret;
334}
335
Harald Weltef3efc592009-07-27 20:11:35 +0200336/* Generate 03.40 TP-SCTS */
337static void gsm340_gen_scts(u_int8_t *scts, time_t time)
338{
339 struct tm *tm = localtime(&time);
Harald Weltef3efc592009-07-27 20:11:35 +0200340
341 *scts++ = bcdify(tm->tm_year % 100);
Stefan Schmidt689537a2009-08-13 21:00:44 +0200342 *scts++ = bcdify(tm->tm_mon + 1);
Harald Weltef3efc592009-07-27 20:11:35 +0200343 *scts++ = bcdify(tm->tm_mday);
344 *scts++ = bcdify(tm->tm_hour);
345 *scts++ = bcdify(tm->tm_min);
346 *scts++ = bcdify(tm->tm_sec);
Steffen Neubauer5727cf42009-11-11 23:02:07 +0900347 *scts++ = bcdify(tm->tm_gmtoff/(60*15));
348}
349
350/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
351static time_t gsm340_scts(u_int8_t *scts)
352{
353 struct tm tm;
354
355 u_int8_t yr = unbcdify(*scts++);
356
357 if (yr <= 80)
358 tm.tm_year = 100 + yr;
359 else
360 tm.tm_year = yr;
361 tm.tm_mon = unbcdify(*scts++) - 1;
362 tm.tm_mday = unbcdify(*scts++);
363 tm.tm_hour = unbcdify(*scts++);
364 tm.tm_min = unbcdify(*scts++);
365 tm.tm_sec = unbcdify(*scts++);
366 /* according to gsm 03.40 time zone is
367 "expressed in quarters of an hour" */
368 tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
369
370 return mktime(&tm);
Harald Weltef3efc592009-07-27 20:11:35 +0200371}
372
Harald Welte76042182009-08-08 16:03:15 +0200373/* generate a msgb containing a TPDU derived from struct gsm_sms,
374 * returns total size of TPDU */
375static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
Harald Weltef3efc592009-07-27 20:11:35 +0200376{
Harald Weltef3efc592009-07-27 20:11:35 +0200377 u_int8_t *smsp;
378 u_int8_t oa[12]; /* max len per 03.40 */
379 u_int8_t oa_len = 0;
Daniel Willmann9aef1452009-08-13 03:39:07 +0200380 u_int8_t octet_len;
Harald Welte76042182009-08-08 16:03:15 +0200381 unsigned int old_msg_len = msg->len;
Harald Weltef3efc592009-07-27 20:11:35 +0200382
383 /* generate first octet with masked bits */
384 smsp = msgb_put(msg, 1);
Harald Welte76042182009-08-08 16:03:15 +0200385 /* TP-MTI (message type indicator) */
Harald Weltef3efc592009-07-27 20:11:35 +0200386 *smsp = GSM340_SMS_DELIVER_SC2MS;
Harald Welte76042182009-08-08 16:03:15 +0200387 /* TP-MMS (more messages to send) */
388 if (0 /* FIXME */)
Harald Weltef3efc592009-07-27 20:11:35 +0200389 *smsp |= 0x04;
Harald Welte76042182009-08-08 16:03:15 +0200390 /* TP-SRI(deliver)/SRR(submit) */
Harald Weltef3efc592009-07-27 20:11:35 +0200391 if (sms->status_rep_req)
392 *smsp |= 0x20;
Harald Welte76042182009-08-08 16:03:15 +0200393 /* TP-UDHI (indicating TP-UD contains a header) */
394 if (sms->ud_hdr_ind)
Harald Weltef3efc592009-07-27 20:11:35 +0200395 *smsp |= 0x40;
Harald Welte76042182009-08-08 16:03:15 +0200396#if 0
397 /* TP-RP (indicating that a reply path exists) */
Harald Weltef3efc592009-07-27 20:11:35 +0200398 if (sms->
399 *smsp |= 0x80;
400#endif
401
402 /* generate originator address */
Harald Welte76042182009-08-08 16:03:15 +0200403 oa_len = gsm340_gen_oa(oa, sizeof(oa), sms->sender);
Harald Weltef3efc592009-07-27 20:11:35 +0200404 smsp = msgb_put(msg, oa_len);
Harald Weltef3efc592009-07-27 20:11:35 +0200405 memcpy(smsp, oa, oa_len);
406
407 /* generate TP-PID */
408 smsp = msgb_put(msg, 1);
409 *smsp = sms->protocol_id;
410
411 /* generate TP-DCS */
412 smsp = msgb_put(msg, 1);
413 *smsp = sms->data_coding_scheme;
414
415 /* generate TP-SCTS */
416 smsp = msgb_put(msg, 7);
417 gsm340_gen_scts(smsp, time(NULL));
Harald Welte76042182009-08-08 16:03:15 +0200418
Harald Weltef3efc592009-07-27 20:11:35 +0200419 /* generate TP-UDL */
420 smsp = msgb_put(msg, 1);
Harald Welte76042182009-08-08 16:03:15 +0200421 *smsp = sms->user_data_len;
Harald Weltef3efc592009-07-27 20:11:35 +0200422
423 /* generate TP-UD */
Daniel Willmann6b024cb2009-08-15 03:01:46 +0200424 switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) {
425 case DCS_7BIT_DEFAULT:
Daniel Willmann9aef1452009-08-13 03:39:07 +0200426 octet_len = sms->user_data_len*7/8;
427 if (sms->user_data_len*7%8 != 0)
428 octet_len++;
Daniel Willmann6b1e8222009-08-12 21:17:06 +0200429 /* Warning, user_data_len indicates the amount of septets
430 * (characters), we need amount of octets occupied */
Daniel Willmann9aef1452009-08-13 03:39:07 +0200431 smsp = msgb_put(msg, octet_len);
432 memcpy(smsp, sms->user_data, octet_len);
Daniel Willmann6b024cb2009-08-15 03:01:46 +0200433 break;
434 case DCS_UCS2:
435 case DCS_8BIT_DATA:
436 smsp = msgb_put(msg, sms->user_data_len);
437 memcpy(smsp, sms->user_data, sms->user_data_len);
438 break;
439 default:
440 DEBUGP(DSMS, "Unhandled Data Coding Scheme: 0x%02X\n", sms->data_coding_scheme);
441 break;
Daniel Willmann6b1e8222009-08-12 21:17:06 +0200442 }
Harald Weltef3efc592009-07-27 20:11:35 +0200443
Harald Welte76042182009-08-08 16:03:15 +0200444 return msg->len - old_msg_len;
Harald Weltef3efc592009-07-27 20:11:35 +0200445}
446
Harald Welteb9c758b2009-07-05 14:02:46 +0200447/* process an incoming TPDU (called from RP-DATA)
448 * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
Harald Welte7e310b12009-03-30 20:56:32 +0000449static int gsm340_rx_tpdu(struct msgb *msg)
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000450{
Harald Welte9176bd42009-07-23 18:46:00 +0200451 struct gsm_bts *bts = msg->lchan->ts->trx->bts;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000452 u_int8_t *smsp = msgb_sms(msg);
Harald Welte7e310b12009-03-30 20:56:32 +0000453 struct gsm_sms *gsms;
Harald Welte76042182009-08-08 16:03:15 +0200454 u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp;
455 u_int8_t *sms_vp;
Harald Welte7e310b12009-03-30 20:56:32 +0000456 u_int8_t da_len_bytes;
457 u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
458 int rc = 0;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000459
Harald Welte76042182009-08-08 16:03:15 +0200460 gsms = sms_alloc();
461 if (!gsms)
Harald Welteb9c758b2009-07-05 14:02:46 +0200462 return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
Harald Welte7e310b12009-03-30 20:56:32 +0000463
464 /* invert those fields where 0 means active/present */
Harald Welte76042182009-08-08 16:03:15 +0200465 sms_mti = *smsp & 0x03;
466 sms_mms = !!(*smsp & 0x04);
467 sms_vpf = (*smsp & 0x18) >> 3;
468 gsms->status_rep_req = (*smsp & 0x20);
469 gsms->ud_hdr_ind = (*smsp & 0x40);
470 sms_rp = (*smsp & 0x80);
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000471
472 smsp++;
Harald Welte76042182009-08-08 16:03:15 +0200473 gsms->msg_ref = *smsp++;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000474
Harald Welte7e310b12009-03-30 20:56:32 +0000475 /* length in bytes of the destination address */
476 da_len_bytes = 2 + *smsp/2 + *smsp%2;
477 if (da_len_bytes > 12) {
478 DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
Harald Welteb9c758b2009-07-05 14:02:46 +0200479 rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
Harald Welte7e310b12009-03-30 20:56:32 +0000480 goto out;
481 }
Harald Welte3cfdb222009-06-12 02:42:11 +0800482 memset(address_lv, 0, sizeof(address_lv));
Harald Welte7e310b12009-03-30 20:56:32 +0000483 memcpy(address_lv, smsp, da_len_bytes);
484 /* mangle first byte to reflect length in bytes, not digits */
Harald Welte3cfdb222009-06-12 02:42:11 +0800485 address_lv[0] = da_len_bytes - 1;
Harald Welte7e310b12009-03-30 20:56:32 +0000486 /* convert to real number */
Harald Welte76042182009-08-08 16:03:15 +0200487 decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1);
Harald Welte7e310b12009-03-30 20:56:32 +0000488 smsp += da_len_bytes;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000489
Harald Welte76042182009-08-08 16:03:15 +0200490 gsms->protocol_id = *smsp++;
491 gsms->data_coding_scheme = *smsp++;
Harald Welte7e310b12009-03-30 20:56:32 +0000492
Harald Welte76042182009-08-08 16:03:15 +0200493 sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme);
Harald Welte7e310b12009-03-30 20:56:32 +0000494
Harald Welte76042182009-08-08 16:03:15 +0200495 switch (sms_vpf) {
Harald Welte7e310b12009-03-30 20:56:32 +0000496 case GSM340_TP_VPF_RELATIVE:
Harald Welte76042182009-08-08 16:03:15 +0200497 sms_vp = smsp++;
Harald Welte7e310b12009-03-30 20:56:32 +0000498 break;
499 case GSM340_TP_VPF_ABSOLUTE:
500 case GSM340_TP_VPF_ENHANCED:
Harald Welte76042182009-08-08 16:03:15 +0200501 sms_vp = smsp;
Harald Welte7e310b12009-03-30 20:56:32 +0000502 smsp += 7;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000503 break;
Daniel Willmann58c83d82009-08-13 03:40:49 +0200504 case GSM340_TP_VPF_NONE:
505 sms_vp = 0;
506 break;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000507 default:
508 DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
Harald Welte76042182009-08-08 16:03:15 +0200509 sms_vpf);
510 return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000511 }
Harald Welte76042182009-08-08 16:03:15 +0200512 gsms->user_data_len = *smsp++;
513 if (gsms->user_data_len) {
514 memcpy(gsms->user_data, smsp, gsms->user_data_len);
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000515
Harald Welte76042182009-08-08 16:03:15 +0200516 switch (sms_alphabet) {
Harald Welte7e310b12009-03-30 20:56:32 +0000517 case DCS_7BIT_DEFAULT:
Harald Welte76042182009-08-08 16:03:15 +0200518 gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len);
Harald Welte7e310b12009-03-30 20:56:32 +0000519 break;
520 case DCS_8BIT_DATA:
521 case DCS_UCS2:
522 case DCS_NONE:
Harald Welte7e310b12009-03-30 20:56:32 +0000523 break;
524 }
525 }
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000526
Harald Welte7e310b12009-03-30 20:56:32 +0000527 DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
528 "PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
Harald Welte76042182009-08-08 16:03:15 +0200529 "UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
530 gsms->protocol_id, gsms->data_coding_scheme,
531 gsms->dest_addr, gsms->user_data_len,
532 sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
533 hexdump(gsms->user_data, gsms->user_data_len));
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000534
Harald Welte76042182009-08-08 16:03:15 +0200535 gsms->sender = subscr_get(msg->lchan->subscr);
Holger Freyther9b177762009-02-16 19:07:18 +0000536
Harald Welte76042182009-08-08 16:03:15 +0200537 gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
Harald Welteb9c758b2009-07-05 14:02:46 +0200538
Harald Welte76042182009-08-08 16:03:15 +0200539 dispatch_signal(SS_SMS, 0, gsms);
Harald Welteb9c758b2009-07-05 14:02:46 +0200540
Harald Welte7e310b12009-03-30 20:56:32 +0000541 /* determine gsms->receiver based on dialled number */
Harald Welte76042182009-08-08 16:03:15 +0200542 gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
Harald Welte7e310b12009-03-30 20:56:32 +0000543 if (!gsms->receiver) {
544 rc = 1; /* cause 1: unknown subscriber */
545 goto out;
546 }
547
Harald Welte76042182009-08-08 16:03:15 +0200548 switch (sms_mti) {
Harald Welte7e310b12009-03-30 20:56:32 +0000549 case GSM340_SMS_SUBMIT_MS2SC:
550 /* MS is submitting a SMS */
Harald Welte76042182009-08-08 16:03:15 +0200551 rc = gsm340_rx_sms_submit(msg, gsms);
Harald Welte7e310b12009-03-30 20:56:32 +0000552 break;
553 case GSM340_SMS_COMMAND_MS2SC:
554 case GSM340_SMS_DELIVER_REP_MS2SC:
Harald Welte76042182009-08-08 16:03:15 +0200555 DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti);
Harald Welteb9c758b2009-07-05 14:02:46 +0200556 rc = GSM411_RP_CAUSE_IE_NOTEXIST;
Harald Welte7e310b12009-03-30 20:56:32 +0000557 break;
558 default:
Harald Welte76042182009-08-08 16:03:15 +0200559 DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
Harald Welteb9c758b2009-07-05 14:02:46 +0200560 rc = GSM411_RP_CAUSE_IE_NOTEXIST;
Harald Welte7e310b12009-03-30 20:56:32 +0000561 break;
562 }
563
Harald Welteb9c758b2009-07-05 14:02:46 +0200564 if (!rc && !gsms->receiver)
565 rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
566
Harald Welte7e310b12009-03-30 20:56:32 +0000567out:
Harald Welte76042182009-08-08 16:03:15 +0200568 sms_free(gsms);
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000569
Harald Welte7e310b12009-03-30 20:56:32 +0000570 return rc;
Daniel Willmanne0fbec82008-12-29 00:44:41 +0000571}
572
Harald Weltef3efc592009-07-27 20:11:35 +0200573static int gsm411_send_rp_ack(struct gsm_trans *trans, u_int8_t msg_ref)
Daniel Willmann471712b2008-12-29 01:54:02 +0000574{
575 struct msgb *msg = gsm411_msgb_alloc();
Daniel Willmann471712b2008-12-29 01:54:02 +0000576
Daniel Willmann471712b2008-12-29 01:54:02 +0000577 DEBUGP(DSMS, "TX: SMS RP ACK\n");
578
Harald Weltef3efc592009-07-27 20:11:35 +0200579 return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ACK_MT, msg_ref);
Daniel Willmann471712b2008-12-29 01:54:02 +0000580}
581
Harald Weltef3efc592009-07-27 20:11:35 +0200582static int gsm411_send_rp_error(struct gsm_trans *trans,
583 u_int8_t msg_ref, u_int8_t cause)
Daniel Willmann471712b2008-12-29 01:54:02 +0000584{
585 struct msgb *msg = gsm411_msgb_alloc();
Daniel Willmann471712b2008-12-29 01:54:02 +0000586
Harald Welte7e310b12009-03-30 20:56:32 +0000587 msgb_tv_put(msg, 1, cause);
Daniel Willmann471712b2008-12-29 01:54:02 +0000588
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200589 DEBUGP(DSMS, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
590 get_value_string(rp_cause_strs, cause));
Daniel Willmann471712b2008-12-29 01:54:02 +0000591
Harald Weltef3efc592009-07-27 20:11:35 +0200592 return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref);
Daniel Willmann471712b2008-12-29 01:54:02 +0000593}
594
Harald Welte7e310b12009-03-30 20:56:32 +0000595/* Receive a 04.11 TPDU inside RP-DATA / user data */
Harald Weltef3efc592009-07-27 20:11:35 +0200596static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
597 struct gsm411_rp_hdr *rph,
Harald Welte7e310b12009-03-30 20:56:32 +0000598 u_int8_t src_len, u_int8_t *src,
599 u_int8_t dst_len, u_int8_t *dst,
600 u_int8_t tpdu_len, u_int8_t *tpdu)
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000601{
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000602 int rc = 0;
603
Harald Welte7e310b12009-03-30 20:56:32 +0000604 if (src_len && src)
605 DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
606
607 if (!dst_len || !dst || !tpdu_len || !tpdu) {
608 DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
Harald Weltef3efc592009-07-27 20:11:35 +0200609 gsm411_send_rp_error(trans, rph->msg_ref,
Harald Welteb9c758b2009-07-05 14:02:46 +0200610 GSM411_RP_CAUSE_INV_MAND_INF);
Harald Welte7e310b12009-03-30 20:56:32 +0000611 return -EIO;
612 }
613 msg->smsh = tpdu;
614
615 DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
Harald Welte7e310b12009-03-30 20:56:32 +0000616
617 rc = gsm340_rx_tpdu(msg);
618 if (rc == 0)
Harald Weltef3efc592009-07-27 20:11:35 +0200619 return gsm411_send_rp_ack(trans, rph->msg_ref);
Harald Welte7e310b12009-03-30 20:56:32 +0000620 else if (rc > 0)
Harald Weltef3efc592009-07-27 20:11:35 +0200621 return gsm411_send_rp_error(trans, rph->msg_ref, rc);
Harald Welte7e310b12009-03-30 20:56:32 +0000622 else
623 return rc;
624}
625
626/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */
Harald Weltef3efc592009-07-27 20:11:35 +0200627static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans,
628 struct gsm411_rp_hdr *rph)
Harald Welte7e310b12009-03-30 20:56:32 +0000629{
630 u_int8_t src_len, dst_len, rpud_len;
631 u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL;
632
633 /* in the MO case, this should always be zero length */
634 src_len = rph->data[0];
635 if (src_len)
636 src = &rph->data[1];
637
638 dst_len = rph->data[1+src_len];
639 if (dst_len)
640 dst = &rph->data[1+src_len+1];
641
642 rpud_len = rph->data[1+src_len+1+dst_len];
643 if (rpud_len)
644 rp_ud = &rph->data[1+src_len+1+dst_len+1];
645
Harald Welteb9c758b2009-07-05 14:02:46 +0200646 DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n",
647 src_len, dst_len, rpud_len);
Harald Weltef3efc592009-07-27 20:11:35 +0200648 return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst,
Harald Welte7e310b12009-03-30 20:56:32 +0000649 rpud_len, rp_ud);
650}
651
Harald Weltecb8f4432009-08-09 14:59:02 +0200652/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */
Harald Weltef3efc592009-07-27 20:11:35 +0200653static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
654 struct gsm411_rp_hdr *rph)
Harald Welteb9c758b2009-07-05 14:02:46 +0200655{
Harald Welte76042182009-08-08 16:03:15 +0200656 struct gsm_sms *sms = trans->sms.sms;
657
Harald Welteb9c758b2009-07-05 14:02:46 +0200658 /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it
659 * successfully received a SMS. We can now safely mark it as
660 * transmitted */
661
Harald Weltecb8f4432009-08-09 14:59:02 +0200662 if (!trans->sms.is_mt) {
663 DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n");
664 return gsm411_send_rp_error(trans, rph->msg_ref,
665 GSM411_RP_CAUSE_MSG_INCOMP_STATE);
666 }
Harald Welte3e0f6172009-07-09 23:52:59 +0200667
Harald Welte76042182009-08-08 16:03:15 +0200668 if (!sms) {
Harald Weltecb8f4432009-08-09 14:59:02 +0200669 DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n");
670 return gsm411_send_rp_error(trans, rph->msg_ref,
671 GSM411_RP_CAUSE_PROTOCOL_ERR);
Harald Welte76042182009-08-08 16:03:15 +0200672 }
673
674 /* mark this SMS as sent in database */
675 db_sms_mark_sent(sms);
676
677 dispatch_signal(SS_SMS, S_SMS_DELIVERED, sms);
678
679 sms_free(sms);
680 trans->sms.sms = NULL;
681
Sylvain Munaut6b0b1032009-09-27 11:16:22 +0200682 /* free the transaction here */
683 trans_free(trans);
Harald Weltecf6a3812009-08-09 19:07:41 +0200684
685 /* check for more messages for this subscriber */
686 sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
687 if (sms)
688 gsm411_send_sms_lchan(msg->lchan, sms);
689 else
Harald Welte (local)daef6062009-08-14 11:41:12 +0200690 rsl_release_request(msg->lchan, trans->sms.link_id);
Harald Welte76042182009-08-08 16:03:15 +0200691
692 return 0;
Harald Welteb9c758b2009-07-05 14:02:46 +0200693}
694
Harald Weltef3efc592009-07-27 20:11:35 +0200695static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
696 struct gsm411_rp_hdr *rph)
Harald Welteb9c758b2009-07-05 14:02:46 +0200697{
Harald Welte76042182009-08-08 16:03:15 +0200698 struct gsm_sms *sms = trans->sms.sms;
Harald Weltef3efc592009-07-27 20:11:35 +0200699 u_int8_t cause_len = rph->data[0];
700 u_int8_t cause = rph->data[1];
701
Harald Welteb9c758b2009-07-05 14:02:46 +0200702 /* Error in response to MT RP_DATA, i.e. the MS did not
703 * successfully receive the SMS. We need to investigate
704 * the cause and take action depending on it */
705
Holger Hans Peter Freytherdc1e1052009-10-22 15:40:33 +0200706 DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause,
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200707 get_value_string(rp_cause_strs, cause));
Harald Weltef3efc592009-07-27 20:11:35 +0200708
Harald Weltecb8f4432009-08-09 14:59:02 +0200709 if (!trans->sms.is_mt) {
710 DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
Harald Welte (local)ee4410a2009-08-17 09:39:55 +0200711#if 0
Harald Weltecb8f4432009-08-09 14:59:02 +0200712 return gsm411_send_rp_error(trans, rph->msg_ref,
713 GSM411_RP_CAUSE_MSG_INCOMP_STATE);
Harald Welte (local)ee4410a2009-08-17 09:39:55 +0200714#endif
Harald Weltecb8f4432009-08-09 14:59:02 +0200715 }
Harald Welte3e0f6172009-07-09 23:52:59 +0200716
Harald Welte76042182009-08-08 16:03:15 +0200717 if (!sms) {
Harald Weltecb8f4432009-08-09 14:59:02 +0200718 DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
Harald Welte (local)ee4410a2009-08-17 09:39:55 +0200719 return -EINVAL;
720#if 0
Harald Weltecb8f4432009-08-09 14:59:02 +0200721 return gsm411_send_rp_error(trans, rph->msg_ref,
722 GSM411_RP_CAUSE_PROTOCOL_ERR);
Harald Welte (local)ee4410a2009-08-17 09:39:55 +0200723#endif
Harald Weltecb8f4432009-08-09 14:59:02 +0200724 }
725
726 if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) {
727 /* MS has not enough memory to store the message. We need
728 * to store this in our database and wati for a SMMA message */
729 /* FIXME */
730 dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
Harald Welte76042182009-08-08 16:03:15 +0200731 }
732
733 sms_free(sms);
734 trans->sms.sms = NULL;
735
Harald Welte (local)86b17172009-08-14 14:52:17 +0200736 //trans_free(trans);
Harald Welte76042182009-08-08 16:03:15 +0200737
Harald Weltef3efc592009-07-27 20:11:35 +0200738 return 0;
Harald Welteb9c758b2009-07-05 14:02:46 +0200739}
740
Harald Weltef3efc592009-07-27 20:11:35 +0200741static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans,
742 struct gsm411_rp_hdr *rph)
Harald Welteb9c758b2009-07-05 14:02:46 +0200743{
Harald Weltecf6a3812009-08-09 19:07:41 +0200744 struct gsm_sms *sms;
Harald Weltef3efc592009-07-27 20:11:35 +0200745 int rc;
746
Harald Weltecf6a3812009-08-09 19:07:41 +0200747 rc = gsm411_send_rp_ack(trans, rph->msg_ref);
748 trans->sms.rp_state = GSM411_RPS_IDLE;
749
Harald Welteb9c758b2009-07-05 14:02:46 +0200750 /* MS tells us that it has memory for more SMS, we need
751 * to check if we have any pending messages for it and then
752 * transfer those */
Harald Welte76042182009-08-08 16:03:15 +0200753 dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr);
Harald Weltef3efc592009-07-27 20:11:35 +0200754
Harald Weltecf6a3812009-08-09 19:07:41 +0200755 /* check for more messages for this subscriber */
756 sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
757 if (sms)
758 gsm411_send_sms_lchan(msg->lchan, sms);
759 else
Harald Welte (local)daef6062009-08-14 11:41:12 +0200760 rsl_release_request(msg->lchan, trans->sms.link_id);
Harald Weltef3efc592009-07-27 20:11:35 +0200761
762 return rc;
Harald Welteb9c758b2009-07-05 14:02:46 +0200763}
764
Harald Weltef3efc592009-07-27 20:11:35 +0200765static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh,
766 struct gsm_trans *trans)
Harald Welte7e310b12009-03-30 20:56:32 +0000767{
Daniel Willmann471712b2008-12-29 01:54:02 +0000768 struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000769 u_int8_t msg_type = rp_data->msg_type & 0x07;
Harald Welte7e310b12009-03-30 20:56:32 +0000770 int rc = 0;
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000771
772 switch (msg_type) {
773 case GSM411_MT_RP_DATA_MO:
Harald Weltef3efc592009-07-27 20:11:35 +0200774 DEBUGP(DSMS, "RX SMS RP-DATA (MO)\n");
775 /* start TR2N and enter 'wait to send RP-ACK state' */
776 trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK;
777 rc = gsm411_rx_rp_data(msg, trans, rp_data);
Harald Welte7e310b12009-03-30 20:56:32 +0000778 break;
779 case GSM411_MT_RP_ACK_MO:
Harald Weltef3efc592009-07-27 20:11:35 +0200780 DEBUGP(DSMS,"RX SMS RP-ACK (MO)\n");
781 rc = gsm411_rx_rp_ack(msg, trans, rp_data);
Harald Welteb9c758b2009-07-05 14:02:46 +0200782 break;
Harald Welte7e310b12009-03-30 20:56:32 +0000783 case GSM411_MT_RP_SMMA_MO:
Harald Weltef3efc592009-07-27 20:11:35 +0200784 DEBUGP(DSMS, "RX SMS RP-SMMA\n");
785 /* start TR2N and enter 'wait to send RP-ACK state' */
786 trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK;
787 rc = gsm411_rx_rp_smma(msg, trans, rp_data);
788 break;
789 case GSM411_MT_RP_ERROR_MO:
790 rc = gsm411_rx_rp_error(msg, trans, rp_data);
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000791 break;
792 default:
Harald Welte7e310b12009-03-30 20:56:32 +0000793 DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
Harald Weltef3efc592009-07-27 20:11:35 +0200794 rc = gsm411_send_rp_error(trans, rp_data->msg_ref,
795 GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000796 break;
797 }
798
799 return rc;
800}
801
Harald Weltef3efc592009-07-27 20:11:35 +0200802/* send CP-ACK to given transaction */
803static int gsm411_tx_cp_ack(struct gsm_trans *trans)
804{
805 struct msgb *msg = gsm411_msgb_alloc();
Harald Weltecf6a3812009-08-09 19:07:41 +0200806 int rc;
Harald Weltef3efc592009-07-27 20:11:35 +0200807
Harald Weltecf6a3812009-08-09 19:07:41 +0200808 rc = gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK);
809
810 if (trans->sms.is_mt) {
811 /* If this is a MT SMS DELIVER, we can clear transaction here */
812 trans->sms.cp_state = GSM411_CPS_IDLE;
Harald Welte (local)86b17172009-08-14 14:52:17 +0200813 //trans_free(trans);
Harald Weltecf6a3812009-08-09 19:07:41 +0200814 }
Holger Hans Peter Freyther09e364b2009-08-10 07:59:27 +0200815
816 return rc;
Harald Weltef3efc592009-07-27 20:11:35 +0200817}
818
819static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
820{
821 struct msgb *msg = gsm411_msgb_alloc();
822 u_int8_t *causep;
823
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200824 DEBUGP(DSMS, "TX CP-ERROR, cause %d (%s)\n", cause,
825 get_value_string(cp_cause_strs, cause));
826
Harald Welte76042182009-08-08 16:03:15 +0200827 causep = msgb_put(msg, 1);
Harald Weltef3efc592009-07-27 20:11:35 +0200828 *causep = cause;
829
830 return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR);
831}
832
833/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */
Harald Welte (local)daef6062009-08-14 11:41:12 +0200834int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000835{
836 struct gsm48_hdr *gh = msgb_l3(msg);
837 u_int8_t msg_type = gh->msg_type;
Harald Weltef3efc592009-07-27 20:11:35 +0200838 u_int8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */
839 struct gsm_lchan *lchan = msg->lchan;
840 struct gsm_trans *trans;
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000841 int rc = 0;
842
Harald Weltef3efc592009-07-27 20:11:35 +0200843 if (!lchan->subscr)
844 return -EIO;
845 /* FIXME: send some error message */
846
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200847 DEBUGP(DSMS, "trans_id=%x ", gh->proto_discr >> 4);
Harald Weltef3efc592009-07-27 20:11:35 +0200848 trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
849 transaction_id);
850 if (!trans) {
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200851 DEBUGPC(DSMS, "(unknown) ");
Harald Weltef3efc592009-07-27 20:11:35 +0200852 trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
853 transaction_id, new_callref++);
854 if (!trans) {
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200855 DEBUGPC(DSMS, "No memory for trans\n");
Harald Weltef3efc592009-07-27 20:11:35 +0200856 /* FIXME: send some error message */
857 return -ENOMEM;
858 }
859 trans->sms.cp_state = GSM411_CPS_IDLE;
860 trans->sms.rp_state = GSM411_RPS_IDLE;
861 trans->sms.is_mt = 0;
Harald Welte (local)daef6062009-08-14 11:41:12 +0200862 trans->sms.link_id = link_id;
Harald Weltef3efc592009-07-27 20:11:35 +0200863
864 trans->lchan = lchan;
865 use_lchan(lchan);
866 }
867
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000868 switch(msg_type) {
869 case GSM411_MT_CP_DATA:
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200870 DEBUGPC(DSMS, "RX SMS CP-DATA\n");
Harald Weltecb8f4432009-08-09 14:59:02 +0200871 /* 5.2.3.1.3: MO state exists when SMC has received
872 * CP-DATA, including sending of the assoc. CP-ACK */
873 /* 5.2.3.2.4: MT state exists when SMC has received
874 * CP-DATA, including sending of the assoc. CP-ACK */
875 trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
Harald Weltef3efc592009-07-27 20:11:35 +0200876
Harald Welte (local)daef6062009-08-14 11:41:12 +0200877 /* SMC instance acknowledges the CP-DATA frame */
878 gsm411_tx_cp_ack(trans);
879
Harald Weltef3efc592009-07-27 20:11:35 +0200880 rc = gsm411_rx_cp_data(msg, gh, trans);
Harald Welte (local)daef6062009-08-14 11:41:12 +0200881#if 0
Harald Weltef3efc592009-07-27 20:11:35 +0200882 /* Send CP-ACK or CP-ERORR in response */
883 if (rc < 0) {
884 rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_NET_FAIL);
885 } else
886 rc = gsm411_tx_cp_ack(trans);
Harald Welte (local)daef6062009-08-14 11:41:12 +0200887#endif
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000888 break;
889 case GSM411_MT_CP_ACK:
Harald Weltef3efc592009-07-27 20:11:35 +0200890 /* previous CP-DATA in this transaction was confirmed */
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200891 DEBUGPC(DSMS, "RX SMS CP-ACK\n");
Harald Weltecb8f4432009-08-09 14:59:02 +0200892 /* 5.2.3.1.3: MO state exists when SMC has received CP-ACK */
893 /* 5.2.3.2.4: MT state exists when SMC has received CP-ACK */
894 trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
Harald Welte41985612009-08-10 00:24:32 +0200895 /* Stop TC1* after CP-ACK has been received */
896 bsc_del_timer(&trans->sms.cp_timer);
Harald Weltecb8f4432009-08-09 14:59:02 +0200897
Harald Weltef3efc592009-07-27 20:11:35 +0200898 if (!trans->sms.is_mt) {
Harald Weltef3efc592009-07-27 20:11:35 +0200899 /* FIXME: we have sont one CP-DATA, which was now
900 * acknowledged. Check if we want to transfer more,
901 * i.e. multi-part message */
902 trans->sms.cp_state = GSM411_CPS_IDLE;
903 trans_free(trans);
904 }
Daniel Willmannbb16e8e2008-12-29 03:53:50 +0000905 break;
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000906 case GSM411_MT_CP_ERROR:
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200907 DEBUGPC(DSMS, "RX SMS CP-ERROR, cause %d (%s)\n", gh->data[0],
908 get_value_string(cp_cause_strs, gh->data[0]));
Harald Welte41985612009-08-10 00:24:32 +0200909 bsc_del_timer(&trans->sms.cp_timer);
Harald Weltef3efc592009-07-27 20:11:35 +0200910 trans->sms.cp_state = GSM411_CPS_IDLE;
911 trans_free(trans);
Daniel Willmannbb16e8e2008-12-29 03:53:50 +0000912 break;
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000913 default:
Harald Welte (local)c89a5112009-08-14 10:42:43 +0200914 DEBUGPC(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type);
Harald Weltef3efc592009-07-27 20:11:35 +0200915 rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST);
Harald Weltecb8f4432009-08-09 14:59:02 +0200916 trans->sms.cp_state = GSM411_CPS_IDLE;
Harald Weltef3efc592009-07-27 20:11:35 +0200917 trans_free(trans);
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000918 break;
919 }
920
Daniel Willmann8b3390e2008-12-28 00:31:09 +0000921 return rc;
922}
923
Harald Welte8c2e36e2008-12-30 15:00:14 +0000924#if 0
Daniel Willmann3b3f0012008-12-30 13:56:46 +0000925/* Test TPDU - ALL YOUR */
926static u_int8_t tpdu_test[] = {
927 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24,
928 0x32, 0x40, 0x1F, 0x41, 0x26, 0x13, 0x94, 0x7D, 0x56, 0xA5, 0x20, 0x28,
929 0xF2, 0xE9, 0x2C, 0x82, 0x82, 0xD2, 0x22, 0x48, 0x58, 0x64, 0x3E, 0x9D,
930 0x47, 0x10, 0xF5, 0x09, 0xAA, 0x4E, 0x01
Daniel Willmanne2a728d2008-12-30 14:03:09 +0000931};
Harald Welte8c2e36e2008-12-30 15:00:14 +0000932#endif
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000933
Harald Weltecf6a3812009-08-09 19:07:41 +0200934/* Take a SMS in gsm_sms structure and send it through an already
935 * existing lchan. We also assume that the caller ensured this lchan already
936 * has a SAPI3 RLL connection! */
Harald Welte76042182009-08-08 16:03:15 +0200937int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000938{
939 struct msgb *msg = gsm411_msgb_alloc();
Harald Weltef3efc592009-07-27 20:11:35 +0200940 struct gsm_trans *trans;
Harald Welte76042182009-08-08 16:03:15 +0200941 u_int8_t *data, *rp_ud_len;
Harald Welte87f5d632009-07-04 17:39:00 +0200942 u_int8_t msg_ref = 42;
Harald Weltecf6a3812009-08-09 19:07:41 +0200943 u_int8_t transaction_id;
Harald Welte76042182009-08-08 16:03:15 +0200944 int rc;
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000945
Harald Weltecf6a3812009-08-09 19:07:41 +0200946 transaction_id = 4; /* FIXME: we always use 4 for now */
947
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000948 msg->lchan = lchan;
949
Harald Welte76042182009-08-08 16:03:15 +0200950 DEBUGP(DSMS, "send_sms_lchan()\n");
Harald Weltef3efc592009-07-27 20:11:35 +0200951
Harald Welte76042182009-08-08 16:03:15 +0200952 /* FIXME: allocate transaction with message reference */
953 trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
954 transaction_id, new_callref++);
955 if (!trans) {
956 DEBUGP(DSMS, "No memory for trans\n");
957 /* FIXME: send some error message */
958 return -ENOMEM;
959 }
960 trans->sms.cp_state = GSM411_CPS_IDLE;
961 trans->sms.rp_state = GSM411_RPS_IDLE;
962 trans->sms.is_mt = 1;
963 trans->sms.sms = sms;
Harald Welte (local)daef6062009-08-14 11:41:12 +0200964 trans->sms.link_id = UM_SAPI_SMS; /* FIXME: main or SACCH ? */
Harald Welte76042182009-08-08 16:03:15 +0200965
966 trans->lchan = lchan;
967 use_lchan(lchan);
968
969 /* Hardcode SMSC Originating Address for now */
Daniel Willmanna3e29842008-12-29 16:03:54 +0000970 data = (u_int8_t *)msgb_put(msg, 8);
Harald Welte87f5d632009-07-04 17:39:00 +0200971 data[0] = 0x07; /* originator length == 7 */
Harald Welteb9c758b2009-07-05 14:02:46 +0200972 data[1] = 0x91; /* type of number: international, ISDN */
973 data[2] = 0x44; /* 447785016005 */
Daniel Willmanna3e29842008-12-29 16:03:54 +0000974 data[3] = 0x77;
975 data[4] = 0x58;
976 data[5] = 0x10;
977 data[6] = 0x06;
978 data[7] = 0x50;
Harald Welte87f5d632009-07-04 17:39:00 +0200979
980 /* Hardcoded Destination Address */
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000981 data = (u_int8_t *)msgb_put(msg, 1);
Harald Welte87f5d632009-07-04 17:39:00 +0200982 data[0] = 0; /* destination length == 0 */
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000983
Harald Welte76042182009-08-08 16:03:15 +0200984 /* obtain a pointer for the rp_ud_len, so we can fill it later */
985 rp_ud_len = (u_int8_t *)msgb_put(msg, 1);
Daniel Willmann4a1e8792008-12-29 06:23:56 +0000986
Harald Welte76042182009-08-08 16:03:15 +0200987#if 1
988 /* generate the 03.40 TPDU */
989 rc = gsm340_gen_tpdu(msg, sms);
990 if (rc < 0) {
991 msgb_free(msg);
992 return rc;
993 }
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000994
Harald Welte76042182009-08-08 16:03:15 +0200995 *rp_ud_len = rc;
996#else
997 data = msgb_put(msg, sizeof(tpdu_test));
Daniel Willmann6fe997e2008-12-29 04:20:41 +0000998 memcpy(data, tpdu_test, sizeof(tpdu_test));
Harald Welte76042182009-08-08 16:03:15 +0200999 *rp_ud_len = sizeof(tpdu_test);
1000#endif
Daniel Willmann6fe997e2008-12-29 04:20:41 +00001001
Harald Welte76042182009-08-08 16:03:15 +02001002 DEBUGP(DSMS, "TX: SMS DELIVER\n");
Daniel Willmann6fe997e2008-12-29 04:20:41 +00001003
Harald Weltef3efc592009-07-27 20:11:35 +02001004 return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
1005 /* FIXME: enter 'wait for RP-ACK' state, start TR1N */
Daniel Willmann6fe997e2008-12-29 04:20:41 +00001006}
Harald Weltef3efc592009-07-27 20:11:35 +02001007
Harald Weltecf6a3812009-08-09 19:07:41 +02001008/* RLL SAPI3 establish callback. Now we have a RLL connection and
1009 * can deliver the actual message */
Harald Weltecb8f4432009-08-09 14:59:02 +02001010static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id,
1011 void *_sms, enum bsc_rllr_ind type)
1012{
1013 struct gsm_sms *sms = _sms;
1014
1015 DEBUGP(DSMS, "rll_ind_cb(lchan=%p, link_id=%u, sms=%p, type=%u\n",
1016 lchan, link_id, sms, type);
1017
1018 switch (type) {
1019 case BSC_RLLR_IND_EST_CONF:
1020 gsm411_send_sms_lchan(lchan, sms);
1021 break;
1022 case BSC_RLLR_IND_REL_IND:
1023 case BSC_RLLR_IND_ERR_IND:
1024 case BSC_RLLR_IND_TIMEOUT:
1025 sms_free(sms);
1026 break;
1027 }
1028}
1029
Harald Weltecf6a3812009-08-09 19:07:41 +02001030/* paging callback. Here we get called if paging a subscriber has
1031 * succeeded or failed. */
Harald Welte76042182009-08-08 16:03:15 +02001032static int paging_cb_send_sms(unsigned int hooknum, unsigned int event,
1033 struct msgb *msg, void *_lchan, void *_sms)
Harald Weltef3efc592009-07-27 20:11:35 +02001034{
Harald Welte76042182009-08-08 16:03:15 +02001035 struct gsm_lchan *lchan = _lchan;
1036 struct gsm_sms *sms = _sms;
1037 int rc;
Harald Weltef3efc592009-07-27 20:11:35 +02001038
Harald Welte76042182009-08-08 16:03:15 +02001039 DEBUGP(DSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p,"
1040 "lchan=%p, sms=%p)\n", hooknum, event, msg, lchan, sms);
1041
1042 if (hooknum != GSM_HOOK_RR_PAGING)
1043 return -EINVAL;
1044
1045 switch (event) {
1046 case GSM_PAGING_SUCCEEDED:
1047 /* Paging aborted without lchan ?!? */
1048 if (!lchan) {
1049 sms_free(sms);
1050 rc = -EIO;
1051 break;
1052 }
Harald Weltecf6a3812009-08-09 19:07:41 +02001053 /* Establish a SAPI3 RLL connection for SMS */
Harald Weltecb8f4432009-08-09 14:59:02 +02001054 rc = rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms);
Harald Welte76042182009-08-08 16:03:15 +02001055 break;
1056 case GSM_PAGING_EXPIRED:
1057 sms_free(sms);
1058 rc = -ETIMEDOUT;
1059 break;
1060 default:
1061 rc = -EINVAL;
1062 break;
1063 }
1064
1065 return rc;
1066}
1067
Harald Weltecf6a3812009-08-09 19:07:41 +02001068/* high-level function to send a SMS to a given subscriber. The function
1069 * will take care of paging the subscriber, establishing the RLL SAPI3
1070 * connection, etc. */
Harald Welte76042182009-08-08 16:03:15 +02001071int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
1072 struct gsm_sms *sms)
1073{
Harald Weltecf6a3812009-08-09 19:07:41 +02001074 struct gsm_lchan *lchan;
Harald Weltee903edf2009-08-15 03:16:17 +02001075 int rc;
Harald Weltecf6a3812009-08-09 19:07:41 +02001076
Harald Welte76042182009-08-08 16:03:15 +02001077 /* check if we already have an open lchan to the subscriber.
1078 * if yes, send the SMS this way */
Harald Weltecf6a3812009-08-09 19:07:41 +02001079 lchan = lchan_for_subscr(subscr);
1080 if (lchan)
1081 return rll_establish(lchan, UM_SAPI_SMS,
1082 rll_ind_cb, sms);
Harald Welte76042182009-08-08 16:03:15 +02001083
1084 /* if not, we have to start paging */
Harald Weltee903edf2009-08-15 03:16:17 +02001085 rc = paging_request(subscr->net, subscr, RSL_CHANNEED_SDCCH,
1086 paging_cb_send_sms, sms);
Harald Welte (local)0abaf332009-08-15 11:25:45 +02001087 if (rc <= 0)
Harald Weltee903edf2009-08-15 03:16:17 +02001088 sms_free(sms);
Harald Welte76042182009-08-08 16:03:15 +02001089
1090 return 0;
1091}
Harald Welte3366a942009-07-28 00:44:49 +02001092
Harald Weltecf6a3812009-08-09 19:07:41 +02001093static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
1094 void *handler_data, void *signal_data)
1095{
1096 struct gsm_subscriber *subscr;
1097 struct gsm_lchan *lchan;
1098 struct gsm_sms *sms;
1099
1100 switch (signal) {
1101 case S_SUBSCR_ATTACHED:
1102 /* A subscriber has attached. Check if there are
1103 * any pending SMS for him to be delivered */
1104 subscr = signal_data;
1105 lchan = lchan_for_subscr(subscr);
1106 if (!lchan)
1107 break;
1108 sms = db_sms_get_unsent_for_subscr(subscr);
1109 if (!sms)
1110 break;
1111 /* Establish a SAPI3 RLL connection for SMS */
1112 rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms);
1113 break;
1114 default:
1115 break;
1116 }
1117 return 0;
1118}
1119
Harald Welte (local)86b17172009-08-14 14:52:17 +02001120void _gsm411_sms_trans_free(struct gsm_trans *trans)
1121{
1122 bsc_del_timer(&trans->sms.cp_timer);
1123}
1124
Harald Welte7bfc2672009-07-28 00:41:45 +02001125static __attribute__((constructor)) void on_dso_load_sms(void)
1126{
Harald Weltecf6a3812009-08-09 19:07:41 +02001127 register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL);
Harald Welte7bfc2672009-07-28 00:41:45 +02001128}