Minor fix: remote.c -> remote.cpp in Makefile.am
[lcr.git] / gsm.cpp
1 /*****************************************************************************\
2 **                                                                           **
3 ** LCR                                                                       **
4 **                                                                           **
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg                                              **
7 **                                                                           **
8 ** mISDN gsm                                                                 **
9 **                                                                           **
10 \*****************************************************************************/ 
11
12 #include "main.h"
13
14 #ifndef _GNU_SOURCE
15 #define _GNU_SOURCE
16 #endif
17 extern "C" {
18 #include "gsm_audio.h"
19 }
20
21 #include <mISDN/mISDNcompat.h>
22
23 struct lcr_gsm *gsm = NULL;
24
25 int new_callref = 1;
26
27 /* names of MNCC-SAP */
28 static const struct _value_string {
29         int msg_type;
30         const char *name;
31 } mncc_names[] = {
32 #if defined(MNCC_SETUP_REQ)
33         { MNCC_SETUP_REQ,       "MNCC_SETUP_REQ" },
34 #endif
35 #if defined(MNCC_SETUP_IND)
36         { MNCC_SETUP_IND,       "MNCC_SETUP_IND" },
37 #endif
38 #if defined(MNCC_SETUP_RSP)
39         { MNCC_SETUP_RSP,       "MNCC_SETUP_RSP" },
40 #endif
41 #if defined(MNCC_SETUP_CNF)
42         { MNCC_SETUP_CNF,       "MNCC_SETUP_CNF" },
43 #endif
44 #if defined(MNCC_SETUP_COMPL_REQ)
45         { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" },
46 #endif
47 #if defined(MNCC_SETUP_COMPL_IND)
48         { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" },
49 #endif
50 #if defined(MNCC_CALL_CONF_IND)
51         { MNCC_CALL_CONF_IND,   "MNCC_CALL_CONF_IND" },
52 #endif
53 #if defined(MNCC_CALL_PROC_REQ)
54         { MNCC_CALL_PROC_REQ,   "MNCC_CALL_PROC_REQ" },
55 #endif
56 #if defined(MNCC_PROGRESS_REQ)
57         { MNCC_PROGRESS_REQ,    "MNCC_PROGRESS_REQ" },
58 #endif
59 #if defined(MNCC_ALERT_REQ)
60         { MNCC_ALERT_REQ,       "MNCC_ALERT_REQ" },
61 #endif
62 #if defined(MNCC_ALERT_IND)
63         { MNCC_ALERT_IND,       "MNCC_ALERT_IND" },
64 #endif
65 #if defined(MNCC_NOTIFY_REQ)
66         { MNCC_NOTIFY_REQ,      "MNCC_NOTIFY_REQ" },
67 #endif
68 #if defined(MNCC_NOTIFY_IND)
69         { MNCC_NOTIFY_IND,      "MNCC_NOTIFY_IND" },
70 #endif
71 #if defined(MNCC_DISC_REQ)
72         { MNCC_DISC_REQ,        "MNCC_DISC_REQ" },
73 #endif
74 #if defined(MNCC_DISC_IND)
75         { MNCC_DISC_IND,        "MNCC_DISC_IND" },
76 #endif
77 #if defined(MNCC_REL_REQ)
78         { MNCC_REL_REQ,         "MNCC_REL_REQ" },
79 #endif
80 #if defined(MNCC_REL_IND)
81         { MNCC_REL_IND,         "MNCC_REL_IND" },
82 #endif
83 #if defined(MNCC_REL_CNF)
84         { MNCC_REL_CNF,         "MNCC_REL_CNF" },
85 #endif
86 #if defined(MNCC_FACILITY_REQ)
87         { MNCC_FACILITY_REQ,    "MNCC_FACILITY_REQ" },
88 #endif
89 #if defined(MNCC_FACILITY_IND)
90         { MNCC_FACILITY_IND,    "MNCC_FACILITY_IND" },
91 #endif
92 #if defined(MNCC_START_DTMF_IND)
93         { MNCC_START_DTMF_IND,  "MNCC_START_DTMF_IND" },
94 #endif
95 #if defined(MNCC_START_DTMF_RSP)
96         { MNCC_START_DTMF_RSP,  "MNCC_START_DTMF_RSP" },
97 #endif
98 #if defined(MNCC_START_DTMF_REJ)
99         { MNCC_START_DTMF_REJ,  "MNCC_START_DTMF_REJ" },
100 #endif
101 #if defined(MNCC_STOP_DTMF_IND)
102         { MNCC_STOP_DTMF_IND,   "MNCC_STOP_DTMF_IND" },
103 #endif
104 #if defined(MNCC_STOP_DTMF_RSP)
105         { MNCC_STOP_DTMF_RSP,   "MNCC_STOP_DTMF_RSP" },
106 #endif
107 #if defined(MNCC_MODIFY_REQ)
108         { MNCC_MODIFY_REQ,      "MNCC_MODIFY_REQ" },
109 #endif
110 #if defined(MNCC_MODIFY_IND)
111         { MNCC_MODIFY_IND,      "MNCC_MODIFY_IND" },
112 #endif
113 #if defined(MNCC_MODIFY_RSP)
114         { MNCC_MODIFY_RSP,      "MNCC_MODIFY_RSP" },
115 #endif
116 #if defined(MNCC_MODIFY_CNF)
117         { MNCC_MODIFY_CNF,      "MNCC_MODIFY_CNF" },
118 #endif
119 #if defined(MNCC_MODIFY_REJ)
120         { MNCC_MODIFY_REJ,      "MNCC_MODIFY_REJ" },
121 #endif
122 #if defined(MNCC_HOLD_IND)
123         { MNCC_HOLD_IND,        "MNCC_HOLD_IND" },
124 #endif
125 #if defined(MNCC_HOLD_CNF)
126         { MNCC_HOLD_CNF,        "MNCC_HOLD_CNF" },
127 #endif
128 #if defined(MNCC_HOLD_REJ)
129         { MNCC_HOLD_REJ,        "MNCC_HOLD_REJ" },
130 #endif
131 #if defined(MNCC_RETRIEVE_IND)
132         { MNCC_RETRIEVE_IND,    "MNCC_RETRIEVE_IND" },
133 #endif
134 #if defined(MNCC_RETRIEVE_CNF)
135         { MNCC_RETRIEVE_CNF,    "MNCC_RETRIEVE_CNF" },
136 #endif
137 #if defined(MNCC_RETRIEVE_REJ)
138         { MNCC_RETRIEVE_REJ,    "MNCC_RETRIEVE_REJ" },
139 #endif
140 #if defined(MNCC_USERINFO_REQ)
141         { MNCC_USERINFO_REQ,    "MNCC_USERINFO_REQ" },
142 #endif
143 #if defined(MNCC_USERINFO_IND)
144         { MNCC_USERINFO_IND,    "MNCC_USERINFO_IND" },
145 #endif
146 #if defined(MNCC_REJ_REQ)
147         { MNCC_REJ_REQ,         "MNCC_REJ_REQ" },
148 #endif
149 #if defined(MNCC_REJ_IND)
150         { MNCC_REJ_IND,         "MNCC_REJ_IND" },
151 #endif
152 #if defined(MNCC_PROGRESS_IND)
153         { MNCC_PROGRESS_IND,    "MNCC_PROGRESS_IND" },
154 #endif
155 #if defined(MNCC_CALL_PROC_IND)
156         { MNCC_CALL_PROC_IND,   "MNCC_CALL_PROC_IND" },
157 #endif
158 #if defined(MNCC_CALL_CONF_REQ)
159         { MNCC_CALL_CONF_REQ,   "MNCC_CALL_CONF_REQ" },
160 #endif
161 #if defined(MNCC_START_DTMF_REQ)
162         { MNCC_START_DTMF_REQ,  "MNCC_START_DTMF_REQ" },
163 #endif
164 #if defined(MNCC_STOP_DTMF_REQ)
165         { MNCC_STOP_DTMF_REQ,   "MNCC_STOP_DTMF_REQ" },
166 #endif
167 #if defined(MNCC_HOLD_REQ)
168         { MNCC_HOLD_REQ,        "MNCC_HOLD_REQ " },
169 #endif
170 #if defined(MNCC_RETRIEVE_REQ)
171         { MNCC_RETRIEVE_REQ,    "MNCC_RETRIEVE_REQ" },
172 #endif
173         { 0,                    NULL }
174 };
175
176 const char *mncc_name(int value)
177 {
178         int i = 0;
179
180         while (mncc_names[i].name) {
181                 if (mncc_names[i].msg_type == value)
182                         return mncc_names[i].name;
183                 i++;
184         }
185         return "unknown";
186 }
187
188 /*
189  * create and send mncc message
190  */
191 struct gsm_mncc *create_mncc(int msg_type, unsigned int callref)
192 {
193         struct gsm_mncc *mncc;
194
195         mncc = (struct gsm_mncc *)MALLOC(sizeof(struct gsm_mncc));
196         mncc->msg_type = msg_type;
197         mncc->callref = callref;
198         return (mncc);
199 }
200 int send_and_free_mncc(void *instance, unsigned int msg_type, void *data)
201 {
202         int ret = 0;
203
204         if (instance) {
205 #ifdef WITH_GSM_BS
206                 ret = mncc_send((struct gsm_network *)instance, msg_type, data);
207 #endif
208 #ifdef WITH_GSM_MS
209                 ret = mncc_send((struct osmocom_ms *)instance, msg_type, data);
210 #endif
211         }
212         free(data);
213
214         return ret;
215 }
216
217 static int delete_event(struct lcr_work *work, void *instance, int index);
218
219 /*
220  * constructor
221  */
222 Pgsm::Pgsm(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : PmISDN(type, mISDNport, portname, settings, channel, exclusive, mode)
223 {
224         p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN;
225         memset(&p_m_g_delete, 0, sizeof(p_m_g_delete));
226         add_work(&p_m_g_delete, delete_event, this, 0);
227         p_m_g_instance = NULL;
228         p_m_g_callref = 0;
229         p_m_g_mode = 0;
230         p_m_g_gsm_b_sock = -1;
231         p_m_g_gsm_b_index = -1;
232         p_m_g_gsm_b_active = 0;
233         p_m_g_notify_pending = NULL;
234         p_m_g_decoder = gsm_audio_create();
235         p_m_g_encoder = gsm_audio_create();
236         if (!p_m_g_encoder || !p_m_g_decoder) {
237                 PERROR("Failed to create GSM audio codec instance\n");
238                 trigger_work(&p_m_g_delete);
239         }
240         p_m_g_rxpos = 0;
241         p_m_g_tch_connected = 0;
242
243         PDEBUG(DEBUG_GSM, "Created new GSMPort(%s).\n", portname);
244 }
245
246 /*
247  * destructor
248  */
249 Pgsm::~Pgsm()
250 {
251         PDEBUG(DEBUG_GSM, "Destroyed GSM process(%s).\n", p_name);
252
253         del_work(&p_m_g_delete);
254
255         /* remove queued message */
256         if (p_m_g_notify_pending)
257                 message_free(p_m_g_notify_pending);
258
259         /* close audio transfer socket */
260         if (p_m_g_gsm_b_sock > -1)
261                 bchannel_close();
262
263         /* close codec */
264         if (p_m_g_encoder)
265                 gsm_audio_destroy(p_m_g_encoder);
266         if (p_m_g_decoder)
267                 gsm_audio_destroy(p_m_g_decoder);
268 }
269
270
271 /* close bsc side bchannel */
272 void Pgsm::bchannel_close(void)
273 {
274         if (p_m_g_gsm_b_sock > -1) {
275                 unregister_fd(&p_m_g_gsm_b_fd);
276                 close(p_m_g_gsm_b_sock);
277         }
278         p_m_g_gsm_b_sock = -1;
279         p_m_g_gsm_b_index = -1;
280         p_m_g_gsm_b_active = 0;
281 }
282
283 static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index);
284
285 /* open external side bchannel */
286 int Pgsm::bchannel_open(int index)
287 {
288         int ret;
289         struct sockaddr_mISDN addr;
290         struct mISDNhead act;
291
292         if (p_m_g_gsm_b_sock > -1) {
293                 PERROR("Socket already created for index %d\n", index);
294                 return(-EIO);
295         }
296
297         /* open socket */
298         ret = p_m_g_gsm_b_sock = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
299         if (ret < 0) {
300                 PERROR("Failed to open bchannel-socket for index %d\n", index);
301                 bchannel_close();
302                 return(ret);
303         }
304         memset(&p_m_g_gsm_b_fd, 0, sizeof(p_m_g_gsm_b_fd));
305         p_m_g_gsm_b_fd.fd = p_m_g_gsm_b_sock;
306         register_fd(&p_m_g_gsm_b_fd, LCR_FD_READ, b_handler, this, 0);
307
308
309         /* bind socket to bchannel */
310         addr.family = AF_ISDN;
311         addr.dev = mISDNloop.port;
312         addr.channel = index+1+(index>15);
313         ret = bind(p_m_g_gsm_b_sock, (struct sockaddr *)&addr, sizeof(addr));
314         if (ret < 0) {
315                 PERROR("Failed to bind bchannel-socket for index %d\n", index);
316                 bchannel_close();
317                 return(ret);
318         }
319         /* activate bchannel */
320         PDEBUG(DEBUG_GSM, "Activating GSM side channel index %i.\n", index);
321         act.prim = PH_ACTIVATE_REQ; 
322         act.id = 0;
323         ret = sendto(p_m_g_gsm_b_sock, &act, MISDN_HEADER_LEN, 0, NULL, 0);
324         if (ret < 0) {
325                 PERROR("Failed to activate index %d\n", index);
326                 bchannel_close();
327                 return(ret);
328         }
329
330         p_m_g_gsm_b_index = index;
331
332         return(0);
333 }
334
335 /* receive from bchannel */
336 void Pgsm::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len)
337 {
338         unsigned char frame[33];
339
340         /* encoder init failed */
341         if (!p_m_g_encoder)
342                 return;
343
344         /* (currently) not connected, so don't flood tch! */
345         if (!p_m_g_tch_connected)
346                 return;
347
348         /* write to rx buffer */
349         while(len--) {
350                 p_m_g_rxdata[p_m_g_rxpos++] = audio_law_to_s32[*data++];
351                 if (p_m_g_rxpos == 160) {
352                         p_m_g_rxpos = 0;
353
354                         /* encode data */
355                         gsm_audio_encode(p_m_g_encoder, p_m_g_rxdata, frame);
356                         frame_send(frame);
357                 }
358         }
359 }
360
361 /* transmit to bchannel */
362 void Pgsm::bchannel_send(unsigned int prim, unsigned int id, unsigned char *data, int len)
363 {
364         unsigned char buf[MISDN_HEADER_LEN+len];
365         struct mISDNhead *hh = (struct mISDNhead *)buf;
366         int ret;
367
368         if (!p_m_g_gsm_b_active)
369                 return;
370
371         /* make and send frame */
372         hh->prim = PH_DATA_REQ;
373         hh->id = 0;
374         memcpy(buf+MISDN_HEADER_LEN, data, len);
375         ret = sendto(p_m_g_gsm_b_sock, buf, MISDN_HEADER_LEN+len, 0, NULL, 0);
376         if (ret <= 0)
377                 PERROR("Failed to send to socket index %d\n", p_m_g_gsm_b_index);
378 }
379
380 void Pgsm::frame_send(void *_frame)
381 {
382         unsigned char buffer[sizeof(struct gsm_data_frame) + 33];
383         struct gsm_data_frame *frame = (struct gsm_data_frame *)buffer;
384         
385         frame->msg_type = GSM_TCHF_FRAME;
386         frame->callref = p_m_g_callref;
387         memcpy(frame->data, _frame, 33);
388 #ifdef WITH_GSM_BS
389         mncc_send((struct gsm_network *)p_m_g_instance, frame->msg_type, frame);
390 #endif
391 #ifdef WITH_GSM_MS
392         mncc_send((struct osmocom_ms *)p_m_g_instance, frame->msg_type, frame);
393 #endif
394 }
395
396
397 void Pgsm::frame_receive(void *arg)
398 {
399         struct gsm_data_frame *frame = (struct gsm_data_frame *)arg;
400         signed short samples[160];
401         unsigned char data[160];
402         int i;
403
404         if (!p_m_g_decoder)
405                 return;
406
407         if ((frame->data[0]>>4) != 0xd)
408                 PERROR("received GSM frame with wrong magig 0x%x\n", frame->data[0]>>4);
409         
410         /* decode */
411         gsm_audio_decode(p_m_g_decoder, frame->data, samples);
412         for (i = 0; i < 160; i++) {
413                 data[i] = audio_s16_to_law[samples[i] & 0xffff];
414         }
415
416         /* send */
417         bchannel_send(PH_DATA_REQ, 0, data, 160);
418 }
419
420
421 /*
422  * create trace
423  */
424 void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned int msg_type, int direction)
425 {
426         char msgtext[64];
427
428         /* select message and primitive text */
429         SCPY(msgtext, mncc_name(msg_type));
430
431         /* add direction */
432         if (port) {
433                 switch(port->p_type) {
434                 case PORT_TYPE_GSM_BS_OUT:
435                         SCAT(msgtext, " LCR->BSC");
436                         break;
437                 case PORT_TYPE_GSM_BS_IN:
438                         SCAT(msgtext, " LCR<-BSC");
439                         break;
440                 case PORT_TYPE_GSM_MS_OUT:
441                         SCAT(msgtext, " LCR->MS");
442                         break;
443                 case PORT_TYPE_GSM_MS_IN:
444                         SCAT(msgtext, " LCR<-MS");
445                         break;
446                 }
447         } else
448                 SCAT(msgtext, " ----");
449
450         /* init trace with given values */
451         start_trace(mISDNport?mISDNport->portnum:-1,
452                     mISDNport?(mISDNport->ifport?mISDNport->ifport->interface:NULL):NULL,
453                     port?numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype, options.national, options.international):NULL,
454                     port?port->p_dialinginfo.id:NULL,
455                     direction,
456                     CATEGORY_CH,
457                     port?port->p_serial:0,
458                     msgtext);
459 }
460
461 /* select free bchannel from loopback interface */
462 int Pgsm::hunt_bchannel(void)
463 {
464         return loop_hunt_bchannel(this, p_m_mISDNport);
465 }
466
467 /* PROCEEDING INDICATION */
468 void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
469 {
470         struct gsm_mncc *mode;
471
472         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
473         if (mncc->fields & MNCC_F_CAUSE) {
474                 add_trace("cause", "coding", "%d", mncc->cause.coding);
475                 add_trace("cause", "location", "%", mncc->cause.location);
476                 add_trace("cause", "value", "%", mncc->cause.value);
477         }
478         end_trace();
479
480         /* modify lchan to GSM codec V1 */
481         gsm_trace_header(p_m_mISDNport, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT);
482         mode = create_mncc(MNCC_LCHAN_MODIFY, p_m_g_callref);
483         mode->lchan_mode = 0x01; /* GSM V1 */
484         add_trace("mode", NULL, "0x%02x", mode->lchan_mode);
485         end_trace();
486         send_and_free_mncc(p_m_g_instance, mode->msg_type, mode);
487
488 }
489
490 /* CALL PROCEEDING INDICATION */
491 void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
492 {
493         struct lcr_msg *message;
494         struct gsm_mncc *frame;
495
496         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
497         end_trace();
498
499         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING);
500         message_put(message);
501
502         new_state(PORT_STATE_OUT_PROCEEDING);
503
504         if (p_m_mISDNport->earlyb && !p_m_g_tch_connected) { /* only if ... */
505                 gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT);
506                 end_trace();
507                 frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref);
508                 send_and_free_mncc(p_m_g_instance, frame->msg_type, frame);
509                 p_m_g_tch_connected = 1;
510         }
511 }
512
513 /* ALERTING INDICATION */
514 void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
515 {
516         struct lcr_msg *message;
517         struct gsm_mncc *frame;
518
519         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
520         end_trace();
521
522         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING);
523         message_put(message);
524
525         new_state(PORT_STATE_OUT_ALERTING);
526
527         if (p_m_mISDNport->earlyb && !p_m_g_tch_connected) { /* only if ... */
528                 gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT);
529                 end_trace();
530                 frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref);
531                 send_and_free_mncc(p_m_g_instance, frame->msg_type, frame);
532                 p_m_g_tch_connected = 1;
533         }
534 }
535
536 /* CONNECT INDICATION */
537 void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
538 {
539         struct gsm_mncc *resp, *frame;
540         struct lcr_msg *message;
541
542         SCPY(p_connectinfo.id, mncc->connected.number);
543         SCPY(p_connectinfo.imsi, mncc->imsi);
544         p_connectinfo.present = INFO_PRESENT_ALLOWED;
545         p_connectinfo.screen = INFO_SCREEN_NETWORK;
546         p_connectinfo.ntype = INFO_NTYPE_UNKNOWN;
547         p_connectinfo.isdn_port = p_m_portnum;
548         SCPY(p_connectinfo.interface, p_m_mISDNport->ifport->interface->name);
549
550         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
551         if (p_connectinfo.id[0])
552                 add_trace("connect", "number", "%s", p_connectinfo.id);
553         else if (mncc->imsi[0])
554                 SPRINT(p_connectinfo.id, "imsi-%s", p_connectinfo.imsi);
555         if (mncc->imsi[0])
556                 add_trace("connect", "imsi", "%s", p_connectinfo.imsi);
557         end_trace();
558
559         /* send resp */
560         gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_COMPL_REQ, DIRECTION_OUT);
561         resp = create_mncc(MNCC_SETUP_COMPL_REQ, p_m_g_callref);
562         end_trace();
563         send_and_free_mncc(p_m_g_instance, resp->msg_type, resp);
564
565         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
566         memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
567         message_put(message);
568
569         new_state(PORT_STATE_CONNECT);
570
571         if (!p_m_g_tch_connected) { /* only if ... */
572                 gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT);
573                 end_trace();
574                 frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref);
575                 send_and_free_mncc(p_m_g_instance, frame->msg_type, frame);
576                 p_m_g_tch_connected = 1;
577         }
578 }
579
580 /* CONNECT ACK INDICATION */
581 void Pgsm::setup_compl_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
582 {
583         struct gsm_mncc *frame;
584
585         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
586         end_trace();
587
588         new_state(PORT_STATE_CONNECT);
589
590         if (!p_m_g_tch_connected) { /* only if ... */
591                 gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT);
592                 end_trace();
593                 frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref);
594                 send_and_free_mncc(p_m_g_instance, frame->msg_type, frame);
595                 p_m_g_tch_connected = 1;
596         }
597 }
598
599 /* DISCONNECT INDICATION */
600 void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
601 {
602         struct lcr_msg *message;
603         int cause = 16, location = 0;
604         struct gsm_mncc *resp;
605
606         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
607         if (mncc->fields & MNCC_F_CAUSE) {
608                 location = mncc->cause.location;
609                 cause = mncc->cause.value;
610                 add_trace("cause", "coding", "%d", mncc->cause.coding);
611                 add_trace("cause", "location", "%d", location);
612                 add_trace("cause", "value", "%d", cause);
613         }
614         end_trace();
615
616         /* send release */
617         resp = create_mncc(MNCC_REL_REQ, p_m_g_callref);
618         gsm_trace_header(p_m_mISDNport, this, MNCC_REL_REQ, DIRECTION_OUT);
619 #if 0
620         resp->fields |= MNCC_F_CAUSE;
621         resp->cause.coding = 3;
622         resp->cause.location = 1;
623         resp->cause.value = cause;
624         add_trace("cause", "coding", "%d", resp->cause.coding);
625         add_trace("cause", "location", "%d", resp->cause.location);
626         add_trace("cause", "value", "%d", resp->cause.value);
627 #endif
628         end_trace();
629         send_and_free_mncc(p_m_g_instance, resp->msg_type, resp);
630
631         /* sending release to endpoint */
632         while(p_epointlist) {
633                 message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
634                 message->param.disconnectinfo.cause = cause;
635                 message->param.disconnectinfo.location = location;
636                 message_put(message);
637                 /* remove epoint */
638                 free_epointlist(p_epointlist);
639         }
640         new_state(PORT_STATE_RELEASE);
641         trigger_work(&p_m_g_delete);
642 }
643
644 /* CC_RELEASE INDICATION */
645 void Pgsm::rel_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
646 {
647         int location = 0, cause = 16;
648         struct lcr_msg *message;
649
650         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
651         if (mncc->fields & MNCC_F_CAUSE) {
652                 location = mncc->cause.location;
653                 cause = mncc->cause.value;
654                 add_trace("cause", "coding", "%d", mncc->cause.coding);
655                 add_trace("cause", "location", "%d", mncc->cause.location);
656                 add_trace("cause", "value", "%d", mncc->cause.value);
657         }
658         end_trace();
659
660         /* sending release to endpoint */
661         while(p_epointlist) {
662                 message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
663                 message->param.disconnectinfo.cause = cause;
664                 message->param.disconnectinfo.location = location;
665                 message_put(message);
666                 /* remove epoint */
667                 free_epointlist(p_epointlist);
668         }
669         new_state(PORT_STATE_RELEASE);
670         trigger_work(&p_m_g_delete);
671 }
672
673 /* NOTIFY INDICATION */
674 void Pgsm::notify_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
675 {
676         struct lcr_msg *message;
677
678         gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
679         add_trace("notify", NULL, "%d", mncc->notify);
680         end_trace();
681
682         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
683         message->param.notifyinfo.notify = mncc->notify;
684         message_put(message);
685 }
686
687 /* MESSAGE_NOTIFY */
688 void Pgsm::message_notify(unsigned int epoint_id, int message_id, union parameter *param)
689 {
690         struct gsm_mncc *mncc;
691         int notify;
692
693 //      printf("if = %d\n", param->notifyinfo.notify);
694         if (param->notifyinfo.notify>INFO_NOTIFY_NONE) {
695                 notify = param->notifyinfo.notify & 0x7f;
696                 if (p_state!=PORT_STATE_CONNECT /*&& p_state!=PORT_STATE_IN_PROCEEDING*/ && p_state!=PORT_STATE_IN_ALERTING) {
697                         /* queue notification */
698                         if (p_m_g_notify_pending)
699                                 message_free(p_m_g_notify_pending);
700                         p_m_g_notify_pending = message_create(ACTIVE_EPOINT(p_epointlist), p_serial, EPOINT_TO_PORT, message_id);
701                         memcpy(&p_m_g_notify_pending->param, param, sizeof(union parameter));
702                 } else {
703                         /* sending notification */
704                         gsm_trace_header(p_m_mISDNport, this, MNCC_NOTIFY_REQ, DIRECTION_OUT);
705                         add_trace("notify", NULL, "%d", notify);
706                         end_trace();
707                         mncc = create_mncc(MNCC_NOTIFY_REQ, p_m_g_callref);
708                         mncc->notify = notify;
709                         send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
710                 }
711         }
712 }
713
714 /* MESSAGE_ALERTING */
715 void Pgsm::message_alerting(unsigned int epoint_id, int message_id, union parameter *param)
716 {
717         struct gsm_mncc *mncc;
718
719         /* send alert */
720         gsm_trace_header(p_m_mISDNport, this, MNCC_ALERT_REQ, DIRECTION_OUT);
721         mncc = create_mncc(MNCC_ALERT_REQ, p_m_g_callref);
722         if (p_m_mISDNport->tones) {
723                 mncc->fields |= MNCC_F_PROGRESS;
724                 mncc->progress.coding = 3; /* GSM */
725                 mncc->progress.location = 1;
726                 mncc->progress.descr = 8;
727                 add_trace("progress", "coding", "%d", mncc->progress.coding);
728                 add_trace("progress", "location", "%d", mncc->progress.location);
729                 add_trace("progress", "descr", "%d", mncc->progress.descr);
730         }
731         end_trace();
732         send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
733
734         new_state(PORT_STATE_IN_ALERTING);
735
736         if (p_m_mISDNport->tones && !p_m_g_tch_connected) { /* only if ... */
737                 gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT);
738                 end_trace();
739                 mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref);
740                 send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
741                 p_m_g_tch_connected = 1;
742         }
743 }
744
745 /* MESSAGE_CONNECT */
746 void Pgsm::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
747 {
748         struct gsm_mncc *mncc;
749
750         /* copy connected information */
751         memcpy(&p_connectinfo, &param->connectinfo, sizeof(p_connectinfo));
752         /* screen outgoing caller id */
753         do_screen(1, p_connectinfo.id, sizeof(p_connectinfo.id), &p_connectinfo.ntype, &p_connectinfo.present, p_m_mISDNport->ifport->interface);
754
755         /* send connect */
756         mncc = create_mncc(MNCC_SETUP_RSP, p_m_g_callref);
757         gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_RSP, DIRECTION_OUT);
758         /* caller information */
759         mncc->fields |= MNCC_F_CONNECTED;
760         mncc->connected.plan = 1;
761         switch (p_callerinfo.ntype) {
762                 case INFO_NTYPE_UNKNOWN:
763                 mncc->connected.type = 0x0;
764                 break;
765                 case INFO_NTYPE_INTERNATIONAL:
766                 mncc->connected.type = 0x1;
767                 break;
768                 case INFO_NTYPE_NATIONAL:
769                 mncc->connected.type = 0x2;
770                 break;
771                 case INFO_NTYPE_SUBSCRIBER:
772                 mncc->connected.type = 0x4;
773                 break;
774                 default: /* INFO_NTYPE_NOTPRESENT */
775                 mncc->fields &= ~MNCC_F_CONNECTED;
776                 break;
777         }
778         switch (p_callerinfo.screen) {
779                 case INFO_SCREEN_USER:
780                 mncc->connected.screen = 0;
781                 break;
782                 default: /* INFO_SCREEN_NETWORK */
783                 mncc->connected.screen = 3;
784                 break;
785         }
786         switch (p_callerinfo.present) {
787                 case INFO_PRESENT_ALLOWED:
788                 mncc->connected.present = 0;
789                 break;
790                 case INFO_PRESENT_RESTRICTED:
791                 mncc->connected.present = 1;
792                 break;
793                 default: /* INFO_PRESENT_NOTAVAIL */
794                 mncc->connected.present = 2;
795                 break;
796         }
797         if (mncc->fields & MNCC_F_CONNECTED) {
798                 SCPY(mncc->connected.number, p_connectinfo.id);
799                 add_trace("connected", "type", "%d", mncc->connected.type);
800                 add_trace("connected", "plan", "%d", mncc->connected.plan);
801                 add_trace("connected", "present", "%d", mncc->connected.present);
802                 add_trace("connected", "screen", "%d", mncc->connected.screen);
803                 add_trace("connected", "number", "%s", mncc->connected.number);
804         }
805         end_trace();
806         send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
807
808         new_state(PORT_STATE_CONNECT_WAITING);
809 }
810
811 /* MESSAGE_DISCONNECT */
812 void Pgsm::message_disconnect(unsigned int epoint_id, int message_id, union parameter *param)
813 {
814         struct gsm_mncc *mncc;
815
816         /* send disconnect */
817         mncc = create_mncc(MNCC_DISC_REQ, p_m_g_callref);
818         gsm_trace_header(p_m_mISDNport, this, MNCC_DISC_REQ, DIRECTION_OUT);
819         if (p_m_mISDNport->tones) {
820                 mncc->fields |= MNCC_F_PROGRESS;
821                 mncc->progress.coding = 3; /* GSM */
822                 mncc->progress.location = 1;
823                 mncc->progress.descr = 8;
824                 add_trace("progress", "coding", "%d", mncc->progress.coding);
825                 add_trace("progress", "location", "%d", mncc->progress.location);
826                 add_trace("progress", "descr", "%d", mncc->progress.descr);
827         }
828         mncc->fields |= MNCC_F_CAUSE;
829         mncc->cause.coding = 3;
830         mncc->cause.location = param->disconnectinfo.location;
831         mncc->cause.value = param->disconnectinfo.cause;
832         add_trace("cause", "coding", "%d", mncc->cause.coding);
833         add_trace("cause", "location", "%d", mncc->cause.location);
834         add_trace("cause", "value", "%d", mncc->cause.value);
835         end_trace();
836         send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
837
838         new_state(PORT_STATE_OUT_DISCONNECT);
839
840         if (p_m_mISDNport->tones && !p_m_g_tch_connected) { /* only if ... */
841                 gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT);
842                 end_trace();
843                 mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref);
844                 send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
845                 p_m_g_tch_connected = 1;
846         }
847 }
848
849
850 /* MESSAGE_RELEASE */
851 void Pgsm::message_release(unsigned int epoint_id, int message_id, union parameter *param)
852 {
853         struct gsm_mncc *mncc;
854
855         /* send release */
856         mncc = create_mncc(MNCC_REL_REQ, p_m_g_callref);
857         gsm_trace_header(p_m_mISDNport, this, MNCC_REL_REQ, DIRECTION_OUT);
858         mncc->fields |= MNCC_F_CAUSE;
859         mncc->cause.coding = 3;
860         mncc->cause.location = param->disconnectinfo.location;
861         mncc->cause.value = param->disconnectinfo.cause;
862         add_trace("cause", "coding", "%d", mncc->cause.coding);
863         add_trace("cause", "location", "%d", mncc->cause.location);
864         add_trace("cause", "value", "%d", mncc->cause.value);
865         end_trace();
866         send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc);
867
868         new_state(PORT_STATE_RELEASE);
869         trigger_work(&p_m_g_delete);
870         return;
871 }
872
873 /*
874  * endpoint sends messages to the port
875  */
876 int Pgsm::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
877 {
878         if (PmISDN::message_epoint(epoint_id, message_id, param))
879                 return(1);
880
881         switch(message_id) {
882                 case MESSAGE_NOTIFY: /* display and notifications */
883                 message_notify(epoint_id, message_id, param);
884                 break;
885
886 //              case MESSAGE_FACILITY: /* facility message */
887 //              message_facility(epoint_id, message_id, param);
888 //              break;
889
890                 case MESSAGE_PROCEEDING: /* message not handles */
891                 break;
892
893                 case MESSAGE_ALERTING: /* call of endpoint is ringing */
894                 if (p_state!=PORT_STATE_IN_PROCEEDING)
895                         break;
896                 message_alerting(epoint_id, message_id, param);
897                 if (p_m_g_notify_pending) {
898                         /* send pending notify message during connect */
899                         message_notify(ACTIVE_EPOINT(p_epointlist), p_m_g_notify_pending->type, &p_m_g_notify_pending->param);
900                         message_free(p_m_g_notify_pending);
901                         p_m_g_notify_pending = NULL;
902                 }
903                 break;
904
905                 case MESSAGE_CONNECT: /* call of endpoint is connected */
906                 if (p_state!=PORT_STATE_IN_PROCEEDING
907                  && p_state!=PORT_STATE_IN_ALERTING)
908                         break;
909                 message_connect(epoint_id, message_id, param);
910                 if (p_m_g_notify_pending) {
911                         /* send pending notify message during connect */
912                         message_notify(ACTIVE_EPOINT(p_epointlist), p_m_g_notify_pending->type, &p_m_g_notify_pending->param);
913                         message_free(p_m_g_notify_pending);
914                         p_m_g_notify_pending = NULL;
915                 }
916                 break;
917
918                 case MESSAGE_DISCONNECT: /* call has been disconnected */
919                 if (p_state!=PORT_STATE_IN_PROCEEDING
920                  && p_state!=PORT_STATE_IN_ALERTING
921                  && p_state!=PORT_STATE_OUT_SETUP
922                  && p_state!=PORT_STATE_OUT_OVERLAP
923                  && p_state!=PORT_STATE_OUT_PROCEEDING
924                  && p_state!=PORT_STATE_OUT_ALERTING
925                  && p_state!=PORT_STATE_CONNECT
926                  && p_state!=PORT_STATE_CONNECT_WAITING)
927                         break;
928                 message_disconnect(epoint_id, message_id, param);
929                 break;
930
931                 case MESSAGE_RELEASE: /* release isdn port */
932                 if (p_state==PORT_STATE_RELEASE)
933                         break;
934                 message_release(epoint_id, message_id, param);
935                 break;
936
937         }
938
939         return(0);
940 }
941
942 /* deletes only if l3id is release, otherwhise it will be triggered then */
943 static int delete_event(struct lcr_work *work, void *instance, int index)
944 {
945         class Pgsm *gsmport = (class Pgsm *)instance;
946
947         delete gsmport;
948
949         return 0;
950 }
951
952 /*
953  * handler of bchannel events
954  */
955 static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index)
956 {
957         class Pgsm *gsmport = (class Pgsm *)instance;
958         int ret;
959         unsigned char buffer[2048+MISDN_HEADER_LEN];
960         struct mISDNhead *hh = (struct mISDNhead *)buffer;
961
962         /* handle message from bchannel */
963         if (gsmport->p_m_g_gsm_b_sock > -1) {
964                 ret = recv(gsmport->p_m_g_gsm_b_sock, buffer, sizeof(buffer), 0);
965                 if (ret >= (int)MISDN_HEADER_LEN) {
966                         switch(hh->prim) {
967                                 /* we don't care about confirms, we use rx data to sync tx */
968                                 case PH_DATA_CNF:
969                                 break;
970                                 /* we receive audio data, we respond to it AND we send tones */
971                                 case PH_DATA_IND:
972                                 gsmport->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN);
973                                 break;
974                                 case PH_ACTIVATE_IND:
975                                 gsmport->p_m_g_gsm_b_active = 1;
976                                 break;
977                                 case PH_DEACTIVATE_IND:
978                                 gsmport->p_m_g_gsm_b_active = 0;
979                                 break;
980                         }
981                 } else {
982                         if (ret < 0 && errno != EWOULDBLOCK)
983                                 PERROR("Read from GSM port, index %d failed with return code %d\n", ret);
984                 }
985         }
986
987         return 0;
988 }
989
990 int gsm_exit(int rc)
991 {
992         /* free gsm instance */
993         if (gsm) {
994                 free(gsm);
995                 gsm = NULL;
996         }
997
998         return(rc);
999 }
1000
1001 int gsm_init(void)
1002 {
1003         /* seed the PRNG */
1004         srand(time(NULL));
1005
1006         /* create gsm instance */
1007         gsm = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm));
1008
1009         return 0;
1010 }
1011
1012 int handle_gsm(void)
1013 {
1014         return 0;
1015 }
1016