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 #ifndef _KHOMP_PVT_H_
45 #define _KHOMP_PVT_H_
46 
47 #include <timer.hpp>
48 #include "globals.h"
49 #include "frame.h"
50 #include "opt.h"
51 #include "logger.h"
52 #include "defs.h"
53 
54 /*!
55  \brief Callback generated from K3L API for every new event on the board.
56  \param[in] obj Object ID (could be a channel or a board, depends on device type) which generated the event.
57  \param[in] e The event itself.
58  \return ksSuccess if the event was treated
59  \see K3L_EVENT Event specification
60  */
61 extern "C" int32 Kstdcall khompEventCallback (int32, K3L_EVENT *);
62 
63 /*!
64  \brief Callback generated from K3L API everytime audio is available on the board.
65  @param[in] deviceid Board on which we get the event
66  @param[in] objectid The channel we are getting the audio from
67  @param[out] read_buffer The audio buffer itself (RAW)
68  @param[in] read_size The buffer size, meaning the amount of data to be read
69  \return ksSuccess if the event was treated
70  */
71 extern "C" void Kstdcall khompAudioListener (int32, int32, byte *, int32);
72 
73 /******************************************************************************/
74 /********************************* Board **************************************/
75 /******************************************************************************/
76 struct Board
77 {
78     /* Timers */
79     struct KhompPvt;
80     typedef KhompPvt *     ChanTimerData;
81     typedef                void (ChanTimerFunc)(ChanTimerData);
82     typedef TimerTemplate < ChanTimerFunc, ChanTimerData > ChanTimer;
83 
84 /******************************************************************************/
85 /******************************** Channel *************************************/
86 struct KhompPvt
87 {
88     typedef SimpleNonBlockLock<25,100>      ChanLockType;
89 
90     typedef enum
91     {
92         CI_MESSAGE_BOX        = 0x01,
93         CI_HUMAN_ANSWER       = 0x02,
94         CI_ANSWERING_MACHINE  = 0x04,
95         CI_CARRIER_MESSAGE    = 0x08,
96         CI_UNKNOWN            = 0x10,
97         CI_FAX                = 0x20,
98     }
99     CallInfoType;
100 
101     struct InitFailure {};
102 
103     struct InvalidSwitchChannel
104     {
105         typedef enum { NULL_SWITCH_CHANNEL, NULL_SWITCH_SESSION_PASSED, NULL_SWITCH_VALUE, FAILED } FailType;
106 
InvalidSwitchChannelBoard::KhompPvt::InvalidSwitchChannel107         InvalidSwitchChannel(FailType fail, std::string msg)
108             : _fail(fail),_msg(msg) {};
109 
110         std::string _msg;
111         FailType _fail;
112     };
113 
114     typedef enum
115     {
116         PLAY_NONE = 0,
117         PLAY_VM_TONE,
118         PLAY_PBX_TONE,
119         PLAY_PUB_TONE,
120         PLAY_RINGBACK,
121         PLAY_FASTBUSY,
122     }
123     CadencesType;
124 
125     typedef enum
126     {
127         INDICA_NONE = 0,
128         INDICA_RING,
129         INDICA_BUSY,
130         INDICA_FAST_BUSY,
131     }
132     IndicationType;
133 
134     typedef enum
135     {
136         CLN_HARD,
137         CLN_SOFT,
138         CLN_FAIL,
139     }
140     CleanupType;
141 
142 /********************************** Call **************************************/
143     struct Call
144     {
145         struct CallStatistics : public Statistics
146         {
CallStatisticsBoard::KhompPvt::Call::CallStatistics147             CallStatistics(Call *call):
148                 _call(call),
149                 _total_time_incoming(0),
150                 _total_time_outgoing(0),
151                 _total_idle_time(0),
152                 _channel_fails(0)
153             {
154                 time(&_base_idle_time);
155                 time(&_base_time);
156             }
157 
idleBoard::KhompPvt::Call::CallStatistics158             void idle()
159             {
160                 if (_call->_flags.check(Kflags::IS_INCOMING) ||
161                     _call->_flags.check(Kflags::IS_OUTGOING) ||
162                     _call->_indication == INDICA_RING ||
163                     _call->_indication == INDICA_BUSY)
164                 {
165                     return;
166                 }
167 
168                 time_t tmp;
169                 time (&tmp);
170 
171                 _total_idle_time += (tmp - _base_idle_time);
172                 time (&_base_idle_time);
173             }
174 
incrementNewCallBoard::KhompPvt::Call::CallStatistics175             void incrementNewCall()
176             {
177                 time (&_base_time);
178             }
179 
incrementHangupBoard::KhompPvt::Call::CallStatistics180             void incrementHangup()
181             {
182                 time_t tmp;
183                 time (&tmp);
184 
185                 if (_call->_flags.check(Kflags::IS_OUTGOING))
186                 {
187                     _total_time_outgoing += (tmp - _base_time);
188                     time (&_base_time);
189                 }
190                 else if (_call->_flags.check(Kflags::IS_INCOMING))
191                 {
192                     _total_time_incoming += (tmp - _base_time);
193                     time (&_base_time);
194                 }
195 
196                 time(&_base_idle_time);
197             }
198 
incrementChannelFailBoard::KhompPvt::Call::CallStatistics199             void incrementChannelFail()
200             {
201                 _channel_fails++;
202             }
203 
getDetailedBoard::KhompPvt::Call::CallStatistics204             std::string getDetailed()
205             {
206                 /* buffer our data to return at the end */
207                 std::string strBuffer;
208 
209                 /* very very important yet! */
210                 idle();
211 
212                 std::string str_incoming_time = timeToString(_total_time_incoming);
213                 std::string str_outgoing_time = timeToString(_total_time_outgoing);
214                 std::string str_idle_time     = timeToString(_total_idle_time);
215 
216                 strBuffer.append(STG(FMT("Total Incoming Time: \t%s\n") % str_incoming_time));
217                 strBuffer.append(STG(FMT("Total Outgoing Time: \t%s\n") % str_outgoing_time));
218                 strBuffer.append(STG(FMT("Total Idle Time: \t%s\n") % str_idle_time));
219                 strBuffer.append(STG(FMT("Number of channel fails: \t%d\n") % _channel_fails));
220 
221                 return strBuffer;
222             }
223 
getDetailedXMLBoard::KhompPvt::Call::CallStatistics224             switch_xml_t getDetailedXML()
225             {
226                 /* very very important yet! */
227                 idle();
228 
229                 std::string str_incoming_time = timeToString(_total_time_incoming);
230                 std::string str_outgoing_time = timeToString(_total_time_outgoing);
231                 std::string str_idle_time     = timeToString(_total_idle_time);
232 
233                 /* total */
234                 switch_xml_t xtotal = switch_xml_new("total");
235 
236                 /* total/incoming_time */
237                 switch_xml_t xin_time = switch_xml_add_child_d(xtotal,"incoming_time",0);
238                 switch_xml_set_txt_d(xin_time, str_incoming_time.c_str());
239 
240                 /* total/outgoing_time */
241                 switch_xml_t xout_time = switch_xml_add_child_d(xtotal,"outgoing_time",0);
242                 switch_xml_set_txt_d(xout_time, str_outgoing_time.c_str());
243 
244                 /* total/idle_time */
245                 switch_xml_t xidle_time = switch_xml_add_child_d(xtotal,"idle_time",0);
246                 switch_xml_set_txt_d(xidle_time, str_idle_time.c_str());
247 
248                 /* total/channel_fails */
249                 switch_xml_t xchannel_fails = switch_xml_add_child_d(xtotal,"channel_fails",0);
250                 switch_xml_set_txt_d(xchannel_fails, STR(FMT("%d") % _channel_fails));
251 
252                 return xtotal;
253             }
254 
clearBoard::KhompPvt::Call::CallStatistics255             void clear()
256             {
257                 time(&_base_time);
258                 time(&_base_idle_time);
259 
260                 _total_idle_time = 0;
261                 _channel_fails = 0;
262 
263                 _total_time_incoming = 0;
264                 _total_time_outgoing = 0;
265             }
266 
267             time_t _base_time;
268             time_t _total_time_incoming;
269             time_t _total_time_outgoing;
270             time_t _total_idle_time;
271             time_t _base_idle_time;     /* base time for idle time refreshing */
272             unsigned int _channel_fails;/* number of channel fails*/
273 
274         protected:
275             Call *_call;  /* associate to call, useful */
276         };
277 
CallBoard::KhompPvt::Call278         Call()
279         {
280             clear();
281             _call_statistics = new CallStatistics(this);
282         }
283 
~CallBoard::KhompPvt::Call284         virtual ~Call() { delete _call_statistics; }
285 
statisticsBoard::KhompPvt::Call286         CallStatistics * statistics() { return _call_statistics; };
287 
288         virtual bool process(std::string name, std::string value = "")
289         {
290             if (name == "pre_answer")
291             {
292                 _pre_answer = true;
293             }
294             else if (name == "orig")
295             {
296                 DBG(FUNC, FMT("orig addr adjusted (%s).") % value.c_str());
297                 _orig_addr = value;
298             }
299             else if (name == "dest")
300             {
301                 _dest_addr = value;
302             }
303 
304             else if (name == "input_volume" || name == "output_volume")
305             {
306                 try
307                 {
308                     int i = Strings::tolong(value);
309 
310                     if (i < -10 || i > 10)
311                     {
312                         LOG(ERROR, FMT("Could not set '%s': '%s' is not a valid number between -10 and 10.") % name % value);
313                     }
314                     else
315                     {
316                         DBG(FUNC, FMT("Changing '%s' volume to '%s'.") % name % value);
317                         if(name == "input_volume")
318                             _input_volume = i;
319                         else
320                             _output_volume = i;
321                     }
322                 }
catchBoard::KhompPvt::Call323                 catch (Strings::invalid_value & e)
324                 {
325                     LOG(ERROR, D("invalid numeric value: %s") % e.value());
326                 }
327             }
328 
329             else
330             {
331                 return false;
332             }
333 
334             return true;
335         }
336 
clearBoard::KhompPvt::Call337         virtual bool clear()
338         {
339 
340             _orig_addr.clear();
341             _dest_addr.clear();
342             _incoming_context.clear();
343             _queued_digits_buffer.clear();
344             _pre_answer = false;
345             _is_progress_sent = false;
346             _collect_call = false;
347             _hangup_cause = 0;
348             _cleanup_upon_hangup = false;
349             _input_volume = 999;
350             _output_volume = 999;
351 
352             _var_dtmf_state = T_UNKNOWN;
353             _var_echo_state = T_UNKNOWN;
354             _var_gain_state = T_UNKNOWN;
355 
356             _flags.clearAll();
357 
358             _cadence = PLAY_NONE;
359             _indication = INDICA_NONE;
360 
361             return true;
362         }
363 
364         /* used while answering calls */
365         std::string _orig_addr;
366         std::string _dest_addr;
367 
368         std::string _incoming_context;
369 
370         std::string _queued_digits_buffer;
371 
372         /* should freeswitch answer before connect event?  */
373         bool _pre_answer;
374 
375         bool _is_progress_sent;
376 
377         /* is a collect call? */
378         bool _collect_call;
379 
380         int _hangup_cause;
381 
382         bool _cleanup_upon_hangup;
383 
384         int _input_volume;
385         int _output_volume;
386 
387         TriState _var_dtmf_state;
388         TriState _var_echo_state;
389         TriState _var_gain_state;
390 
391         Kflags _flags;
392 
393         CadencesType _cadence;
394         IndicationType _indication;
395 
396         ChanTimer::Index _idx_co_ring;
397         ChanTimer::Index _idx_pbx_ring;
398 
399         CallStatistics *_call_statistics;
400     };
401 /******************************************************************************/
402 public:
403 
404     /* KhompPvt constructor */
405     KhompPvt(K3LAPIBase::GenericTarget & target);
406 
407     /* KhompPvt destructor */
~KhompPvtBoard::KhompPvt408     virtual ~KhompPvt()
409     {
410         delete _pvt_statistics;
411         _session = NULL;
412     }
413 
414     struct PvtStatistics : public Statistics
415     {
PvtStatisticsBoard::KhompPvt::PvtStatistics416         PvtStatistics(KhompPvt * pvt):
417             _pvt(pvt) {}
418 
419         std::string getDetailedRates();
420         switch_xml_t getDetailedRatesXML();
421 
422         std::string getDetailed();
423         switch_xml_t getDetailedXML();
424 
425         std::string getRow();
426         switch_xml_t getNode();
427 
428 
clearBoard::KhompPvt::PvtStatistics429         void clear()
430         {
431             _pvt->call()->statistics()->clear();
432         }
433 
434         KhompPvt * _pvt;
435     };
436 
437     /* Virtual Methods */
438 
439     /*!
440      \defgroup KhompEvents
441                 Callbacks that boards can implement to produce the expected
442                 particular behaviour. Refer to Khomp documentation for a
443                 detailed description of each method.
444      */
445     /*@{*/
446     virtual bool onChannelRelease(K3L_EVENT *);
447     virtual bool onNewCall(K3L_EVENT *);
448     virtual bool onCallSuccess(K3L_EVENT *);
449     virtual bool onCallFail(K3L_EVENT *);
450     virtual bool onConnect(K3L_EVENT *);
451     virtual bool onDisconnect(K3L_EVENT *);
452     virtual bool onAudioStatus(K3L_EVENT *);
453     virtual bool onCollectCall(K3L_EVENT *);
454     virtual bool onSeizureStart(K3L_EVENT *);
455     virtual bool onDtmfDetected(K3L_EVENT *);
456     virtual bool onNoAnswer(K3L_EVENT *);
457     virtual bool onDtmfSendFinish(K3L_EVENT *);
458     virtual bool onEvUntreated(K3L_EVENT *);
459     virtual bool eventHandler(K3L_EVENT *);
460     /*@}*/
461 
462     virtual bool doChannelAnswer(CommandRequest &);
463     virtual bool doChannelHangup(CommandRequest &);
464     bool commandHandler(CommandRequest &);
465 
466     virtual int makeCall(std::string params = "");
467     virtual bool setupConnection();
468 
causeFromCallFailBoard::KhompPvt469     virtual int causeFromCallFail(int fail)  { return SWITCH_CAUSE_USER_BUSY; };
callFailFromCauseBoard::KhompPvt470     virtual int callFailFromCause(int cause) { return -1; };
reportFailToReceiveBoard::KhompPvt471     virtual void reportFailToReceive(int fail_code)
472     {
473         call()->_indication = INDICA_FAST_BUSY;
474     }
475     virtual bool setCollectCall();
476 
477     virtual bool indicateBusyUnlocked(int cause, bool sent_signaling = false);
478 
479     virtual bool cleanup(CleanupType type = CLN_HARD);
480 
481     virtual RingbackDefs::RingbackStType sendRingBackStatus(int rb_value = RingbackDefs::RB_SEND_DEFAULT)
482     {
483         return RingbackDefs::RBST_UNSUPPORTED;
484     }
485 
486     virtual bool sendPreAudio(int rb_value = RingbackDefs::RB_SEND_NOTHING)
487     {
488         if (rb_value != RingbackDefs::RB_SEND_NOTHING)
489         {
490             if(sendRingBackStatus(rb_value) == RingbackDefs::RBST_FAILURE)
491                 return false;
492         }
493 
494         return true;
495     }
496 
isOKBoard::KhompPvt497     virtual bool isOK(void) { return false; }
isPhysicalFreeBoard::KhompPvt498     virtual bool isPhysicalFree() { return false; }
499     virtual bool isFree(bool just_phy = false);
500 
hasNumberDialBoard::KhompPvt501     virtual bool hasNumberDial() { return true; }
502 
getSpecialVariablesBoard::KhompPvt503     virtual void getSpecialVariables()
504     {
505         try
506         {
507             const char * str_sup = getFSChannelVar("KDTMFSuppression");
508             const char * str_agc = getFSChannelVar("KAutoGainControl");
509             const char * str_eco = getFSChannelVar("KEchoCanceller");
510 
511             call()->_var_dtmf_state = (str_sup ? ((!SAFE_strcasecmp(str_sup, "true") || !SAFE_strcasecmp(str_sup, "on"))? T_TRUE : T_FALSE) : T_UNKNOWN);
512             call()->_var_echo_state = (str_eco ? (!SAFE_strcasecmp(str_eco, "true") ? T_TRUE : T_FALSE) : T_UNKNOWN);
513             call()->_var_gain_state = (str_agc ? (!SAFE_strcasecmp(str_agc, "true") ? T_TRUE : T_FALSE) : T_UNKNOWN);
514         }
515         catch(Board::KhompPvt::InvalidSwitchChannel & err)
516         {
517             LOG(ERROR, PVT_FMT(_target, "%s") % err._msg.c_str());
518         }
519 
520         try
521         {
522             char * volume = (char*) getFSChannelVar("KSetVolume");
523 
524             if(!volume)
525             {
526                 return;
527             }
528 
529             std::string datastr(volume);
530 
531             Strings::trim(datastr);
532 
533             Strings::vector_type params;
534             Strings::tokenize(datastr, params, "|,", 2);
535 
536             int inpvol = INT_MAX;
537             int outvol = INT_MAX;
538 
539             /**/ if (params.size() == 1)
540             {
541                 int vol = (params[0] != "none" ? Strings::tolong(params[0]) : INT_MAX);
542 
543                 inpvol = vol;
544                 outvol = vol;
545             }
546             else if (params.size() == 2)
547             {
548                 inpvol = (params[0] != "none" ? Strings::tolong(params[0]) : INT_MAX);
549                 outvol = (params[1] != "none" ? Strings::tolong(params[1]) : INT_MAX);
550             }
551             else
552             {
553                 LOG(ERROR, "invalid number of arguments for KSetVolume!");
554                 return;
555             }
556 
557             if (inpvol != INT_MAX)
558             {
559                 if(_call->_input_volume < -10 || _call->_input_volume > 10)
560                 {
561                     _call->_input_volume = inpvol;
562                 }
563             }
564 
565             if (outvol != INT_MAX)
566             {
567                 if(_call->_output_volume < -10 || _call->_output_volume > 10)
568                 {
569                     _call->_output_volume = outvol;
570                 }
571             }
572 
573         }
574         catch(Board::KhompPvt::InvalidSwitchChannel & err)
575         {
576             LOG(ERROR, PVT_FMT(_target, "%s") % err._msg.c_str());
577         }
578         catch (Strings::invalid_value e)
579         {
580             LOG(ERROR, FMT("invalid numeric value: %s") % e.value());
581         }
582     }
583 
setSpecialVariablesBoard::KhompPvt584     virtual void setSpecialVariables() {}
585 
applicationBoard::KhompPvt586     virtual bool application(ApplicationType type, switch_core_session_t * session, const char *data)
587     {
588         switch(type)
589         {
590         case FAX_ADJUST:
591         case FAX_SEND:
592         case FAX_RECEIVE:
593             LOG(ERROR, PVT_FMT(_target, "not a digital and not "
594                     "a fxo Khomp channel, fax not supported"));
595             break;
596         default:
597             LOG(ERROR, PVT_FMT(_target,
598                     "application not supported"));
599             break;
600         }
601 
602         return false;
603     }
604 
605     /* statistics functions */
606     virtual std::string getStatistics(Statistics::Type type);
607     virtual switch_xml_t getStatisticsXML(Statistics::Type type);
clearStatisticsBoard::KhompPvt608     virtual void clearStatistics()
609     {
610         _pvt_statistics->clear();
611     }
612 
613     virtual bool indicateRinging();
614     virtual bool sendDtmf(std::string digit);
615     virtual void cleanupIndications(bool force);
616 
617     /* Methods */
618 
619     bool indicateProgress();
620 
621     bool signalDTMF(char d);
622 
623     bool loopWhileFlagTimed(Kflags::FlagType flag, int &timeout, bool clear = true)
624     {
625         bool pvt_locked = true;
626         bool quit       = false;
627 
628         unsigned int sleeps = 0;
629 
630         while ((timeout != 0) && !quit)
631         {
632             for (; (sleeps < 10) && !quit; sleeps++)
633             {
634                 /* unlock our pvt struct */
635                 if (pvt_locked)
636                 {
637                     _mutex.unlock();
638                     pvt_locked = false;
639                 }
640 
641                 /* wait a little while (100ms is good?) */
642                 usleep (100000);
643                 ++sleeps;
644 
645                 /* re-lock pvt struct */
646                 switch (_mutex.lock())
647                 {
648                     case SimpleLock::ISINUSE:
649                     case SimpleLock::FAILURE:
650                         LOG(ERROR, PVT_FMT(_target, "unable to lock pvt_mutex, trying again."));
651 
652                         sched_yield();
653                         continue;
654 
655                     default:
656                         break;
657                 }
658 
659                 pvt_locked = true;
660 
661                 if(clear)
662                 {
663                     if(!call()->_flags.check(flag))
664                     {
665                         quit = true;
666                         break;
667                     }
668                 }
669                 else
670                 {
671                     if(call()->_flags.check(flag))
672                     {
673                         quit = true;
674                         break;
675                     }
676 
677                 }
678             }
679 
680             /* decrement timeout, zero "sleeps". */
681             timeout = (timeout > 0) ? timeout - 1 : timeout;
682             sleeps = 0;
683         }
684 
685         /* pvt should always be locked when retuning here. */
686         return pvt_locked;
687     }
688 
targetBoard::KhompPvt689     K3LAPIBase::GenericTarget & target()
690     {
691         return _target;
692     }
693 
sessionBoard::KhompPvt694     void session(switch_core_session_t * newSession)
695     {
696         _session = newSession;
697     }
698 
sessionBoard::KhompPvt699     switch_core_session_t * session()
700     {
701         return _session;
702     }
703 
ownerBoard::KhompPvt704     void owner(switch_core_session_t * owner) { _owner = owner; }
705 
ownerBoard::KhompPvt706     switch_core_session_t * owner() const { return _owner; }
707 
708     /*
709        Returns the FreeSWITCH channel (switch_channel_t)
710        Can throw InvalidSwitchChannel, so must be carefull
711     */
getFSChannelBoard::KhompPvt712     switch_channel_t * getFSChannel()
713     {
714         if(!session())
715             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t");
716         switch_channel_t *c = switch_core_session_get_channel(session());
717         if(!c)
718             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_CHANNEL,"null switch_channel_t obtained");
719         return c;
720     }
721 
722     /*
723        Returns the FreeSWITCH channel (switch_channel_t) from given session
724        Can throw InvalidSwitchChannel, so must be carefull
725     */
getFSChannelBoard::KhompPvt726     static switch_channel_t * getFSChannel(switch_core_session_t * s)
727     {
728         if(!s)
729             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t passed");
730         switch_channel_t *c = switch_core_session_get_channel(s);
731         if(!c)
732             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_CHANNEL,"null switch_channel_t obtained");
733         return c;
734     }
735 
736     /*
737        Returns the FreeSWITCH partner session (switch_core_session_t)
738        Can throw InvalidSwitchChannel, so must be carefull
739        Don't forget to unlock [unlockPartner(switch_core_session_t*)]
740     */
getFSLockedPartnerSessionBoard::KhompPvt741     switch_core_session_t * getFSLockedPartnerSession()
742     {
743         if(!session())
744             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t");
745 
746         switch_core_session_t * p_s = NULL;
747         switch_core_session_get_partner(session(), &p_s);
748 
749         if(!p_s)
750             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t (partner)");
751         return p_s;
752     }
753 
754     /* Unlock a partner session */
unlockPartnerBoard::KhompPvt755     void unlockPartner(switch_core_session_t * s)
756     {
757         if(!s)
758             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t passed");
759         switch_core_session_rwunlock(s);
760     }
761 
762     /* Get the uuid of session */
getUUIDBoard::KhompPvt763     char * getUUID()
764     {
765         if(!session())
766             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t");
767         return switch_core_session_get_uuid(session());
768     }
769 
770     /* Get the uuid of given session */
getUUIDBoard::KhompPvt771     char * getUUID(switch_core_session_t * s)
772     {
773         if(!s)
774             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_SESSION_PASSED,"null switch_core_session_t passed");
775         return switch_core_session_get_uuid(s);
776     }
777 
778     /*
779        Set a variable into a FS channel
780        Can throw InvalidSwitchChannel, so must be carefull
781     */
setFSChannelVarBoard::KhompPvt782     void setFSChannelVar(const char * name, const char * value)
783     {
784         if(!name || !value)
785             return;
786 
787         switch_channel_t *c = getFSChannel(_owner);
788         if(!c)
789             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_CHANNEL,"null switch_channel_t obtained");
790         switch_channel_set_variable(c,name,value);
791     }
792 
793    /*
794       Set a variable into a FS channel
795       Can throw InvalidSwitchChannel, so must be carefull
796    */
setFSChannelVarBoard::KhompPvt797    void setFSChannelVar(switch_channel_t *c, const char * name, const char * value)
798    {
799        if(!c)
800            throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_CHANNEL,"null switch_channel_t obtained");
801 
802        if(!name || !value)
803            return;
804 
805        switch_channel_set_variable(c,name,value);
806    }
807 
808     /*
809        Get a varibale from a FS channel
810        Can throw InvalidSwitchChannel, so must be carefull
811     */
getFSChannelVarBoard::KhompPvt812     const char * getFSChannelVar(const char * value)
813     {
814         if(!value)
815             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_VALUE, "value is null");
816 
817         switch_channel_t *c = getFSChannel(_owner);
818         if(!c)
819             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_CHANNEL,"null switch_channel_t obtained");
820         return switch_channel_get_variable(c,value);
821     }
822 
823     /*
824        Get a varibale from a FS from a given channel
825        Can throw InvalidSwitchChannel, so must be carefull
826     */
getFSChannelVarBoard::KhompPvt827     const char * getFSChannelVar(switch_channel_t *c, const char * value)
828     {
829         if(!c)
830             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_CHANNEL,"null switch_channel_t obtained");
831         if(!value)
832             throw InvalidSwitchChannel(InvalidSwitchChannel::NULL_SWITCH_VALUE, "value is null");
833         return switch_channel_get_variable(c,value);
834     }
835 
836     /* Set a global variable  (without any throw) */
setFSGlobalVarBoard::KhompPvt837     void setFSGlobalVar(const char * name, const char * value)
838     {
839         if(!name || !value)
840             return;
841 
842         switch_core_set_variable(name,value);
843     }
844 
845     /* Get a global variable (without any throw) */
getFSGlobalVarBoard::KhompPvt846     const char * getFSGlobalVar(const char * name)
847     {
848         if(!name)
849             return NULL;
850 
851 #if SWITCH_LESS_THAN(1,0,6)
852         const char * tmp = switch_core_get_variable(name);
853 
854         if(!tmp) return NULL;
855 
856         const char * val = strdup(tmp);
857 
858         return val;
859 #else
860         return switch_core_get_variable_dup(name);
861 #endif
862     }
863 
freeFSGlobalVarBoard::KhompPvt864     void freeFSGlobalVar(const char ** val)
865     {
866         if(!val || !*val) return;
867 
868 #if SWITCH_LESS_THAN(1,0,6)
869         free((void *)*val);
870         *val = NULL;
871 #else
872         char * v = (char *)*val;
873         switch_safe_free(v);
874         *val=NULL;
875 #endif
876     }
877 
mixerBoard::KhompPvt878     bool mixer(const char *file, const char *func, int line,
879             byte track, KMixerSource src, int32 index)
880     {
881         KMixerCommand mix;
882 
883         mix.Track = track;
884         mix.Source = src;
885         mix.SourceIndex = index;
886 
887         return command(file, func, line, CM_MIXER, (const char *)&mix);
888     }
889 
890     /* Error handling for send command */
891     bool command(const char *file, const char *func, int line, int code,
892             const char *params = NULL, bool log = true)
893     {
894         try
895         {
896             Globals::k3lapi.command(_target, code, params);
897         }
catchBoard::KhompPvt898         catch(K3LAPI::failed_command & e)
899         {
900             if(log)
901             {
902                 DBG(FUNC, OBJ_FMT(e.dev,e.obj,"Command '%s' has failed with error '%s'")
903                    % Verbose::commandName(e.code).c_str()
904                    % Verbose::status((KLibraryStatus)e.rc).c_str());
905             }
906 
907             return false;
908         }
909 
910         return true;
911     }
912 
913     //TODO: Unir os dois metodos
914     int commandState(const char *file, const char *func, int line, int code,
915             const char *params = NULL)
916     {
917         try
918         {
919             Globals::k3lapi.command(_target, code, params);
920         }
catchBoard::KhompPvt921         catch(K3LAPI::failed_command & e)
922         {
923             LOG(ERROR,OBJ_FMT(e.dev,e.obj,"Command '%s' has failed with error '%s'")
924                % Verbose::commandName(e.code).c_str()
925                % Verbose::status((KLibraryStatus)e.rc).c_str());
926 
927             return e.rc;
928         }
929 
930         return ksSuccess;
931     }
932 
933     /*!
934      \brief Will init part of our private structure and setup all the read/write
935      buffers along with the proper codecs. Right now, only PCMA.
936     */
937     switch_status_t justAlloc(bool is_answering = true, switch_memory_pool_t **pool = NULL);
938     switch_status_t justStart(switch_caller_profile_t *profile = NULL);
939 
940     void destroy(switch_core_session_t * s = NULL);
941     void destroyAll();
942 
943     void doHangup();
944 
945     void setHangupCause(int cause, bool set_now = false)
946     {
947         if(_call->_hangup_cause)
948         {
949 
950             DBG(FUNC,PVT_FMT(_target,"cause already set to %s") % switch_channel_cause2str((switch_call_cause_t) _call->_hangup_cause));
951             return;
952         }
953 
954         if(!session())
955         {
956             DBG(FUNC,PVT_FMT(_target,"session is null"));
957             return;
958         }
959 
960         switch_channel_t * channel = NULL;
961 
962         try
963         {
964             channel = getFSChannel();
965         }
catchBoard::KhompPvt966         catch(Board::KhompPvt::InvalidSwitchChannel & err)
967         {
968             DBG(FUNC, PVT_FMT(_target,"%s") % err._msg.c_str());
969             return;
970         }
971 
972         int cause_from_freeswitch = switch_channel_get_cause(channel);
973         if(cause_from_freeswitch != SWITCH_CAUSE_NONE)
974         {
975             DBG(FUNC,PVT_FMT(_target,"cause already set to %s from freeswitch") % switch_channel_cause2str((switch_call_cause_t)cause_from_freeswitch));
976             _call->_hangup_cause = cause_from_freeswitch;
977             return;
978         }
979 
980         if(!cause)
981         {
982             DBG(FUNC,PVT_FMT(_target,"cause not defined"));
983         }
984         else
985         {
986             DBG(FUNC,PVT_FMT(_target,"setting cause to '%s'") % switch_channel_cause2str((switch_call_cause_t) cause));
987             _call->_hangup_cause = cause;
988 
989             // not set variable in channel owner
990             if(set_now)
991             {
992                 switch_channel_hangup(channel, (switch_call_cause_t)_call->_hangup_cause);
993             }
994         }
995     }
996 
997     bool startCadence(CadencesType type);
stopCadenceBoard::KhompPvt998     bool stopCadence()
999     {
1000         if(call()->_cadence != PLAY_NONE)
1001         {
1002             call()->_cadence = PLAY_NONE;
1003 
1004             command(KHOMP_LOG, CM_STOP_CADENCE);
1005         }
1006     }
1007 
1008     bool startStream(bool enable_mixer = true);
1009     bool stopStream(bool enable_mixer = true);
1010 
1011     bool startListen(bool conn_rx = true);
1012     bool stopListen(void);
1013 
1014     bool obtainRX(bool with_delay = false);
1015     bool obtainTX();
1016 
1017     bool setVolume(const char * type, int volume);
setVolumeBoard::KhompPvt1018     bool setVolume()
1019     {
1020         bool ret = false;
1021 
1022         if(_call->_input_volume >= -10 && _call->_input_volume <= 10)
1023         {
1024             setVolume("input", _call->_input_volume);
1025             ret = true;
1026         }
1027 
1028         if(_call->_output_volume >= -10 && _call->_output_volume <= 10)
1029         {
1030             setVolume("output", _call->_output_volume);
1031             ret = true;
1032         }
1033 
1034         return ret;
1035     }
1036 
getSignalingBoard::KhompPvt1037     KSignaling getSignaling(void)
1038     {
1039         return Globals::k3lapi.channel_config(_target.device,_target.object).Signaling;
1040     }
1041 
1042     std::string getStateString(void);
1043 
1044     bool dtmfSuppression(bool enable);
1045     bool echoCancellation(bool enable);
1046     virtual bool autoGainControl(bool enable);
1047 
1048     /* Timer callbacks */
1049     static void pbxRingGen(Board::KhompPvt * pvt);
1050     static void coRingGen(Board::KhompPvt * pvt);
1051 
1052     virtual int getActiveChannel(bool invalid_as_not_found);
1053 
1054     /* Let's validate the contexts */
1055     virtual bool validContexts(MatchExtension::ContextListType & contexts,
1056                                std::string extra_string = "")
1057     {
1058         DBG(FUNC,PVT_FMT(_target,"c"));
1059 
1060         /*
1061         if(!_group_context.empty())
1062         {
1063             contexts.insert(contexts.begin(), _group_context);
1064             //contexts.push_back(_group_context);
1065         }
1066         */
1067 
1068         for (MatchExtension::ContextListType::iterator i = contexts.begin(); i != contexts.end(); i++)
1069             replaceTemplate((*i),  "DD", _target.device);
1070 
1071         BEGIN_CONTEXT
1072         {
1073             const K3L_DEVICE_CONFIG & dev_cfg = Globals::k3lapi.device_config(_target);
1074 
1075             for (MatchExtension::ContextListType::iterator i = contexts.begin(); i != contexts.end(); i++)
1076                 replaceTemplate((*i), "SSSS", atoi(dev_cfg.SerialNumber));
1077         }
1078         END_CONTEXT
1079 
1080         DBG(FUNC,PVT_FMT(_target,"r"));
1081         return true;
1082     }
1083 
cleanupBuffersBoard::KhompPvt1084     void cleanupBuffers()
1085     {
1086         DBG(FUNC,PVT_FMT(_target, "Cleanup buffers"));
1087 
1088         _reader_frames.clear();
1089         _writer_frames.clear();
1090 
1091         for(unsigned int i = 0; i < SILENCE_PACKS; i++)
1092         {
1093             /* add silence to the read buffer */
1094             if (!_reader_frames.give((const char *)Board::_cng_buffer, Globals::switch_packet_size))
1095             {
1096                 LOG(ERROR, PVT_FMT(target(), "Problem in Reader Buffer"));
1097             }
1098 
1099             /* add silence to the writer buffer */
1100             if (!_writer_frames.give((const char *)Board::_cng_buffer, Globals::boards_packet_size))
1101             {
1102                 LOG(ERROR, PVT_FMT(target(), "Problem in Writer Buffer"));
1103             }
1104         }
1105     }
1106 
freeStateBoard::KhompPvt1107     bool freeState()
1108     {
1109         return !(_call->_flags.check(Kflags::IS_INCOMING) || _call->_flags.check(Kflags::IS_OUTGOING));
1110     }
1111 
callBoard::KhompPvt1112     Call * call() { return _call; }
1113 
1114     K3LAPIBase::GenericTarget           _target;    /*!< The device/channel pair to bind this pvt to */
1115     ChanLockType             _mutex;     /*!< Used for *our* internal locking */
1116     Call                    *_call;
1117     switch_core_session_t   *_session;   /*!< The session to which this pvt is associated with */
1118     switch_core_session_t   *_owner;
1119     bool                     _has_fail;
1120 
1121     switch_caller_profile_t *_caller_profile;
1122 
1123     switch_codec_t           _read_codec;
1124     switch_codec_t           _write_codec;
1125 
1126     FrameSwitchManager       _reader_frames;
1127     FrameBoardsManager       _writer_frames;
1128 
1129     std::string              _group_context;
1130 
1131     PvtStatistics           *_pvt_statistics;
1132 
1133     std::string              _mohclass;
1134     std::string              _language;
1135     std::string              _accountcode;
1136 };
1137 
1138 /******************************************************************************/
1139 /******************************************************************************/
1140     typedef std::vector < Board * >    VectorBoard;
1141     typedef std::vector < KhompPvt * > VectorChannel;  /*!< Collection of pointers of KhompPvts */
1142 
1143      /*
1144         these (below) are going to rule the elements ordering in our multiset
1145         (used as a "ordering-save priority queue").
1146      */
1147     struct PvtCallCompare
1148     {
operatorBoard::PvtCallCompare1149         bool operator() (KhompPvt * pvt1, KhompPvt * pvt2) const
1150         {
1151             /* true if pvt1 precedes pvt2 */
1152             return (Board::getStats(pvt1->target().device,pvt1->target().object,kcsiOutbound) <
1153                     Board::getStats(pvt2->target().device,pvt2->target().object,kcsiOutbound));
1154             return true;
1155         }
1156     };
1157 
1158     typedef std::multiset< KhompPvt *, PvtCallCompare > PriorityCallQueue;
1159 
1160 public:
1161 
1162     /* Board constructor */
BoardBoard1163     Board(int id) : _device_id(id) {}
1164 
1165     /* Board destructor */
~BoardBoard1166     virtual ~Board() {}
1167 
idBoard1168     int id() { return _device_id; }
1169 
channelBoard1170     KhompPvt * channel(int obj)
1171     {
1172         try
1173         {
1174             KhompPvt * pvt = _channels.at(obj);
1175 
1176             if(!pvt)
1177             {
1178                 throw;
1179             }
1180 
1181             return pvt;
1182         }
1183         catch (...)
1184         {
1185             throw K3LAPITraits::invalid_channel(_device_id, obj);
1186         }
1187     }
1188 
chanEventHandlerBoard1189     ChanEventHandler * chanEventHandler() { return _event_handler; }
1190 
chanCommandHandlerBoard1191     ChanCommandHandler * chanCommandHandler() { return _command_handler; }
1192 
1193     virtual void initializeChannels(void);
1194     void finalizeChannels(void);
1195 
eventHandlerBoard1196     virtual bool eventHandler(const int obj, K3L_EVENT *e)
1197     {
1198         DBG(STRM, D("(Generic Board) c"));
1199 
1200         bool ret = true;
1201 
1202         switch(e->Code)
1203         {
1204         case EV_REQUEST_DEVICE_SECURITY_KEY:
1205             break;
1206         default:
1207             try
1208             {
1209                 ret = channel(obj)->eventHandler(e);
1210             }
1211             catch (K3LAPITraits::invalid_channel & invalid)
1212             {
1213                 LOG(ERROR, OBJ_FMT(_device_id,obj,"(Generic Board) r (invalid channel on event '%s')")
1214                 % Verbose::eventName(e->Code).c_str());
1215 
1216                 return false;
1217             }
1218 
1219             break;
1220         }
1221 
1222         DBG(STRM, D("(Generic Board) r"));
1223 
1224         return ret;
1225     }
1226 
1227 public:
1228     ChanTimer            _timers;
1229 protected:
1230     const int            _device_id;
1231     ChanEventHandler   * _event_handler; /* The device event handler */
1232     ChanCommandHandler * _command_handler; /* The device command handler */
1233     VectorChannel        _channels;
1234 
1235 public:
1236     /* static stuff */
1237     static bool initializeK3L(void);
1238     static bool finalizeK3L(void);
1239     static bool initializeHandlers(void);
1240     static bool finalizeHandlers(void);
1241     static void initializeBoards(void);
1242     static void finalizeBoards(void);
1243     static void initializeCngBuffer(void);
1244     static bool initialize(void);
1245     static bool finalize(void);
1246 
1247     /* Thread Device Event Handler */
1248     static int eventThread(void *);
1249 
1250     /* Thread Device Command Handler */
1251     static int commandThread(void *);
1252 
1253     /*!
1254       \brief Lookup channels and boards when dialed.
1255       \param allocation_string The dialstring as put on Dialplan. [Khomp/[a|A|0-board_high]/[a|A|0-channel_high]/dest].
1256       \param new_session Session allocated for this call.
1257       \param[out] cause Cause returned. Returns NULL if suceeded if not, the proper cause.
1258       \return KhompPvt to be used on the call.
1259       */
1260     static KhompPvt * find_channel(char* allocation_string, switch_core_session_t * new_session, switch_call_cause_t * cause);
1261     static void khomp_add_event_board_data(const K3LAPIBase::GenericTarget target, switch_event_t *event);
1262 
boardBoard1263     static Board * board(int dev)
1264     {
1265         try
1266         {
1267             Board * b = _boards.at(dev);
1268 
1269             if(!b)
1270             {
1271                 throw;
1272             }
1273 
1274             return b;
1275         }
1276         catch(...)
1277         {
1278             throw K3LAPITraits::invalid_device(dev);
1279         }
1280     }
1281 
getBoard1282     static KhompPvt * get(int32 device, int32 object)
1283     {
1284         //if (!Globals::k3lapi.valid_channel(device, object))
1285         //    throw K3LAPI::invalid_channel(device, object);
1286 
1287         try
1288         {
1289             return board(device)->channel(object);
1290         }
1291         catch(...)
1292         {
1293             throw K3LAPITraits::invalid_channel(device, object);
1294         }
1295     }
1296 
getBoard1297     static KhompPvt * get(K3LAPIBase::GenericTarget & target)
1298     {
1299         //if (!Globals::k3lapi.valid_channel(target.device, target.object))
1300         //    throw K3LAPI::invalid_channel(target.device, target.object);
1301 
1302         try
1303         {
1304             return board(target.device)->channel(target.object);
1305             //return KhompPvt::_pvts[target.device][target.object];
1306         }
1307         catch(...)
1308         {
1309             return NULL;
1310             //throw K3LAPI::invalid_channel(target.device, target.object);
1311         }
1312     }
1313 
getStatsBoard1314     static unsigned int getStats(int32 device, int32 object, uint32 index)
1315     {
1316         unsigned int stats = (unsigned int)-1;
1317 
1318         try
1319         {
1320             stats = Globals::k3lapi.channel_stats(device, object, index);
1321         }
1322         catch(K3LAPITraits::invalid_channel & err)
1323         {
1324         //K::logger::logg(C_WARNING, B(dev,channel, "Command get_stats has failed with error '%s'.") %
1325         //                          Verbose::status((KLibraryStatus) stt_res));
1326         }
1327 
1328         return stats;
1329     }
1330 
1331     static KhompPvt * queueFindFree(PriorityCallQueue &pqueue);
1332     static void queueAddChannel(PriorityCallQueue &pqueue, unsigned int board, unsigned int object);
1333     static KhompPvt * findFree(unsigned int board, unsigned int object, bool fully_available = true);
1334     static void applyGlobalVolume(void);
1335 
1336 public:
1337 
1338     static VectorBoard     _boards;
1339     static char            _cng_buffer[Globals::cng_buffer_size];
1340 
1341     static Kommuter        kommuter;
1342 
1343 };
1344 
1345 /******************************************************************************/
1346 /******************************************************************************/
1347 /******************************************************************************/
1348 
1349 #endif /* _KHOMP_PVT_H_*/
1350