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