📄 quicknetdevice.cxx
字号:
/* ==================================================================== * The Vovida Software License, Version 1.0 * * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The names "VOCAL", "Vovida Open Communication Application Library", * and "Vovida Open Communication Application Library (VOCAL)" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact vocal@vovida.org. * * 4. Products derived from this software may not be called "VOCAL", nor * may "VOCAL" appear in their name, without prior written * permission of Vovida Networks, Inc. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * ==================================================================== * * This software consists of voluntary contributions made by Vovida * Networks, Inc. and many individuals on behalf of Vovida Networks, * Inc. For more information on Vovida Networks, Inc., please see * <http://www.vovida.org/>. * */static const char* const QuickNetDevice_cxx_Version = "$Id: QuickNetDevice.cxx,v 1.44 2002/03/19 21:40:16 jason Exp $";#include "global.h"/* stdlib */#include <iostream>#include <cstdio>#include <unistd.h>#include <sys/ioctl.h>#include <csignal>/* sockets */#include <sys/types.h>#include <sys/socket.h>#include <fcntl.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <cerrno>#include <linux/telephony.h>#include <linux/ixjuser.h>#include "VTime.hxx"#include "Rtp.hxx"#include "VCondition.h"/* error handling */#include "QuickNetDevice.hxx"#include "UaDeviceEvent.hxx"#include "UaConfiguration.hxx"// #define ULAW_PAYLOAD 1012#define ULAW_SAMPLE_RATE 240#define RESID_RTP_RATE 240#define NETWORK_RTP_RATE 160#define FLASH_TIMEOUT 1000000using namespace Vocal;//***************************************************************************// QuickNetDevice::QuickNetDevice// description: Constructor. opens the quicknet device. initialize the// hookstate. resets the device to get it ready.//***************************************************************************QuickNetDevice::QuickNetDevice( const char* deviceName, Sptr < Fifo < Sptr < SipProxyEvent > > > inputQ, Sptr < Fifo < Sptr < SipProxyEvent > > > outputQ ) : ResGwDevice( deviceName, inputQ, outputQ ), audioStack(0), inRtpPkt(0), ringbackFd( -1), sendRingback(false), cwBeep( 0 ), cwCadenceOn( true ), cwCnt( 0 ), cwBeepOn( false ){ // initialize queues sessionQ = outputQ; myQ = inputQ; // open hardware device myFD = open(deviceName, O_RDWR); if (myFD < 0) { cpLog( LOG_ERR, "Cannot open %s", deviceName ); exit( 1 ); } // close and reopen to make sure device is not in a funny state ioctl(myFD, IXJCTL_PORT, PORT_POTS); ioctl(myFD, PHONE_REC_STOP); ioctl(myFD, PHONE_PLAY_STOP); ioctl(myFD, IXJCTL_AEC_STOP); close(myFD); myFD = open(deviceName, O_RDWR); ioctl(myFD, IXJCTL_PORT, PORT_POTS); if (myFD < 0) { cpLog( LOG_ERR, "Cannot re-open ", deviceName ); exit(1); } // initialize hookstate if (ioctl(myFD, PHONE_HOOKSTATE)) hookStateOffhook = true; else hookStateOffhook = false; // store the device name. myDeviceName = deviceName; stdinFD = fileno( stdin ); tcgetattr( stdinFD, &initialTerm ); struct termios termAttr = initialTerm; termAttr.c_lflag &= ~ICANON; termAttr.c_cc[VMIN] = 1; termAttr.c_cc[VTIME] = 0; if ( tcsetattr( stdinFD, TCSANOW, &termAttr ) != 0 ) { cerr << "Unable to set new terminal mode." << endl; exit( -1 ); } if( fcntl( stdinFD, F_SETFL, O_NONBLOCK ) == -1 ) { cerr << "Unable to set stdin mode." << endl; exit( -1 ); } // read in the call waiting beep from tone file cpLog( LOG_DEBUG, "reading call waiting beep from Tone/cwbeep" ); int cwBeepFd = open("Tone/cwbeep", O_RDONLY); if ( cwBeepFd == -1 ) { cpLog( LOG_ERR, "can not open Tone/cwbeep" ); return; } cwBeep = new char[CWBEEP_ON_TIME]; int nsample = read(cwBeepFd, cwBeep, CWBEEP_ON_TIME); if ( nsample != CWBEEP_ON_TIME ) cpLog( LOG_ERR, "Not enougth call waiting beep samples read" ); else cpLog( LOG_DEBUG, "call waiting beep successfully read"); close(cwBeepFd);} // end QuickNetDevice::QuickNetDevice()//***************************************************************************// QuickNetDevice::~QuickNetDevice// description: Destructor//***************************************************************************QuickNetDevice::~QuickNetDevice(){ close(myFD); tcsetattr( stdinFD, TCSANOW, &initialTerm );} // end QuickNetDevice::~QuickNetDevice()//***************************************************************************// QuickNetDevice::hardwareMain// description: main processing loop of the hardware//***************************************************************************void*QuickNetDevice::hardwareMain (void* parms){ // process forever on behalf of QuickNetDevice hardware fd_set readfds; struct timeval tv; int retval; int maxFd = 128; // should be 0 then increment as needed when addToFdSet // reset file descriptor FD_ZERO(&readfds); addToFdSet(&readfds); /** * block on select for asyncronous events, but poll to process * audio and signal requests from endpoints in message queue * The following values were optimized by trial and error for * the quicknet ixj-0.3.18gpl drivers. */ tv.tv_sec = 0; tv.tv_usec = 300; if((retval = select(maxFd, &readfds, 0, 0, &tv)) < 0) { cpLog( LOG_ERR, "select() returned error %d", retval ); } else { if(process(&readfds) < 0) { cpLog( LOG_ERR, "hardware encountered an error" ); assert(0); } } if( myQ->size() > 0 ) { cpLog( LOG_DEBUG, "Signal from call control" ); processSessionMsg( myQ->getNext() ); } return 0;} // end QuickNetDevice::hardwareMain()voidQuickNetDevice::processUrlInput(){ char keystroke = '\0'; if( read( stdinFD, &keystroke, 1 ) == 0 ) { return; } if( !hookStateOffhook ) { cpLog( LOG_ERR, "The phone must be off-hook first" ); return; // Ignore input is it is not off hook } Sptr < UaDeviceEvent > event = new UaDeviceEvent( sessionQ ) ; assert( event != 0 ); if(myEntryState == EntryStateEnterUrl) { // the user is entering a URL, so do something else switch( keystroke ) { case '\x08': case '\x7F': // Backspace (\b) or Delete (DEL) { if( myTextEntry.length() > 0 ) { // xxx this is lame cout << "\b \b\b \b\b \b"; cout.flush(); myTextEntry.setchar(myTextEntry.length() - 1, ' '); myTextEntry.removeSpaces(); } else { cout << "\b \b\b \b"; cout.flush(); } break; } case '\x0A': // Line Feed (\n) -> done { cpLog( LOG_DEBUG, "URL is %s", myTextEntry.logData() ); event->type = DeviceEventCallUrl; event->text = myTextEntry; myTextEntry = ""; myEntryState = EntryStateTelephoneUI; break; } default: { if( keystroke >= '\x20' && keystroke <= '\x7E' ) { // Append a character between ' ' and '~' myTextEntry.setchar(myTextEntry.length(), keystroke); } else { // Treat everything else as abort // e.g. '\1B': // Esc -> abort myTextEntry = ""; myEntryState = EntryStateTelephoneUI; cout << endl; cpLog( LOG_DEBUG, "Abort URL input" ); } break; } } } if (event->type != DeviceEventUndefined) { assert( sessionQ != 0 ); event->callId = callId; sessionQ->add( event ); }}//***************************************************************************// QuickNetDevice::process//// description: process any events detected on the hardware (dtmf digit// press, onhook & offhook, etc.) and reports the events back// to the session via the fifo queue.//***************************************************************************intQuickNetDevice::process (fd_set* fd){ if( FD_ISSET( stdinFD, fd ) ) { processUrlInput(); FD_CLR( stdinFD, fd ); } vusleep(0); // needed to stabilize hookstate readings deviceMutex.lock(); if( ioctl(myFD, PHONE_HOOKSTATE) ) { // user is offhook if( hookStateOffhook == false ) { // report change from onhook to offhook hookStateOffhook = true; reportEvent(sessionQ, DeviceEventHookUp); } // check for DTMF digit from hardware if (ioctl(myFD, PHONE_DTMF_READY)) { // new digit switch (ioctl(myFD, PHONE_GET_DTMF)) { case 0x01: reportEvent(sessionQ, DeviceEventDtmf1); break; case 0x02: reportEvent(sessionQ, DeviceEventDtmf2); break; case 0x03: reportEvent(sessionQ, DeviceEventDtmf3); break; case 0x04: reportEvent(sessionQ, DeviceEventDtmf4); break; case 0x05: reportEvent(sessionQ, DeviceEventDtmf5); break; case 0x06: reportEvent(sessionQ, DeviceEventDtmf6); break; case 0x07: reportEvent(sessionQ, DeviceEventDtmf7); break; case 0x08: reportEvent(sessionQ, DeviceEventDtmf8); break; case 0x09: reportEvent(sessionQ, DeviceEventDtmf9); break; case 0x0A: reportEvent(sessionQ, DeviceEventDtmfStar); break; case 0x0B: reportEvent(sessionQ, DeviceEventDtmf0); break; case 0x0C: reportEvent(sessionQ, DeviceEventDtmfHash); break; default: cpLog(LOG_ERR, "Unrecognized DTMF digit from hardware"); break; } } } else { if( hookStateOffhook == true ) { vusleep(20000); // needed to stabilize hookstate readings // read the hookstate again to make sure an onhook has been generated if( !ioctl(myFD, PHONE_HOOKSTATE) ) { deviceMutex.unlock(); onhookOrFlash(); deviceMutex.lock(); } } } deviceMutex.unlock(); return 0;} // end QuickNetDevice::process()//***************************************************************************// QuickNetDevice::processRTP// description: main processing loop for RTP//***************************************************************************voidQuickNetDevice::processRTP (){ deviceMutex.lock(); if (audioStack == 0) { vusleep(10000); deviceMutex.unlock(); return ; } RtpSessionState sessionState = audioStack->getSessionState(); //only process receive if receive is enabled if ( sessionState == rtp_session_recvonly || sessionState == rtp_session_sendrecv ) { inRtpPkt = audioStack->receive (); if (inRtpPkt) { if (inRtpPkt->getPayloadUsage() != RESID_RTP_RATE) cpLog(LOG_DEBUG, "RtpStack API size doesn't match RESID_RTP_RATE"); char *vBuf; if ( cwBeepOn ) { vBuf = getCwBeep(); if ( vBuf == 0 ) vBuf = inRtpPkt->getPayloadLoc(); } else { vBuf = inRtpPkt->getPayloadLoc(); } write (myFD, vBuf, RESID_RTP_RATE); // need to delete since RTP stack doesn't do it any more delete inRtpPkt; inRtpPkt = NULL; } } if ( sessionState == rtp_session_sendonly || sessionState == rtp_session_sendrecv ) { // cc = number of samples int cc; if ( sendRingback ) { cc = getRingbackTone( outBufferPkt, RESID_RTP_RATE); } else { cc = read(myFD, outBufferPkt, RESID_RTP_RATE); } if (audioStack) { audioStack->transmitRaw (outBufferPkt, cc); } } if (audioStack && sessionState != rtp_session_inactive && sessionState != rtp_session_undefined ) { audioStack->processRTCP(); } deviceMutex.unlock();}intQuickNetDevice::addToFdSet (fd_set* fd){ if ( ( audioActive || audioHalfActive ) && audioStack )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -