Data-Over-Voice
[lcr.git] / fxs.cpp
1 /*****************************************************************************\
2 **                                                                           **
3 ** LCR                                                                       **
4 **                                                                           **
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg                                              **
7 **                                                                           **
8 ** mISDN fxs                                                                 **
9 **                                                                           **
10 \*****************************************************************************/ 
11
12 /* Procedures:
13
14
15 *****TBD: THIS TEXT IS OLD ******
16
17 off-hook indication:
18         no port instance:
19                 Create port/endpoint instance and send SETUP message to endpoint.
20         port instance active:
21                 Send CONNECT message to endpoint.
22         port instance inactive:
23                 Put inactive port instance active. Send RETRIEVE message to endpoint.
24 on-hook indication:
25         Release active port instance. Send RELEASE message to endpoint if exists.
26         inactive port instance:
27                 Send ring request. Use caller ID on incomming call or connected ID on outgoing call.
28 hookflash indication:
29         active port instance not connected:
30                 Release active port instance. Send RELEASE message to endpoint if exists.
31         active port instance connected:
32                 Put active port instance inactive. Send HOLD MESSAGE to endpoint.
33         inactive port instance:
34                 Put inactive port instance active. Send RETRIEVE message to endpoint.
35         no inactive port instance:
36                 Create port/endpoint instance and send SETUP message to endpoint.
37 keypulse indication:
38         active port instance in incomming overlap state:
39                 Send INFORMATION message to endpoint.
40         active port instance in other state:
41                 Send KEYPAD message to endpoint, if exists.
42 SETUP message:
43         no instance:
44                 Create port instance and send ALERTING message to endpoint.
45                 Send ring request. Use caller ID.
46         only one instance active:
47                 Create port instance and send ALERTING message to endpoint.
48                 Send knock sound. Send ALERTING message to endpoint.
49         one instance on hold:
50                 Send RELEASE message (cause = BUSY) to endpoint.
51 PROCEEDING / ALERTING / CONNECT message:
52         (change state only)
53 DISCONNECT message:
54         is inactive port instance:
55                 Release port instance. Send RELEASE message to endpoint.
56 RELEASE message:
57         is active port instance:
58                 Create hangup tone (release tone)
59         is inactive port instance:
60                 Release port instance.
61 */
62
63 #include "main.h"
64 #include "myisdn.h"
65 // socket mISDN
66 //#include <sys/socket.h>
67 extern "C" {
68 }
69
70 #ifdef ISDN_P_FXS_POTS
71
72 static int fxs_age = 0;
73
74 static int delete_event(struct lcr_work *work, void *instance, int index);
75
76 static int dtmf_timeout(struct lcr_timer *timer, void *instance, int index)
77 {
78         class Pfxs *pfxs = (class Pfxs *)instance;
79
80         /* allow DTMF dialing now */
81         PDEBUG(DEBUG_ISDN, "%s: allow DTMF now\n", pfxs->p_name);
82         pfxs->p_m_fxs_allow_dtmf = 1;
83
84         return 0;
85 }
86
87 /*
88  * constructor
89  */
90 Pfxs::Pfxs(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, struct interface *interface, int mode) : PmISDN(type, mISDNport, portname, settings, interface, 0, 0, mode)
91 {
92         p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN;
93
94         memset(&p_m_fxs_delete, 0, sizeof(p_m_fxs_delete));
95         add_work(&p_m_fxs_delete, delete_event, this, 0);
96         p_m_fxs_allow_dtmf = 0;
97         memset(&p_m_fxs_dtmf_timer, 0, sizeof(p_m_fxs_dtmf_timer));
98         add_timer(&p_m_fxs_dtmf_timer, dtmf_timeout, this, 0);
99         p_m_fxs_age = fxs_age++;
100         p_m_fxs_knocking = 0;
101
102         PDEBUG(DEBUG_ISDN, "Created new FXSPort(%s). Currently %d objects use, FXS port #%d\n", portname, mISDNport->use, p_m_portnum);
103 }
104
105
106 /*
107  * destructor
108  */
109 Pfxs::~Pfxs()
110 {
111         del_timer(&p_m_fxs_dtmf_timer);
112         del_work(&p_m_fxs_delete);
113 }
114
115 /* deletes only if l3id is release, otherwhise it will be triggered then */
116 static int delete_event(struct lcr_work *work, void *instance, int index)
117 {
118         class Pfxs *pots = (class Pfxs *)instance;
119
120         delete pots;
121
122         return 0;
123 }
124
125
126 int Pfxs::hunt_bchannel(void)
127 {
128         if (p_m_mISDNport->b_num < 1)
129                 return -47;
130         if (p_m_mISDNport->b_port[0])
131                 return -17;
132         return 1;
133 }
134
135 int Pfxs::ph_control_pots(unsigned int cont, unsigned char *data, int len)
136 {
137         unsigned char buffer[MISDN_HEADER_LEN+sizeof(int)+len];
138         struct mISDNhead *ctrl = (struct mISDNhead *)buffer;
139         unsigned int *d = (unsigned int *)(buffer+MISDN_HEADER_LEN);
140         int ret;
141
142         ctrl->prim = PH_CONTROL_REQ;
143         ctrl->id = 0;
144         *d++ = cont;
145         if (len)
146                 memcpy(d, data, len);
147         ret = sendto(p_m_mISDNport->pots_sock.fd, buffer, MISDN_HEADER_LEN+sizeof(int)+len, 0, NULL, 0);
148         if (ret <= 0)
149                 PERROR("Failed to send to socket %d\n", p_m_mISDNport->pots_sock.fd);
150
151         return ret;
152 }
153
154 void Pfxs::pickup_ind(unsigned int cont)
155 {
156         struct interface *interface = p_m_mISDNport->ifport->interface;
157         class Endpoint *epoint;
158         struct lcr_msg *message;
159         int ret, channel;
160
161         p_m_fxs_age = fxs_age++;
162
163         if (p_m_fxs_knocking) {
164                 ph_control_pots(POTS_CW_OFF, NULL, 0);
165                 p_m_fxs_knocking = 0;
166         }
167
168         chan_trace_header(p_m_mISDNport, this, "PICKUP", DIRECTION_NONE);
169
170         if (interface->ifmsn && interface->ifmsn->msn[0]) {
171                 SCPY(p_callerinfo.id, interface->ifmsn->msn);
172                 add_trace("caller", "ID", "%s", p_callerinfo.id);
173         }
174         p_callerinfo.present = INFO_PRESENT_ALLOWED;
175         p_callerinfo.isdn_port = p_m_portnum;
176         SCPY(p_callerinfo.interface, p_m_mISDNport->ifport->interface->name);
177         p_capainfo.source_mode = B_MODE_TRANSPARENT;
178         p_capainfo.bearer_capa = INFO_BC_AUDIO;
179         p_capainfo.bearer_info1 = 0x80 + ((options.law=='a')?3:2);
180         p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
181
182         if ((cont & (~POTS_KP_MASK)) == POTS_KP_VAL) {
183                 p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
184                 p_dialinginfo.id[0] = cont & POTS_KP_MASK;
185         }
186
187         if (!p_m_b_channel) {
188                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) alloc bchannel\n", p_name);
189                 /* hunt bchannel */
190                 ret = channel = hunt_bchannel();
191                 if (ret < 0)
192                         goto no_channel;
193
194                 /* open channel */
195                 ret = seize_bchannel(channel, 1);
196                 if (ret < 0) {
197 no_channel:
198                         /*
199                          * NOTE: we send MT_RELEASE_COMPLETE to "REJECT" the channel
200                          * in response to the setup
201                          */
202                         add_trace("error", NULL, "no b-channel");
203                         end_trace();
204                         new_state(PORT_STATE_RELEASE);
205                         trigger_work(&p_m_fxs_delete);
206                         return;
207                 }
208                 end_trace();
209                 bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE);
210         } else {
211                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) bchannel is already active\n", p_name);
212         }
213
214         /* create endpoint */
215         if (p_epointlist)
216                 FATAL("Incoming call but already got an endpoint.\n");
217         if (!(epoint = new Endpoint(p_serial, 0)))
218                 FATAL("No memory for Endpoint instance\n");
219         epoint->ep_app = new_endpointapp(epoint, 0, p_m_mISDNport->ifport->interface->app); //incoming
220         epointlist_new(epoint->ep_serial);
221
222         /* indicate flash control */
223         if (cont == POTS_HOOK_FLASH || cont == POTS_EARTH_KEY)
224                 p_dialinginfo.flash = 1;
225
226         /* send setup message to endpoit */
227         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_SETUP);
228         message->param.setup.isdn_port = p_m_portnum;
229         message->param.setup.port_type = p_type;
230 //      message->param.setup.dtmf = !p_m_mISDNport->ifport->nodtmf;
231         memcpy(&message->param.setup.callerinfo, &p_callerinfo, sizeof(struct caller_info));
232         memcpy(&message->param.setup.dialinginfo, &p_dialinginfo, sizeof(struct dialing_info));
233         memcpy(&message->param.setup.capainfo, &p_capainfo, sizeof(struct capa_info));
234         message_put(message);
235
236         new_state(PORT_STATE_IN_OVERLAP);
237
238         schedule_timer(&p_m_fxs_dtmf_timer, 0, 500000);
239 }
240
241 void Pfxs::hangup_ind(unsigned int cont)
242 {
243         struct lcr_msg *message;
244
245         /* deactivate bchannel */
246         chan_trace_header(p_m_mISDNport, this, "HANGUP", DIRECTION_NONE);
247         end_trace();
248         drop_bchannel();
249         PDEBUG(DEBUG_ISDN, "Pfxs(%s) drop bchannel\n", p_name);
250
251         /* send release message, if not already */
252         if (p_epointlist) {
253                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
254                 message->param.disconnectinfo.cause = 16;
255                 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
256                 message_put(message);
257         }
258         new_state(PORT_STATE_RELEASE);
259         trigger_work(&p_m_fxs_delete);
260 }
261
262 void Pfxs::answer_ind(unsigned int cont)
263 {
264         struct lcr_msg *message;
265         int ret, channel;
266
267         if (p_m_fxs_knocking) {
268                 ph_control_pots(POTS_CW_OFF, NULL, 0);
269                 p_m_fxs_knocking = 0;
270         }
271
272         chan_trace_header(p_m_mISDNport, this, "ANSWER", DIRECTION_NONE);
273         if (!p_m_b_channel) {
274                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) alloc bchannel\n", p_name);
275                 /* hunt bchannel */
276                 ret = channel = hunt_bchannel();
277                 if (ret < 0)
278                         goto no_channel;
279
280                 /* open channel */
281                 ret = seize_bchannel(channel, 1);
282                 if (ret < 0) {
283 no_channel:
284                         /*
285                          * NOTE: we send MT_RELEASE_COMPLETE to "REJECT" the channel
286                          * in response to the setup
287                          */
288                         add_trace("error", NULL, "no b-channel");
289                         end_trace();
290                         new_state(PORT_STATE_RELEASE);
291                         trigger_work(&p_m_fxs_delete);
292                         return;
293                 }
294                 end_trace();
295                 bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE);
296         } else {
297                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) bchannel is already active\n", p_name);
298         }
299
300         if (p_m_hold) {
301                 p_m_hold = 0;
302                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
303                 message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL;
304                 message->param.notifyinfo.local = 1; /* call is held by supplementary service */
305                 message_put(message);
306         } else {
307                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
308                 message_put(message);
309         }
310
311         new_state(PORT_STATE_CONNECT);
312 }
313
314 void Pfxs::hold_ind(unsigned int cont)
315 {
316         struct lcr_msg *message;
317
318         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
319         message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_HOLD;
320         message->param.notifyinfo.local = 1; /* call is held by supplementary service */
321         message_put(message);
322
323         /* deactivate bchannel */
324         chan_trace_header(p_m_mISDNport, this, "HOLD", DIRECTION_NONE);
325         end_trace();
326         drop_bchannel();
327         PDEBUG(DEBUG_ISDN, "Pfxs(%s) drop bchannel\n", p_name);
328
329         p_m_hold = 1;
330 }
331
332 void Pfxs::retrieve_ind(unsigned int cont)
333 {
334         struct lcr_msg *message;
335         int ret, channel;
336
337         p_m_fxs_age = fxs_age++;
338
339         if (p_m_fxs_knocking) {
340                 ph_control_pots(POTS_CW_OFF, NULL, 0);
341                 p_m_fxs_knocking = 0;
342         }
343
344         if (cont == POTS_ON_HOOK) {
345                 const char *callerid;
346
347                 if (p_state == PORT_STATE_CONNECT) {
348                         new_state(PORT_STATE_OUT_ALERTING);
349 #if 0
350                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING);
351                         message_put(message);
352 #endif
353                         chan_trace_header(p_m_mISDNport, this, "RING (retrieve)", DIRECTION_NONE);
354                 } else
355                         chan_trace_header(p_m_mISDNport, this, "RING (after knocking)", DIRECTION_NONE);
356                 if (p_type == PORT_TYPE_POTS_FXS_IN) {
357                         if (p_connectinfo.id[0]) {
358                                 callerid = numberrize_callerinfo(p_connectinfo.id, p_connectinfo.ntype, options.national, options.international);
359                                 add_trace("connect", "number", callerid);
360                         } else {
361                                 callerid = p_dialinginfo.id;
362                                 add_trace("dialing", "number", callerid);
363                         }
364                 } else {
365                         callerid = numberrize_callerinfo(p_callerinfo.id, p_callerinfo.ntype, options.national, options.international);
366                         add_trace("caller", "id", callerid);
367                 }
368                 ph_control_pots(POTS_RING_ON, (unsigned char *)callerid, strlen(callerid));
369                 end_trace();
370                 return;
371         }
372
373         chan_trace_header(p_m_mISDNport, this, "RETRIEVE", DIRECTION_NONE);
374         if (!p_m_b_channel) {
375                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) alloc bchannel\n", p_name);
376                 /* hunt bchannel */
377                 ret = channel = hunt_bchannel();
378                 if (ret < 0)
379                         goto no_channel;
380
381                 /* open channel */
382                 ret = seize_bchannel(channel, 1);
383                 if (ret < 0) {
384 no_channel:
385                         /*
386                          * NOTE: we send MT_RELEASE_COMPLETE to "REJECT" the channel
387                          * in response to the setup
388                          */
389                         add_trace("error", NULL, "no b-channel");
390                         end_trace();
391                         new_state(PORT_STATE_RELEASE);
392                         trigger_work(&p_m_fxs_delete);
393                         return;
394                 }
395                 end_trace();
396                 bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE);
397         } else {
398                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) bchannel is already active\n", p_name);
399         }
400
401         p_m_hold = 0;
402         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
403         message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL;
404         message->param.notifyinfo.local = 1; /* call is held by supplementary service */
405         message_put(message);
406 }
407
408 void Pfxs::keypulse_ind(unsigned int cont)
409 {
410         struct lcr_msg *message;
411
412         p_m_fxs_allow_dtmf = 0; /* disable DTMF from now on */
413         chan_trace_header(p_m_mISDNport, this, "PULSE", DIRECTION_NONE);
414         add_trace("KP", NULL, "%c", cont & DTMF_TONE_MASK);
415         end_trace();
416         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
417         message->param.information.id[0] = cont & POTS_KP_MASK;
418         PDEBUG(DEBUG_ISDN, "Pfxs(%s) PH_CONTROL INDICATION  KP digit '%c'\n", p_name, message->param.information.id[0]);
419         message_put(message);
420 }
421
422 void Pfxs::reject_ind(unsigned int cont)
423 {
424         struct lcr_msg *message;
425
426         if (p_m_fxs_knocking) {
427                 ph_control_pots(POTS_CW_OFF, NULL, 0);
428                 p_m_fxs_knocking = 0;
429         }
430
431         /* deactivate bchannel */
432         chan_trace_header(p_m_mISDNport, this, "REJECT", DIRECTION_NONE);
433         end_trace();
434         drop_bchannel();
435         PDEBUG(DEBUG_ISDN, "Pfxs(%s) drop bchannel\n", p_name);
436
437         /* send release message, if not already */
438         if (p_epointlist) {
439                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
440                 message->param.disconnectinfo.cause = 16;
441                 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
442                 message_put(message);
443         }
444         new_state(PORT_STATE_RELEASE);
445         trigger_work(&p_m_fxs_delete);
446 }
447
448
449 void Pfxs::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
450 {
451         struct lcr_msg *message;
452         class Port *port;
453         class Pfxs *pots;
454         struct epoint_list *epointlist;
455         const char *callerid;
456         int any_call = 0;
457
458         memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
459         memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
460
461         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
462         message_put(message);
463
464         new_state(PORT_STATE_OUT_ALERTING);
465
466         /* attach only if not already */
467         epointlist = p_epointlist;
468         while(epointlist) {
469                 if (epointlist->epoint_id == epoint_id)
470                         break;
471                 epointlist = epointlist->next;
472         }
473         if (!epointlist)
474                 epointlist_new(epoint_id);
475
476         /* find port in connected active state */
477         port = port_first;
478         while(port) {
479                 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
480                         pots = (class Pfxs *)port;
481                         if (pots->p_m_mISDNport == p_m_mISDNport) {
482                                 if (pots != this)
483                                         any_call = 1;
484                                 if (pots->p_state == PORT_STATE_CONNECT && !pots->p_m_hold)
485                                         break; // found
486                         }
487                 }
488                 port = port->next;
489         }
490
491         if (port) {
492                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) knock because there is an ongoing active call\n", p_name);
493                 chan_trace_header(p_m_mISDNport, this, "KNOCK", DIRECTION_NONE);
494                 callerid = numberrize_callerinfo(p_callerinfo.id, p_callerinfo.ntype, options.national, options.international);
495                 add_trace("caller", "id", callerid);
496                 end_trace();
497                 ph_control_pots(POTS_CW_ON, (unsigned char *)callerid, strlen(callerid));
498                 p_m_fxs_knocking = 1;
499                 return;
500         }
501         if (any_call) {
502                 /* reject call, because we have a call, but we are not connected */
503                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) reject because there is an ongoing and incomplete call\n", p_name);
504                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
505                 message->param.disconnectinfo.cause = 17; // busy
506                 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
507                 message_put(message);
508                 new_state(PORT_STATE_RELEASE);
509                 trigger_work(&p_m_fxs_delete);
510                 return;
511         }
512         PDEBUG(DEBUG_ISDN, "Pfxs(%s) ring because there is not calll\n", p_name);
513         chan_trace_header(p_m_mISDNport, this, "RING", DIRECTION_NONE);
514         callerid = numberrize_callerinfo(p_callerinfo.id, p_callerinfo.ntype, options.national, options.international);
515         add_trace("caller", "id", callerid);
516         end_trace();
517         ph_control_pots(POTS_RING_ON, (unsigned char *)callerid, strlen(callerid));
518 }
519
520 void Pfxs::message_proceeding(unsigned int epoint_id, int message_id, union parameter *param)
521 {
522         new_state(PORT_STATE_IN_PROCEEDING);
523 }
524
525 void Pfxs::message_alerting(unsigned int epoint_id, int message_id, union parameter *param)
526 {
527         new_state(PORT_STATE_IN_ALERTING);
528 }
529
530 void Pfxs::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
531 {
532         new_state(PORT_STATE_CONNECT);
533
534         memcpy(&p_connectinfo, &param->connectinfo, sizeof(struct connect_info));
535 }
536
537 void Pfxs::message_disconnect(unsigned int epoint_id, int message_id, union parameter *param)
538 {
539         if (p_state == PORT_STATE_OUT_ALERTING) {
540                 if (p_m_fxs_knocking) {
541                         ph_control_pots(POTS_CW_OFF, NULL, 0);
542                         p_m_fxs_knocking = 0;
543                 } else {
544                         ph_control_pots(POTS_RING_OFF, NULL, 0);
545                 }
546                 if (p_epointlist) {
547                         struct lcr_msg *message;
548                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
549                         message->param.disconnectinfo.cause = 16;
550                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
551                         message_put(message);
552                 }
553                 free_epointid(epoint_id);
554                 new_state(PORT_STATE_RELEASE);
555                 trigger_work(&p_m_fxs_delete);
556                 return;
557         }
558
559         new_state(PORT_STATE_OUT_DISCONNECT);
560 }
561
562 void Pfxs::message_release(unsigned int epoint_id, int message_id, union parameter *param)
563 {
564         chan_trace_header(p_m_mISDNport, this, "CLEAR", DIRECTION_NONE);
565         end_trace();
566
567         if (p_state == PORT_STATE_OUT_ALERTING) {
568                 if (p_m_fxs_knocking) {
569                         ph_control_pots(POTS_CW_OFF, NULL, 0);
570                         p_m_fxs_knocking = 0;
571                 } else {
572                         ph_control_pots(POTS_RING_OFF, NULL, 0);
573                 }
574                 trigger_work(&p_m_fxs_delete);
575         }
576         if (p_state == PORT_STATE_CONNECT) {
577                 if (!p_m_hold)
578                         set_tone("", "release");
579                 else
580                         trigger_work(&p_m_fxs_delete);
581         }
582
583         new_state(PORT_STATE_RELEASE);
584
585         free_epointid(epoint_id);
586 }
587
588 /*
589  * endpoint sends messages to the port
590  */
591 int Pfxs::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
592 {
593         if (PmISDN::message_epoint(epoint_id, message_id, param))
594                 return(1);
595
596         switch(message_id) {
597                 case MESSAGE_SETUP: /* dial-out command received from epoint */
598                 message_setup(epoint_id, message_id, param);
599                 break;
600
601                 case MESSAGE_PROCEEDING: /* call of endpoint is proceeding */
602                 message_proceeding(epoint_id, message_id, param);
603                 break;
604
605                 case MESSAGE_ALERTING: /* call of endpoint is ringing */
606                 message_alerting(epoint_id, message_id, param);
607                 break;
608
609                 case MESSAGE_CONNECT: /* call of endpoint is connected */
610                 message_connect(epoint_id, message_id, param);
611                 break;
612
613                 case MESSAGE_DISCONNECT: /* call has been disconnected */
614                 message_disconnect(epoint_id, message_id, param);
615                 break;
616
617                 case MESSAGE_RELEASE: /* release isdn port */
618                 if (p_state==PORT_STATE_RELEASE) {
619                         break;
620                 }
621                 message_release(epoint_id, message_id, param);
622                 break;
623         }
624
625         return(1);
626 }
627
628 /*
629  * data from isdn-stack (layer-1) to pbx (port class)
630  */
631 int stack2manager_fxs(struct mISDNport *mISDNport, unsigned int cont)
632 {
633         class Port *port;
634         class Pfxs *pots, *latest_pots = NULL, *alerting_pots = NULL;
635         int latest = -1;
636         char name[32];
637
638         PDEBUG(DEBUG_ISDN, "cont(0x%x)\n", cont);
639
640         if ((cont & (~POTS_KP_MASK)) == POTS_KP_VAL) {
641                 /* find port in dialing state */
642                 port = port_first;
643                 while(port) {
644                         if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
645                                 pots = (class Pfxs *)port;
646                                 if (pots->p_m_mISDNport == mISDNport
647                                  && pots->p_state == PORT_STATE_IN_OVERLAP)
648                                         break; // found
649                         }
650                         port = port->next;
651                 }
652                 if (port) {
653                         pots->keypulse_ind(cont);
654                         return 0;
655                 }
656                 goto flash;
657         }
658
659         switch (cont) {
660         case POTS_OFF_HOOK:
661                 /* find ringing */
662                 port = port_first;
663                 while(port) {
664                         if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
665                                 pots = (class Pfxs *)port;
666                                 if (pots->p_m_mISDNport == mISDNport
667                                  && pots->p_state == PORT_STATE_OUT_ALERTING)
668                                         break; // found
669                         }
670                         port = port->next;
671                 }
672                 if (port) {
673                         pots->answer_ind(cont);
674                         break;
675                 }
676
677 setup:
678                 /* creating port object */
679                 SPRINT(name, "%s-%d-in", mISDNport->ifport->interface->name, mISDNport->portnum);
680                 pots = new Pfxs(PORT_TYPE_POTS_FXS_IN, mISDNport, name, NULL, mISDNport->ifport->interface, B_MODE_TRANSPARENT);
681                 if (!pots)
682                         FATAL("Failed to create Port instance\n");
683                 pots->pickup_ind(cont);
684                 break;
685
686         case POTS_ON_HOOK:
687                 if (mISDNport->ifport->pots_transfer) {
688                         struct lcr_msg *message;
689                         class Pfxs *pots1 = NULL, *pots2 = NULL;
690                         int count = 0;
691                         port = port_first;
692                         while(port) {
693                                 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
694                                         pots = (class Pfxs *)port;
695                                         if (pots->p_m_mISDNport == mISDNport) {
696                                                 if (pots->p_state == PORT_STATE_CONNECT
697                                                  || pots->p_state == PORT_STATE_IN_PROCEEDING
698                                                  || pots->p_state == PORT_STATE_IN_ALERTING) {
699                                                         if (count == 0)
700                                                                 pots1 = pots;
701                                                         if (count == 1)
702                                                                 pots2 = pots;
703                                                         count++;
704                                                 }
705                                         }
706                                 }
707                                 port = port->next;
708                         }
709
710                         if (count == 2) {
711                                 if (pots1->p_state == PORT_STATE_CONNECT) {
712                                         message = message_create(pots1->p_serial, ACTIVE_EPOINT(pots1->p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
713                                         message_put(message);
714                                 }
715                                 else if (pots2->p_state == PORT_STATE_CONNECT) {
716                                         message = message_create(pots2->p_serial, ACTIVE_EPOINT(pots2->p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
717                                         message_put(message);
718                                 }
719                                 pots1->hangup_ind(cont);
720                                 pots2->hangup_ind(cont);
721                                 break;
722                         }
723                 }
724                 if (mISDNport->ifport->pots_ring) {
725                         /* release all except calls on hold, let the latest call ring */
726                         port = port_first;
727                         while(port) {
728                                 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
729                                         pots = (class Pfxs *)port;
730                                         if (pots->p_m_mISDNport == mISDNport) {
731                                                 if (pots->p_state == PORT_STATE_CONNECT && pots->p_m_hold) {
732                                                         if (pots->p_m_fxs_age > latest) {
733                                                                 latest = pots->p_m_fxs_age;
734                                                                 latest_pots = pots;
735                                                         }
736                                                 }
737                                                 if (pots->p_state == PORT_STATE_OUT_ALERTING)
738                                                         alerting_pots = pots;
739                                         }
740                                 }
741                                 port = port->next;
742                         }
743                         port = port_first;
744                         while(port) {
745                                 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
746                                         pots = (class Pfxs *)port;
747                                         if (pots->p_m_mISDNport == mISDNport) {
748                                                 if ((pots->p_state != PORT_STATE_CONNECT || !pots->p_m_hold) && pots != alerting_pots) {
749                                                         PDEBUG(DEBUG_ISDN, "Pfxs(%s) release because pots-ring-after-hangup set and call not on hold / alerting\n", pots->p_name);
750                                                         pots->hangup_ind(cont);
751                                                 }
752                                         }
753                                 }
754                                 port = port->next;
755                         }
756                         if (alerting_pots) {
757                                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) answer because pots-ring-after-hangup set and call is alerting (knocking)\n", alerting_pots->p_name);
758                                 alerting_pots->retrieve_ind(cont);
759                                 break;
760                         }
761                         if (latest_pots) {
762                                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) retrieve because pots-ring-after-hangup set and call is latest on hold\n", latest_pots->p_name);
763                                 latest_pots->retrieve_ind(cont);
764                                 break;
765                         }
766                 } else {
767                         /* release all pots */
768                         port = port_first;
769                         while(port) {
770                                 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
771                                         pots = (class Pfxs *)port;
772                                         if (pots->p_m_mISDNport == mISDNport) {
773                                                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) release because pots-ring-after-hangup not set\n", pots->p_name);
774                                                 pots->hangup_ind(cont);
775                                         }
776                                 }
777                                 port = port->next;
778                         }
779                 }
780                 break;
781         case POTS_HOOK_FLASH:
782         case POTS_EARTH_KEY:
783 flash:
784                 if (!mISDNport->ifport->pots_flash) {
785                         PDEBUG(DEBUG_ISDN, "Pfxs flash key is disabled\n");
786                         break;
787                 }
788                 /* hold active pots / release not active pots */
789                 port = port_first;
790                 while(port) {
791                         if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
792                                 pots = (class Pfxs *)port;
793                                 if (pots->p_m_mISDNport == mISDNport) {
794                                         if (pots->p_state == PORT_STATE_CONNECT) {
795                                                 if (pots->p_m_hold) {
796                                                         if (pots->p_m_fxs_age > latest) {
797                                                                 latest = pots->p_m_fxs_age;
798                                                                 latest_pots = pots;
799                                                         }
800                                                 } else {
801                                                         PDEBUG(DEBUG_ISDN, "Pfxs(%s) hold, because flash on active call\n", pots->p_name);
802                                                         pots->hold_ind(cont);
803                                                 }
804                                         } else if (pots->p_state == PORT_STATE_OUT_ALERTING) {
805                                                 alerting_pots = pots;
806                                         } else {
807                                                 PDEBUG(DEBUG_ISDN, "Pfxs(%s) hangup, because flash on incomplete/released call\n", pots->p_name);
808                                                 pots->hangup_ind(cont);
809                                         }
810                                 }
811                         }
812                         port = port->next;
813                 }
814 #if 0
815                 /* now we have our bchannel available, so we can look alerting port to answer */
816                 if (alerting_pots) {
817                         PDEBUG(DEBUG_ISDN, "Pfxs(%s) answer because call is alerting (knocking)\n", alerting_pots->p_name);
818                         alerting_pots->answer_ind(cont);
819                         break;
820                 }
821                 if (latest_pots) {
822                         PDEBUG(DEBUG_ISDN, "Pfxs(%s) retrieve because call is latest on hold\n", latest_pots->p_name);
823                         latest_pots->retrieve_ind(cont);
824                         break;
825                 }
826 #endif
827                 goto setup;
828
829                 default:
830                 PERROR("unhandled message: xontrol(0x%x)\n", cont);
831                 return(-EINVAL);
832         }
833         return(0);
834 }
835
836 #endif /* ISDN_P_FXS_POTS */