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