blob: 20555101a37ef6bee1d9235370209406b6fda62f [file] [log] [blame]
Harald Welte59b04682009-06-10 05:40:52 +08001/* 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>
6 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
7 *
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 Welteb78996d2009-07-27 20:11:35 +020031#include <time.h>
Harald Welte59b04682009-06-10 05:40:52 +080032#include <netinet/in.h>
33
34#include <openbsc/msgb.h>
35#include <openbsc/tlv.h>
36#include <openbsc/debug.h>
37#include <openbsc/gsm_data.h>
38#include <openbsc/gsm_subscriber.h>
39#include <openbsc/gsm_04_11.h>
40#include <openbsc/gsm_04_08.h>
41#include <openbsc/gsm_utils.h>
42#include <openbsc/abis_rsl.h>
43#include <openbsc/signal.h>
44#include <openbsc/db.h>
Harald Weltea8379772009-06-20 22:36:41 +020045#include <openbsc/talloc.h>
Harald Welteb78996d2009-07-27 20:11:35 +020046#include <openbsc/transaction.h>
Harald Welte68b7df22009-08-08 16:03:15 +020047#include <openbsc/paging.h>
Harald Welte59b04682009-06-10 05:40:52 +080048
49#define GSM411_ALLOC_SIZE 1024
50#define GSM411_ALLOC_HEADROOM 128
51
Harald Weltea8379772009-06-20 22:36:41 +020052static void *tall_gsms_ctx;
53
Harald Welteb78996d2009-07-27 20:11:35 +020054static u_int32_t new_callref = 0x40000001;
55
Harald Welte68b7df22009-08-08 16:03:15 +020056struct gsm_sms *sms_alloc(void)
57{
58 return talloc_zero(tall_gsms_ctx, struct gsm_sms);
59}
60
61void sms_free(struct gsm_sms *sms)
62{
63 /* drop references to subscriber structure */
64 if (sms->sender)
65 subscr_put(sms->sender);
66 if (sms->receiver)
67 subscr_put(sms->receiver);
68
69 talloc_free(sms);
70}
71
Harald Welte59b04682009-06-10 05:40:52 +080072struct msgb *gsm411_msgb_alloc(void)
73{
Harald Welte9cfc9352009-06-26 19:39:35 +020074 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
75 "GSM 04.11");
Harald Welte59b04682009-06-10 05:40:52 +080076}
77
Harald Welte7e2f57d2009-07-04 17:39:00 +020078static int gsm411_sendmsg(struct msgb *msg)
Harald Welte59b04682009-06-10 05:40:52 +080079{
80 if (msg->lchan)
81 msg->trx = msg->lchan->ts->trx;
82
83 msg->l3h = msg->data;
84
Harald Welte68b7df22009-08-08 16:03:15 +020085 DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
86
Harald Welte59b04682009-06-10 05:40:52 +080087 return rsl_data_request(msg, 0);
88}
89
Harald Welte7e2f57d2009-07-04 17:39:00 +020090/* Prefix msg with a 04.08/04.11 CP header */
Harald Welteb78996d2009-07-27 20:11:35 +020091static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
92 u_int8_t msg_type)
Harald Welte7e2f57d2009-07-04 17:39:00 +020093{
94 struct gsm48_hdr *gh;
95
96 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
97 /* Outgoing needs the highest bit set */
Harald Welteb78996d2009-07-27 20:11:35 +020098 gh->proto_discr = trans->protocol | (trans->transaction_id<<4);
Harald Welte7e2f57d2009-07-04 17:39:00 +020099 gh->msg_type = msg_type;
100
Harald Welteb78996d2009-07-27 20:11:35 +0200101 /* assign the outgoing lchan */
102 msg->lchan = trans->lchan;
103
104 /* mobile originating */
105 switch (gh->msg_type) {
106 case GSM411_MT_CP_DATA:
107 /* 5.2.3.1.2: enter MO-wait for CP-ack */
108 trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK;
109 break;
110 }
111
Harald Welte7e2f57d2009-07-04 17:39:00 +0200112 return gsm411_sendmsg(msg);
113}
114
115/* Prefix msg with a RP-DATA header and send as CP-DATA */
Harald Welteb78996d2009-07-27 20:11:35 +0200116static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
117 u_int8_t rp_msg_type, u_int8_t rp_msg_ref)
Harald Welte7e2f57d2009-07-04 17:39:00 +0200118{
119 struct gsm411_rp_hdr *rp;
120
121 /* GSM 04.11 RP-DATA header */
122 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
123 rp->len = msg->len;
124 rp->msg_type = rp_msg_type;
125 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
126
Harald Welteb78996d2009-07-27 20:11:35 +0200127 return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
Harald Welte7e2f57d2009-07-04 17:39:00 +0200128}
129
Harald Welte68b7df22009-08-08 16:03:15 +0200130static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
Harald Welte59b04682009-06-10 05:40:52 +0800131{
132 u_int8_t vp;
133 unsigned long minutes;
134
Harald Welte68b7df22009-08-08 16:03:15 +0200135 switch (sms_vpf) {
Harald Welte59b04682009-06-10 05:40:52 +0800136 case GSM340_TP_VPF_RELATIVE:
137 /* Chapter 9.2.3.12.1 */
Harald Welte68b7df22009-08-08 16:03:15 +0200138 vp = *(sms_vp);
Harald Welte59b04682009-06-10 05:40:52 +0800139 if (vp <= 143)
140 minutes = vp + 1 * 5;
141 else if (vp <= 167)
142 minutes = 12*60 + (vp-143) * 30;
143 else if (vp <= 196)
144 minutes = vp-166 * 60 * 24;
145 else
146 minutes = vp-192 * 60 * 24 * 7;
147 break;
148 case GSM340_TP_VPF_ABSOLUTE:
149 /* Chapter 9.2.3.12.2 */
150 /* FIXME: like service center time stamp */
151 DEBUGP(DSMS, "VPI absolute not implemented yet\n");
152 break;
153 case GSM340_TP_VPF_ENHANCED:
154 /* Chapter 9.2.3.12.3 */
155 /* FIXME: implementation */
156 DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
157 break;
158 }
159 return minutes;
160}
161
162/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
163enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
164{
165 u_int8_t cgbits = dcs >> 4;
166 enum sms_alphabet alpha = DCS_NONE;
167
168 if ((cgbits & 0xc) == 0) {
169 if (cgbits & 2)
170 DEBUGP(DSMS, "Compressed SMS not supported yet\n");
171
172 switch (dcs & 3) {
173 case 0:
174 alpha = DCS_7BIT_DEFAULT;
175 break;
176 case 1:
177 alpha = DCS_8BIT_DATA;
178 break;
179 case 2:
180 alpha = DCS_UCS2;
181 break;
182 }
183 } else if (cgbits == 0xc || cgbits == 0xd)
184 alpha = DCS_7BIT_DEFAULT;
185 else if (cgbits == 0xe)
186 alpha = DCS_UCS2;
187 else if (cgbits == 0xf) {
188 if (dcs & 4)
189 alpha = DCS_8BIT_DATA;
190 else
191 alpha = DCS_7BIT_DEFAULT;
192 }
193
194 return alpha;
195}
196
Harald Welte68b7df22009-08-08 16:03:15 +0200197static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
Harald Welte59b04682009-06-10 05:40:52 +0800198{
199 if (db_sms_store(gsms) != 0) {
200 DEBUGP(DSMS, "Failed to store SMS in Database\n");
Harald Welte156c5e62009-07-05 14:02:46 +0200201 return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
Harald Welte59b04682009-06-10 05:40:52 +0800202 }
Harald Welte68b7df22009-08-08 16:03:15 +0200203 /* dispatch a signal to tell higher level about it */
204 dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
Harald Welte59b04682009-06-10 05:40:52 +0800205 return 0;
206}
207
Harald Welte68b7df22009-08-08 16:03:15 +0200208/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
209static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
210 struct gsm_subscriber *subscr)
Harald Welteb78996d2009-07-27 20:11:35 +0200211{
Harald Welte68b7df22009-08-08 16:03:15 +0200212 int len_in_bytes;
Harald Welteb78996d2009-07-27 20:11:35 +0200213
Harald Welte68b7df22009-08-08 16:03:15 +0200214 oa[1] = 0xb9; /* networks-specific number, private numbering plan */
215
216 len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
217
218 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
219 oa[0] = strlen(subscr->extension) & 0xff;
220
221 return len_in_bytes;
Harald Welteb78996d2009-07-27 20:11:35 +0200222}
223
224static u_int8_t bcdify(u_int8_t value)
225{
226 u_int8_t ret;
227
Harald Welte68b7df22009-08-08 16:03:15 +0200228 ret = value / 10;
229 ret |= (value % 10) << 4;
Harald Welteb78996d2009-07-27 20:11:35 +0200230
231 return ret;
232}
233
234/* Generate 03.40 TP-SCTS */
235static void gsm340_gen_scts(u_int8_t *scts, time_t time)
236{
237 struct tm *tm = localtime(&time);
Harald Welteb78996d2009-07-27 20:11:35 +0200238
239 *scts++ = bcdify(tm->tm_year % 100);
240 *scts++ = bcdify(tm->tm_mon);
241 *scts++ = bcdify(tm->tm_mday);
242 *scts++ = bcdify(tm->tm_hour);
243 *scts++ = bcdify(tm->tm_min);
244 *scts++ = bcdify(tm->tm_sec);
245 *scts++ = 0; /* FIXME: timezone */
246}
247
Harald Welte68b7df22009-08-08 16:03:15 +0200248/* generate a msgb containing a TPDU derived from struct gsm_sms,
249 * returns total size of TPDU */
250static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
Harald Welteb78996d2009-07-27 20:11:35 +0200251{
Harald Welteb78996d2009-07-27 20:11:35 +0200252 u_int8_t *smsp;
253 u_int8_t oa[12]; /* max len per 03.40 */
254 u_int8_t oa_len = 0;
Harald Welte68b7df22009-08-08 16:03:15 +0200255 unsigned int old_msg_len = msg->len;
Harald Welteb78996d2009-07-27 20:11:35 +0200256
257 /* generate first octet with masked bits */
258 smsp = msgb_put(msg, 1);
Harald Welte68b7df22009-08-08 16:03:15 +0200259 /* TP-MTI (message type indicator) */
Harald Welteb78996d2009-07-27 20:11:35 +0200260 *smsp = GSM340_SMS_DELIVER_SC2MS;
Harald Welte68b7df22009-08-08 16:03:15 +0200261 /* TP-MMS (more messages to send) */
262 if (0 /* FIXME */)
Harald Welteb78996d2009-07-27 20:11:35 +0200263 *smsp |= 0x04;
Harald Welte68b7df22009-08-08 16:03:15 +0200264 /* TP-SRI(deliver)/SRR(submit) */
Harald Welteb78996d2009-07-27 20:11:35 +0200265 if (sms->status_rep_req)
266 *smsp |= 0x20;
Harald Welte68b7df22009-08-08 16:03:15 +0200267 /* TP-UDHI (indicating TP-UD contains a header) */
268 if (sms->ud_hdr_ind)
Harald Welteb78996d2009-07-27 20:11:35 +0200269 *smsp |= 0x40;
Harald Welte68b7df22009-08-08 16:03:15 +0200270#if 0
271 /* TP-RP (indicating that a reply path exists) */
Harald Welteb78996d2009-07-27 20:11:35 +0200272 if (sms->
273 *smsp |= 0x80;
274#endif
275
276 /* generate originator address */
Harald Welte68b7df22009-08-08 16:03:15 +0200277 oa_len = gsm340_gen_oa(oa, sizeof(oa), sms->sender);
Harald Welteb78996d2009-07-27 20:11:35 +0200278 smsp = msgb_put(msg, oa_len);
Harald Welteb78996d2009-07-27 20:11:35 +0200279 memcpy(smsp, oa, oa_len);
280
281 /* generate TP-PID */
282 smsp = msgb_put(msg, 1);
283 *smsp = sms->protocol_id;
284
285 /* generate TP-DCS */
286 smsp = msgb_put(msg, 1);
287 *smsp = sms->data_coding_scheme;
288
289 /* generate TP-SCTS */
290 smsp = msgb_put(msg, 7);
291 gsm340_gen_scts(smsp, time(NULL));
Harald Welte68b7df22009-08-08 16:03:15 +0200292
Harald Welteb78996d2009-07-27 20:11:35 +0200293 /* generate TP-UDL */
294 smsp = msgb_put(msg, 1);
Harald Welte68b7df22009-08-08 16:03:15 +0200295 *smsp = sms->user_data_len;
Harald Welteb78996d2009-07-27 20:11:35 +0200296
297 /* generate TP-UD */
Harald Welte68b7df22009-08-08 16:03:15 +0200298 smsp = msgb_put(msg, sms->user_data_len);
299 memcpy(smsp, sms->user_data, sms->user_data_len);
Harald Welteb78996d2009-07-27 20:11:35 +0200300
Harald Welte68b7df22009-08-08 16:03:15 +0200301 return msg->len - old_msg_len;
Harald Welteb78996d2009-07-27 20:11:35 +0200302}
303
Harald Welte156c5e62009-07-05 14:02:46 +0200304/* process an incoming TPDU (called from RP-DATA)
305 * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
Harald Welte59b04682009-06-10 05:40:52 +0800306static int gsm340_rx_tpdu(struct msgb *msg)
307{
Harald Welte75350412009-07-23 18:46:00 +0200308 struct gsm_bts *bts = msg->lchan->ts->trx->bts;
Harald Welte59b04682009-06-10 05:40:52 +0800309 u_int8_t *smsp = msgb_sms(msg);
Harald Welte59b04682009-06-10 05:40:52 +0800310 struct gsm_sms *gsms;
Harald Welte68b7df22009-08-08 16:03:15 +0200311 u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp;
312 u_int8_t *sms_vp;
Harald Welte59b04682009-06-10 05:40:52 +0800313 u_int8_t da_len_bytes;
314 u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
315 int rc = 0;
316
Harald Welte68b7df22009-08-08 16:03:15 +0200317 gsms = sms_alloc();
318 if (!gsms)
Harald Welte156c5e62009-07-05 14:02:46 +0200319 return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
Harald Welte59b04682009-06-10 05:40:52 +0800320
321 /* invert those fields where 0 means active/present */
Harald Welte68b7df22009-08-08 16:03:15 +0200322 sms_mti = *smsp & 0x03;
323 sms_mms = !!(*smsp & 0x04);
324 sms_vpf = (*smsp & 0x18) >> 3;
325 gsms->status_rep_req = (*smsp & 0x20);
326 gsms->ud_hdr_ind = (*smsp & 0x40);
327 sms_rp = (*smsp & 0x80);
Harald Welte59b04682009-06-10 05:40:52 +0800328
329 smsp++;
Harald Welte68b7df22009-08-08 16:03:15 +0200330 gsms->msg_ref = *smsp++;
Harald Welte59b04682009-06-10 05:40:52 +0800331
332 /* length in bytes of the destination address */
333 da_len_bytes = 2 + *smsp/2 + *smsp%2;
334 if (da_len_bytes > 12) {
335 DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
Harald Welte156c5e62009-07-05 14:02:46 +0200336 rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
Harald Welte59b04682009-06-10 05:40:52 +0800337 goto out;
338 }
Harald Welte3794e152009-06-12 02:42:11 +0800339 memset(address_lv, 0, sizeof(address_lv));
Harald Welte59b04682009-06-10 05:40:52 +0800340 memcpy(address_lv, smsp, da_len_bytes);
341 /* mangle first byte to reflect length in bytes, not digits */
Harald Welte3794e152009-06-12 02:42:11 +0800342 address_lv[0] = da_len_bytes - 1;
Harald Welte59b04682009-06-10 05:40:52 +0800343 /* convert to real number */
Harald Welte68b7df22009-08-08 16:03:15 +0200344 decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1);
Harald Welte59b04682009-06-10 05:40:52 +0800345 smsp += da_len_bytes;
346
Harald Welte68b7df22009-08-08 16:03:15 +0200347 gsms->protocol_id = *smsp++;
348 gsms->data_coding_scheme = *smsp++;
Harald Welte59b04682009-06-10 05:40:52 +0800349
Harald Welte68b7df22009-08-08 16:03:15 +0200350 sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme);
Harald Welte59b04682009-06-10 05:40:52 +0800351
Harald Welte68b7df22009-08-08 16:03:15 +0200352 switch (sms_vpf) {
Harald Welte59b04682009-06-10 05:40:52 +0800353 case GSM340_TP_VPF_RELATIVE:
Harald Welte68b7df22009-08-08 16:03:15 +0200354 sms_vp = smsp++;
Harald Welte59b04682009-06-10 05:40:52 +0800355 break;
356 case GSM340_TP_VPF_ABSOLUTE:
357 case GSM340_TP_VPF_ENHANCED:
Harald Welte68b7df22009-08-08 16:03:15 +0200358 sms_vp = smsp;
Harald Welte59b04682009-06-10 05:40:52 +0800359 smsp += 7;
360 break;
361 default:
362 DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
Harald Welte68b7df22009-08-08 16:03:15 +0200363 sms_vpf);
364 return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
Harald Welte59b04682009-06-10 05:40:52 +0800365 }
Harald Welte68b7df22009-08-08 16:03:15 +0200366 gsms->user_data_len = *smsp++;
367 if (gsms->user_data_len) {
368 memcpy(gsms->user_data, smsp, gsms->user_data_len);
Harald Welte59b04682009-06-10 05:40:52 +0800369
Harald Welte68b7df22009-08-08 16:03:15 +0200370 switch (sms_alphabet) {
Harald Welte59b04682009-06-10 05:40:52 +0800371 case DCS_7BIT_DEFAULT:
Harald Welte68b7df22009-08-08 16:03:15 +0200372 gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len);
Harald Welte59b04682009-06-10 05:40:52 +0800373 break;
374 case DCS_8BIT_DATA:
375 case DCS_UCS2:
376 case DCS_NONE:
Harald Welte59b04682009-06-10 05:40:52 +0800377 break;
378 }
379 }
380
381 DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
382 "PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
Harald Welte68b7df22009-08-08 16:03:15 +0200383 "UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
384 gsms->protocol_id, gsms->data_coding_scheme,
385 gsms->dest_addr, gsms->user_data_len,
386 sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
387 hexdump(gsms->user_data, gsms->user_data_len));
Harald Welte59b04682009-06-10 05:40:52 +0800388
Harald Welte68b7df22009-08-08 16:03:15 +0200389 gsms->sender = subscr_get(msg->lchan->subscr);
Harald Welte59b04682009-06-10 05:40:52 +0800390
Harald Welte68b7df22009-08-08 16:03:15 +0200391 gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
Harald Welte156c5e62009-07-05 14:02:46 +0200392
Harald Welte68b7df22009-08-08 16:03:15 +0200393 dispatch_signal(SS_SMS, 0, gsms);
Harald Welte156c5e62009-07-05 14:02:46 +0200394
Harald Welte59b04682009-06-10 05:40:52 +0800395 /* determine gsms->receiver based on dialled number */
Harald Welte68b7df22009-08-08 16:03:15 +0200396 gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
Harald Welte59b04682009-06-10 05:40:52 +0800397 if (!gsms->receiver) {
398 rc = 1; /* cause 1: unknown subscriber */
399 goto out;
400 }
401
Harald Welte68b7df22009-08-08 16:03:15 +0200402 switch (sms_mti) {
Harald Welte59b04682009-06-10 05:40:52 +0800403 case GSM340_SMS_SUBMIT_MS2SC:
404 /* MS is submitting a SMS */
Harald Welte68b7df22009-08-08 16:03:15 +0200405 rc = gsm340_rx_sms_submit(msg, gsms);
Harald Welte59b04682009-06-10 05:40:52 +0800406 break;
407 case GSM340_SMS_COMMAND_MS2SC:
408 case GSM340_SMS_DELIVER_REP_MS2SC:
Harald Welte68b7df22009-08-08 16:03:15 +0200409 DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti);
Harald Welte156c5e62009-07-05 14:02:46 +0200410 rc = GSM411_RP_CAUSE_IE_NOTEXIST;
Harald Welte59b04682009-06-10 05:40:52 +0800411 break;
412 default:
Harald Welte68b7df22009-08-08 16:03:15 +0200413 DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
Harald Welte156c5e62009-07-05 14:02:46 +0200414 rc = GSM411_RP_CAUSE_IE_NOTEXIST;
Harald Welte59b04682009-06-10 05:40:52 +0800415 break;
416 }
417
Harald Welte156c5e62009-07-05 14:02:46 +0200418 if (!rc && !gsms->receiver)
419 rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
420
Harald Welte59b04682009-06-10 05:40:52 +0800421out:
Harald Welte68b7df22009-08-08 16:03:15 +0200422 sms_free(gsms);
Harald Welte59b04682009-06-10 05:40:52 +0800423
424 return rc;
425}
426
Harald Welteb78996d2009-07-27 20:11:35 +0200427static int gsm411_send_rp_ack(struct gsm_trans *trans, u_int8_t msg_ref)
Harald Welte59b04682009-06-10 05:40:52 +0800428{
429 struct msgb *msg = gsm411_msgb_alloc();
Harald Welte59b04682009-06-10 05:40:52 +0800430
Harald Welte59b04682009-06-10 05:40:52 +0800431 DEBUGP(DSMS, "TX: SMS RP ACK\n");
432
Harald Welteb78996d2009-07-27 20:11:35 +0200433 return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ACK_MT, msg_ref);
Harald Welte59b04682009-06-10 05:40:52 +0800434}
435
Harald Welteb78996d2009-07-27 20:11:35 +0200436static int gsm411_send_rp_error(struct gsm_trans *trans,
437 u_int8_t msg_ref, u_int8_t cause)
Harald Welte59b04682009-06-10 05:40:52 +0800438{
439 struct msgb *msg = gsm411_msgb_alloc();
Harald Welte59b04682009-06-10 05:40:52 +0800440
Harald Welte59b04682009-06-10 05:40:52 +0800441 msgb_tv_put(msg, 1, cause);
442
443 DEBUGP(DSMS, "TX: SMS RP ERROR (cause %02d)\n", cause);
444
Harald Welteb78996d2009-07-27 20:11:35 +0200445 return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref);
Harald Welte59b04682009-06-10 05:40:52 +0800446}
447
448/* Receive a 04.11 TPDU inside RP-DATA / user data */
Harald Welteb78996d2009-07-27 20:11:35 +0200449static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
450 struct gsm411_rp_hdr *rph,
Harald Welte59b04682009-06-10 05:40:52 +0800451 u_int8_t src_len, u_int8_t *src,
452 u_int8_t dst_len, u_int8_t *dst,
453 u_int8_t tpdu_len, u_int8_t *tpdu)
454{
Harald Welte59b04682009-06-10 05:40:52 +0800455 int rc = 0;
456
457 if (src_len && src)
458 DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
459
460 if (!dst_len || !dst || !tpdu_len || !tpdu) {
461 DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
Harald Welteb78996d2009-07-27 20:11:35 +0200462 gsm411_send_rp_error(trans, rph->msg_ref,
Harald Welte156c5e62009-07-05 14:02:46 +0200463 GSM411_RP_CAUSE_INV_MAND_INF);
Harald Welte59b04682009-06-10 05:40:52 +0800464 return -EIO;
465 }
466 msg->smsh = tpdu;
467
468 DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
Harald Welte59b04682009-06-10 05:40:52 +0800469
470 rc = gsm340_rx_tpdu(msg);
471 if (rc == 0)
Harald Welteb78996d2009-07-27 20:11:35 +0200472 return gsm411_send_rp_ack(trans, rph->msg_ref);
Harald Welte59b04682009-06-10 05:40:52 +0800473 else if (rc > 0)
Harald Welteb78996d2009-07-27 20:11:35 +0200474 return gsm411_send_rp_error(trans, rph->msg_ref, rc);
Harald Welte59b04682009-06-10 05:40:52 +0800475 else
476 return rc;
477}
478
479/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */
Harald Welteb78996d2009-07-27 20:11:35 +0200480static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans,
481 struct gsm411_rp_hdr *rph)
Harald Welte59b04682009-06-10 05:40:52 +0800482{
483 u_int8_t src_len, dst_len, rpud_len;
484 u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL;
485
486 /* in the MO case, this should always be zero length */
487 src_len = rph->data[0];
488 if (src_len)
489 src = &rph->data[1];
490
491 dst_len = rph->data[1+src_len];
492 if (dst_len)
493 dst = &rph->data[1+src_len+1];
494
495 rpud_len = rph->data[1+src_len+1+dst_len];
496 if (rpud_len)
497 rp_ud = &rph->data[1+src_len+1+dst_len+1];
498
Harald Welte156c5e62009-07-05 14:02:46 +0200499 DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n",
500 src_len, dst_len, rpud_len);
Harald Welteb78996d2009-07-27 20:11:35 +0200501 return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst,
Harald Welte59b04682009-06-10 05:40:52 +0800502 rpud_len, rp_ud);
503}
504
Harald Welteb78996d2009-07-27 20:11:35 +0200505static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
506 struct gsm411_rp_hdr *rph)
Harald Welte156c5e62009-07-05 14:02:46 +0200507{
Harald Welte68b7df22009-08-08 16:03:15 +0200508 struct gsm_sms *sms = trans->sms.sms;
509
Harald Welte156c5e62009-07-05 14:02:46 +0200510 /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it
511 * successfully received a SMS. We can now safely mark it as
512 * transmitted */
513
Harald Weltedd62b162009-07-09 23:52:59 +0200514 /* we need to look-up the transaction based on rph->msg_ref to
515 * identify which particular RP_DATA/SMS-submit was ACKed */
516
Harald Welte68b7df22009-08-08 16:03:15 +0200517 if (!sms) {
518 DEBUGP(DSMS, "RX RP-ACK (MT) but no sms in transaction?!?\n");
519 put_lchan(trans->lchan);
520 return -EIO;
521 }
522
523 /* mark this SMS as sent in database */
524 db_sms_mark_sent(sms);
525
526 dispatch_signal(SS_SMS, S_SMS_DELIVERED, sms);
527
528 sms_free(sms);
529 trans->sms.sms = NULL;
530
531 put_lchan(trans->lchan);
532
533 return 0;
Harald Welte156c5e62009-07-05 14:02:46 +0200534}
535
Harald Welteb78996d2009-07-27 20:11:35 +0200536static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
537 struct gsm411_rp_hdr *rph)
Harald Welte156c5e62009-07-05 14:02:46 +0200538{
Harald Welte68b7df22009-08-08 16:03:15 +0200539 struct gsm_sms *sms = trans->sms.sms;
Harald Welteb78996d2009-07-27 20:11:35 +0200540 u_int8_t cause_len = rph->data[0];
541 u_int8_t cause = rph->data[1];
542
Harald Welte156c5e62009-07-05 14:02:46 +0200543 /* Error in response to MT RP_DATA, i.e. the MS did not
544 * successfully receive the SMS. We need to investigate
545 * the cause and take action depending on it */
546
Harald Welteb78996d2009-07-27 20:11:35 +0200547 DEBUGP(DSMS, "RX SMS RP-ERROR Cause=0x%02x\n", cause);
548
Harald Weltedd62b162009-07-09 23:52:59 +0200549 /* we need to look-up the transaction based on rph->msg_ref to
550 * identify which particular RP_DATA/SMS-submit failed */
551
Harald Welte68b7df22009-08-08 16:03:15 +0200552 if (!sms) {
553 DEBUGP(DSMS, "RX RP-ERR (MT) but no sms in transaction?!?\n");
554 put_lchan(trans->lchan);
555 return -EIO;
556 }
557
558 sms_free(sms);
559 trans->sms.sms = NULL;
560
561 put_lchan(trans->lchan);
562
Harald Welteb78996d2009-07-27 20:11:35 +0200563 return 0;
Harald Welte156c5e62009-07-05 14:02:46 +0200564}
565
Harald Welteb78996d2009-07-27 20:11:35 +0200566static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans,
567 struct gsm411_rp_hdr *rph)
Harald Welte156c5e62009-07-05 14:02:46 +0200568{
Harald Welteb78996d2009-07-27 20:11:35 +0200569 int rc;
570
Harald Welte156c5e62009-07-05 14:02:46 +0200571 /* MS tells us that it has memory for more SMS, we need
572 * to check if we have any pending messages for it and then
573 * transfer those */
Harald Welte68b7df22009-08-08 16:03:15 +0200574 dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr);
Harald Welteb78996d2009-07-27 20:11:35 +0200575
576 rc = gsm411_send_rp_ack(trans, rph->msg_ref);
577 trans->sms.rp_state = GSM411_RPS_IDLE;
578
579 return rc;
Harald Welte156c5e62009-07-05 14:02:46 +0200580}
581
Harald Welteb78996d2009-07-27 20:11:35 +0200582static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh,
583 struct gsm_trans *trans)
Harald Welte59b04682009-06-10 05:40:52 +0800584{
585 struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
586 u_int8_t msg_type = rp_data->msg_type & 0x07;
587 int rc = 0;
588
589 switch (msg_type) {
590 case GSM411_MT_RP_DATA_MO:
Harald Welteb78996d2009-07-27 20:11:35 +0200591 DEBUGP(DSMS, "RX SMS RP-DATA (MO)\n");
592 /* start TR2N and enter 'wait to send RP-ACK state' */
593 trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK;
594 rc = gsm411_rx_rp_data(msg, trans, rp_data);
Harald Welte59b04682009-06-10 05:40:52 +0800595 break;
596 case GSM411_MT_RP_ACK_MO:
Harald Welteb78996d2009-07-27 20:11:35 +0200597 DEBUGP(DSMS,"RX SMS RP-ACK (MO)\n");
598 rc = gsm411_rx_rp_ack(msg, trans, rp_data);
Harald Welte156c5e62009-07-05 14:02:46 +0200599 break;
Harald Welte59b04682009-06-10 05:40:52 +0800600 case GSM411_MT_RP_SMMA_MO:
Harald Welteb78996d2009-07-27 20:11:35 +0200601 DEBUGP(DSMS, "RX SMS RP-SMMA\n");
602 /* start TR2N and enter 'wait to send RP-ACK state' */
603 trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK;
604 rc = gsm411_rx_rp_smma(msg, trans, rp_data);
605 break;
606 case GSM411_MT_RP_ERROR_MO:
607 rc = gsm411_rx_rp_error(msg, trans, rp_data);
Harald Welte59b04682009-06-10 05:40:52 +0800608 break;
609 default:
610 DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
Harald Welteb78996d2009-07-27 20:11:35 +0200611 rc = gsm411_send_rp_error(trans, rp_data->msg_ref,
612 GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
Harald Welte59b04682009-06-10 05:40:52 +0800613 break;
614 }
615
616 return rc;
617}
618
Harald Welteb78996d2009-07-27 20:11:35 +0200619/* send CP-ACK to given transaction */
620static int gsm411_tx_cp_ack(struct gsm_trans *trans)
621{
622 struct msgb *msg = gsm411_msgb_alloc();
623
624 return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK);
625}
626
627static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
628{
629 struct msgb *msg = gsm411_msgb_alloc();
630 u_int8_t *causep;
631
Harald Welte68b7df22009-08-08 16:03:15 +0200632 causep = msgb_put(msg, 1);
Harald Welteb78996d2009-07-27 20:11:35 +0200633 *causep = cause;
634
635 return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR);
636}
637
638/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */
Harald Welte59b04682009-06-10 05:40:52 +0800639int gsm0411_rcv_sms(struct msgb *msg)
640{
641 struct gsm48_hdr *gh = msgb_l3(msg);
642 u_int8_t msg_type = gh->msg_type;
Harald Welteb78996d2009-07-27 20:11:35 +0200643 u_int8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */
644 struct gsm_lchan *lchan = msg->lchan;
645 struct gsm_trans *trans;
Harald Welte59b04682009-06-10 05:40:52 +0800646 int rc = 0;
647
Harald Welteb78996d2009-07-27 20:11:35 +0200648 if (!lchan->subscr)
649 return -EIO;
650 /* FIXME: send some error message */
651
652 trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
653 transaction_id);
654 if (!trans) {
655 DEBUGP(DSMS, "Unknown transaction ID %x, "
656 "creating new trans\n", transaction_id);
657 trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
658 transaction_id, new_callref++);
659 if (!trans) {
660 DEBUGP(DSMS, "No memory for trans\n");
661 /* FIXME: send some error message */
662 return -ENOMEM;
663 }
664 trans->sms.cp_state = GSM411_CPS_IDLE;
665 trans->sms.rp_state = GSM411_RPS_IDLE;
666 trans->sms.is_mt = 0;
667
668 trans->lchan = lchan;
669 use_lchan(lchan);
670 }
671
Harald Welte59b04682009-06-10 05:40:52 +0800672 switch(msg_type) {
673 case GSM411_MT_CP_DATA:
Harald Welteb78996d2009-07-27 20:11:35 +0200674 DEBUGP(DSMS, "RX SMS CP-DATA\n");
675 if (!trans->sms.is_mt) {
676 /* 5.2.3.1.3: MO state exists when SMC has received
677 * CP-DATA, including sending of the assoc. CP-ACK */
678 trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
679 }
680
681 rc = gsm411_rx_cp_data(msg, gh, trans);
682 /* Send CP-ACK or CP-ERORR in response */
683 if (rc < 0) {
684 rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_NET_FAIL);
685 } else
686 rc = gsm411_tx_cp_ack(trans);
Harald Welte59b04682009-06-10 05:40:52 +0800687 break;
688 case GSM411_MT_CP_ACK:
Harald Welteb78996d2009-07-27 20:11:35 +0200689 /* previous CP-DATA in this transaction was confirmed */
690 DEBUGP(DSMS, "RX SMS CP-ACK\n");
691 if (!trans->sms.is_mt) {
692 /* 5.2.3.1.3: MO state exists when SMC has received
693 * CP-ACK */
694 trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
695 /* FIXME: we have sont one CP-DATA, which was now
696 * acknowledged. Check if we want to transfer more,
697 * i.e. multi-part message */
698 trans->sms.cp_state = GSM411_CPS_IDLE;
699 trans_free(trans);
700 }
Harald Welte59b04682009-06-10 05:40:52 +0800701 break;
702 case GSM411_MT_CP_ERROR:
Harald Welteb78996d2009-07-27 20:11:35 +0200703 DEBUGP(DSMS, "RX SMS CP-ERROR, cause 0x%02x\n", gh->data[0]);
704 trans->sms.cp_state = GSM411_CPS_IDLE;
705 trans_free(trans);
Harald Welte59b04682009-06-10 05:40:52 +0800706 break;
707 default:
Harald Welteb78996d2009-07-27 20:11:35 +0200708 DEBUGP(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type);
709 rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST);
710 trans_free(trans);
Harald Welte59b04682009-06-10 05:40:52 +0800711 break;
712 }
713
Harald Welte59b04682009-06-10 05:40:52 +0800714 return rc;
715}
716
Harald Welte59b04682009-06-10 05:40:52 +0800717#if 0
Harald Welte59b04682009-06-10 05:40:52 +0800718/* Test TPDU - ALL YOUR */
719static u_int8_t tpdu_test[] = {
720 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24,
721 0x32, 0x40, 0x1F, 0x41, 0x26, 0x13, 0x94, 0x7D, 0x56, 0xA5, 0x20, 0x28,
722 0xF2, 0xE9, 0x2C, 0x82, 0x82, 0xD2, 0x22, 0x48, 0x58, 0x64, 0x3E, 0x9D,
723 0x47, 0x10, 0xF5, 0x09, 0xAA, 0x4E, 0x01
724};
725#endif
726
Harald Welte68b7df22009-08-08 16:03:15 +0200727/* Take a SMS in gsm_sms structure and send it through lchan */
728int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
Harald Welte59b04682009-06-10 05:40:52 +0800729{
730 struct msgb *msg = gsm411_msgb_alloc();
Harald Welteb78996d2009-07-27 20:11:35 +0200731 struct gsm_trans *trans;
Harald Welte68b7df22009-08-08 16:03:15 +0200732 u_int8_t *data, *rp_ud_len;
Harald Welte7e2f57d2009-07-04 17:39:00 +0200733 u_int8_t msg_ref = 42;
Harald Welte68b7df22009-08-08 16:03:15 +0200734 u_int8_t transaction_id = 1; /* FIXME: random */
735 int rc;
Harald Welte59b04682009-06-10 05:40:52 +0800736
737 msg->lchan = lchan;
738
Harald Welte68b7df22009-08-08 16:03:15 +0200739 DEBUGP(DSMS, "send_sms_lchan()\n");
Harald Welteb78996d2009-07-27 20:11:35 +0200740
Harald Welte68b7df22009-08-08 16:03:15 +0200741 /* FIXME: allocate transaction with message reference */
742 trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
743 transaction_id, new_callref++);
744 if (!trans) {
745 DEBUGP(DSMS, "No memory for trans\n");
746 /* FIXME: send some error message */
747 return -ENOMEM;
748 }
749 trans->sms.cp_state = GSM411_CPS_IDLE;
750 trans->sms.rp_state = GSM411_RPS_IDLE;
751 trans->sms.is_mt = 1;
752 trans->sms.sms = sms;
753
754 trans->lchan = lchan;
755 use_lchan(lchan);
756
757 /* Hardcode SMSC Originating Address for now */
Harald Welte59b04682009-06-10 05:40:52 +0800758 data = (u_int8_t *)msgb_put(msg, 8);
Harald Welte7e2f57d2009-07-04 17:39:00 +0200759 data[0] = 0x07; /* originator length == 7 */
Harald Welte156c5e62009-07-05 14:02:46 +0200760 data[1] = 0x91; /* type of number: international, ISDN */
761 data[2] = 0x44; /* 447785016005 */
Harald Welte59b04682009-06-10 05:40:52 +0800762 data[3] = 0x77;
763 data[4] = 0x58;
764 data[5] = 0x10;
765 data[6] = 0x06;
766 data[7] = 0x50;
Harald Welte7e2f57d2009-07-04 17:39:00 +0200767
768 /* Hardcoded Destination Address */
Harald Welte59b04682009-06-10 05:40:52 +0800769 data = (u_int8_t *)msgb_put(msg, 1);
Harald Welte7e2f57d2009-07-04 17:39:00 +0200770 data[0] = 0; /* destination length == 0 */
Harald Welte59b04682009-06-10 05:40:52 +0800771
Harald Welte68b7df22009-08-08 16:03:15 +0200772 /* obtain a pointer for the rp_ud_len, so we can fill it later */
773 rp_ud_len = (u_int8_t *)msgb_put(msg, 1);
Harald Welte59b04682009-06-10 05:40:52 +0800774
Harald Welte68b7df22009-08-08 16:03:15 +0200775#if 1
776 /* generate the 03.40 TPDU */
777 rc = gsm340_gen_tpdu(msg, sms);
778 if (rc < 0) {
779 msgb_free(msg);
780 return rc;
781 }
Harald Welte59b04682009-06-10 05:40:52 +0800782
Harald Welte68b7df22009-08-08 16:03:15 +0200783 *rp_ud_len = rc;
784#else
785 data = msgb_put(msg, sizeof(tpdu_test));
Harald Welte59b04682009-06-10 05:40:52 +0800786 memcpy(data, tpdu_test, sizeof(tpdu_test));
Harald Welte68b7df22009-08-08 16:03:15 +0200787 *rp_ud_len = sizeof(tpdu_test);
788#endif
Harald Welte59b04682009-06-10 05:40:52 +0800789
Harald Welte68b7df22009-08-08 16:03:15 +0200790 DEBUGP(DSMS, "TX: SMS DELIVER\n");
Harald Welte59b04682009-06-10 05:40:52 +0800791
Harald Welteb78996d2009-07-27 20:11:35 +0200792 return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
793 /* FIXME: enter 'wait for RP-ACK' state, start TR1N */
Harald Welte59b04682009-06-10 05:40:52 +0800794}
Harald Welteb78996d2009-07-27 20:11:35 +0200795
Harald Welte68b7df22009-08-08 16:03:15 +0200796/* paging callback */
797static int paging_cb_send_sms(unsigned int hooknum, unsigned int event,
798 struct msgb *msg, void *_lchan, void *_sms)
Harald Welteb78996d2009-07-27 20:11:35 +0200799{
Harald Welte68b7df22009-08-08 16:03:15 +0200800 struct gsm_lchan *lchan = _lchan;
801 struct gsm_sms *sms = _sms;
802 int rc;
Harald Welteb78996d2009-07-27 20:11:35 +0200803
Harald Welte68b7df22009-08-08 16:03:15 +0200804 DEBUGP(DSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p,"
805 "lchan=%p, sms=%p)\n", hooknum, event, msg, lchan, sms);
806
807 if (hooknum != GSM_HOOK_RR_PAGING)
808 return -EINVAL;
809
810 switch (event) {
811 case GSM_PAGING_SUCCEEDED:
812 /* Paging aborted without lchan ?!? */
813 if (!lchan) {
814 sms_free(sms);
815 rc = -EIO;
816 break;
817 }
818 rc = gsm411_send_sms_lchan(lchan, sms);
819 break;
820 case GSM_PAGING_EXPIRED:
821 sms_free(sms);
822 rc = -ETIMEDOUT;
823 break;
824 default:
825 rc = -EINVAL;
826 break;
827 }
828
829 return rc;
830}
831
832int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
833 struct gsm_sms *sms)
834{
835 /* check if we already have an open lchan to the subscriber.
836 * if yes, send the SMS this way */
837 //if (subscr->lchan)
838 //return gsm411_send_sms_lchan(subscr->lchan, sms);
839
840 /* if not, we have to start paging */
841 paging_request(subscr->net, subscr, RSL_CHANNEED_SDCCH,
842 paging_cb_send_sms, sms);
843
844 return 0;
845}
Harald Welte5b359d82009-07-28 00:44:49 +0200846
Harald Welte932e20d2009-07-28 00:41:45 +0200847static __attribute__((constructor)) void on_dso_load_sms(void)
848{
Harald Welte932e20d2009-07-28 00:41:45 +0200849 tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 1, "sms");
850}