1 /*******************************************************************************
2 
3     KHOMP generic endpoint/channel library.
4     Copyright (C) 2007-2010 Khomp Ind. & Com.
5 
6   The contents of this file are subject to the Mozilla Public License
7   Version 1.1 (the "License"); you may not use this file except in compliance
8   with the License. You may obtain a copy of the License at
9   http://www.mozilla.org/MPL/
10 
11   Software distributed under the License is distributed on an "AS IS" basis,
12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
13   the specific language governing rights and limitations under the License.
14 
15   Alternatively, the contents of this file may be used under the terms of the
16   "GNU Lesser General Public License 2.1" license (the “LGPL" License), in which
17   case the provisions of "LGPL License" are applicable instead of those above.
18 
19   If you wish to allow use of your version of this file only under the terms of
20   the LGPL License and not to allow others to use your version of this file
21   under the MPL, indicate your decision by deleting the provisions above and
22   replace them with the notice and other provisions required by the LGPL
23   License. If you do not delete the provisions above, a recipient may use your
24   version of this file under either the MPL or the LGPL License.
25 
26   The LGPL header follows below:
27 
28     This library is free software; you can redistribute it and/or
29     modify it under the terms of the GNU Lesser General Public
30     License as published by the Free Software Foundation; either
31     version 2.1 of the License, or (at your option) any later version.
32 
33     This library is distributed in the hope that it will be useful,
34     but WITHOUT ANY WARRANTY; without even the implied warranty of
35     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36     Lesser General Public License for more details.
37 
38     You should have received a copy of the GNU Lesser General Public License
39     along with this library; if not, write to the Free Software Foundation,
40     Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
41 
42 *******************************************************************************/
43 
44 #include "khomp_pvt_fxo.h"
45 #include "lock.h"
46 #include "logger.h"
47 
makeCall(std::string params)48 int BoardFXO::KhompPvtFXO::makeCall(std::string params)
49 {
50     DBG(FUNC,PVT_FMT(_target, "(FXO) c"));
51 
52     int ret = ksSuccess;
53 
54     /* we always have audio */
55     call()->_flags.set(Kflags::HAS_PRE_AUDIO);
56 
57     if(callFXO()->_call_info_drop == 0 && !callFXO()->_call_info_report)
58     {
59         command(KHOMP_LOG, CM_DISABLE_CALL_ANSWER_INFO);
60     }
61 
62     if (!callFXO()->_pre_digits.empty())
63     {
64         /* Seize the line at local PABX. */
65 
66         callFXO()->_flags.set(Kflags::CALL_WAIT_SEIZE);
67 
68         if (!command(KHOMP_LOG, CM_SEIZE, call()->_orig_addr.c_str()))
69             return ksFail;
70 
71         int timeout = 150;
72 
73         if(!loopWhileFlagTimed(Kflags::CALL_WAIT_SEIZE, timeout))
74             return ksFail;
75 
76         if (callFXO()->_flags.check(Kflags::CALL_WAIT_SEIZE) || (timeout <= 0))
77             return ksFail;
78 
79         /* Grab line from local PABX. */
80 
81         callFXO()->_flags.set(Kflags::WAIT_SEND_DTMF);
82 
83         if (!command(KHOMP_LOG, CM_DIAL_DTMF, callFXO()->_pre_digits.c_str()))
84             return ksFail;
85 
86         if(!loopWhileFlagTimed(Kflags::WAIT_SEND_DTMF, timeout))
87             return ksFail;
88 
89         if (callFXO()->_flags.check(Kflags::WAIT_SEND_DTMF) || (timeout <= 0))
90             return ksFail;
91 
92         /* Seize line from public central (works because the     *
93         * continuous cadence is always detected by the k3l api. */
94 
95         callFXO()->_flags.set(Kflags::CALL_WAIT_SEIZE);
96 
97         if (!command(KHOMP_LOG, CM_SEIZE, call()->_orig_addr.c_str()))
98             return ksFail;
99 
100         if(!loopWhileFlagTimed(Kflags::CALL_WAIT_SEIZE, timeout))
101             return ksFail;
102 
103         if (callFXO()->_flags.check(Kflags::CALL_WAIT_SEIZE) || (timeout <= 0))
104             return ksFail;
105 
106         /* we want the audio as soon as dialing ends */
107         callFXO()->_flags.set(Kflags::EARLY_RINGBACK);
108 
109         if (!command(KHOMP_LOG, CM_DIAL_DTMF, call()->_dest_addr.c_str()))
110             return ksFail;
111     }
112     else
113     {
114         /* we want the audio as soon as dialing ends */
115         callFXO()->_flags.set(Kflags::EARLY_RINGBACK);
116 
117 
118         if(!call()->_orig_addr.empty())
119             params += STG(FMT(" orig_addr=\"%s\"") % _call->_orig_addr);
120 
121 
122         ret = KhompPvt::makeCall(params);
123 
124         if(ret != ksSuccess)
125         {
126             LOG(ERROR, PVT_FMT(target(), "Fail on make call"));
127         }
128 
129         call()->_cleanup_upon_hangup = (ret == ksInvalidParams);
130     }
131 
132     DBG(FUNC,PVT_FMT(_target, "(FXO) r"));
133     return ret;
134 }
135 
doChannelAnswer(CommandRequest & cmd)136 bool BoardFXO::KhompPvtFXO::doChannelAnswer(CommandRequest &cmd)
137 {
138     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
139 
140     bool ret = true;
141 
142     try
143     {
144         ScopedPvtLock lock(this);
145 
146         // is this a collect call?
147         bool has_recv_collect_call = _call->_collect_call;
148 
149         if(has_recv_collect_call)
150             DBG(FUNC, PVT_FMT(target(), "receive a collect call"));
151 
152         if(call()->_flags.check(Kflags::DROP_COLLECT))
153             DBG(FUNC, PVT_FMT(target(), "flag DROP_COLLECT == true"));
154 
155         // do we have to drop collect calls?
156         bool has_drop_collect_call = call()->_flags.check(Kflags::DROP_COLLECT);
157 
158         // do we have to drop THIS call?
159         bool do_drop_call = has_drop_collect_call && has_recv_collect_call;
160 
161         if(!do_drop_call)
162         {
163             command(KHOMP_LOG, CM_CONNECT);
164         }
165 
166         if(has_drop_collect_call)
167         {
168             if(has_recv_collect_call)
169             {
170                 usleep(75000);
171 
172                 DBG(FUNC, PVT_FMT(target(), "disconnecting collect call doChannelAnswer FXO"));
173                 command(KHOMP_LOG,CM_DISCONNECT);
174 
175                 // thou shalt not talk anymore!
176                 stopListen();
177                 stopStream();
178             }
179             else
180             {
181                 DBG(FUNC, PVT_FMT(target(), "dropping collect call at doChannelAnswer FXO"));
182                 command(KHOMP_LOG, CM_DROP_COLLECT_CALL);
183             }
184         }
185 
186         ret = KhompPvt::doChannelAnswer(cmd);
187 
188     }
189     catch (ScopedLockFailed & err)
190     {
191         LOG(ERROR, PVT_FMT(_target,"(FXO) r (unable to lock %s!)") % err._msg.c_str() );
192         return false;
193     }
194 
195     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
196 
197     return ret;
198 }
199 
onNewCall(K3L_EVENT * e)200 bool BoardFXO::KhompPvtFXO::onNewCall(K3L_EVENT *e)
201 {
202     DBG(FUNC,PVT_FMT(_target,"(FXO) c"));
203 
204     bool ret = true;
205 
206     try
207     {
208         ScopedPvtLock lock(this);
209 
210         /* we always have audio */
211         call()->_flags.set(Kflags::HAS_PRE_AUDIO);
212 
213         ret = KhompPvt::onNewCall(e);
214 
215         if(!ret)
216             return false;
217 
218         startListen();
219         startStream();
220 
221     }
222     catch(ScopedLockFailed & err)
223     {
224         LOG(ERROR, PVT_FMT(target(), "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
225         return false;
226     }
227 
228     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
229 
230     return ret;
231 }
232 
onDisconnect(K3L_EVENT * e)233 bool BoardFXO::KhompPvtFXO::onDisconnect(K3L_EVENT *e)
234 {
235     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
236 
237     bool ret = true;
238 
239     try
240     {
241         ScopedPvtLock lock(this);
242 
243         command(KHOMP_LOG, CM_DISCONNECT);
244 
245         ret = KhompPvt::onDisconnect(e);
246 
247     }
248     catch (ScopedLockFailed & err)
249     {
250         LOG(ERROR, PVT_FMT(target(), "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
251         return false;
252     }
253 
254     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
255 
256     return ret;
257 }
258 
onChannelRelease(K3L_EVENT * e)259 bool BoardFXO::KhompPvtFXO::onChannelRelease(K3L_EVENT *e)
260 {
261     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
262 
263     bool ret = true;
264 
265     try
266     {
267         ScopedPvtLock lock(this);
268 
269         if (call()->_flags.check(Kflags::FAX_SENDING))
270         {
271             DBG(FUNC, PVT_FMT(_target, "stopping fax tx"));
272             _fax->stopFaxTX();
273         }
274         else if (call()->_flags.check(Kflags::FAX_RECEIVING))
275         {
276             DBG(FUNC, PVT_FMT(_target, "stopping fax rx"));
277             _fax->stopFaxRX();
278         }
279 
280         command(KHOMP_LOG, CM_ENABLE_CALL_ANSWER_INFO);
281 
282         ret = KhompPvt::onChannelRelease(e);
283     }
284     catch(ScopedLockFailed & err)
285     {
286         LOG(ERROR, PVT_FMT(target(), "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
287         return false;
288     }
289 
290     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
291     return ret;
292 }
293 
onCallSuccess(K3L_EVENT * e)294 bool BoardFXO::KhompPvtFXO::onCallSuccess(K3L_EVENT *e)
295 {
296     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
297 
298     bool ret = true;
299 
300     try
301     {
302         ScopedPvtLock lock(this);
303 
304         ret = KhompPvt::onCallSuccess(e);
305 
306         if (call()->_pre_answer)
307         {
308             dtmfSuppression(Opt::_options._out_of_band_dtmfs() && !call()->_flags.check(Kflags::FAX_DETECTED));
309 
310             startListen();
311             startStream();
312             switch_channel_mark_pre_answered(getFSChannel());
313         }
314     }
315     catch (ScopedLockFailed & err)
316     {
317         LOG(ERROR, PVT_FMT(_target, "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
318         return false;
319     }
320     catch (Board::KhompPvt::InvalidSwitchChannel & err)
321     {
322         LOG(ERROR, PVT_FMT(target(), "(FXO) r (%s)") % err._msg.c_str() );
323         return false;
324     }
325 
326     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
327 
328     return ret;
329 }
330 
onCallFail(K3L_EVENT * e)331 bool BoardFXO::KhompPvtFXO::onCallFail(K3L_EVENT *e)
332 {
333     bool ret = true;
334     try
335     {
336         ScopedPvtLock lock(this);
337 
338         command(KHOMP_LOG, CM_DISCONNECT);
339 
340         setHangupCause(causeFromCallFail(e->AddInfo), true);
341 
342         ret = KhompPvt::onCallFail(e);
343 
344     }
345     catch (ScopedLockFailed & err)
346     {
347         LOG(ERROR, PVT_FMT(target(), "unable to lock %s!") % err._msg.c_str() );
348         return false;
349     }
350 
351     return ret;
352 }
353 
onAudioStatus(K3L_EVENT * e)354 bool BoardFXO::KhompPvtFXO::onAudioStatus(K3L_EVENT *e)
355 {
356     DBG(STRM, PVT_FMT(_target, "(FXO) c"));
357 
358     try
359     {
360         //ScopedPvtLock lock(this);
361 
362         if(e->AddInfo == kmtFax)
363         {
364             DBG(STRM, PVT_FMT(_target, "Fax detected"));
365 
366             /* hadn't we did this already? */
367             bool already_detected = call()->_flags.check(Kflags::FAX_DETECTED);
368 
369             time_t time_was = call()->_call_statistics->_base_time;
370             time_t time_now = time(NULL);
371 
372             bool detection_timeout = (time_now > (time_was + (time_t) (Opt::_options._fax_adjustment_timeout())));
373 
374             BEGIN_CONTEXT
375             {
376                 ScopedPvtLock lock(this);
377 
378                 /* already adjusted? do not adjust again. */
379                 if (already_detected || detection_timeout)
380                     break;
381 
382                 if (callFXO()->_call_info_drop != 0 || callFXO()->_call_info_report)
383                 {
384                     /* we did not detected fax yet, send answer info! */
385                     setAnswerInfo(Board::KhompPvt::CI_FAX);
386 
387                     if (callFXO()->_call_info_drop & Board::KhompPvt::CI_FAX)
388                     {
389                         /* fastest way to force a disconnection */
390                         command(KHOMP_LOG,CM_DISCONNECT);//,SCE_HIDE);
391                     }
392                 }
393 
394                 if (Opt::_options._auto_fax_adjustment())
395                 {
396                     DBG(FUNC, PVT_FMT(_target, "communication will be adjusted for fax!"));
397                     _fax->adjustForFax();
398                 }
399             }
400             END_CONTEXT
401 
402             if (!already_detected)
403             {
404                 ScopedPvtLock lock(this);
405 
406                 /* adjust the flag */
407                 call()->_flags.set(Kflags::FAX_DETECTED);
408             }
409         }
410     }
411     catch (ScopedLockFailed & err)
412     {
413         LOG(ERROR, PVT_FMT(_target, "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
414         return false;
415     }
416 
417     bool ret = KhompPvt::onAudioStatus(e);
418 
419     DBG(STRM, PVT_FMT(_target, "(FXO) r"));
420 
421     return ret;
422 }
423 
onSeizeSuccess(K3L_EVENT * e)424 bool BoardFXO::KhompPvtFXO::onSeizeSuccess(K3L_EVENT *e)
425 {
426     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
427 
428     try
429     {
430         ScopedPvtLock lock(this);
431 
432         callFXO()->_flags.clear(Kflags::CALL_WAIT_SEIZE);
433 
434     }
435     catch (ScopedLockFailed & err)
436     {
437         LOG(ERROR, PVT_FMT(_target, "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
438         return false;
439     }
440 
441     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
442 
443     return true;
444 }
445 
onDtmfDetected(K3L_EVENT * e)446 bool BoardFXO::KhompPvtFXO::onDtmfDetected(K3L_EVENT *e)
447 {
448     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
449 
450     bool ret = true;
451 
452     try
453     {
454         ScopedPvtLock lock(this);
455 
456         if (callFXO()->_flags.check(Kflags::WAIT_SEND_DTMF) ||
457                 callFXO()->_flags.check(Kflags::CALL_WAIT_SEIZE))
458         {
459             /* waiting digit or seize means DEADLOCK if we try to
460              *  queue something below. */
461             DBG(FUNC, PVT_FMT(_target, "not queueing dtmf, waiting stuff!"));
462             return true;
463         }
464 
465         ret = KhompPvt::onDtmfDetected(e);
466     }
467     catch (ScopedLockFailed & err)
468     {
469         LOG(ERROR, PVT_FMT(_target, "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
470         return false;
471     }
472 
473     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
474 
475     return ret;
476 }
477 
onDtmfSendFinish(K3L_EVENT * e)478 bool BoardFXO::KhompPvtFXO::onDtmfSendFinish(K3L_EVENT *e)
479 {
480     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
481 
482     bool ret = true;
483 
484     try
485     {
486         ScopedPvtLock lock(this);
487 
488         ret = KhompPvt::onDtmfSendFinish(e);
489 
490         if (callFXO()->_flags.check(Kflags::EARLY_RINGBACK))
491         {
492             callFXO()->_flags.clear(Kflags::EARLY_RINGBACK);
493 
494             /* start grabbing */
495             startListen();
496 
497             /* activate resources early... */
498             bool fax_detected = callFXO()->_flags.check(Kflags::FAX_DETECTED);
499 
500             bool res_out_of_band_dtmf = Opt::_options._suppression_delay() && Opt::_options._out_of_band_dtmfs() && !fax_detected;
501             bool res_echo_cancellator = Opt::_options._echo_canceller() && !fax_detected;
502             bool res_auto_gain_cntrol = Opt::_options._auto_gain_control() && !fax_detected;
503 
504             if (!call()->_flags.check(Kflags::KEEP_DTMF_SUPPRESSION))
505                 dtmfSuppression(res_out_of_band_dtmf);
506 
507             if (!call()->_flags.check(Kflags::KEEP_ECHO_CANCELLATION))
508                 echoCancellation(res_echo_cancellator);
509 
510             if (!call()->_flags.check(Kflags::KEEP_AUTO_GAIN_CONTROL))
511                 autoGainControl(res_auto_gain_cntrol);
512 
513             /* start sending audio if wanted so */
514             if (Opt::_options._fxo_send_pre_audio())
515                 startStream();
516 
517             //TODO: Verificar isso aqui
518             if (call()->_pre_answer)
519             {
520                 /* tell the user we are answered! */
521                 switch_channel_mark_answered(getFSChannel());
522                 //pvt->signal_state(AST_CONTROL_ANSWER);
523             }
524             else
525             {
526                 /* are we ringing, now? lets try this way! */
527                 switch_channel_mark_ring_ready(getFSChannel());
528                 //pvt->signal_state(AST_CONTROL_RINGING);
529             }
530         }
531     }
532     catch (ScopedLockFailed & err)
533     {
534         LOG(ERROR, PVT_FMT(_target, "(FXO) r (unable to lock %s!)") % err._msg.c_str() );
535         return false;
536     }
537     catch(Board::KhompPvt::InvalidSwitchChannel & err)
538     {
539         LOG(ERROR, PVT_FMT(_target, "(FXO) r (no valid channel: %s)") % err._msg.c_str());
540         return false;
541     }
542 
543     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
544 
545     return ret;
546 }
547 
onCallAnswerInfo(K3L_EVENT * e)548 bool BoardFXO::KhompPvtFXO::onCallAnswerInfo(K3L_EVENT *e)
549 {
550     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
551     try
552     {
553         ScopedPvtLock lock(this);
554 
555         int info_code = -1;
556 
557         switch (e->AddInfo)
558         {
559             case kcsiCellPhoneMessageBox:
560                 info_code = CI_MESSAGE_BOX;
561                 break;
562             case kcsiHumanAnswer:
563                 info_code = CI_HUMAN_ANSWER;
564                 break;
565             case kcsiAnsweringMachine:
566                 info_code = CI_ANSWERING_MACHINE;
567                 break;
568             case kcsiCarrierMessage:
569                 info_code = CI_CARRIER_MESSAGE;
570                 break;
571             case kcsiUnknown:
572                 info_code = CI_UNKNOWN;
573                 break;
574             default:
575                 DBG(FUNC, PVT_FMT(_target, "got an unknown call answer info '%d', ignoring...") % e->AddInfo);
576                 break;
577         }
578 
579         if (info_code != -1)
580         {
581             if (callFXO()->_call_info_report)
582             {
583                 //TODO: HOW WE TREAT THAT
584                 // make the channel export this
585                 setAnswerInfo(info_code);
586             }
587 
588             if (callFXO()->_call_info_drop & info_code)
589             {
590                 command(KHOMP_LOG,CM_DISCONNECT);
591             }
592         }
593     }
594     catch (ScopedLockFailed & err)
595     {
596         LOG(ERROR, PVT_FMT(_target,"(FXO) r (unable to lock %s!)") % err._msg.c_str() );
597         return false;
598     }
599 
600     DBG(FUNC, PVT_FMT(_target, "(FXO) r"));
601     return true;
602 }
603 
application(ApplicationType type,switch_core_session_t * session,const char * data)604 bool BoardFXO::KhompPvtFXO::application(ApplicationType type, switch_core_session_t * session, const char *data)
605 {
606     switch(type)
607     {
608         case FAX_ADJUST:
609             return _fax->adjustForFax();
610         case FAX_SEND:
611             return _fax->sendFax(session, data);
612         case FAX_RECEIVE:
613             return _fax->receiveFax(session, data);
614         case USER_TRANSFER:
615             return _transfer->userTransfer(session, data);
616         default:
617             return KhompPvt::application(type, session, data);
618     }
619 
620     return true;
621 }
622 
setupConnection()623 bool BoardFXO::KhompPvtFXO::setupConnection()
624 {
625     if(!call()->_flags.check(Kflags::IS_INCOMING) && !call()->_flags.check(Kflags::IS_OUTGOING))
626     {
627         DBG(FUNC,PVT_FMT(_target, "Channel already disconnected"));
628         return false;
629     }
630 
631     callFXO()->_flags.clear(Kflags::CALL_WAIT_SEIZE);
632     callFXO()->_flags.clear(Kflags::WAIT_SEND_DTMF);
633 
634     /* if received some disconnect from 'drop collect call'
635        feature of some pbx, then leave the call rock and rolling */
636     //Board::board(_target.device)->_timers.del(callFXO()->_idx_disconnect);
637 
638     bool fax_detected = callFXO()->_flags.check(Kflags::FAX_DETECTED) || (callFXO()->_var_fax_adjust == T_TRUE);
639 
640     bool res_out_of_band_dtmf = (call()->_var_dtmf_state == T_UNKNOWN || fax_detected ?
641         Opt::_options._suppression_delay() && Opt::_options._out_of_band_dtmfs() && !fax_detected : (call()->_var_dtmf_state == T_TRUE));
642 
643     bool res_echo_cancellator = (call()->_var_echo_state == T_UNKNOWN || fax_detected ?
644         Opt::_options._echo_canceller() && !fax_detected : (call()->_var_echo_state == T_TRUE));
645 
646     bool res_auto_gain_cntrol = (call()->_var_gain_state == T_UNKNOWN || fax_detected ?
647         Opt::_options._auto_gain_control() && !fax_detected : (call()->_var_gain_state == T_TRUE));
648 
649     if (!call()->_flags.check(Kflags::REALLY_CONNECTED))
650     {
651         obtainRX(res_out_of_band_dtmf);
652 
653         /* esvazia buffers de leitura/escrita */
654         cleanupBuffers();
655 
656         if (!call()->_flags.check(Kflags::KEEP_DTMF_SUPPRESSION))
657             dtmfSuppression(res_out_of_band_dtmf);
658 
659         if (!call()->_flags.check(Kflags::KEEP_ECHO_CANCELLATION))
660             echoCancellation(res_echo_cancellator);
661 
662         if (!call()->_flags.check(Kflags::KEEP_AUTO_GAIN_CONTROL))
663             autoGainControl(res_auto_gain_cntrol);
664 
665         startListen(false);
666 
667         startStream();
668 
669         DBG(FUNC, PVT_FMT(_target, "(FXO) Audio callbacks initialized successfully"));
670     }
671 
672     return KhompPvt::setupConnection();
673 }
674 
autoGainControl(bool enable)675 bool BoardFXO::KhompPvtFXO::autoGainControl(bool enable)
676 {
677     bool ret = KhompPvt::autoGainControl(enable);
678 
679     /* enable this AGC also, can be very useful */
680     ret &= command(KHOMP_LOG, (enable ? CM_ENABLE_PLAYER_AGC : CM_DISABLE_PLAYER_AGC));
681     return ret;
682 }
683 
setAnswerInfo(int answer_info)684 void BoardFXO::KhompPvtFXO::setAnswerInfo(int answer_info)
685 {
686     const char * value = answerInfoToString(answer_info);
687 
688     if (value == NULL)
689     {
690         DBG(FUNC, PVT_FMT(_target,"signaled unknown call answer info '%d', using 'Unknown'...") % answer_info);
691         value = "Unknown";
692     }
693 
694     DBG(FUNC,PVT_FMT(_target,"KCallAnswerInfo: %s") % value);
695 
696     try
697     {
698         setFSChannelVar("KCallAnswerInfo", value);
699     }
700     catch(Board::KhompPvt::InvalidSwitchChannel & err)
701     {
702         LOG(ERROR,PVT_FMT(_target,"Cannot obtain the channel variable: %s") % err._msg.c_str());
703     }
704 }
705 
indicateBusyUnlocked(int cause,bool sent_signaling)706 bool BoardFXO::KhompPvtFXO::indicateBusyUnlocked(int cause, bool sent_signaling)
707 {
708     DBG(FUNC, PVT_FMT(_target, "(FXO) c"));
709 
710     if(!KhompPvt::indicateBusyUnlocked(cause, sent_signaling))
711     {
712         DBG(FUNC, PVT_FMT(_target, "(FXO) r (false)"));
713         return false;
714     }
715 
716     if(call()->_flags.check(Kflags::IS_INCOMING))
717     {
718         /* already connected or sent signaling... */
719         mixer(KHOMP_LOG, 1, kmsGenerator, kmtBusy);
720 
721         if(!call()->_flags.check(Kflags::CONNECTED) && !sent_signaling)
722         {
723             /* we are talking about branches, not trunks */
724             command(KHOMP_LOG, CM_CONNECT);
725             callFXO()->_busy_disconnect = Board::board(_target.device)->_timers.add(Opt::_options._fxo_busy_disconnection(), &BoardFXO::KhompPvtFXO::busyDisconnect, this);
726         }
727     }
728     else if(call()->_flags.check(Kflags::IS_OUTGOING))
729     {
730         /* already connected or sent signaling... */
731         mixer(KHOMP_LOG, 1, kmsGenerator, kmtBusy);
732     }
733 
734     DBG(FUNC,PVT_FMT(_target, "(FXO) r"));
735     return true;
736 }
737 
busyDisconnect(Board::KhompPvt * pvt)738 void BoardFXO::KhompPvtFXO::busyDisconnect(Board::KhompPvt * pvt)
739 {
740     DBG(FUNC, PVT_FMT(pvt->target(), "Disconnecting FXO"));
741 
742     try
743     {
744         ScopedPvtLock lock(pvt);
745         pvt->command(KHOMP_LOG, CM_DISCONNECT);
746     }
747     catch (...)
748     {
749         LOG(ERROR, PVT_FMT(pvt->target(), "unable to lock the pvt !"));
750     }
751 }
752 
reportFailToReceive(int fail_code)753 void BoardFXO::KhompPvtFXO::reportFailToReceive(int fail_code)
754 {
755     KhompPvt::reportFailToReceive(fail_code);
756 
757     command(KHOMP_LOG, CM_CONNECT);
758     command(KHOMP_LOG, CM_DISCONNECT);
759 }
760 
validContexts(MatchExtension::ContextListType & contexts,std::string extra_context)761 bool BoardFXO::KhompPvtFXO::validContexts(
762         MatchExtension::ContextListType & contexts, std::string extra_context)
763 {
764     DBG(FUNC,PVT_FMT(_target,"(FXO) c"));
765 
766     if(!_group_context.empty())
767     {
768         contexts.push_back(_group_context);
769     }
770 
771     contexts.push_back(Opt::_options._context_fxo());
772     contexts.push_back(Opt::_options._context2_fxo());
773 
774     for (MatchExtension::ContextListType::iterator i = contexts.begin(); i != contexts.end(); i++)
775     {
776         replaceTemplate((*i), "CC", _target.object);
777     }
778 
779     bool ret = Board::KhompPvt::validContexts(contexts,extra_context);
780 
781     DBG(FUNC,PVT_FMT(_target,"(FXO) r"));
782 
783     return ret;
784 }
785 
isOK()786 bool BoardFXO::KhompPvtFXO::isOK()
787 {
788     try
789     {
790         ScopedPvtLock lock(this);
791 
792         K3L_CHANNEL_STATUS status;
793 
794         if (k3lGetDeviceStatus (_target.device, _target.object + ksoChannel, &status, sizeof (status)) != ksSuccess)
795             return false;
796 
797         return (status.AddInfo == kfcsEnabled);
798     }
799     catch (ScopedLockFailed & err)
800     {
801         LOG(ERROR, PVT_FMT(target(), "unable to lock %s!") % err._msg.c_str() );
802     }
803 
804     return false;
805 }
806 
sendDtmf(std::string digit)807 bool BoardFXO::KhompPvtFXO::sendDtmf(std::string digit)
808 {
809     if(_transfer->checkUserXferUnlocked(digit))
810     {
811         DBG(FUNC, PVT_FMT(target(), "started (or waiting for) an user xfer"));
812         return true;
813     }
814 
815     bool ret = KhompPvt::sendDtmf(callFXO()->_digits_buffer);
816 
817     callFXO()->_digits_buffer.clear();
818 
819     return ret;
820 }
821 
822