t38modem-2.0.0/0000775000076400007640000000000011551247020014020 5ustar frolovfrolov00000000000000t38modem-2.0.0/h323lib/0000775000076400007640000000000011551247020015166 5ustar frolovfrolov00000000000000t38modem-2.0.0/h323lib/h323ep.h0000664000076400007640000001026311342244531016347 0ustar frolovfrolov00000000000000/* * h323ep.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: h323ep.h,v $ * Revision 1.24 2010/02/27 16:33:29 vfrolov * Implemented --bearer-capability option * * Revision 1.23 2009/07/29 10:39:04 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.22 2009/07/15 13:23:19 vfrolov * Added Descriptions(args) * * Revision 1.21 2008/09/11 16:10:54 frolov * Ported to H323 Plus trunk * * Revision 1.20 2007/05/17 08:32:44 vfrolov * Moved class T38Modem from main.h and main.cxx to main_process.cxx * * Revision 1.19 2007/05/10 10:40:33 vfrolov * Added ability to continuously resend last UDPTL packet * * Revision 1.18 2007/03/23 10:14:35 vfrolov * Implemented voice mode functionality * * Revision 1.17 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.16 2005/04/28 09:12:06 vfrolov * Made tidy up * * Revision 1.15 2003/12/19 15:25:27 vfrolov * Removed class AudioDelay (utilized PAdaptiveDelay) * Renamed pmodemQ to pmodem_pool * Fixed modem loss on no route * * Revision 1.14 2003/04/07 06:52:46 vfrolov * Fixed --save option * * Revision 1.13 2002/11/18 22:57:53 craigs * Added patches from Vyacheslav Frolov for CORRIGENDUM * * Revision 1.12 2002/11/05 13:46:51 vfrolov * Added missed --username option to help * Utilized "localpartyname" option from "dial" request * * Revision 1.11 2002/05/22 12:01:39 vfrolov * Implemented redundancy error protection scheme * * Revision 1.10 2002/05/16 00:11:02 robertj * Changed t38 handler creation function for new API * * Revision 1.9 2002/05/15 16:17:34 vfrolov * Implemented per modem routing for I/C calls * * Revision 1.8 2002/04/30 11:05:32 vfrolov * Implemented T.30 Calling Tone (CNG) generation * * Revision 1.7 2002/03/22 09:39:47 vfrolov * Removed obsoleted option -f * * Revision 1.6 2002/03/01 09:45:11 vfrolov * Added Copyright header * Added sending "established" command on connection established * Implemented mode change on receiving "requestmode" command * Added setting lastReadCount * Added some other changes * * Revision 1.5 2002/02/12 11:25:29 vfrolov * Removed obsoleted code * * Revision 1.4 2002/01/10 06:10:02 craigs * Added MPL header * */ #ifndef _H323EP_H #define _H323EP_H #include class PseudoModem; class PseudoModemQ; class MyH323Connection; class MyH323EndPoint : public H323EndPoint { PCLASSINFO(MyH323EndPoint, H323EndPoint); public: MyH323EndPoint(); static PString ArgSpec(); static PStringArray Descriptions(); static PStringArray Descriptions(const PConfigArgs & args); static PBoolean Create(const PConfigArgs &args); // overrides from H323EndPoint virtual H323Connection * CreateConnection(unsigned callReference, void *userData); // new functions PBoolean Initialise(const PConfigArgs &args); const PIntArray &BearerCapability() const { return bearerCapability; } PseudoModem * PMAlloc(const PString &number) const; void PMFree(PseudoModem *pmodem) const; void SetOptions(MyH323Connection &conn, OpalT38Protocol *t38handler) const; protected: PseudoModemQ *pmodem_pool; PStringArray routes; WORD connectPort; PIntArray bearerCapability; int in_redundancy; int ls_redundancy; int hs_redundancy; int re_interval; PBoolean old_asn; PDECLARE_NOTIFIER(PObject, MyH323EndPoint, OnMyCallback); }; #endif // _H323EP_H // End of File /////////////////////////////////////////////////////////////// t38modem-2.0.0/h323lib/t38modem_2005.vcproj0000664000076400007640000004554111455110341020530 0ustar frolovfrolov00000000000000 t38modem-2.0.0/h323lib/g7231_fake.cxx0000664000076400007640000000624011062241416017445 0ustar frolovfrolov00000000000000/* * g7231_fake.cxx * * Fake G.723.1 codec for T38FAX Pseudo Modem * * Copyright (c) 2001-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Equivalence Pty Ltd * * Contributor(s): * * $Log: g7231_fake.cxx,v $ * Revision 1.5 2008/09/11 16:10:54 frolov * Ported to H323 Plus trunk * * Revision 1.4 2005/02/03 11:32:11 vfrolov * Fixed MSVC compile warnings * * Revision 1.3 2003/12/18 13:16:46 vfrolov * Fixed all CPU usage * * Revision 1.2 2002/07/23 01:15:24 craigs * Added Capability clone function * * Revision 1.1 2002/04/30 04:17:54 craigs * Initial version * */ // mostly "stolen" from OpenAM #include #include #include #include "g7231_fake.h" #define G7231_BANDWIDTH 63 #define G7231_SAMPLES_PER_BLOCK 240 ////////////////////////////////////////////// G7231_Fake_Capability::G7231_Fake_Capability() : H323AudioCapability(8, 4) { } PObject * G7231_Fake_Capability::Clone() { return new G7231_Fake_Capability(*this); } PBoolean G7231_Fake_Capability::OnSendingPDU(H245_AudioCapability & cap, unsigned packetSize) const { // set the choice to the correct type cap.SetTag(GetSubType()); // get choice data H245_AudioCapability_g7231 & g7231 = cap; // max number of audio frames per PDU we want to send g7231.m_maxAl_sduAudioFrames = packetSize; // no silence suppression g7231.m_silenceSuppression = FALSE; return TRUE; } PBoolean G7231_Fake_Capability::OnReceivedPDU(const H245_AudioCapability & cap, unsigned & packetSize) { const H245_AudioCapability_g7231 & g7231 = cap; packetSize = g7231.m_maxAl_sduAudioFrames; return TRUE; } PObject * G7231_Fake_Capability::Clone() const { return new G7231_Fake_Capability(); } H323Codec * G7231_Fake_Capability::CreateCodec(H323Codec::Direction direction) const { return new G7231_Fake_Codec(direction); } /////////////////////////////////////////////////////////////// G7231_Fake_Codec::G7231_Fake_Codec(Direction dir) : H323AudioCodec("G.723.1", dir) { } int G7231_Fake_Codec::GetFrameLen(int val) { static const int frameLen[] = { 24, 20, 4, 1 }; return frameLen[val & 3]; } PBoolean G7231_Fake_Codec::Read(BYTE * /*buffer*/, unsigned & length, RTP_DataFrame &) { length = 0; delayRead.Delay((G7231_SAMPLES_PER_BLOCK*2)/16); return TRUE; } PBoolean G7231_Fake_Codec::Write(const BYTE * /* buffer */, unsigned length, const RTP_DataFrame & /* rtp */, unsigned & writtenLength) { writtenLength = length; delayWrite.Delay((G7231_SAMPLES_PER_BLOCK*2)/16); return TRUE; } unsigned G7231_Fake_Codec::GetBandwidth() const { return G7231_BANDWIDTH; } t38modem-2.0.0/h323lib/g7231_fake.h0000664000076400007640000000442711062241416017077 0ustar frolovfrolov00000000000000/* * g7231_fake.h * * Fake G.723.1 codec for T38FAX Pseudo Modem * * Copyright (c) 2001-2002 Equivalence Pty Ltd * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Equivalence Pty Ltd * * Contributor(s): Vyacheslav Frolov * * $Log: g7231_fake.h,v $ * Revision 1.4 2008/09/11 16:10:54 frolov * Ported to H323 Plus trunk * * Revision 1.3 2003/12/18 13:16:52 vfrolov * Fixed all CPU usage * * Revision 1.2 2002/07/23 01:15:24 craigs * Added Capability clone function * * Revision 1.1 2002/04/30 04:17:54 craigs * Initial version * */ // mostly "stolen" from OpenAM #ifndef _PM_G7231_FAKE_H #define _PM_G7231_FAKE_H #include #include #include class G7231_Fake_Codec : public H323AudioCodec { PCLASSINFO(G7231_Fake_Codec, H323AudioCodec); public: G7231_Fake_Codec(Direction dir); unsigned GetBandwidth() const; static int GetFrameLen(int val); PBoolean Read(BYTE * buffer, unsigned & length, RTP_DataFrame &); PBoolean Write(const BYTE * buffer, unsigned length, const RTP_DataFrame & rtp, unsigned & frames); protected: PAdaptiveDelay delayRead; PAdaptiveDelay delayWrite; }; class G7231_Fake_Capability : public H323AudioCapability { PCLASSINFO(G7231_Fake_Capability, H323AudioCapability) public: G7231_Fake_Capability(); PObject * Clone(); unsigned GetSubType() const { return H245_AudioCapability::e_g7231; } PString GetFormatName() const { return "G.723.1"; } H323Codec * CreateCodec(H323Codec::Direction direction) const; PBoolean OnSendingPDU(H245_AudioCapability & cap, unsigned packetSize) const; PBoolean OnReceivedPDU(const H245_AudioCapability & pdu, unsigned & packetSize); PObject * Clone() const; }; #endif t38modem-2.0.0/h323lib/t38modem_2005.sln0000664000076400007640000000205011443412675020021 0ustar frolovfrolov00000000000000Microsoft Visual Studio Solution File, Format Version 9.00 # Visual C++ Express 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "t38modem", "t38modem_2005.vcproj", "{5684EA1E-9DEC-4701-A81E-FBF8133A77B5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 No Trace|Win32 = No Trace|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Debug|Win32.ActiveCfg = Debug|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Debug|Win32.Build.0 = Debug|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.No Trace|Win32.ActiveCfg = No Trace|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.No Trace|Win32.Build.0 = No Trace|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Release|Win32.ActiveCfg = Release|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal t38modem-2.0.0/h323lib/t38modem_openh323_2005.sln0000664000076400007640000000206111453255517021445 0ustar frolovfrolov00000000000000Microsoft Visual Studio Solution File, Format Version 9.00 # Visual C++ Express 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "t38modem", "t38modem_openh323_2005.vcproj", "{5684EA1E-9DEC-4701-A81E-FBF8133A77B5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 No Trace|Win32 = No Trace|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Debug|Win32.ActiveCfg = Debug|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Debug|Win32.Build.0 = Debug|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.No Trace|Win32.ActiveCfg = No Trace|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.No Trace|Win32.Build.0 = No Trace|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Release|Win32.ActiveCfg = Release|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal t38modem-2.0.0/h323lib/audio_chan.cxx0000664000076400007640000000401311453122402017777 0ustar frolovfrolov00000000000000/* * audio_chan.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2010 Vyacheslav Frolov * * t38modem Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is t38modem. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: audio_chan.cxx,v $ * Revision 1.1 2010/10/06 16:34:10 vfrolov * Initial revision * */ #include #include "audio_chan.h" #include "../audio.h" #include "../pmodem.h" #define new PNEW /////////////////////////////////////////////////////////////// ModemAudioChannel::ModemAudioChannel(PBoolean out, PseudoModem * pmodem) : audioEngine(pmodem->NewPtrAudioEngine()) , outDirection(out) { if (audioEngine != NULL) { if (outDirection) audioEngine->OpenOut(EngineBase::HOWNEROUT(this)); else audioEngine->OpenIn(EngineBase::HOWNERIN(this)); } } ModemAudioChannel::~ModemAudioChannel() { if (audioEngine != NULL) { if (outDirection) audioEngine->CloseOut(EngineBase::HOWNEROUT(this)); else audioEngine->CloseIn(EngineBase::HOWNERIN(this)); ReferenceObject::DelPointer(audioEngine); } } PBoolean ModemAudioChannel::Read(void * buffer, PINDEX amount) { if (!audioEngine->Read(EngineBase::HOWNEROUT(this), buffer, amount)) return FALSE; lastReadCount = amount; return TRUE; } PBoolean ModemAudioChannel::Write(const void * buffer, PINDEX len) { if (!audioEngine->Write(EngineBase::HOWNERIN(this), buffer, len)) return FALSE; lastWriteCount = len; return TRUE; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/h323lib/.cvsignore0000664000076400007640000000007011233350464017167 0ustar frolovfrolov00000000000000obj_* Debug Release NoTrace *.ncb *.vcproj.*.user *.suo t38modem-2.0.0/h323lib/h323ep.cxx0000664000076400007640000007071411453124673016740 0ustar frolovfrolov00000000000000/* * h323ep.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Equivalence Pty Ltd * * Contributor(s): Vyacheslav Frolov * * $Log: h323ep.cxx,v $ * Revision 1.56 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.55 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.54 2010/02/27 16:33:29 vfrolov * Implemented --bearer-capability option * * Revision 1.53 2009/07/29 10:39:04 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.52 2009/07/27 16:21:24 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.51 2009/07/15 13:23:19 vfrolov * Added Descriptions(args) * * Revision 1.50 2008/09/11 16:10:54 frolov * Ported to H323 Plus trunk * * Revision 1.49 2007/05/24 17:05:51 vfrolov * Fixed cout buffering delays * * Revision 1.48 2007/05/17 08:32:44 vfrolov * Moved class T38Modem from main.h and main.cxx to main_process.cxx * * Revision 1.47 2007/05/10 10:40:33 vfrolov * Added ability to continuously resend last UDPTL packet * * Revision 1.46 2007/03/23 10:14:35 vfrolov * Implemented voice mode functionality * * Revision 1.45 2007/03/01 14:03:06 vfrolov * Added ability to set range of ports to use * * Revision 1.44 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.43 2006/12/11 10:23:38 vfrolov * Fixed typo * * Revision 1.42 2006/04/29 20:31:28 dominance * add -v option to keep debian packaging happy ;) * * Revision 1.41 2005/04/28 09:12:07 vfrolov * Made tidy up * * Revision 1.40 2005/02/24 11:21:11 vfrolov * Added ability to put list to --route option * * Revision 1.39 2005/02/04 10:18:48 vfrolov * Fixed warnings for No Trace build * * Revision 1.38 2004/10/29 10:51:03 vfrolov * Added missing OS class to trace * Fixed MSVC compiler warning * * Revision 1.37 2004/10/27 13:22:42 vfrolov * Added flags to call to PTrace::Initialise() * Added version message to trace * * Revision 1.36 2004/10/20 13:40:03 vfrolov * Put date and time to trace * * Revision 1.35 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * Revision 1.34 2004/05/09 07:46:11 csoutheren * Updated to compile with new PIsDescendant function * * Revision 1.33 2003/12/19 15:25:06 vfrolov * Removed class AudioDelay (utilized PAdaptiveDelay) * Renamed pmodemQ to pmodem_pool * Fixed modem loss on no route * * Revision 1.32 2003/12/04 11:23:51 vfrolov * Added "h323:" prefix to remoteParty * * Revision 1.31 2003/04/14 07:04:33 vfrolov * Added missing --disable and --prefer options. * Reported by Alexandre Aractingi. * * Revision 1.30 2003/04/07 06:52:43 vfrolov * Fixed --save option * * Revision 1.29 2002/12/20 10:12:46 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.28 2002/11/18 22:57:53 craigs * Added patches from Vyacheslav Frolov for CORRIGENDUM * * Revision 1.27 2002/11/10 09:22:47 robertj * Moved constants for "well known" ports to better place (OPAL change). * * Revision 1.26 2002/11/05 13:46:44 vfrolov * Added missed --username option to help * Utilized "localpartyname" option from "dial" request * * Revision 1.25 2002/05/22 12:01:36 vfrolov * Implemented redundancy error protection scheme * * Revision 1.24 2002/05/16 00:11:02 robertj * Changed t38 handler creation function for new API * * Revision 1.23 2002/05/15 16:17:29 vfrolov * Implemented per modem routing for I/C calls * * Revision 1.22 2002/04/30 11:05:24 vfrolov * Implemented T.30 Calling Tone (CNG) generation * * Revision 1.21 2002/04/30 04:06:06 craigs * Added option to set G.723.1 codec * * Revision 1.20 2002/04/27 10:20:41 vfrolov * Added ability to disable listen for incoming calls * * Revision 1.19 2002/04/19 14:09:48 vfrolov * Enabled T.38 mode request for O/G connections * * Revision 1.18 2002/04/18 08:04:13 vfrolov * Disabled the in band DTMF detection * * Revision 1.17 2002/04/17 08:55:18 vfrolov * Utilized trace output of H323Channel::Direction enum * * Revision 1.16 2002/03/22 09:39:13 vfrolov * Removed obsoleted option -f * * Revision 1.15 2002/03/05 12:40:23 vfrolov * Changed class hierarchy * PseudoModem is abstract * PseudoModemBody is child of PseudoModem * Added PseudoModemQ::CreateModem() to create instances * * Revision 1.14 2002/03/01 09:45:03 vfrolov * Added Copyright header * Added sending "established" command on connection established * Implemented mode change on receiving "requestmode" command * Added setting lastReadCount * Added some other changes * * Revision 1.13 2002/02/12 11:25:23 vfrolov * Removed obsoleted code * * Revision 1.12 2002/02/11 08:48:43 vfrolov * Changed some trace and cout messages * * Revision 1.11 2002/01/10 06:16:00 craigs * Added muLaw codec as well as ALaw * * Revision 1.10 2002/01/10 06:09:47 craigs * Changed to use RequestModeChangeT38 * Added MPL header * */ #include #include #include #include "h323ep.h" #include "g7231_fake.h" #include "t38protocol.h" #include "audio_chan.h" #include "../enginebase.h" #include "../pmodem.h" #include "../drivers.h" #define new PNEW /////////////////////////////////////////////////////////////// class MyH323Connection : public H323Connection { PCLASSINFO(MyH323Connection, H323Connection); public: MyH323Connection(MyH323EndPoint &, unsigned); ~MyH323Connection(); PBoolean Attach(PseudoModem *_pmodem); // overrides from H323Connection AnswerCallResponse OnAnswerCall(const PString &, const H323SignalPDU &, H323SignalPDU &); PBoolean OnSendSignalSetup(H323SignalPDU & setupPDU); void OnEstablished(); PBoolean OnStartLogicalChannel(H323Channel & channel); void OnClosedLogicalChannel(const H323Channel & channel); void OnUserInputString(const PString & value); OpalT38Protocol * CreateT38ProtocolHandler(); PBoolean OpenAudioChannel(PBoolean, unsigned, H323AudioCodec & codec); protected: const MyH323EndPoint & ep; PMutex connMutex; PseudoModem * pmodem; EngineBase * userInputEngine; }; /////////////////////////////////////////////////////////////// PString MyH323EndPoint::ArgSpec() { return PseudoModemDrivers::ArgSpec() + "p-ptty:" "-route:" "-redundancy:" "-repeat:" "-old-asn." "F-fastenable." "T-h245tunneldisable." "-bearer-capability:" "G-g7231code." "D-disable:" "P-prefer:" "g-gatekeeper:" "n-no-gatekeeper." "-require-gatekeeper." "-no-require-gatekeeper." "i-interface:" "-no-interface." "-listenport:" "-no-listenport." "-connectport:" "-no-connectport." "-ports:" "u-username:" "-no-username." ; } PStringArray MyH323EndPoint::Descriptions() { PStringArray descriptions = PString( " -p --ptty [num@]tty[,...] : Pseudo ttys (mandatory).\n" " Can be used multiple times.\n" " If tty prefixed by num@ then tty will\n" " accept incoming calls only\n" " for numbers with prefix num.\n" " Use none@tty to disable incoming calls.\n" " See Drivers section for supported tty's formats.\n" " --route prefix@host[,...] : Route numbers with prefix num to host.\n" " Can be used multiple times.\n" " Discards prefix num from numbers.\n" " Use 'all' to route all numbers.\n" " Mandatory if not using GK.\n" " --redundancy I[L[H]] : Set redundancy for error recovery for\n" " (I)ndication, (L)ow speed and (H)igh\n" " speed IFP packets. I, L and H are digits.\n" " --repeat ms : Continuously resend last UDPTL packet each ms\n" " milliseconds.\n" " --old-asn : Use original ASN.1 sequence in T.38 (06/98)\n" " Annex A (w/o CORRIGENDUM No. 1 fix).\n" " -i --interface ip : Bind to a specific interface.\n" " --no-listenport : Disable listen for incoming calls.\n" " --listenport port : Listen on a specific port.\n" " --connectport port : Connect to a specific port.\n" " --ports T:B-M[,...] : For (T)ype set (B)ase and (M)ax ports to use.\n" " T is 'udp', 'rtp' or 'tcp'. B and M are numbers.\n" " -g --gatekeeper host : Specify gatekeeper host.\n" " -n --no-gatekeeper : Disable gatekeeper discovery.\n" " --require-gatekeeper : Exit if gatekeeper discovery fails.\n" " -F --fastenable : Enable fast start.\n" " -T --h245tunneldisable : Disable H245 tunnelling.\n" " --bearer-capability S:C:R:P\n" " : Bearer capability information element (Q.931)\n" " S - coding standard (0-3)\n" " C - information transfer capability (0-31)\n" " R - information transfer rate (1-127)\n" " P - user information layer 1 protocol (2-5).\n" " -G --g7231enable : Enable G.723.1 codec, rather than G.711.\n" " -D --disable codec : Disable the specified codec.\n" " Can be used multiple times.\n" " -P --prefer codec : Prefer the specified codec.\n" " Can be used multiple times.\n" " -u --username str : Set the local endpoint name to str.\n" "Drivers:\n" ).Lines(); PStringArray ds; ds = PseudoModemDrivers::Descriptions(); for (PINDEX i = 0 ; i < ds.GetSize() ; i++) descriptions.Append(new PString(PString(" ") + ds[i])); return descriptions; } PStringArray MyH323EndPoint::Descriptions(const PConfigArgs & /*args*/) { PStringArray descriptions; return descriptions; } PBoolean MyH323EndPoint::Create(const PConfigArgs &args) { MyH323EndPoint *endpoint = new MyH323EndPoint(); PString userName; if (args.HasOption('u')) userName = args.GetOptionString('u'); else userName = PProcess::Current().GetName() + " v" + PProcess::Current().GetVersion(); endpoint->SetLocalUserName(userName); if (!endpoint->Initialise(args)) return FALSE; // start the H.323 listener H323ListenerTCP * listener = NULL; if (!args.HasOption("no-listenport")) { PIPSocket::Address interfaceAddress(INADDR_ANY); WORD listenPort = H323EndPoint::DefaultTcpPort; if (args.HasOption("listenport")) listenPort = (WORD)args.GetOptionString("listenport").AsInteger(); if (args.HasOption('i')) interfaceAddress = PIPSocket::Address(args.GetOptionString('i')); listener = new H323ListenerTCP(*endpoint, interfaceAddress, listenPort); if (!endpoint->StartListener(listener)) { cout << "Could not open H.323 listener port on " << listener->GetListenerPort() << endl; delete listener; return FALSE; } } if (args.HasOption('g')) { PString gkName = args.GetOptionString('g'); if (endpoint->SetGatekeeper(gkName, new H323TransportUDP(*endpoint))) cout << "Gatekeeper set: " << *endpoint->GetGatekeeper() << endl; else { cout << "Error registering with gatekeeper at \"" << gkName << '"' << endl; return FALSE; } } else if (!args.HasOption('n')) { cout << "Searching for gatekeeper..." << flush; if (endpoint->DiscoverGatekeeper(new H323TransportUDP(*endpoint))) cout << "\nGatekeeper found: " << *endpoint->GetGatekeeper() << endl; else { cout << "\nNo gatekeeper found." << endl; if (args.HasOption("require-gatekeeper")) return FALSE; } } cout << "Waiting for incoming calls for \"" << endpoint->GetLocalUserName() << '"' << PString(listener ? "" : " disabled") << endl; return TRUE; } /////////////////////////////////////////////////////////////// MyH323EndPoint::MyH323EndPoint() { pmodem_pool = new PseudoModemQ(); //autoStartTransmitFax = TRUE; in_redundancy = -1; ls_redundancy = -1; hs_redundancy = -1; re_interval = -1; old_asn = FALSE; } void MyH323EndPoint::OnMyCallback(PObject &from, INT myPTRACE_PARAM(extra)) { if (PIsDescendant(&from, PStringToString) ) { PStringToString &request = (PStringToString &)from; PString command = request("command"); myPTRACE(1, "MyH323EndPoint::OnMyCallback command=" << command << " extra=" << extra); PString modemToken = request("modemtoken"); PString response = "reject"; if (command == "dial") { PseudoModem *modem = pmodem_pool->Dequeue(modemToken); if (modem != NULL) { PString num = request("number"); PString remote; if (GetGatekeeper() != NULL) { remote = num; } else { for( PINDEX i = 0 ; i < routes.GetSize() ; i++ ) { PString r = routes[i]; PStringArray rs = r.Tokenise("@", FALSE); if( rs.GetSize() == 2 ) { if( rs[0] == "all" ) { remote = rs[1]; break; } else if (num.Find(rs[0]) == 0) { remote = rs[1]; num.Delete(0, rs[0].GetLength()); break; } } } // add in the route and port if required if (!remote.IsEmpty()) { num += "@" + remote; if ((num.Find(':') == P_MAX_INDEX) && (connectPort != H323EndPoint::DefaultTcpPort)) num += psprintf(":%i", connectPort); } } if (remote.IsEmpty()) request.SetAt("diag", "noroute"); else { PTRACE(1, "MyH323EndPoint::OnMyCallback MakeCall(" << num << ")"); PString LocalPartyName = request("localpartyname"); PString callToken; MakeCall("h323:" + num, callToken, (void *)(LocalPartyName.IsEmpty() ? NULL : (const char*)LocalPartyName)); request.SetAt("calltoken", callToken); H323Connection * _conn = FindConnectionWithLock(callToken); if (_conn != NULL) { cout << "O/G connection to " << num; if (!LocalPartyName.IsEmpty()) cout << " from " << LocalPartyName; cout << endl; PAssert(PIsDescendant(_conn, MyH323Connection), PInvalidCast); MyH323Connection *conn = (MyH323Connection *)_conn; if (conn->Attach(modem)) { response = "confirm"; modem = NULL; } _conn->Unlock(); } } if (modem != NULL) pmodem_pool->Enqueue(modem); } } else if (command == "answer") { PString callToken = request("calltoken"); H323Connection * _conn = FindConnectionWithLock(callToken); if( _conn != NULL ) { _conn->AnsweringCall(H323Connection::AnswerCallNow); _conn->Unlock(); response = "confirm"; } } else if (command == "requestmode") { PString callToken = request("calltoken"); H323Connection * _conn = FindConnectionWithLock(callToken); if( _conn != NULL ) { if (request("mode") == "fax") { if (_conn->RequestModeChangeT38()) { PTRACE(2, "MyH323EndPoint::OnMyCallback RequestMode T38 - OK"); response = "confirm"; } else { PTRACE(2, "MyH323EndPoint::OnMyCallback RequestMode T38 - fail"); } } else { PTRACE(2, "MyH323EndPoint::OnMyCallback unknown mode"); } _conn->Unlock(); } } else if (command == "clearcall") { PString callToken = request("calltoken"); if( ClearCall(callToken) ) { response = "confirm"; } } else if (command == "addmodem") { if (pmodem_pool->Enqueue(modemToken)) { response = "confirm"; } } request.SetAt("response", response); myPTRACE(1, "MyH323EndPoint::OnMyCallback request={\n" << request << "}"); } else { myPTRACE(1, "MyH323EndPoint::OnMyCallback unknown class " << from.GetClass() << " extra=" << extra); } } H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference, void *userData) { MyH323Connection *connection = new MyH323Connection(*this, callReference); if (connection && userData) connection->SetLocalPartyName((const char *)userData); return connection; } PseudoModem * MyH323EndPoint::PMAlloc(const PString &number) const { return pmodem_pool->DequeueWithRoute(number); } void MyH323EndPoint::PMFree(PseudoModem *pmodem) const { if (pmodem != NULL) pmodem_pool->Enqueue(pmodem); } void MyH323EndPoint::SetOptions(MyH323Connection &/*conn*/, OpalT38Protocol *t38handler) const { // TODO: make it per host if (t38handler != NULL) { PAssert(PIsDescendant(t38handler, T38Protocol), PInvalidCast); ((T38Protocol *)t38handler)->SetRedundancy( in_redundancy, ls_redundancy, hs_redundancy, re_interval); if (old_asn) ((T38Protocol *)t38handler)->SetOldASN(); } } PBoolean MyH323EndPoint::Initialise(const PConfigArgs &args) { DisableFastStart(!args.HasOption('F')); DisableH245Tunneling(args.HasOption('T')); DisableDetectInBandDTMF(TRUE); SetSilenceDetectionMode(H323AudioCodec::NoSilenceDetection); if (args.HasOption("bearer-capability")) { PString bc = args.GetOptionString("bearer-capability"); PStringArray sBC = bc.Tokenise(":", FALSE); PIntArray iBC(4); if (sBC.GetSize() == iBC.GetSize()) { for (PINDEX i = 0 ; i < iBC.GetSize() ; i++) iBC[i] = sBC[i].AsUnsigned(); if (iBC[0] >= 0 && iBC[0] <= 3 && iBC[1] >= 0 && iBC[1] <= 31 && iBC[2] >= 1 && iBC[2] <= 127 && iBC[3] >= 2 && iBC[3] <= 5) { PTRACE(3, "MyH323EndPoint::Initialise: Bearer-Capability=" << bc); bearerCapability = iBC; } else { iBC[0] = -1; } } else { iBC[0] = -1; } if (iBC[0] < 0) { PTRACE(3, "MyH323EndPoint::Initialise: Wrong Bearer-Capability=" << bc << " (ignored)"); } } if (args.HasOption("ports")) { PString p = args.GetOptionString("ports"); PStringArray ports = p.Tokenise(",\r\n", FALSE); for (PINDEX i = 0 ; i < ports.GetSize() ; i++) { p = ports[i]; PStringArray ps = p.Tokenise(":-", FALSE); if (ps.GetSize() == 3) { if (ps[0] == "udp") SetUDPPorts(ps[1].AsUnsigned(), ps[2].AsUnsigned()); else if (ps[0] == "rtp") SetRtpIpPorts(ps[1].AsUnsigned(), ps[2].AsUnsigned()); else if (ps[0] == "tcp") SetTCPPorts(ps[1].AsUnsigned(), ps[2].AsUnsigned()); } } PTRACE(1, "UDP ports: " << GetUDPPortBase() << "-" << GetUDPPortMax()); PTRACE(1, "RTP ports: " << GetRtpIpPortBase() << "-" << GetRtpIpPortMax()); PTRACE(1, "TCP ports: " << GetTCPPortBase() << "-" << GetTCPPortMax()); } if (args.HasOption("connectport")) connectPort = (WORD)args.GetOptionString("connectport").AsInteger(); else connectPort = H323EndPoint::DefaultTcpPort; if (args.HasOption("route")) { PString r = args.GetOptionString("route"); routes = r.Tokenise(",\r\n", FALSE); cout << "Route O/G calls:" << endl; for( PINDEX i = 0 ; i < routes.GetSize() ; i++ ) { r = routes[i]; PStringArray rs = r.Tokenise("@", FALSE); if( rs.GetSize() == 2 ) { cout << " " << rs[0] << " --> " << rs[1] << endl; PTRACE(1, "Route " << rs[0] << " --> " << rs[1]); if( rs[0] == "all" ) break; } } } if (args.HasOption('p')) { PString tty = args.GetOptionString('p'); PStringArray ttys = tty.Tokenise(",\r\n ", FALSE); for( PINDEX i = 0 ; i < ttys.GetSize() ; i++ ) { tty = ttys[i]; PStringArray atty = tty.Tokenise("@", FALSE); PString r = ""; if (atty.GetSize() == 2) { r = atty[0]; tty = atty[1]; } else if (atty.GetSize() == 1) { tty = atty[0]; } if (!pmodem_pool->CreateModem(tty, r, args, PCREATE_NOTIFIER(OnMyCallback))) cout << "Can't create modem for " << tty << endl; } } if (args.HasOption("redundancy")) { const char *r = args.GetOptionString("redundancy"); if (isdigit(r[0])) { in_redundancy = r[0] - '0'; if (isdigit(r[1])) { ls_redundancy = r[1] - '0'; if (isdigit(r[2])) { hs_redundancy = r[2] - '0'; } } } } if (args.HasOption("repeat")) re_interval = (int)args.GetOptionString("repeat").AsInteger(); if (args.HasOption("old-asn")) old_asn = TRUE; if (args.HasOption('G')) SetCapability(0, 0, new G7231_Fake_Capability()); else { SetCapability(0, 0, new H323_G711Capability(H323_G711Capability::muLaw, H323_G711Capability::At64k)); SetCapability(0, 0, new H323_G711Capability(H323_G711Capability::ALaw, H323_G711Capability::At64k)); } SetCapability(0, 0, new H323_T38Capability(H323_T38Capability::e_UDP)); //SetCapability(0, 0, new H323_T38NonStandardCapability(181, 0, 18)); SetCapability(0, 0, new H323_UserInputCapability(H323_UserInputCapability::BasicString)); //AddAllUserInputCapabilities(0, P_MAX_INDEX); capabilities.Remove(args.GetOptionString('D').Lines()); capabilities.Reorder(args.GetOptionString('P').Lines()); cout << "Codecs (in preference order):\n" << setprecision(2) << capabilities << endl; return TRUE; } /////////////////////////////////////////////////////////////// MyH323Connection::MyH323Connection(MyH323EndPoint & _ep, unsigned callReference) : H323Connection(_ep, callReference), ep(_ep) , pmodem(NULL) , userInputEngine(NULL) { } MyH323Connection::~MyH323Connection() { cout << "Closing connection" << endl; if (pmodem != NULL) { PStringToString request; request.SetAt("command", "clearcall"); request.SetAt("calltoken", GetCallToken()); if( !pmodem->Request(request) ) { myPTRACE(1, "MyH323Connection::~MyH323Connection error request={\n" << request << "}"); } ep.PMFree(pmodem); } if (userInputEngine != NULL) ReferenceObject::DelPointer(userInputEngine); } void MyH323Connection::OnEstablished() { PAssert(pmodem != NULL, "pmodem is NULL"); PStringToString request; request.SetAt("command", "established"); request.SetAt("calltoken", GetCallToken()); if( !pmodem->Request(request) ) { myPTRACE(1, "MyH323Connection::OnEstablished error request={\n" << request << "}"); } } PBoolean MyH323Connection::Attach(PseudoModem *_pmodem) { PWaitAndSignal mutex(connMutex); if (pmodem != NULL) return FALSE; pmodem = _pmodem; return TRUE; } OpalT38Protocol * MyH323Connection::CreateT38ProtocolHandler() { PTRACE(2, "MyH323Connection::CreateT38ProtocolHandler"); PWaitAndSignal mutex(connMutex); PAssert(pmodem != NULL, "pmodem is NULL"); /* * we can't have more then one t38handler per connection * at the same time and we should delete it on connection clean */ if (t38handler == NULL) { PTRACE(2, "MyH323Connection::CreateT38ProtocolHandler create new one"); T38Protocol *t38protocol = new T38Protocol(pmodem); if (t38protocol && t38protocol->IsOK()) { ep.SetOptions(*this, t38protocol); t38handler = t38protocol; } else { delete t38protocol; PTRACE(1, "MyH323Connection::CreateT38ProtocolHandler can't create"); } } return t38handler; } H323Connection::AnswerCallResponse MyH323Connection::OnAnswerCall(const PString & /*caller*/, const H323SignalPDU & setupPDU, H323SignalPDU & /*connectPDU*/) { PString number; cout << "I/C connection"; PTRACE(1, "I/C connection"); if (setupPDU.GetSourceE164(number)) { cout << " from " << number; PTRACE(1, "From: " << number); } if (setupPDU.GetDestinationE164(number)) { cout << " to " << number; PTRACE(1, "To: " << number); } else { number = ""; } cout << endl; PseudoModem *_pmodem = ep.PMAlloc(number); if (_pmodem == NULL) { myPTRACE(1, "... denied (all modems busy)"); return AnswerCallDenied; } if (!Attach(_pmodem)) { myPTRACE(1, "... denied (internal error)"); ep.PMFree(_pmodem); return AnswerCallDenied; } RenameCurrentThread(pmodem->ptyName() + "(c)"); PStringToString request; request.SetAt("command", "call"); request.SetAt("calltoken", GetCallToken()); PString srcNum; if( setupPDU.GetSourceE164(srcNum) ) request.SetAt("srcnum", srcNum); PString dstNum; if( setupPDU.GetDestinationE164(dstNum) ) request.SetAt("dstnum", dstNum); unsigned distinctiveRing = setupPDU.GetDistinctiveRing(); if( distinctiveRing ) request.SetAt("distinctivering", psprintf("%u", distinctiveRing)); if( !pmodem->Request(request) ) { myPTRACE(1, "... denied (modem is not ready)"); // or we can try other modem return AnswerCallDenied; } PString response = request("response"); if( response == "confirm" ) { AnswerCallResponse resp = AnswerCallPending; myPTRACE(1, "... Ok " << resp); return resp; } myPTRACE(1, "... denied (no confirm)"); return AnswerCallDenied; } PBoolean MyH323Connection::OnSendSignalSetup(H323SignalPDU & setupPDU) { const PIntArray &bearerCapability = ep.BearerCapability(); if (!bearerCapability.IsEmpty()) { PTRACE(3, "MyH323Connection::OnSendSignalSetup: Set Bearer capability '" << bearerCapability << "'"); setupPDU.GetQ931().SetBearerCapabilities( Q931::InformationTransferCapability(bearerCapability[1]), bearerCapability[2], bearerCapability[0], bearerCapability[3]); } return H323Connection::OnSendSignalSetup(setupPDU); } PBoolean MyH323Connection::OnStartLogicalChannel(H323Channel & channel) { myPTRACE(1, "MyH323Connection::OnStartLogicalChannel ch=" << channel << " cp=" << channel.GetCapability() << " sid=" << channel.GetSessionID() << " " << channel.GetDirection()); if (!H323Connection::OnStartLogicalChannel(channel)) return FALSE; cout << "Started logical channel: " << channel << " " << channel.GetCapability() << " " << channel.GetDirection() << endl; return TRUE; } void MyH323Connection::OnClosedLogicalChannel(const H323Channel & channel) { PTRACE(2, "MyH323Connection::OnClosedLogicalChannel beg"); H323Connection::OnClosedLogicalChannel(channel); myPTRACE(1, "MyH323Connection::OnClosedLogicalChannel ch=" << channel << " cp=" << channel.GetCapability() << " sid=" << channel.GetSessionID() << " " << channel.GetDirection()); } PBoolean MyH323Connection::OpenAudioChannel(PBoolean isEncoding, unsigned /*bufferSize*/, H323AudioCodec & codec) { PTRACE(2, "MyH323Connection::OpenAudioChannel " << codec << (isEncoding ? " encoding" : " decoding")); if (pmodem == NULL) return FALSE; ModemAudioChannel *audioChannel = new ModemAudioChannel(isEncoding, pmodem); if (!audioChannel || !audioChannel->IsOK()) { delete audioChannel; PTRACE(1, "MyH323Connection::OpenAudioChannel can't create ModemAudioChannel"); } codec.AttachChannel(audioChannel, TRUE); return TRUE; } void MyH323Connection::OnUserInputString(const PString & value) { if (userInputEngine == NULL) { if (pmodem == NULL) return; userInputEngine = pmodem->NewPtrUserInputEngine(); if (userInputEngine == NULL) return; } userInputEngine->WriteUserInput(value); } /////////////////////////////////////////////////////////////// t38modem-2.0.0/h323lib/audio_chan.h0000664000076400007640000000313511453122402017430 0ustar frolovfrolov00000000000000/* * audio_chan.h * * T38FAX Pseudo Modem * * Copyright (c) 2010 Vyacheslav Frolov * * t38modem Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is t38modem. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: audio_chan.h,v $ * Revision 1.1 2010/10/06 16:34:10 vfrolov * Initial revision * */ #ifndef _AUDIO_CHAN_H #define _AUDIO_CHAN_H /////////////////////////////////////////////////////////////// class AudioEngine; class PseudoModem; /////////////////////////////////////////////////////////////// class ModemAudioChannel : public PChannel { PCLASSINFO(ModemAudioChannel, PChannel); public: /**@name Construction */ //@{ ModemAudioChannel(PBoolean out, PseudoModem * pmodem); ~ModemAudioChannel(); //@} /**@name Operations */ //@{ PBoolean IsOK() const { return audioEngine != NULL; } PBoolean Read(void * buffer, PINDEX amount); PBoolean Write(const void * buffer, PINDEX len); //@} private: AudioEngine * audioEngine; PBoolean outDirection; }; /////////////////////////////////////////////////////////////// #endif // _AUDIO_CHAN_H t38modem-2.0.0/h323lib/t38protocol.cxx0000664000076400007640000003235211453124673020130 0ustar frolovfrolov00000000000000/* * t38protocol.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2009-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: t38protocol.cxx,v $ * Revision 1.3 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.2 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.1 2009/07/27 16:09:24 vfrolov * Initial revision * */ #include #include #include #include "t38protocol.h" #include "../t38engine.h" #include "../pmodem.h" #define new PNEW /////////////////////////////////////////////////////////////// T38Protocol::T38Protocol(PseudoModem * pmodem) : t38engine(pmodem->NewPtrT38Engine()) , in_redundancy(0) , ls_redundancy(0) , hs_redundancy(0) , re_interval(-1) { } T38Protocol::~T38Protocol() { if (t38engine != NULL) ReferenceObject::DelPointer(t38engine); } void T38Protocol::SetRedundancy(int indication, int low_speed, int high_speed, int repeat_interval) { if (indication >= 0) in_redundancy = indication; if (low_speed >= 0) ls_redundancy = low_speed; if (high_speed >= 0) hs_redundancy = high_speed; re_interval = repeat_interval; myPTRACE(3, t38engine->Name() << " T38Protocol::SetRedundancy indication=" << in_redundancy << " low_speed=" << ls_redundancy << " high_speed=" << hs_redundancy << " repeat_interval=" << re_interval ); } #ifdef OPTIMIZE_CORRIGENDUM_IFP #define T38_IFP_NOT_NATIVE T38_PreCorrigendum_IFPPacket #define T38_IFP_NOT_NATIVE_NAME "Pre-corrigendum IFP" #define IS_NATIVE_ASN corrigendumASN #define IS_EXTENDABLE FALSE #else #define T38_IFP_NOT_NATIVE T38_IFPPacket #define T38_IFP_NOT_NATIVE_NAME "IFP" #define IS_NATIVE_ASN (!corrigendumASN) #define IS_EXTENDABLE TRUE #endif static void EncodeIFPPacket(PASN_OctetString &ifp_packet, const T38_IFP &T38_ifp, PBoolean nativeASN) { if (!nativeASN && T38_ifp.HasOptionalField(T38_IFPPacket::e_data_field)) { T38_IFP ifp = T38_ifp; PINDEX count = ifp.m_data_field.GetSize(); for( PINDEX i = 0 ; i < count ; i++ ) { ifp.m_data_field[i].m_field_type.SetExtendable(IS_EXTENDABLE); } ifp_packet.EncodeSubType(ifp); } else { ifp_packet.EncodeSubType(T38_ifp); } } PBoolean T38Protocol::HandleRawIFP(const PASN_OctetString & pdu) { T38_IFP ifp; if (IS_NATIVE_ASN) { if (pdu.DecodeSubType(ifp)) return t38engine->HandlePacket(EngineBase::HOWNERIN(this), ifp); PTRACE(2, "T38\t" T38_IFP_NAME " decode failure:\n " << setprecision(2) << ifp); return TRUE; } T38_IFP_NOT_NATIVE old_ifp; if (!pdu.DecodeSubType(old_ifp)) { PTRACE(2, "T38\t" T38_IFP_NOT_NATIVE_NAME " decode failure:\n " << setprecision(2) << old_ifp); return TRUE; } ifp.m_type_of_msg = old_ifp.m_type_of_msg; if (old_ifp.HasOptionalField(T38_IFPPacket::e_data_field)) { ifp.IncludeOptionalField(T38_IFPPacket::e_data_field); PINDEX count = old_ifp.m_data_field.GetSize(); ifp.m_data_field.SetSize(count); for (PINDEX i = 0 ; i < count; i++) { ifp.m_data_field[i].m_field_type = old_ifp.m_data_field[i].m_field_type; if (old_ifp.m_data_field[i].HasOptionalField(T38_Data_Field_subtype::e_field_data)) { ifp.m_data_field[i].IncludeOptionalField(T38_Data_Field_subtype::e_field_data); ifp.m_data_field[i].m_field_data = old_ifp.m_data_field[i].m_field_data; } } } return t38engine->HandlePacket(EngineBase::HOWNERIN(this), ifp); } PBoolean T38Protocol::Originate() { RenameCurrentThread(t38engine->Name() + "(tx)"); PTRACE(2, "T38\tOriginate, transport=" << *transport); long seq = -1; int maxRedundancy = 0; #if PTRACING int repeated = 0; #endif T38_UDPTLPacket udptl; udptl.m_error_recovery.SetTag(T38_UDPTLPacket_error_recovery::e_secondary_ifp_packets); #if REPEAT_INDICATOR_SENDING T38_IFP lastifp; #endif t38engine->OpenOut(EngineBase::HOWNEROUT(this)); for (;;) { T38_IFP ifp; int res; if (seq < 0) { t38engine->SetPreparePacketTimeout(EngineBase::HOWNEROUT(this), -1); } else { int timeout = ( #ifdef REPEAT_INDICATOR_SENDING lastifp.m_type_of_msg.GetTag() == T38_Type_of_msg::e_t30_indicator || #endif maxRedundancy > 0) ? t38engine->msPerOut * 3 : -1; if (re_interval > 0 && (timeout <= 0 || timeout > re_interval)) timeout = re_interval; t38engine->SetPreparePacketTimeout(EngineBase::HOWNEROUT(this), timeout); } res = t38engine->PreparePacket(EngineBase::HOWNEROUT(this), ifp); #ifdef REPEAT_INDICATOR_SENDING if (res > 0) lastifp = ifp; else if (res < 0 && lastifp.m_type_of_msg.GetTag() == T38_Type_of_msg::e_t30_indicator) { // send indicator again ifp = lastifp; res = 1; } #endif if (res > 0) { T38_UDPTLPacket_error_recovery &recovery = udptl.m_error_recovery; if (recovery.GetTag() == T38_UDPTLPacket_error_recovery::e_secondary_ifp_packets) { T38_UDPTLPacket_error_recovery_secondary_ifp_packets &secondary = recovery; int nRedundancy = secondary.GetSize() + 1; if (nRedundancy > maxRedundancy) nRedundancy = maxRedundancy; if (nRedundancy < 0) nRedundancy = 0; secondary.SetSize(nRedundancy); if (nRedundancy > 0) { for (int i = nRedundancy - 1 ; i > 0 ; i--) { secondary[i] = secondary[i - 1]; } secondary[0].SetValue(udptl.m_primary_ifp_packet.GetValue()); } } else { PTRACE_IF(3, maxRedundancy > 0, "T38\tNot implemented yet " << recovery.GetTagName()); } udptl.m_seq_number = ++seq & 0xFFFF; EncodeIFPPacket(udptl.m_primary_ifp_packet, ifp, IS_NATIVE_ASN); /* * Calculate maxRedundancy for current ifp packet */ maxRedundancy = hs_redundancy; switch( ifp.m_type_of_msg.GetTag() ) { case T38_Type_of_msg::e_t30_indicator: maxRedundancy = in_redundancy; break; case T38_Type_of_msg::e_data: switch( (T38_Type_of_msg_data)ifp.m_type_of_msg ) { case T38_Type_of_msg_data::e_v21: maxRedundancy = ls_redundancy; break; } break; } #if 0 // recovery test if (seq % 2) continue; #endif } else if (res < 0) { if (maxRedundancy > 0) maxRedundancy--; #if 1 /* * Optimise repeated packet each time */ T38_UDPTLPacket_error_recovery &recovery = udptl.m_error_recovery; if (recovery.GetTag() == T38_UDPTLPacket_error_recovery::e_secondary_ifp_packets) { T38_UDPTLPacket_error_recovery_secondary_ifp_packets &secondary = recovery; int nRedundancy = secondary.GetSize(); if (nRedundancy > maxRedundancy) nRedundancy = maxRedundancy; if (nRedundancy < 0) nRedundancy = 0; secondary.SetSize(nRedundancy); } else { PTRACE(3, "T38\tNot implemented yet " << recovery.GetTagName()); } #endif #if PTRACING repeated++; #endif } else break; PPER_Stream rawData; udptl.Encode(rawData); rawData.CompleteEncoding(); #if PTRACING if (res > 0) { if (PTrace::CanTrace(4)) { PTRACE(4, "T38\tSending PDU:\n ifp = " << setprecision(2) << ifp << "\n UDPTL = " << setprecision(2) << udptl << "\n " << setprecision(2) << rawData); } else if (PTrace::CanTrace(3)) { PTRACE(3, "T38\tSending PDU: seq=" << seq << "\n ifp = " << setprecision(2) << ifp); } else { PTRACE(2, "T38\tSending PDU:" " seq=" << seq << " type=" << ifp.m_type_of_msg.GetTagName()); } } else { PTRACE(4, "T38\tSending PDU again:\n UDPTL = " << setprecision(2) << udptl << "\n " << setprecision(2) << rawData); } #endif if (!transport->WritePDU(rawData)) { PTRACE(1, "T38\tOriginate - WritePDU ERROR: " << transport->GetErrorText()); break; } } myPTRACE(2, "T38\tSend statistics: sequence=" << seq << " repeated=" << repeated << GetThreadTimes(", CPU usage: ")); return FALSE; } PBoolean T38Protocol::Answer() { RenameCurrentThread(t38engine->Name() + "(rx)"); PTRACE(2, "T38\tAnswer, transport=" << *transport); // We can't get negotiated sender's address and port, // so accept first packet from any address and port transport->SetPromiscuous(transport->AcceptFromAny); int consecutiveBadPackets = 0; long expectedSequenceNumber = 0; #if PTRACING int totalrecovered = 0; int totallost = 0; int repeated = 0; #endif t38engine->OpenIn(EngineBase::HOWNERIN(this)); for (;;) { PPER_Stream rawData; if (!transport->ReadPDU(rawData)) { PTRACE(1, "T38\tError reading PDU: " << transport->GetErrorText(PChannel::LastReadError)); break; } T38_UDPTLPacket udptl; if (udptl.Decode(rawData)) { consecutiveBadPackets = 0; // When we get the first packet, we know sender's address and port, // so accept next packets from sender's address and port only if (expectedSequenceNumber == 0) { PTRACE(3, "T38\tReceived first packet, remote=" << transport->GetLastReceivedAddress()); transport->SetPromiscuous(transport->AcceptFromLastReceivedOnly); } } else { consecutiveBadPackets++; PTRACE(2, "T38\tRaw data decode failure:\n " << setprecision(2) << rawData << "\n UDPTL = " << setprecision(2) << udptl); if (consecutiveBadPackets > 3) { PTRACE(1, "T38\tRaw data decode failed multiple times, aborting!"); break; } continue; } long receivedSequenceNumber = (udptl.m_seq_number & 0xFFFF) + (expectedSequenceNumber & ~0xFFFFL); long lost = receivedSequenceNumber - expectedSequenceNumber; if (lost < -0x10000L/2) { lost += 0x10000L; receivedSequenceNumber += 0x10000L; } else if (lost > 0x10000L/2) { lost -= 0x10000L; receivedSequenceNumber -= 0x10000L; } PTRACE(4, "T38\tReceived PDU:\n " << setprecision(2) << rawData << "\n UDPTL = " << setprecision(2) << udptl); if (lost < 0) { PTRACE(4, "T38\tRepeated packet " << receivedSequenceNumber); #if PTRACING repeated++; #endif continue; } else if(lost > 0) { const T38_UDPTLPacket_error_recovery &recovery = udptl.m_error_recovery; if (recovery.GetTag() == T38_UDPTLPacket_error_recovery::e_secondary_ifp_packets) { const T38_UDPTLPacket_error_recovery_secondary_ifp_packets &secondary = recovery; int nRedundancy = secondary.GetSize(); if (lost > nRedundancy) { if (!t38engine->HandlePacketLost(EngineBase::HOWNERIN(this), lost - nRedundancy)) break; #if PTRACING totallost += lost - nRedundancy; #endif lost = nRedundancy; } else nRedundancy = lost; receivedSequenceNumber -= lost; for (int i = nRedundancy - 1 ; i >= 0 ; i--) { PTRACE(3, "T38\tReceived ifp seq=" << receivedSequenceNumber << " (secondary)"); if (!HandleRawIFP(secondary[i])) goto done; #if PTRACING totalrecovered++; #endif lost--; receivedSequenceNumber++; } receivedSequenceNumber += lost; } else { PTRACE(3, "T38\tNot implemented yet " << recovery.GetTagName()); } if (lost) { if (!t38engine->HandlePacketLost(EngineBase::HOWNERIN(this), lost)) break; #if PTRACING totallost += lost; #endif } } #if 0 // recovery test expectedSequenceNumber = receivedSequenceNumber; continue; #endif PTRACE(3, "T38\tReceived ifp seq=" << receivedSequenceNumber); if (!HandleRawIFP(udptl.m_primary_ifp_packet)) break; expectedSequenceNumber = receivedSequenceNumber + 1; } done: myPTRACE(2, "T38\tReceive statistics: sequence=" << expectedSequenceNumber << " repeated=" << repeated << " recovered=" << totalrecovered << " lost=" << totallost << GetThreadTimes(", CPU usage: ")); return FALSE; } /////////////////////////////////////////////////////////////// void T38Protocol::CleanUpOnTermination() { myPTRACE(1, t38engine->Name() << " T38Protocol::CleanUpOnTermination"); OpalT38Protocol::CleanUpOnTermination(); t38engine->CloseIn(EngineBase::HOWNERIN(this)); t38engine->CloseOut(EngineBase::HOWNEROUT(this)); } /////////////////////////////////////////////////////////////// t38modem-2.0.0/h323lib/t38modem_openh323_2005.vcproj0000664000076400007640000004561311455110341022151 0ustar frolovfrolov00000000000000 t38modem-2.0.0/h323lib/t38protocol.h0000664000076400007640000000424111450624233017543 0ustar frolovfrolov00000000000000/* * t38protocol.h * * T38FAX Pseudo Modem * * Copyright (c) 2009-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: t38protocol.h,v $ * Revision 1.2 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.1 2009/07/27 16:09:24 vfrolov * Initial revision * */ #ifndef _T38PROTOCOL_H #define _T38PROTOCOL_H #include /////////////////////////////////////////////////////////////// class PASN_OctetString; class T38Engine; class PseudoModem; /////////////////////////////////////////////////////////////// class T38Protocol : public OpalT38Protocol { PCLASSINFO(T38Protocol, OpalT38Protocol); public: /**@name Construction */ //@{ T38Protocol(PseudoModem * pmodem); ~T38Protocol(); //@} /**@name Operations */ //@{ PBoolean IsOK() const { return t38engine != NULL; } void SetRedundancy( int indication, int low_speed, int high_speed, int repeat_interval ); /**The calling SetOldASN() is aquivalent to the following change of the t38.asn: - t4-non-ecm-sig-end, - ... + t4-non-ecm-sig-end */ void SetOldASN() { corrigendumASN = FALSE; } //@} PBoolean HandleRawIFP(const PASN_OctetString & pdu); PBoolean Originate(); PBoolean Answer(); void CleanUpOnTermination(); private: T38Engine *t38engine; int in_redundancy; int ls_redundancy; int hs_redundancy; int re_interval; }; /////////////////////////////////////////////////////////////// #endif // _T38PROTOCOL_H t38modem-2.0.0/t30tone.cxx0000664000076400007640000001505611455110341016045 0ustar frolovfrolov00000000000000/* * t30tone.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2002-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: t30tone.cxx,v $ * Revision 1.8 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.7 2010/09/10 18:00:44 vfrolov * Cleaned up code * * Revision 1.6 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.5 2007/03/23 10:14:36 vfrolov * Implemented voice mode functionality * * Revision 1.4 2005/02/03 11:32:12 vfrolov * Fixed MSVC compile warnings * * Revision 1.3 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.2 2002/06/07 06:25:15 robertj * Added math.h for use of sin() function. * Fixed GNU warnings. * * Revision 1.1 2002/04/30 10:59:07 vfrolov * Initial revision * */ #include #include "pmutils.h" #include "t30tone.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// typedef PInt16 SIMPLE_TYPE; #define BYTES_PER_SIMPLE sizeof(SIMPLE_TYPE) #define SIMPLES_PER_SEC 8000 /////////////////////////////////////////////////////////////// #define CNG_HZ 1100 #define CNG_ON_MSEC 500 #define CNG_OFF_MSEC 3000 /////////////////////////////////////////////////////////////// #define CNG_FILTER_BUF_LEN ((((SIMPLES_PER_SEC + CNG_HZ - 1)/CNG_HZ + 1)/2)*2) #define tone_trace(cng_filter_buf, power, cng_power, cng_power_norm) #ifndef tone_trace static void tone_trace(const long *cng_filter_buf, long power, long cng_power, long cng_power_norm) { PString str; for (PINDEX i = 0 ; i < CNG_FILTER_BUF_LEN ; i++) { str += PString(cng_filter_buf[i]); str += " "; } myPTRACE(1, str << " pw=" << power << " cng_pw=" << cng_power << " norm=" << cng_power_norm); } #endif enum { cng_phase_off_head, cng_phase_on, cng_phase_off_tail, }; T30ToneDetect::T30ToneDetect() { cng_filter_buf = new long[CNG_FILTER_BUF_LEN]; memset(cng_filter_buf, 0, CNG_FILTER_BUF_LEN*sizeof(cng_filter_buf[0])); index = 0; power = 0; cng_on_count = 0; cng_off_count = 0; cng_phase = cng_phase_off_head; } T30ToneDetect::~T30ToneDetect() { delete [] cng_filter_buf; } #define CNG_FILTER_CHUNK_LEN 20 #define CNG_ON_CHUNKS_MIN (((CNG_HZ*CNG_ON_MSEC)*60)/((CNG_FILTER_CHUNK_LEN*1000)*100)) #define CNG_ON_CHUNKS_MAX (((CNG_HZ*CNG_ON_MSEC)*140)/((CNG_FILTER_CHUNK_LEN*1000)*100)) #define CNG_OFF_CHUNKS_MIN (((CNG_HZ*CNG_OFF_MSEC)*30)/((CNG_FILTER_CHUNK_LEN*1000)*100)) PBoolean T30ToneDetect::Write(const void * buffer, PINDEX len) { PBoolean detected = FALSE; SIMPLE_TYPE *pBuf = (SIMPLE_TYPE *)buffer; len /= BYTES_PER_SIMPLE; while (len) { PINDEX iBuf = 0; // filter CNG and calculate total power PINDEX index0 = index; while (index < CNG_FILTER_BUF_LEN*CNG_FILTER_CHUNK_LEN) { iBuf = ((index - index0)*SIMPLES_PER_SEC)/(CNG_FILTER_BUF_LEN*CNG_HZ); if (iBuf >= len) return detected; SIMPLE_TYPE pw = pBuf[iBuf]; cng_filter_buf[index%CNG_FILTER_BUF_LEN] += pw; if (pw < 0) power -= pw; else power += pw; index++; } // calculate CNG power PINDEX i; long cng_power_cur = 0; for (i = 0 ; i < CNG_FILTER_BUF_LEN/2 ; i++) cng_power_cur += cng_filter_buf[i]; for (; i < CNG_FILTER_BUF_LEN ; i++) cng_power_cur -= cng_filter_buf[i]; long cng_power; if (cng_power_cur < 0) cng_power = -cng_power_cur; else cng_power = cng_power_cur; for (i = 1 ; i < CNG_FILTER_BUF_LEN/2 ; i++) { cng_power_cur -= cng_filter_buf[i]*2; cng_power_cur += cng_filter_buf[i + CNG_FILTER_BUF_LEN/2]*2; if (cng_power_cur < 0) { if (cng_power < -cng_power_cur) cng_power = -cng_power_cur; } else { if (cng_power < cng_power_cur) cng_power = cng_power_cur; } } long cng_power_norm = cng_power * 1000; if (power > 1) cng_power_norm /= power; if (cng_on_count > 1) cng_power_norm = (cng_power_norm*140)/100; if (cng_off_count > 1) cng_power_norm = (cng_power_norm*80)/100; if (cng_power_norm > 500) { cng_on_count++; switch (cng_phase) { case cng_phase_off_head: if (cng_off_count >= CNG_OFF_CHUNKS_MIN) cng_phase = cng_phase_on; else cng_phase = cng_phase_off_head; break; case cng_phase_on: break; case cng_phase_off_tail: cng_phase = cng_phase_off_head; break; default: cng_phase = cng_phase_off_head; } if (cng_off_count) { myPTRACE(2, "cng_off_count=" << cng_off_count); cng_off_count = 0; } tone_trace(cng_filter_buf, power, cng_power, cng_power_norm); } else { cng_off_count++; switch (cng_phase) { case cng_phase_off_head: break; case cng_phase_on: if (cng_on_count >= CNG_ON_CHUNKS_MIN && cng_on_count <= CNG_ON_CHUNKS_MAX) cng_phase = cng_phase_off_tail; else cng_phase = cng_phase_off_head; break; case cng_phase_off_tail: if (cng_off_count >= CNG_OFF_CHUNKS_MIN) { myPTRACE(1, "Detected CNG"); cng_phase = cng_phase_off_head; detected = TRUE; } break; default: cng_phase = cng_phase_off_head; } if (cng_on_count) { myPTRACE(2, "cng_on_count=" << cng_on_count); tone_trace(cng_filter_buf, power, cng_power, cng_power_norm); cng_on_count = 0; } } // reatart filter memset(cng_filter_buf, 0, CNG_FILTER_BUF_LEN*sizeof(cng_filter_buf[0])); index = 0; power = 0; // next chunk iBuf++; pBuf += iBuf; len -= iBuf; } return detected; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/fcs.cxx0000664000076400007640000000305210072547074015330 0ustar frolovfrolov00000000000000/* * fcs.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2003-2004 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: fcs.cxx,v $ * Revision 1.3 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.2 2004/02/17 13:22:50 vfrolov * Fixed MSVC compile errors * * Revision 1.1 2003/12/04 13:29:00 vfrolov * Initial revision * * */ #include #include "fcs.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// void FCS::build(const void *_pBuf, PINDEX count) { const BYTE *pBuf = (const BYTE *)_pBuf; for (PINDEX i = 0 ; i < count ; i++) { BYTE c = *(pBuf++); for (BYTE m = 0x80 ; m ; m >>= 1) { fcs <<= 1; if (c & m) fcs ^= 0x10000; if (fcs & 0x10000) fcs ^= 0x11021; } } } /////////////////////////////////////////////////////////////// t38modem-2.0.0/pmodem.cxx0000664000076400007640000001366011061726064016041 0ustar frolovfrolov00000000000000/* * pmodem.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmodem.cxx,v $ * Revision 1.11 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.10 2007/03/22 16:26:04 vfrolov * Fixed compiler warnings * * Revision 1.9 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.8 2006/05/23 14:48:54 vfrolov * Fixed race condition (reported by Tamas) * * Revision 1.7 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * Revision 1.6 2004/05/09 07:46:11 csoutheren * Updated to compile with new PIsDescendant function * * Revision 1.5 2002/05/15 16:17:44 vfrolov * Implemented per modem routing for I/C calls * * Revision 1.4 2002/03/05 12:35:52 vfrolov * Added Copyright header * Changed class hierarchy * PseudoModem is abstract * PseudoModemBody is child of PseudoModem * Added PseudoModemQ::CreateModem() to create instances * Some OS specific code moved from pmodem.cxx to pty.cxx * * Revision 1.3 2002/02/08 12:58:22 vfrolov * Defined Linux and FreeBSD patterns in ttyPattern(). * * Revision 1.2 2002/01/10 06:10:02 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #include #include "pmodem.h" #include "drivers.h" #define new PNEW /////////////////////////////////////////////////////////////// PLIST(_PseudoModemList, PseudoModem); class PseudoModemList : protected _PseudoModemList { PCLASSINFO(PseudoModemList, _PseudoModemList); public: PINDEX Append(PseudoModem *modem); PseudoModem *Find(const PString &modemToken) const; protected: PseudoModem *_Find(const PString &modemToken) const; PMutex Mutex; }; PINDEX PseudoModemList::Append(PseudoModem *modem) { PWaitAndSignal mutexWait(Mutex); if (_Find(modem->modemToken())) { myPTRACE(1, "PseudoModemList::Append can't add " << modem->ptyName() << " to modem list"); delete modem; return P_MAX_INDEX; } PINDEX i = _PseudoModemList::Append(modem); myPTRACE(3, "PseudoModemList::Append " << modem->ptyName() << " (" << i << ") OK"); return i; } PseudoModem *PseudoModemList::Find(const PString &modemToken) const { PWaitAndSignal mutexWait(Mutex); return _Find(modemToken); } PseudoModem *PseudoModemList::_Find(const PString &modemToken) const { PObject *object; for (PINDEX i = 0 ; (object = GetAt(i)) != NULL ; i++) { PAssert(PIsDescendant(object, PseudoModem), PInvalidCast); PseudoModem *modem = (PseudoModem *)object; if (modem->modemToken() == modemToken) return modem; } return NULL; } /////////////////////////////////////////////////////////////// PObject::Comparison PseudoModem::Compare(const PObject & obj) const { PAssert(PIsDescendant(&obj, PseudoModem), PInvalidCast); const PseudoModem & other = (const PseudoModem &)obj; return modemToken().Compare(other.modemToken()); } /////////////////////////////////////////////////////////////// PseudoModemQ::PseudoModemQ() { pmodem_list = new PseudoModemList(); } PseudoModemQ::~PseudoModemQ() { delete pmodem_list; } PBoolean PseudoModemQ::CreateModem( const PString &tty, const PString &route, const PConfigArgs &args, const PNotifier &callbackEndPoint ) { PseudoModem *modem = PseudoModemDrivers::CreateModem(tty, route, args, callbackEndPoint); if (!modem) return FALSE; if (pmodem_list->Append(modem) == P_MAX_INDEX) return FALSE; modem->Resume(); return TRUE; } void PseudoModemQ::Enqueue(PseudoModem *modem) { myPTRACE((modem != NULL) ? 3 : 1, "PseudoModemQ::Enqueue " << ((modem != NULL) ? modem->ptyName() : "BAD")); PWaitAndSignal mutexWait(Mutex); _PseudoModemQ::Enqueue(modem); } PBoolean PseudoModemQ::Enqueue(const PString &modemToken) { PseudoModem *modem = pmodem_list->Find(modemToken); if (!modem) { myPTRACE(1, "PseudoModemQ::Enqueue BAD token " << modemToken); return FALSE; } Enqueue(modem); return TRUE; } PseudoModem *PseudoModemQ::DequeueWithRoute(const PString &number) { PWaitAndSignal mutexWait(Mutex); PObject *object; for( PINDEX i = 0 ; (object = GetAt(i)) != NULL ; i++ ) { PAssert(PIsDescendant(object, PseudoModem), PInvalidCast); PseudoModem *modem = (PseudoModem *)object; if (modem->CheckRoute(number) && modem->IsReady()) { if (!Remove(modem)) modem = NULL; myPTRACE(3, "PseudoModemQ::DequeueWithRoute " << ((modem != NULL) ? modem->ptyName() : "BAD")); return modem; } } return NULL; } PseudoModem *PseudoModemQ::Find(const PString &modemToken) const { PObject *object; for( PINDEX i = 0 ; (object = GetAt(i)) != NULL ; i++ ) { PAssert(PIsDescendant(object, PseudoModem), PInvalidCast); PseudoModem *modem = (PseudoModem *)object; if( modem->modemToken() == modemToken ) { return modem; } } return NULL; } PseudoModem *PseudoModemQ::Dequeue(const PString &modemToken) { PWaitAndSignal mutexWait(Mutex); PseudoModem *modem = Find(modemToken); if (modem != NULL && !Remove(modem)) modem = NULL; myPTRACE(1, "PseudoModemQ::Dequeue " << ((modem != NULL) ? modem->ptyName() : "BAD")); return modem; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/t38engine.h0000664000076400007640000002176411455110341016005 0ustar frolovfrolov00000000000000/* * t38engine.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: t38engine.h,v $ * Revision 1.42 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.41 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.40 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.39 2010/09/22 15:39:19 vfrolov * Moved ResetModemState() to EngineBase * Replaced _ResetModemState() by OnResetModemState() * * Revision 1.38 2010/09/08 17:22:24 vfrolov * Redesigned modem engine (continue) * * Revision 1.37 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.36 2009/12/02 09:06:42 vfrolov * Added a short delay after transmitting of signal before call clearing * * Revision 1.35 2009/11/26 07:21:37 vfrolov * Added delay between transmitting of signals * * Revision 1.34 2009/11/19 14:48:28 vfrolov * Moved common code to class EngineBase * * Revision 1.33 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.32 2009/11/10 08:13:38 vfrolov * Fixed race condition on re-opening T38Engine * * Revision 1.31 2009/11/06 10:19:29 vfrolov * Fixed indication handling after re-opening T38Engine * * Revision 1.30 2009/10/27 18:53:49 vfrolov * Added ability to re-open T38Engine * Added ability to prepare IFP packets with adaptive delay/period * * Revision 1.29 2009/07/27 16:21:24 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.28 2008/09/24 14:51:45 frolov * Added 5 sec. timeout for DCE's transmit buffer empty * * Revision 1.27 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.26 2007/05/10 10:40:33 vfrolov * Added ability to continuously resend last UDPTL packet * * Revision 1.25 2007/05/03 09:21:47 vfrolov * Added compile time optimization for original ASN.1 sequence * in T.38 (06/98) Annex A or for CORRIGENDUM No. 1 fix * * Revision 1.24 2007/04/11 14:19:15 vfrolov * Changed for OPAL * * Revision 1.23 2007/03/23 10:14:36 vfrolov * Implemented voice mode functionality * * Revision 1.22 2006/12/22 12:51:00 vfrolov * Fixed class of MutexModemCallback * Added volatile to isCarrierIn * Removed stateIn * * Revision 1.21 2006/12/11 11:19:48 vfrolov * Fixed race condition with modem Callback * * Revision 1.20 2005/07/20 12:42:36 vfrolov * Done less strict comparison in MODPARS::IsEqual() * * Revision 1.19 2005/07/18 11:39:48 vfrolov * Changed for OPAL * * Revision 1.18 2004/06/18 15:06:29 vfrolov * Fixed race condition by adding mutex for modemCallback * * Revision 1.17 2004/03/01 17:10:39 vfrolov * Fixed duplicated mutexes * Added volatile to T38Mode * * Revision 1.16 2003/12/04 16:10:05 vfrolov * Implemented FCS generation * Implemented ECM support * * Revision 1.15 2003/01/08 16:46:28 vfrolov * Added cbpOutBufNoFull and isOutBufFull() * Added data speed tracing * * Revision 1.14 2002/12/19 11:54:47 vfrolov * Removed DecodeIFPPacket() and utilized HandleRawIFP() * * Revision 1.13 2002/11/28 09:17:36 vfrolov * Added missing const * * Revision 1.12 2002/11/18 22:57:53 craigs * Added patches from Vyacheslav Frolov for CORRIGENDUM * * Revision 1.11 2002/05/22 12:01:50 vfrolov * Implemented redundancy error protection scheme * * Revision 1.10 2002/05/07 11:06:18 vfrolov * Discarded const from ModemCallbackWithUnlock() * * Revision 1.9 2002/05/07 10:15:44 vfrolov * Fixed dead lock on modemCallback * * Revision 1.8 2002/04/19 14:29:40 vfrolov * Added Copyright header * * Revision 1.7 2002/04/19 13:59:04 vfrolov * Added SendOnIdle() * * Revision 1.6 2002/02/11 16:46:21 vfrolov * Discarded transport arg from Originate() and Answer() * Thanks to Christopher Curtis * * Revision 1.5 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.4 2002/01/06 03:48:45 craigs * Added changes to support efax 0.9 * Thanks to Vyacheslav Frolov * * Revision 1.3 2002/01/03 21:36:42 craigs * Added additional logic to work with efax * Thanks to Vyacheslav Frolov * * Revision 1.2 2002/01/01 23:59:52 craigs * Lots of additional implementation thanks to Vyacheslav Frolov * */ #ifndef _T38ENGINE_H #define _T38ENGINE_H #include "pmutils.h" #include #include "hdlc.h" #include "t30.h" #include "enginebase.h" /////////////////////////////////////////////////////////////// class MODPARS { public: MODPARS( int _val = -1, unsigned _ind = unsigned(-1), int _lenInd = -1, unsigned _msgType = unsigned(-1), int _br = -1 ); PBoolean IsModValid() const { return val >= 0; } PBoolean IsEqual(const MODPARS &mp) const { return msgType == mp.msgType; } EngineBase::DataType dataType; EngineBase::DataType dataTypeT38; int val; unsigned ind; int lenInd; unsigned msgType; int br; }; /////////////////////////////////////////////////////////////// #ifdef OPTIMIZE_CORRIGENDUM_IFP #define T38_IFP T38_IFPPacket #define T38_IFP_NAME "IFP" #else #define T38_IFP T38_PreCorrigendum_IFPPacket #define T38_IFP_NAME "Pre-corrigendum IFP" #endif /////////////////////////////////////////////////////////////// class ModStream; class T38_IFP; class T38Engine : public EngineBase { PCLASSINFO(T38Engine, EngineBase); public: enum { msPerOut = 30 }; /**@name Construction */ //@{ T38Engine(const PString &_name); ~T38Engine(); //@} /**@name Modem API */ //@{ virtual void SendOnIdle(DataType _dataType); virtual PBoolean SendStart(DataType _dataType, int param); virtual int Send(const void *pBuf, PINDEX count); virtual PBoolean SendStop(PBoolean moreFrames, int _callbackParam); virtual PBoolean isOutBufFull() const; virtual PBoolean SendingNotCompleted() const; virtual PBoolean RecvWait(DataType _dataType, int param, int _callbackParam, PBoolean &done); virtual PBoolean RecvStart(int _callbackParam); virtual int Recv(void *pBuf, PINDEX count); virtual void RecvStop(); virtual int RecvDiag() const; /**Prepare outgoing T.38 packet. If returns 0, then the writing loop should be terminated. If returns >0, then the ifp packet is correct and should be sent. If returns <0, then the ifp packet is not correct (timeout). */ int PreparePacket( HOWNEROUT hOwner, T38_IFP & ifp ); /**Set outgoing T.38 packet prepare timeout. */ void SetPreparePacketTimeout( HOWNEROUT hOwner, int timeout, int period = -1 ); /**Handle incoming T.38 packet. If returns FALSE, then the reading loop should be terminated. */ PBoolean HandlePacket( HOWNERIN hOwner, const T38_IFP & ifp ); /**Handle lost T.38 packets. If returns FALSE, then the reading loop should be terminated. */ PBoolean HandlePacketLost( HOWNERIN hOwner, unsigned nLost ); //@} protected: virtual void OnAttach(); virtual void OnDetach(); virtual void OnResetModemState(); virtual void OnChangeModemClass(); virtual void OnOpenIn(); virtual void OnOpenOut(); virtual void OnCloseIn(); virtual void OnCloseOut(); virtual void OnChangeEnableFakeIn(); virtual void OnChangeEnableFakeOut(); private: void SignalOutDataReady() { outDataReadySyncPoint.Signal(); } void WaitOutDataReady() { outDataReadySyncPoint.Wait(); } PBoolean WaitOutDataReady(const PTimeInterval & timeout) { return outDataReadySyncPoint.Wait(timeout); } private: DataStream bufOut; int preparePacketTimeout; int preparePacketPeriod; PAdaptiveDelay preparePacketDelay; int stateOut; DataType onIdleOut; int callbackParamOut; MODPARS ModParsOut; PBoolean delaySignalOut; PBoolean startedTimeOutBufEmpty; PTime timeOutBufEmpty; PTime timeDelayEndOut; PTime timeBeginOut; PINDEX countOut; PBoolean moreFramesOut; HDLC hdlcOut; int callbackParamIn; volatile int isCarrierIn; #if PTRACING PTime timeBeginIn; #endif PINDEX countIn; T30 t30; ModStream *modStreamIn; ModStream *modStreamInSaved; volatile int stateModem; PSyncPoint outDataReadySyncPoint; }; /////////////////////////////////////////////////////////////// #endif // _T38ENGINE_H t38modem-2.0.0/t30.h0000664000076400007640000000316111223116131014572 0ustar frolovfrolov00000000000000/* * t30.h * * T38FAX Pseudo Modem * * Copyright (c) 2003-2009 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: t30.h,v $ * Revision 1.4 2009/07/02 11:39:05 vfrolov * Added T30() constructor * * Revision 1.3 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.2 2003/12/04 15:51:54 vfrolov * Removed unused DIS member * * Revision 1.1 2003/12/04 13:38:52 vfrolov * Initial revision * */ #ifndef _T30_H #define _T30_H #include "pmutils.h" /////////////////////////////////////////////////////////////// class T30 { public: T30() : cfr(FALSE), ecm(FALSE) {} void v21Begin() { v21frame = PBYTEArray(); } void v21Data(void *pBuf, PINDEX len) { v21frame.Concatenate(PBYTEArray((BYTE *)pBuf, len)); } void v21End(PBoolean sent); PBoolean hdlcOnly() const { return cfr && ecm; } private: PBYTEArray v21frame; PBoolean cfr; PBoolean ecm; }; /////////////////////////////////////////////////////////////// #endif // _T30_H t38modem-2.0.0/version.h0000664000076400007640000000160611551246122015663 0ustar frolovfrolov00000000000000/* * version.h * * T38FAX Pseudo Modem * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * */ #ifndef _T38M_VERSION_H #define _T38M_VERSION_H #define MAJOR_VERSION 2 #define MINOR_VERSION 0 #define BUILD_TYPE ReleaseCode #define BUILD_NUMBER 0 #endif // _T38M_VERSION_H t38modem-2.0.0/drv_pty.cxx0000664000076400007640000003422511225164340016242 0ustar frolovfrolov00000000000000/* * drv_pty.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2009 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: drv_pty.cxx,v $ * Revision 1.11 2009/07/08 18:43:44 vfrolov * Added PseudoModem::ttyName() * * Revision 1.10 2009/05/19 07:29:04 vfrolov * Commented Termios code uncompatible with some systems * * Revision 1.9 2009/03/13 09:44:32 vfrolov * Fixed Segmentation fault (wrong PString usage) * * Revision 1.8 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.7 2007/07/17 10:03:22 vfrolov * Added Unix98 PTY support * * Revision 1.6 2007/02/21 08:04:20 vfrolov * Added printing message to stdout if no pty device * * Revision 1.5 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.4 2006/10/19 10:44:15 vfrolov * Fixed big file descriptors problem (replaced select() by poll()) * * Revision 1.3 2006/10/18 14:54:53 vfrolov * Added hPty >= FD_SETSIZE check * * Revision 1.2 2004/10/20 14:00:17 vfrolov * Fixed race condition with SignalDataReady()/WaitDataReady() * * Revision 1.1 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * * Log: pty.cxx,v * * Revision 1.7 2002/12/30 12:49:42 vfrolov * Added tracing thread's CPU usage (Linux only) * * Revision 1.6 2002/12/20 10:13:04 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.5 2002/03/05 12:37:45 vfrolov * Some OS specific code moved from pmodem.cxx to pty.cxx * * Revision 1.4 2002/03/01 08:53:16 vfrolov * Added Copyright header * Some OS specific code moved from pmodemi.cxx to pty.cxx * Added error code string to log * Fixed race condition with fast close and open slave tty * Some other changes * * Revision 1.3 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.2 2002/01/01 23:43:57 craigs * Added fix from Vyacheslav Frolov * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #include #include "drv_pty.h" #ifdef MODEM_DRIVER_Pty #include #define new PNEW /////////////////////////////////////////////////////////////// class UniPty : public ModemThreadChild { PCLASSINFO(UniPty, ModemThreadChild); public: UniPty(PseudoModemPty &_parent, int _hPty); protected: PseudoModemPty &Parent() { return (PseudoModemPty &)parent; } int hPty; }; /////////////////////////////////////////////////////////////// class InPty : public UniPty { PCLASSINFO(InPty, UniPty); public: InPty(PseudoModemPty &_parent, int _hPty); protected: virtual void Main(); }; /////////////////////////////////////////////////////////////// class OutPty : public UniPty { PCLASSINFO(OutPty, UniPty); public: OutPty(PseudoModemPty &_parent, int _hPty); protected: virtual void Main(); }; /////////////////////////////////////////////////////////////// UniPty::UniPty(PseudoModemPty &_parent, int _hPty) : ModemThreadChild(_parent), hPty(_hPty) { } /////////////////////////////////////////////////////////////// InPty::InPty(PseudoModemPty &_parent, int _hPty) : UniPty(_parent, _hPty) { } void InPty::Main() { RenameCurrentThread(Parent().ptyName() + "(i)"); myPTRACE(1, "--> Started"); for (;;) { pollfd pollfd; pollfd.fd = hPty; pollfd.events = POLLIN; if (stop) break; ::poll(&pollfd, 1, 5000); if (pollfd.revents) { char cbuf[1024]; int len; if (stop) break; len = ::read(hPty, cbuf, sizeof(cbuf)); if (len < 0) { int err = errno; myPTRACE(1, "--> read ERROR " << len << " " << strerror(err)); SignalStop(); break; } if (len == 0) { SignalStop(); break; } if (len > 0) { Parent().ToInPtyQ(cbuf, len); if (stop) break; } } } myPTRACE(1, "--> Stopped" << GetThreadTimes(", CPU usage: ")); } /////////////////////////////////////////////////////////////// OutPty::OutPty(PseudoModemPty &_parent, int _hPty) : UniPty(_parent, _hPty) { } void OutPty::Main() { RenameCurrentThread(Parent().ptyName() + "(o)"); myPTRACE(1, "<-- Started"); PBYTEArray *buf = NULL; PINDEX done = 0; for (;;) { pollfd pollfd; pollfd.fd = hPty; pollfd.events = POLLOUT; while (!buf) { if (stop) break; buf = Parent().FromOutPtyQ(); if (buf) { done = 0; break; } WaitDataReady(); } if (stop) break; ::poll(&pollfd, 1, 5000); if (pollfd.revents) { int len; if (stop) break; len = ::write(hPty, (const BYTE *)*buf + done, buf->GetSize() - done); if (len < 0) { int err = errno; myPTRACE(1, "<-- write ERROR " << len << " " << strerror(err)); SignalStop(); break; } done += len; if (buf->GetSize() <= done) { if (buf->GetSize() < done) { myPTRACE(1, "<-- " << buf->GetSize() << "(size) < (done)" << done << " " << len); } delete buf; buf = NULL; } } } if (buf) { if (buf->GetSize() != done) myPTRACE(1, "<-- Not sent " << PRTHEX(PBYTEArray((const BYTE *)*buf + done, buf->GetSize() - done))); delete buf; } myPTRACE(1, "<-- Stopped" << GetThreadTimes(", CPU usage: ")); } /////////////////////////////////////////////////////////////// #ifdef USE_LEGACY_PTY static const char *ttyPatternLegacy() { #if defined(P_LINUX) #define TTY_PATTERN "^(/dev/)?tty[pqrstuvwxyzabcde][0123456789abcdef]$" #endif #if defined(P_FREEBSD) #define TTY_PATTERN "^(/dev/)?tty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv]$" #endif #ifndef TTY_PATTERN #define TTY_PATTERN "^(/dev/)?tty..$" #endif return TTY_PATTERN; #undef TTY_PATTERN } static PBoolean ttyCheckLegacy(const PString &_tty) { PRegularExpression regLegacy(ttyPatternLegacy(), PRegularExpression::Extended); return _tty.FindRegEx(regLegacy) == 0; } #endif // USE_LEGACY_PTY /////////////////////////////////////////////////////////////// #ifdef USE_UNIX98_PTY static const char *ttyPatternUnix98() { return "^\\+.+$"; } static PBoolean ttyCheckUnix98(const PString &_tty) { PRegularExpression regUnix98(ttyPatternUnix98(), PRegularExpression::Extended); return _tty.FindRegEx(regUnix98) == 0; } static const char *ptyPathUnix98() { return "/dev/ptmx"; } static void ttyUnlinkUnix98(const char *ttypath) { char ptsName[64]; memset(ptsName, 0, sizeof(ptsName)); if (readlink(ttypath, ptsName, sizeof(ptsName) - 1) >= 0 && ::unlink(ttypath) == 0) myPTRACE(1, "PseudoModemPty::OpenPty removed link " << ttypath << " -> " << ptsName); } #endif // USE_UNIX98_PTY /////////////////////////////////////////////////////////////// PseudoModemPty::PseudoModemPty( const PString &_tty, const PString &_route, #ifdef USE_UNIX98_PTY const PConfigArgs &args, #else const PConfigArgs &/*args*/, #endif const PNotifier &_callbackEndPoint) : PseudoModemBody(_tty, _route, _callbackEndPoint), hPty(-1), inPty(NULL), outPty(NULL) { valid = TRUE; #ifdef USE_LEGACY_PTY if (ttyCheckLegacy(_tty)) { if (_tty[0] != '/') ttypath = "/dev/" + _tty; else ttypath = _tty; (ptypath = ttypath)[5] = 'p'; ptyname = ptypath.Mid(5); } else #endif // USE_LEGACY_PTY #ifdef USE_UNIX98_PTY if (ttyCheckUnix98(_tty)) { if (args.HasOption("pts-dir")) { ttypath = args.GetOptionString("pts-dir"); if (!ttypath.IsEmpty() && ttypath.Right(1) != "/") ttypath += "/"; } ttypath += _tty.Mid(1); ptypath = ptyPathUnix98(); PINDEX i = ttypath.FindLast('/'); if (i == P_MAX_INDEX) i = 0; else i++; ptyname = ttypath.Mid(i); } else #endif // USE_UNIX98_PTY { myPTRACE(1, "PseudoModemPty::PseudoModemPty bad on " << _tty); valid = FALSE; } } PseudoModemPty::~PseudoModemPty() { StopAll(); ClosePty(); } PBoolean PseudoModemPty::CheckTty(const PString &_tty) { #ifdef USE_LEGACY_PTY if (ttyCheckLegacy(_tty)) return TRUE; #endif #ifdef USE_UNIX98_PTY if (ttyCheckUnix98(_tty)) return TRUE; #endif return FALSE; } PString PseudoModemPty::ArgSpec() { return #ifdef USE_UNIX98_PTY "-pts-dir:" #endif ""; } PStringArray PseudoModemPty::Description() { PStringArray descriptions = PString( "Uses pseudo-tty (pty) devices to communicate with a fax application.\n" #ifdef USE_LEGACY_PTY "For legacy ptys the tty should match to the regexp\n" " '" + PString(ttyPatternLegacy()) + "'\n" #endif #ifdef USE_UNIX98_PTY "For Unix98 ptys the tty should match to the regexp\n" " '" + PString(ttyPatternUnix98()) + "'\n" "(the first character '+' will be replaced by a base directory).\n" "Options:\n" " --pts-dir dir : Set a base directory for Unix98 scheme,\n" " default is empty.\n" #endif ).Lines(); return descriptions; } const PString &PseudoModemPty::ttyPath() const { return ttypath; } ModemThreadChild *PseudoModemPty::GetPtyNotifier() { return outPty; } PBoolean PseudoModemPty::StartAll() { if (IsOpenPty() && (inPty = new InPty(*this, hPty)) && (outPty = new OutPty(*this, hPty)) && (PseudoModemBody::StartAll()) ) { inPty->Resume(); outPty->Resume(); return TRUE; } StopAll(); ClosePty(); return FALSE; } void PseudoModemPty::StopAll() { if (inPty) { inPty->SignalStop(); inPty->WaitForTermination(); PWaitAndSignal mutexWait(Mutex); delete inPty; inPty = NULL; } if (outPty) { outPty->SignalStop(); outPty->WaitForTermination(); PWaitAndSignal mutexWait(Mutex); delete outPty; outPty = NULL; } PseudoModemBody::StopAll(); } PBoolean PseudoModemPty::OpenPty() { if (IsOpenPty()) { // check hPty health pollfd pollfd; pollfd.fd = hPty; pollfd.events = POLLIN; ::poll(&pollfd, 1, 0); if (pollfd.revents) { char cbuf[1]; int len; len = ::read(hPty, cbuf, 1); if (len < 0) { // hPty is ill (slave tty was closed and still was not opened) int err = errno; // close hPty ASAP ClosePty(); myPTRACE(1, "PseudoModemPty::OpenPty read ERROR " << len << " " << strerror(err)); } else if (len > 0) { PBYTEArray *buf = new PBYTEArray((const BYTE *)cbuf, len); myPTRACE(3, "PseudoModemPty::OpenPty read " << PRTHEX(*buf)); ToInPtyQ(buf); } } if (IsOpenPty()) { // hPty is good (slave tty was not closed or was opened after closing) return TRUE; } } int delay = 0; while ((hPty = ::open(ptypath, O_RDWR | O_NOCTTY)) < 0) { int err = errno; myPTRACE(delay + 1, "PseudoModemPty::OpenPty open " << ptypath << " ERROR: " << strerror(err)); if (err == ENOENT) { cout << "Could not open " << ptypath << ": " << strerror(err) << endl; return FALSE; } myPTRACE(delay + 1, "PseudoModemPty::OpenPty will try again to open " << ptypath); if (delay > 0) PThread::Sleep(delay * 1000); if (++delay > 5) delay = 5; } #if defined FD_TRACE_LEVEL && defined PTRACING #ifdef FD_SETSIZE if (hPty > (FD_SETSIZE*3)/4) myPTRACE(FD_TRACE_LEVEL, "PseudoModemPty::OpenPty WARNING: hPty=" << hPty << ", FD_SETSIZE=" << FD_SETSIZE); #else #warning FD_SETSIZE not defined! #endif #endif #if 0 struct termios Termios; if (::tcgetattr(hPty, &Termios) != 0) { int err = errno; myPTRACE(1, "PseudoModemPty::OpenPty tcgetattr " << ptyname << " ERROR: " << strerror(err)); ClosePty(); return FALSE; } Termios.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT); Termios.c_iflag |= IGNBRK; //Termios.c_iflag &= ~IGNBRK; //Termios.c_iflag |= BRKINT; Termios.c_cc[VMIN] = 1; Termios.c_cc[VTIME] = 0; if (::tcsetattr(hPty, TCSANOW, &Termios) != 0) { int err = errno; myPTRACE(1, "PseudoModemPty::OpenPty tcsetattr " << ptyname << " ERROR: " << strerror(err)); ClosePty(); return FALSE; } #endif #ifdef USE_UNIX98_PTY if (ptypath == ptyPathUnix98()) { ttyUnlinkUnix98(ttypath); if (::unlockpt(hPty) != 0) { int err = errno; myPTRACE(1, "PseudoModemPty::OpenPty unlockpt " << ptyname << " ERROR: " << strerror(err)); ClosePty(); return FALSE; } char ptsName[64]; if (::ptsname_r(hPty, ptsName, sizeof(ptsName)) != 0) { int err = errno; myPTRACE(1, "PseudoModemPty::OpenPty ptsname_r " << ptyname << " ERROR: " << strerror(err)); ClosePty(); return FALSE; } if (::symlink(ptsName, ttypath) != 0) { int err = errno; myPTRACE(1, "PseudoModemPty::OpenPty symlink " << ttypath << " -> " << ptsName << " ERROR: " << strerror(err)); ClosePty(); return FALSE; } myPTRACE(1, "PseudoModemPty::OpenPty added link " << ttypath << " -> " << ptsName); } #endif // USE_UNIX98_PTY return TRUE; } void PseudoModemPty::ClosePty() { if (!IsOpenPty()) return; #ifdef USE_UNIX98_PTY ttyUnlinkUnix98(ttypath); #endif if (::close(hPty) != 0) { int err = errno; myPTRACE(1, "PseudoModemPty::ClosePty close " << ptyname << " ERROR: " << strerror(err)); } hPty = -1; } void PseudoModemPty::MainLoop() { if (AddModem()) { while (!stop && OpenPty() && StartAll()) { while (!stop && !childstop) { WaitDataReady(); } StopAll(); } ClosePty(); } } /////////////////////////////////////////////////////////////// #endif // MODEM_DRIVER_Pty t38modem-2.0.0/dle.h0000664000076400007640000000344111222137437014744 0ustar frolovfrolov00000000000000/* * dle.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2009 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: dle.h,v $ * Revision 1.5 2009/06/29 13:18:23 vfrolov * Disabled changing bitRev in Clean() * * Revision 1.4 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.3 2002/04/19 14:29:30 vfrolov * Added Copyright header * * Revision 1.2 2002/01/10 06:10:02 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #ifndef _DLE_H #define _DLE_H #include "pmutils.h" /////////////////////////////////////////////////////////////// class DLEData : public DataStream { PCLASSINFO(DLEData, DataStream); public: DLEData() : dle(FALSE), recvEtx(FALSE), bitRev(FALSE) { } int PutDleData(const void *pBuf, PINDEX count); int GetDleData(void *pBuf, PINDEX count); void BitRev(PBoolean _bitRev) { bitRev = _bitRev; } virtual void Clean() { DataStream::Clean(); dle = recvEtx = FALSE; } protected: PBoolean dle; PBoolean recvEtx; PBoolean bitRev; }; /////////////////////////////////////////////////////////////// #endif // _DLE_H t38modem-2.0.0/precompile.cxx0000664000076400007640000000201510200367456016707 0ustar frolovfrolov00000000000000/* * precompile.cxx * * PWLib application source file for t38modem * * Precompiled header generation file. * * Copyright (c) 1993-1998 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Portions are Copyright (C) 1993 Free Software Foundation, Inc. * All Rights Reserved. * * Contributor(s): ______________________________________. */ #include // End of File /////////////////////////////////////////////////////////////// t38modem-2.0.0/t38engine.cxx0000664000076400007640000015573111455110341016362 0ustar frolovfrolov00000000000000/* * t38engine.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: t38engine.cxx,v $ * Revision 1.73 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.72 2010/10/08 06:09:53 vfrolov * Added parameter assert to SetPreparePacketTimeout() * * Revision 1.71 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.70 2010/09/22 15:39:19 vfrolov * Moved ResetModemState() to EngineBase * Replaced _ResetModemState() by OnResetModemState() * * Revision 1.69 2010/09/08 17:22:23 vfrolov * Redesigned modem engine (continue) * * Revision 1.68 2010/04/22 15:41:30 vfrolov * Fixed +FRS delay if remote does not send no-signal indicator * * Revision 1.67 2010/03/23 08:58:14 vfrolov * Fixed issues with +FTS and +FRS * * Revision 1.66 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.65 2010/02/27 11:11:54 vfrolov * Added missing redo * * Revision 1.64 2010/01/28 10:27:03 vfrolov * Added handling T.38 CED indication * * Revision 1.63 2009/12/02 09:06:42 vfrolov * Added a short delay after transmitting of signal before call clearing * * Revision 1.62 2009/11/26 07:21:37 vfrolov * Added delay between transmitting of signals * * Revision 1.61 2009/11/19 14:48:28 vfrolov * Moved common code to class EngineBase * * Revision 1.60 2009/11/19 11:18:16 vfrolov * Added handling T.38 CED indication * * Revision 1.59 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.58 2009/11/10 09:56:54 vfrolov * Fixed isCarrierIn handling * * Revision 1.57 2009/11/10 08:13:38 vfrolov * Fixed race condition on re-opening T38Engine * * Revision 1.56 2009/11/06 10:19:29 vfrolov * Fixed indication handling after re-opening T38Engine * * Revision 1.55 2009/10/27 18:53:49 vfrolov * Added ability to re-open T38Engine * Added ability to prepare IFP packets with adaptive delay/period * * Revision 1.54 2009/07/27 16:21:24 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.53 2009/07/03 09:12:04 vfrolov * Included opal/buildopts.h * * Revision 1.52 2009/07/02 15:15:43 vfrolov * Fixed handling 5 sec buffer empty event for +FTM command * * Revision 1.51 2009/07/02 13:03:22 vfrolov * Fixed aborting +FTM immediately after CONNECT if no data in the buffer * * Revision 1.50 2009/02/05 14:15:18 vfrolov * Added missing cbpOutBufNoFull notification (for ECM) * * Revision 1.49 2009/01/27 14:00:50 vfrolov * Added missing startedTimeOutBufEmpty initialization * * Revision 1.48 2008/09/24 14:51:45 frolov * Added 5 sec. timeout for DCE's transmit buffer empty * * Revision 1.47 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.46 2007/05/24 17:03:54 vfrolov * Added more PTRACING checks * * Revision 1.45 2007/05/10 10:40:33 vfrolov * Added ability to continuously resend last UDPTL packet * * Revision 1.44 2007/05/03 09:21:47 vfrolov * Added compile time optimization for original ASN.1 sequence * in T.38 (06/98) Annex A or for CORRIGENDUM No. 1 fix * * Revision 1.43 2007/04/26 13:57:17 vfrolov * Changed logging for OPAL * * Revision 1.42 2007/04/11 14:19:15 vfrolov * Changed for OPAL * * Revision 1.41 2007/03/23 10:14:36 vfrolov * Implemented voice mode functionality * * Revision 1.40 2006/12/11 11:19:48 vfrolov * Fixed race condition with modem Callback * * Revision 1.39 2006/12/07 10:50:39 vfrolov * Fixed possible dead lock * * Revision 1.38 2006/10/20 10:04:00 vfrolov * Added code for ignoring repeated indicators * Added code for sending repeated indicators (disabled by default) * * Revision 1.37 2006/07/05 04:37:17 csoutheren * Applied 1488904 - SetPromiscuous(AcceptFromLastReceivedOnly) for T.38 * Thanks to Vyacheslav Frolov * * Revision 1.36 2005/07/21 06:49:02 vfrolov * Added missing CompleteEncoding() * * Revision 1.35 2005/07/18 11:39:47 vfrolov * Changed for OPAL * * Revision 1.34 2005/03/03 16:09:06 vfrolov * Fixed memory leak * * Revision 1.33 2005/02/04 10:18:49 vfrolov * Fixed warnings for No Trace build * * Revision 1.32 2005/02/03 11:32:12 vfrolov * Fixed MSVC compile warnings * * Revision 1.31 2004/08/24 16:12:10 vfrolov * Fixed bit counter overflow * * Revision 1.30 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.29 2004/06/18 15:06:29 vfrolov * Fixed race condition by adding mutex for modemCallback * * Revision 1.28 2004/03/01 17:10:32 vfrolov * Fixed duplicated mutexes * Added volatile to T38Mode * * Revision 1.27 2003/12/04 16:09:58 vfrolov * Implemented FCS generation * Implemented ECM support * * Revision 1.26 2003/01/08 16:55:52 vfrolov * Added cbpOutBufNoFull and isOutBufFull() * Added data speed tracing * Fixed "thread did not terminate" (added msMaxOutDelay) * Discarded useless name tracing * * Revision 1.25 2002/12/30 12:49:46 vfrolov * Added tracing thread's CPU usage (Linux only) * * Revision 1.24 2002/12/20 10:13:08 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.23 2002/12/19 14:19:20 vfrolov * Added missing brackets (fixed all CPU usage reported by Markus Storm) * * Revision 1.22 2002/12/19 11:54:43 vfrolov * Removed DecodeIFPPacket() and utilized HandleRawIFP() * * Revision 1.21 2002/11/28 09:17:31 vfrolov * Added missing const * * Revision 1.20 2002/11/21 07:16:46 robertj * Changed promiscuous mode to be three way. Fixes race condition in gkserver * which can cause crashes or more PDUs to be sent to the wrong place. * * Revision 1.19 2002/11/18 23:12:17 craigs * Removed reference to t38old.h * * Revision 1.18 2002/11/18 23:01:58 craigs * Changed name of pre CORRIGENDUM ASN * * Revision 1.17 2002/11/18 22:57:53 craigs * Added patches from Vyacheslav Frolov for CORRIGENDUM * * Revision 1.16 2002/11/15 07:43:52 vfrolov * Do not wait no-signal if received *-sig-end * Fixed compiler warnings * * Revision 1.15 2002/05/22 15:21:26 vfrolov * Added missed enableTimeout check * Fixed bad ifp tracing bug * * Revision 1.14 2002/05/22 12:01:45 vfrolov * Implemented redundancy error protection scheme * * Revision 1.13 2002/05/15 16:05:17 vfrolov * Changed algorithm of handling isCarrierIn * Removed delay after sending dtSilence * * Revision 1.12 2002/05/08 16:33:16 vfrolov * Adjusted post training delays * * Revision 1.11 2002/05/07 11:06:12 vfrolov * Discarded const from ModemCallbackWithUnlock() * * Revision 1.10 2002/05/07 10:15:38 vfrolov * Fixed dead lock on modemCallback * * Revision 1.9 2002/04/19 13:58:59 vfrolov * Added SendOnIdle() * * Revision 1.8 2002/03/01 09:02:04 vfrolov * Added Copyright header * Added name name to trace messages * * Revision 1.7 2002/02/11 16:46:18 vfrolov * Discarded transport arg from Originate() and Answer() * Thanks to Christopher Curtis * * Revision 1.6 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.5 2002/01/06 03:48:45 craigs * Added changes to support efax 0.9 * Thanks to Vyacheslav Frolov * * Revision 1.4 2002/01/03 21:36:42 craigs * Added additional logic to work with efax * Thanks to Vyacheslav Frolov * * Revision 1.3 2002/01/02 04:50:34 craigs * General formatting cleanups whilst looking for efax problem * * Revision 1.2 2002/01/01 23:59:52 craigs * Lots of additional implementation thanks to Vyacheslav Frolov * */ #include #ifdef USE_OPAL #include #include #else #include #endif #include "t38engine.h" #define new PNEW #define T38I(t30_indicator) T38_Type_of_msg_t30_indicator::t30_indicator #define T38D(msg_data) T38_Type_of_msg_data::msg_data #define T38F(field_type) T38_Data_Field_subtype_field_type::field_type #define msMaxOutDelay (msPerOut*5) #ifdef P_LINUX #define mySleep(ms) usleep((ms) * 1000L) #else #define mySleep(ms) PThread::Sleep(ms) #endif /////////////////////////////////////////////////////////////// enum StateOut { stOutIdle, stOutCedWait, stOutSilenceWait, stOutIndWait, stOutData, stOutHdlcFcs, stOutDataNoSig, stOutNoSig, }; /////////////////////////////////////////////////////////////// enum StateModem { stmIdle, stmOutMoreData, stmOutNoMoreData, stmInWaitSilence, stmInWaitData, stmInReadyData, stmInRecvData, }; #define isStateModemOut() (stateModem >= stmOutMoreData && stateModem <= stmOutNoMoreData) #define isStateModemIn() (stateModem >= stmInWaitData && stateModem <= stmInRecvData) /////////////////////////////////////////////////////////////// class ModStream { public: ModStream(const MODPARS &_ModPars); ~ModStream(); void PushBuf(); PBoolean DeleteFirstBuf(); PBoolean PopBuf(); int GetData(void *pBuf, PINDEX count); int PutData(const void *pBuf, PINDEX count); PBoolean SetDiag(int diag); PBoolean PutEof(int diag = 0); void Move(ModStream &from); DataStream *firstBuf; DataStreamQ bufQ; DataStream *lastBuf; // if not NULL then shold be in bufQ or firstBuf MODPARS ModPars; HDLC hdlc; }; ModStream::ModStream(const MODPARS &_ModPars) : firstBuf(NULL), lastBuf(NULL), ModPars(_ModPars) { } ModStream::~ModStream() { if (firstBuf != NULL) { PTRACE(1, "ModStream::~ModStream firstBuf != NULL, clean"); delete firstBuf; } PTRACE_IF(1, bufQ.GetSize() > 0, "ModStream::~ModStream bufQ.GetSize()=" << bufQ.GetSize() << ", clean"); } void ModStream::PushBuf() { lastBuf = new DataStream(); bufQ.Enqueue(lastBuf); } PBoolean ModStream::DeleteFirstBuf() { if (firstBuf != NULL) { if (lastBuf == firstBuf) lastBuf = NULL; delete firstBuf; firstBuf = NULL; return TRUE; } return FALSE; } PBoolean ModStream::PopBuf() { if (DeleteFirstBuf()) { PTRACE(1, "ModStream::PopBuf DeleteFirstBuf(), clean"); } firstBuf = bufQ.Dequeue(); if (!firstBuf) return FALSE; if (ModPars.dataType == EngineBase::dtRaw && ModPars.dataTypeT38 == EngineBase::dtHdlc) { hdlc = HDLC(); hdlc.PutHdlcData(firstBuf); hdlc.GetRawStart(10); } return TRUE; } int ModStream::GetData(void *pBuf, PINDEX count) { if (firstBuf == NULL) { myPTRACE(1, "ModStream::GetData firstBuf == NULL"); return -1; } if (ModPars.dataType == EngineBase::dtRaw && ModPars.dataTypeT38 == EngineBase::dtHdlc) { int len; while ((len = hdlc.GetData(pBuf, count)) < 0) { DataStream *_firstBuf; if ((_firstBuf = bufQ.Dequeue()) != NULL) { DeleteFirstBuf(); firstBuf = _firstBuf; hdlc.PutHdlcData(firstBuf); hdlc.GetRawStart(); } else { if ((firstBuf->GetDiag() & EngineBase::diagNoCarrier) == 0) { DeleteFirstBuf(); return 0; } return -1; } } return len; } if (ModPars.dataTypeT38 != ModPars.dataType) { myPTRACE(1, "ModStream::GetData ModPars.dataType(" << ModPars.dataType << ") != ModPars.dataTypeT38(" << ModPars.dataTypeT38 << ")"); return -1; } return firstBuf->GetData(pBuf, count); } int ModStream::PutData(const void *pBuf, PINDEX count) { if( lastBuf == NULL ) { myPTRACE(1, "ModStream::PutData lastBuf == NULL"); return -1; } return lastBuf->PutData(pBuf, count); } PBoolean ModStream::SetDiag(int diag) { if( lastBuf == NULL ) return FALSE; lastBuf->SetDiag(lastBuf->GetDiag() | diag); return TRUE; } PBoolean ModStream::PutEof(int diag) { if( lastBuf == NULL ) return FALSE; lastBuf->SetDiag(lastBuf->GetDiag() | diag).PutEof(); lastBuf = NULL; return TRUE; } void ModStream::Move(ModStream &from) { if( from.firstBuf != NULL ) { bufQ.Enqueue(from.firstBuf); from.firstBuf = NULL; } DataStream *buf; while( (buf = from.bufQ.Dequeue()) != NULL ) { bufQ.Enqueue(buf); } lastBuf = from.lastBuf; from.lastBuf = NULL; } /////////////////////////////////////////////////////////////// MODPARS::MODPARS(int _val, unsigned _ind, int _lenInd, unsigned _msgType, int _br) : dataType(EngineBase::dtNone), dataTypeT38(EngineBase::dtNone), val(_val), ind(_ind), lenInd(_lenInd), msgType(_msgType), br(_br) { } static const MODPARS mods[] = { MODPARS( 3, T38I(e_v21_preamble), 900, T38D(e_v21), 300 ), MODPARS( 24, T38I(e_v27_2400_training), 1100, T38D(e_v27_2400), 2400 ), MODPARS( 48, T38I(e_v27_4800_training), 900, T38D(e_v27_4800), 4800 ), MODPARS( 72, T38I(e_v29_7200_training), 300, T38D(e_v29_7200), 7200 ), MODPARS( 73, T38I(e_v17_7200_long_training), 1500, T38D(e_v17_7200), 7200 ), MODPARS( 74, T38I(e_v17_7200_short_training), 300, T38D(e_v17_7200), 7200 ), MODPARS( 96, T38I(e_v29_9600_training), 300, T38D(e_v29_9600), 9600 ), MODPARS( 97, T38I(e_v17_9600_long_training), 1500, T38D(e_v17_9600), 9600 ), MODPARS( 98, T38I(e_v17_9600_short_training), 300, T38D(e_v17_9600), 9600 ), MODPARS( 121, T38I(e_v17_12000_long_training), 1500, T38D(e_v17_12000), 12000 ), MODPARS( 122, T38I(e_v17_12000_short_training), 300, T38D(e_v17_12000), 12000 ), MODPARS( 145, T38I(e_v17_14400_long_training), 1500, T38D(e_v17_14400), 14400 ), MODPARS( 146, T38I(e_v17_14400_short_training), 300, T38D(e_v17_14400), 14400 ), }; static const MODPARS invalidMods; enum GetModParsBy { by_val, by_ind, }; static const MODPARS &GetModPars(int key, enum GetModParsBy by = by_val) { for( PINDEX i = 0 ; i < PINDEX(sizeof(mods)/sizeof(mods[0])) ; i++ ) { switch( by ) { case by_val: if( mods[i].val == key ) return mods[i]; break; case by_ind: if( mods[i].ind == (unsigned)key ) return mods[i]; break; default: return invalidMods; } } return invalidMods; } /////////////////////////////////////////////////////////////// static void t38indicator(T38_IFP &ifp, unsigned type) { ifp.m_type_of_msg.SetTag(T38_Type_of_msg::e_t30_indicator); (T38_Type_of_msg_t30_indicator &)ifp.m_type_of_msg = type; } #ifdef OPTIMIZE_CORRIGENDUM_IFP #define T38_DATA_FIELD T38_Data_Field_subtype #else #define T38_DATA_FIELD T38_PreCorrigendum_Data_Field_subtype #endif static T38_DATA_FIELD &t38data(T38_IFP &ifp, unsigned type, unsigned field_type) { ifp.m_type_of_msg.SetTag(T38_Type_of_msg::e_data); (T38_Type_of_msg_data &)ifp.m_type_of_msg = type; ifp.IncludeOptionalField(T38_IFPPacket::e_data_field); ifp.m_data_field.SetSize(ifp.m_data_field.GetSize()+1); T38_DATA_FIELD &Data_Field = ifp.m_data_field[0]; Data_Field.m_field_type = field_type; return Data_Field; } static void t38data(T38_IFP &ifp, unsigned type, unsigned field_type, const PBYTEArray &data) { T38_DATA_FIELD &Data_Field = t38data(ifp, type, field_type); if( data.GetSize() > 0 ) { Data_Field.IncludeOptionalField(T38_Data_Field_subtype::e_field_data); Data_Field.m_field_data = data; } } /////////////////////////////////////////////////////////////// class FakePreparePacketThread : public PThread { PCLASSINFO(FakePreparePacketThread, PThread); public: FakePreparePacketThread(T38Engine &engine) : PThread(30000) , t38engine(engine) { PTRACE(3, t38engine.Name() << " FakePreparePacketThread"); t38engine.AddReference(); } ~FakePreparePacketThread() { PTRACE(3, t38engine.Name() << " ~FakePreparePacketThread"); ReferenceObject::DelPointer(&t38engine); } protected: virtual void Main(); T38Engine &t38engine; }; void FakePreparePacketThread::Main() { PTRACE(3, t38engine.Name() << " FakePreparePacketThread::Main started"); t38engine.OpenOut(EngineBase::HOWNEROUT(this), TRUE); t38engine.SetPreparePacketTimeout(EngineBase::HOWNEROUT(this), -1); #if PTRACING unsigned long count = 0; #endif for (;;) { T38_IFP ifp; int res; res = t38engine.PreparePacket(EngineBase::HOWNEROUT(this), ifp); if (res == 0) break; #if PTRACING if (res > 0) { count++; PTRACE(4, t38engine.Name() << " FakePreparePacketThread::Main ifp = " << setprecision(2) << ifp); } #endif } t38engine.CloseOut(EngineBase::HOWNEROUT(this)); PTRACE(3, t38engine.Name() << " FakePreparePacketThread::Main stopped, faked out " << count << " IFP packets"); } /////////////////////////////////////////////////////////////// T38Engine::T38Engine(const PString &_name) : EngineBase(_name + " T38Engine") , bufOut(2048) , preparePacketTimeout(-1) , preparePacketPeriod(-1) , preparePacketDelay() , stateOut(stOutNoSig) , onIdleOut(dtNone) , callbackParamOut(cbpReset) , ModParsOut() , delaySignalOut(FALSE) , startedTimeOutBufEmpty(FALSE) , timeOutBufEmpty() , timeDelayEndOut() , timeBeginOut() , countOut(0) , moreFramesOut(FALSE) , hdlcOut() , callbackParamIn(cbpReset) , isCarrierIn(0) #if PTRACING , timeBeginIn() #endif , countIn(0) , t30() , modStreamIn(NULL) , modStreamInSaved(NULL) , stateModem(stmIdle) { PTRACE(2, name << " T38Engine"); } T38Engine::~T38Engine() { PTRACE(1, name << " ~T38Engine"); if (modStreamIn != NULL) delete modStreamIn; if (modStreamInSaved != NULL) delete modStreamInSaved; } void T38Engine::OnOpenIn() { EngineBase::OnOpenIn(); } void T38Engine::OnOpenOut() { EngineBase::OnOpenOut(); } void T38Engine::OnCloseIn() { EngineBase::OnCloseIn(); SignalOutDataReady(); } void T38Engine::OnCloseOut() { EngineBase::OnCloseOut(); SignalOutDataReady(); } void T38Engine::OnChangeEnableFakeIn() { EngineBase::OnChangeEnableFakeIn(); if (IsOpenIn() || !isEnableFakeIn) return; isCarrierIn = 0; if (modStreamInSaved != NULL) { myPTRACE(1, name << " OnChangeEnableFakeIn modStreamInSaved != NULL, clean"); delete modStreamInSaved; modStreamInSaved = NULL; } if (modStreamIn != NULL && modStreamIn->lastBuf != NULL) { myPTRACE(1, name << " OnChangeEnableFakeIn modStreamIn->lastBuf != NULL"); modStreamIn->PutEof((countIn == 0 ? 0 : diagOutOfOrder) | diagNoCarrier); if (stateModem == stmInRecvData) { ModemCallbackWithUnlock(callbackParamIn); if (IsOpenIn() || !isEnableFakeIn) return; } } if (stateModem == stmInWaitSilence) { stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamIn); //if (IsOpenIn() || !isEnableFakeIn) // return; } } void T38Engine::OnChangeEnableFakeOut() { EngineBase::OnChangeEnableFakeOut(); SignalOutDataReady(); if (IsOpenOut() || !isEnableFakeOut) return; if (stateModem != stmOutMoreData && stateModem != stmOutNoMoreData) return; (new FakePreparePacketThread(*this))->Resume(); } void T38Engine::OnAttach() { EngineBase::OnAttach(); } void T38Engine::OnDetach() { EngineBase::OnDetach(); SignalOutDataReady(); } void T38Engine::OnChangeModemClass() { EngineBase::OnChangeModemClass(); } /////////////////////////////////////////////////////////////// // void T38Engine::OnResetModemState() { EngineBase::OnResetModemState(); if (modStreamIn && modStreamIn->DeleteFirstBuf()) { PTRACE(1, name << " T38Engine::OnResetModemState modStreamIn->DeleteFirstBuf(), clean"); } bufOut.PutEof(); if (stateModem != stmIdle) { if (!isStateModemOut()) { myPTRACE(1, name << " T38Engine::OnResetModemState stateModem(" << stateModem << ") != stmIdle, reset"); stateModem = stmIdle; } else myPTRACE(1, name << " T38Engine::OnResetModemState stateModem(" << stateModem << ") != stmIdle"); } onIdleOut = dtNone; callbackParamIn = cbpReset; callbackParamOut = cbpReset; } PBoolean T38Engine::isOutBufFull() const { PWaitAndSignal mutexWait(Mutex); return bufOut.isFull(); } /////////////////////////////////////////////////////////////// void T38Engine::SendOnIdle(DataType _dataType) { PTRACE(2, name << " SendOnIdle " << _dataType); PWaitAndSignal mutexWaitModem(MutexModem); PWaitAndSignal mutexWait(Mutex); onIdleOut = _dataType; SignalOutDataReady(); } PBoolean T38Engine::SendStart(DataType _dataType, int param) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; if (stateModem != stmIdle) { myPTRACE(1, name << " SendStart stateModem(" << stateModem << ") != stmIdle"); return FALSE; } PWaitAndSignal mutexWait(Mutex); if (modStreamIn != NULL) { delete modStreamIn; modStreamIn = NULL; } if (modStreamInSaved != NULL && _dataType != dtSilence) { delete modStreamInSaved; modStreamInSaved = NULL; } ModParsOut = invalidMods; switch( _dataType ) { case dtCed: ModParsOut.dataTypeT38 = ModParsOut.dataType = _dataType; ModParsOut.ind = T38I(e_ced); ModParsOut.lenInd = param; break; case dtSilence: ModParsOut.dataTypeT38 = ModParsOut.dataType = _dataType; ModParsOut.ind = T38I(e_no_signal); ModParsOut.lenInd = param; break; case dtHdlc: case dtRaw: ModParsOut = GetModPars(param); ModParsOut.dataType = _dataType; ModParsOut.dataTypeT38 = (ModParsOut.msgType == T38D(e_v21) || t30.hdlcOnly()) ? dtHdlc : dtRaw; if (!ModParsOut.IsModValid()) return FALSE; break; default: return FALSE; } bufOut.Clean(); // reset eof stateModem = stmOutMoreData; SignalOutDataReady(); return TRUE; } int T38Engine::Send(const void *pBuf, PINDEX count) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return -1; if (stateModem != stmOutMoreData) { myPTRACE(1, name << " Send stateModem(" << stateModem << ") != stmOutMoreData"); return -1; } PWaitAndSignal mutexWait(Mutex); int res = bufOut.PutData(pBuf, count); if (res < 0) { myPTRACE(1, name << " Send res(" << res << ") < 0"); } SignalOutDataReady(); return res; } PBoolean T38Engine::SendStop(PBoolean moreFrames, int _callbackParam) { PWaitAndSignal mutexWaitModem(MutexModem); if(!IsModemOpen()) return FALSE; if (stateModem != stmOutMoreData ) { myPTRACE(1, name << " SendStop stateModem(" << stateModem << ") != stmOutMoreData"); return FALSE; } PWaitAndSignal mutexWait(Mutex); bufOut.PutEof(); stateModem = stmOutNoMoreData; moreFramesOut = moreFrames; callbackParamOut = _callbackParam; PTRACE(3, name << " SendStop moreFramesOut=" << moreFramesOut << " callbackParamOut=" << callbackParamOut); SignalOutDataReady(); return TRUE; } /////////////////////////////////////////////////////////////// PBoolean T38Engine::RecvWait(DataType _dataType, int param, int _callbackParam, PBoolean &done) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; if( stateModem != stmIdle ) { myPTRACE(1, name << " RecvWait stateModem(" << stateModem << ") != stmIdle"); return FALSE; } PWaitAndSignal mutexWait(Mutex); switch( _dataType ) { case dtHdlc: case dtRaw: break; case dtSilence: if (modStreamIn != NULL) { delete modStreamIn; modStreamIn = NULL; if (isCarrierIn && !modStreamInSaved) { callbackParamIn = _callbackParam; stateModem = stmInWaitSilence; return TRUE; } } done = TRUE; return TRUE; default: return FALSE; } const MODPARS &ModParsIn = GetModPars(param); if (!ModParsIn.IsModValid()) return FALSE; callbackParamIn = _callbackParam; if (modStreamIn != NULL) { if (modStreamIn->DeleteFirstBuf()) { PTRACE(1, name << " RecvWait modStreamIn->DeleteFirstBuf(), clean"); } if (modStreamIn->bufQ.GetSize() > 0) { PTRACE(1, name << " RecvWait modStreamIn->bufQ.GetSize()=" << modStreamIn->bufQ.GetSize()); if (ModParsIn.IsEqual(modStreamIn->ModPars)) { PTRACE(1, name << " RecvWait ModParsIn == modStreamIn->ModPars(" << modStreamIn->ModPars.val << ")"); modStreamIn->ModPars.dataType = _dataType; modStreamIn->ModPars.dataTypeT38 = (modStreamIn->ModPars.msgType == T38D(e_v21) || t30.hdlcOnly()) ? dtHdlc : dtRaw; stateModem = stmInReadyData; done = TRUE; return TRUE; } } delete modStreamIn; } modStreamIn = new ModStream(ModParsIn); modStreamIn->ModPars.dataType = _dataType; modStreamIn->ModPars.dataTypeT38 = (modStreamIn->ModPars.msgType == T38D(e_v21) || t30.hdlcOnly()) ? dtHdlc : dtRaw; if (modStreamInSaved != NULL) { if (modStreamIn->ModPars.IsEqual(modStreamInSaved->ModPars)) { myPTRACE(2, name << " RecvWait modStreamIn->ModPars == modStreamInSaved->ModPars(" << modStreamInSaved->ModPars.val << ")"); modStreamIn->Move(*modStreamInSaved); delete modStreamInSaved; modStreamInSaved = NULL; } else { myPTRACE(2, name << " RecvWait modStreamIn->ModPars(" << modStreamIn->ModPars.val << ") != modStreamInSaved->ModPars(" << modStreamInSaved->ModPars.val << ")"); modStreamIn->PushBuf(); modStreamIn->PutEof(diagDiffSig); } stateModem = stmInReadyData; done = TRUE; return TRUE; } stateModem = stmInWaitData; return TRUE; } PBoolean T38Engine::RecvStart(int _callbackParam) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; if (stateModem != stmInReadyData ) { myPTRACE(1, name << " RecvStart stateModem(" << stateModem << ") != stmInReadyData"); return FALSE; } PWaitAndSignal mutexWait(Mutex); callbackParamIn = _callbackParam; if (modStreamIn != NULL) { if (modStreamIn->PopBuf()) { if (modStreamIn->ModPars.msgType == T38D(e_v21)) t30.v21Begin(); stateModem = stmInRecvData; return TRUE; } myPTRACE(1, name << " RecvStart can't receive firstBuf"); delete modStreamIn; modStreamIn = NULL; } else { myPTRACE(1, name << " RecvStart modStreamIn == NULL"); } stateModem = stmIdle; return FALSE; } int T38Engine::Recv(void *pBuf, PINDEX count) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return -1; if( stateModem != stmInRecvData ) { myPTRACE(1, name << " Recv stateModem(" << stateModem << ") != stmInRecvData"); return -1; } PWaitAndSignal mutexWait(Mutex); if( modStreamIn == NULL ) { myPTRACE(1, name << " Recv modStreamIn == NULL"); return -1; } int len = modStreamIn->GetData(pBuf, count); if (modStreamIn->ModPars.msgType == T38D(e_v21)) { if (len > 0) t30.v21Data(pBuf, len); else if (len < 0) t30.v21End(FALSE); } return len; } int T38Engine::RecvDiag() const { PWaitAndSignal mutexWaitModem(MutexModem); PWaitAndSignal mutexWait(Mutex); if( modStreamIn == NULL ) { myPTRACE(1, name << " RecvDiag modStreamIn == NULL"); return diagError; } if( modStreamIn->firstBuf == NULL ) { myPTRACE(1, name << " RecvDiag modStreamIn->firstBuf == NULL"); return diagError; } return modStreamIn->firstBuf->GetDiag(); } void T38Engine::RecvStop() { PWaitAndSignal mutexWaitModem(MutexModem); if(!IsModemOpen()) return; if(!isStateModemIn()) { myPTRACE(1, name << " RecvStop stateModem(" << stateModem << ") in not receiving data state"); return; } PWaitAndSignal mutexWait(Mutex); if (modStreamIn) modStreamIn->DeleteFirstBuf(); if (isStateModemIn()) stateModem = stmIdle; } /////////////////////////////////////////////////////////////// PBoolean T38Engine::SendingNotCompleted() const { PWaitAndSignal mutexWait(Mutex); if (hOwnerOut == NULL) return FALSE; if (stateOut != stOutIdle) return TRUE; if (delaySignalOut && timeBeginOut > PTime()) return TRUE; return FALSE; } /////////////////////////////////////////////////////////////// void T38Engine::SetPreparePacketTimeout(HOWNEROUT hOwner, int timeout, int period) { PAssert((timeout == 0 && period > 0) || (timeout != 0 && period < 0), "Invalid timeout/period"); if (hOwnerOut != hOwner) return; PWaitAndSignal mutexWait(Mutex); if (hOwnerOut != hOwner) return; preparePacketTimeout = timeout; preparePacketPeriod = period; if (preparePacketPeriod > 0) preparePacketDelay.Restart(); } /////////////////////////////////////////////////////////////// int T38Engine::PreparePacket(HOWNEROUT hOwner, T38_IFP & ifp) { if (hOwnerOut != hOwner || !IsModemOpen()) return 0; PWaitAndSignal mutexWait(MutexOut); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; { PWaitAndSignal mutexWait(Mutex); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; if (firstOut) { firstOut = FALSE; ModemCallbackWithUnlock(cbpUpdateState); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; preparePacketDelay.Restart(); } } //myPTRACE(1, name << " PreparePacket begin stM=" << stateModem << " stO=" << stateOut); ifp = T38_IFP(); PBoolean doDalay = TRUE; PTime preparePacketTimeoutEnd = (preparePacketTimeout > 0 ? (PTime() + preparePacketTimeout) : PTime(0)); if (preparePacketPeriod > 0) { preparePacketDelay.Delay(preparePacketPeriod); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } for(;;) { PBoolean redo = FALSE; if (doDalay) { //PTRACE(1, name << " +++++ stM=" << stateModem << " stO=" << stateOut << " " // << timeDelayEndOut.AsString("hh:mm:ss.uuu\t", PTime::Local)); for (;;) { PTimeInterval delay = timeDelayEndOut - PTime(); if (delay.GetMilliSeconds() <= 0) break; if (preparePacketTimeout >= 0) { if (preparePacketTimeout == 0) return -1; PTimeInterval timeout = preparePacketTimeoutEnd - PTime(); if (timeout.GetMilliSeconds() <= 0) return -1; if (delay.GetMilliSeconds() > timeout.GetMilliSeconds()) delay = timeout; } if (delay.GetMilliSeconds() > msMaxOutDelay) delay = msMaxOutDelay; mySleep(delay.GetMilliSeconds()); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } } else { doDalay = TRUE; } if (hOwnerOut != hOwner || !IsModemOpen()) return 0; for(;;) { PBoolean waitData = FALSE; { PWaitAndSignal mutexWait(Mutex); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; if (isStateModemOut() || stateOut != stOutIdle) { switch (stateOut) { case stOutIdle: if (delaySignalOut) { if (ModParsOut.dataType != dtSilence && timeBeginOut > PTime()) { redo = TRUE; myPTRACE(4, name << " PreparePacket delaySignalOut"); break; } delaySignalOut = FALSE; } if (isCarrierIn) { myPTRACE(3, name << " PreparePacket isCarrierIn=" << isCarrierIn << " for dataType=" << ModParsOut.dataType); /* * We can't to begin sending data while the carrier is detected because * it's possible that all data (including indication) will be losted. * It's too critical for image data because it's possible to receive * MCF generated for previous page after sending small page that was * not delivered. */ int waitms; switch (ModParsOut.dataType) { case dtHdlc: waitms = 500; break; // it's can't be too long case dtRaw: waitms = 2000; break; // it's can't be too short default: waitms = 0; break; } if (waitms) { if (isCarrierIn == 1) { isCarrierIn = 2; timeBeginOut = PTime() + PTimeInterval(waitms); redo = TRUE; break; } else if (timeBeginOut > PTime()) { redo = TRUE; break; } else { myPTRACE(1, name << " PreparePacket isCarrierIn expired"); isCarrierIn = 0; } } } switch (ModParsOut.dataTypeT38) { case dtHdlc: case dtRaw: t38indicator(ifp, ModParsOut.ind); stateOut = stOutIndWait; break; case dtCed: t38indicator(ifp, ModParsOut.ind); stateOut = stOutCedWait; break; case dtSilence: stateOut = stOutSilenceWait; redo = TRUE; break; default: myPTRACE(1, name << " PreparePacket bad dataTypeT38=" << ModParsOut.dataTypeT38); return 0; } break; //////////////////////////////////////////////////// case stOutCedWait: stateOut = stOutNoSig; stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamOut); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; redo = TRUE; break; //////////////////////////////////////////////////// case stOutSilenceWait: stateOut = stOutIdle; stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamOut); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; doDalay = FALSE; redo = TRUE; break; //////////////////////////////////////////////////// case stOutIndWait: stateOut = stOutData; countOut = 0; startedTimeOutBufEmpty = FALSE; timeBeginOut = PTime(); hdlcOut = HDLC(); if (ModParsOut.msgType == T38D(e_v21)) t30.v21Begin(); switch (ModParsOut.dataType) { case dtHdlc: hdlcOut.PutHdlcData(&bufOut); break; case dtRaw: hdlcOut.PutRawData(&bufOut); break; default: myPTRACE(1, name << " PreparePacket bad dataType=" << ModParsOut.dataType); return 0; } switch (ModParsOut.dataTypeT38) { case dtHdlc: hdlcOut.GetHdlcStart(TRUE); break; case dtRaw: if (ModParsOut.dataType == dtHdlc) { myPTRACE(1, name << " PreparePacket sending dtHdlc like dtRaw not implemented"); return 0; } hdlcOut.GetRawStart(); break; default: myPTRACE(1, name << " PreparePacket bad dataTypeT38=" << ModParsOut.dataTypeT38); return 0; } redo = TRUE; break; //////////////////////////////////////////////////// case stOutData: { BYTE b[(msPerOut * 14400)/(8*1000)]; PINDEX len = (msPerOut * ModParsOut.br)/(8*1000); if (len > PINDEX(sizeof(b))) len = sizeof(b); PBoolean wasFull = bufOut.isFull(); int count = hdlcOut.GetData(b, len); if (wasFull && !bufOut.isFull()) { ModemCallbackWithUnlock(cbpOutBufNoFull); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } switch( count ) { case -1: startedTimeOutBufEmpty = FALSE; switch (ModParsOut.dataTypeT38) { case dtHdlc: stateOut = stOutHdlcFcs; break; case dtRaw: stateOut = stOutDataNoSig; break; default: myPTRACE(1, name << " PreparePacket stOutData bad dataTypeT38=" << ModParsOut.dataTypeT38); return 0; } redo = TRUE; break; case 0: if (hdlcOut.getLastChar() != -1 && (ModParsOut.dataType == dtHdlc || hdlcOut.getLastChar() != 0)) { ModemCallbackWithUnlock(cbpOutBufEmpty); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } else if (!startedTimeOutBufEmpty) { timeOutBufEmpty = PTime() + PTimeInterval(5000); startedTimeOutBufEmpty = TRUE; } else if (timeOutBufEmpty <= PTime()) { ModemCallbackWithUnlock(cbpOutBufEmpty); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } waitData = TRUE; break; default: startedTimeOutBufEmpty = FALSE; switch (ModParsOut.dataTypeT38) { case dtHdlc: if (ModParsOut.msgType == T38D(e_v21)) { t30.v21Data(b, count); } t38data(ifp, ModParsOut.msgType, T38F(e_hdlc_data), PBYTEArray(b, count)); break; case dtRaw: t38data(ifp, ModParsOut.msgType, T38F(e_t4_non_ecm_data), PBYTEArray(b, count)); break; default: myPTRACE(1, name << " PreparePacket stOutData bad dataTypeT38=" << ModParsOut.dataTypeT38); return 0; } countOut += count; } } break; //////////////////////////////////////////////////// case stOutHdlcFcs: if (ModParsOut.msgType == T38D(e_v21)) { t30.v21End(TRUE); t30.v21Begin(); } if (ModParsOut.dataType == dtRaw) { PBoolean wasFull = bufOut.isFull(); if (countOut) t38data(ifp, ModParsOut.msgType, hdlcOut.isFcsOK() ? T38F(e_hdlc_fcs_OK) : T38F(e_hdlc_fcs_BAD)); else redo = TRUE; hdlcOut.GetHdlcStart(FALSE); countOut = 0; if (hdlcOut.GetData(NULL, 0) != -1) stateOut = stOutData; else stateOut = stOutDataNoSig; if (wasFull && !bufOut.isFull()) { ModemCallbackWithUnlock(cbpOutBufNoFull); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } } else { if( stateModem != stmOutNoMoreData ) { myPTRACE(1, name << " PreparePacket stOutHdlcFcs stateModem(" << stateModem << ") != stmOutNoMoreData"); return 0; } if (countOut) t38data(ifp, ModParsOut.msgType, T38F(e_hdlc_fcs_OK)); else redo = TRUE; countOut = 0; bufOut.Clean(); // reset eof hdlcOut.PutHdlcData(&bufOut); hdlcOut.GetHdlcStart(FALSE); if (moreFramesOut) { stateOut = stOutData; stateModem = stmOutMoreData; ModemCallbackWithUnlock(callbackParamOut); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; } else { stateOut = stOutDataNoSig; } } break; //////////////////////////////////////////////////// case stOutDataNoSig: #if PTRACING if (myCanTrace(3) || (myCanTrace(2) && ModParsOut.dataType == dtRaw)) { PInt64 msTime = (PTime() - timeBeginOut).GetMilliSeconds(); myPTRACE(2, name << " Sent " << hdlcOut.getRawCount() << " bytes in " << msTime << " ms (" << (PInt64(hdlcOut.getRawCount()) * 8 * 1000)/(msTime ? msTime : 1) << " bits/s)"); } #endif if( stateModem != stmOutNoMoreData ) { myPTRACE(1, name << " PreparePacket stOutDataNoSig stateModem(" << stateModem << ") != stmOutNoMoreData"); return 0; } switch (ModParsOut.dataTypeT38) { case dtHdlc: t38data(ifp, ModParsOut.msgType, T38F(e_hdlc_sig_end)); break; case dtRaw: t38data(ifp, ModParsOut.msgType, T38F(e_t4_non_ecm_sig_end)); break; default: myPTRACE(1, name << " PreparePacket stOutDataNoSig bad dataTypeT38=" << ModParsOut.dataTypeT38); return 0; } stateOut = stOutNoSig; stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamOut); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; break; //////////////////////////////////////////////////// case stOutNoSig: t38indicator(ifp, T38I(e_no_signal)); stateOut = stOutIdle; delaySignalOut = TRUE; timeBeginOut = PTime() + PTimeInterval(75); break; default: myPTRACE(1, name << " PreparePacket bad stateOut=" << stateOut); return 0; } } else { switch (onIdleOut) { case dtCng: t38indicator(ifp, T38I(e_cng)); break; default: PTRACE(1, name << " SendOnIdle dataType(" << onIdleOut << ") is not supported"); case dtNone: waitData = TRUE; } onIdleOut = dtNone; } } if (!waitData) break; if (preparePacketTimeout >= 0) { if (preparePacketTimeout == 0) return -1; PTimeInterval timeout = preparePacketTimeoutEnd - PTime(); if (timeout.GetMilliSeconds() <= 0 || !WaitOutDataReady(timeout.GetMilliSeconds())) return -1; } else { if (startedTimeOutBufEmpty) { PInt64 timeout = (timeOutBufEmpty - PTime()).GetMilliSeconds() + 1; if (timeout > 0) WaitOutDataReady(timeout); } else { WaitOutDataReady(); } } if (hOwnerOut != hOwner || !IsModemOpen()) return 0; { PWaitAndSignal mutexWait(Mutex); if (hOwnerOut != hOwner || !IsModemOpen()) return 0; if (stateOut == stOutData) { #if PTRACING if (myCanTrace(3) || (myCanTrace(2) && ModParsOut.dataType == dtRaw)) { PInt64 msTime = (PTime() - timeBeginOut).GetMilliSeconds(); myPTRACE(2, name << " Sent " << hdlcOut.getRawCount() << " bytes in " << msTime << " ms (" << (PInt64(hdlcOut.getRawCount()) * 8 * 1000)/(msTime ? msTime : 1) << " bits/s)"); } #endif myPTRACE(1, name << " PreparePacket DTE's data delay, reset " << hdlcOut.getRawCount()); hdlcOut.resetRawCount(); timeBeginOut = PTime() - PTimeInterval(msPerOut); doDalay = FALSE; } } } switch (stateOut) { case stOutIdle: timeDelayEndOut = PTime() + msPerOut; break; case stOutCedWait: timeDelayEndOut = PTime() + ModParsOut.lenInd; break; case stOutSilenceWait: timeDelayEndOut = PTime() + ModParsOut.lenInd; break; case stOutIndWait: timeDelayEndOut = PTime() + ModParsOut.lenInd; break; case stOutData: case stOutHdlcFcs: timeDelayEndOut = timeBeginOut + (PInt64(hdlcOut.getRawCount()) * 8 * 1000)/ModParsOut.br + msPerOut; break; case stOutDataNoSig: timeDelayEndOut = PTime() + msPerOut; break; case stOutNoSig: timeDelayEndOut = PTime() + msPerOut; break; default: timeDelayEndOut = PTime(); } if (!redo) break; } return 1; } /////////////////////////////////////////////////////////////// PBoolean T38Engine::HandlePacketLost(HOWNERIN hOwner, unsigned myPTRACE_PARAM(nLost)) { myPTRACE(1, name << " HandlePacketLost " << nLost); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; ModStream *modStream = modStreamIn; if( modStream == NULL || modStream->lastBuf == NULL ) { modStream = modStreamInSaved; } if( !(modStream == NULL || modStream->lastBuf == NULL) ) { if( modStream->ModPars.msgType == T38D(e_v21) ) { modStream->SetDiag(diagBadFcs); } } return TRUE; } /////////////////////////////////////////////////////////////// PBoolean T38Engine::HandlePacket(HOWNERIN hOwner, const T38_IFP & ifp) { #if PTRACING if (PTrace::CanTrace(3)) { PTRACE(3, name << " HandlePacket Received ifp\n " << setprecision(2) << ifp); } else { PTRACE(2, name << " HandlePacket Received ifp type=" << ifp.m_type_of_msg.GetTagName()); } #endif if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; switch (ifp.m_type_of_msg.GetTag()) { case T38_Type_of_msg::e_t30_indicator: { T38_Type_of_msg_t30_indicator type_of_msg = ifp.m_type_of_msg; if ((modStreamIn != NULL) && (modStreamIn->lastBuf != NULL && modStreamIn->ModPars.ind == type_of_msg) || (modStreamInSaved != NULL) && (modStreamInSaved->lastBuf != NULL && modStreamInSaved->ModPars.ind == type_of_msg)) { myPTRACE(3, name << " HandlePacket ignored repeated indicator " << type_of_msg); break; } if (modStreamIn != NULL && modStreamIn->lastBuf != NULL) { myPTRACE(1, name << " HandlePacket indicator && modStreamIn->lastBuf != NULL"); if (firstIn && countIn == 0 && type_of_msg == T38I(e_no_signal)) { myPTRACE(1, name << " HandlePacket ignored first indicator " << type_of_msg); break; } else { modStreamIn->PutEof(diagOutOfOrder | diagNoCarrier); myPTRACE(1, name << " HandlePacket out of order " << type_of_msg); if (stateModem == stmInRecvData) { ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } } } if (modStreamInSaved != NULL) { myPTRACE(1, name << " HandlePacket indicator && modStreamInSaved != NULL, clean"); delete modStreamInSaved; modStreamInSaved = NULL; } switch (type_of_msg) { case T38I(e_no_signal): isCarrierIn = 0; if (stateModem == stmInWaitSilence) { stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } break; case T38I(e_ced): OnUserInput('a'); isCarrierIn = 0; if (stateModem == stmInWaitSilence) { stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } break; case T38I(e_cng): OnUserInput('c'); isCarrierIn = 0; if (stateModem == stmInWaitSilence) { stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } break; case T38I(e_v21_preamble): case T38I(e_v27_2400_training): case T38I(e_v27_4800_training): case T38I(e_v29_7200_training): case T38I(e_v29_9600_training): case T38I(e_v17_7200_short_training): case T38I(e_v17_7200_long_training): case T38I(e_v17_9600_short_training): case T38I(e_v17_9600_long_training): case T38I(e_v17_12000_short_training): case T38I(e_v17_12000_long_training): case T38I(e_v17_14400_short_training): case T38I(e_v17_14400_long_training): isCarrierIn = 1; modStreamInSaved = new ModStream(GetModPars(type_of_msg, by_ind)); modStreamInSaved->PushBuf(); countIn = 0; if (stateModem == stmInWaitSilence) { stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } else if (stateModem == stmInWaitData) { if (modStreamIn != NULL) { if (modStreamIn->ModPars.IsEqual(modStreamInSaved->ModPars)) { modStreamIn->Move(*modStreamInSaved); delete modStreamInSaved; modStreamInSaved = NULL; } else { myPTRACE(2, name << " T38Engine::HandlePacket modStreamIn->ModPars(" << modStreamIn->ModPars.val << ") != modStreamInSaved->ModPars(" << modStreamInSaved->ModPars.val << ")"); modStreamIn->PushBuf(); modStreamIn->PutEof(diagDiffSig); } } else { myPTRACE(1, name << " HandlePacket modStreamIn == NULL"); } stateModem = stmInReadyData; ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } break; default: myPTRACE(1, name << " HandlePacket type_of_msg is bad !!! " << setprecision(2) << ifp); } break; } case T38_Type_of_msg::e_data: { unsigned type_of_msg = (T38_Type_of_msg_data)ifp.m_type_of_msg; ModStream *modStream = modStreamIn; if (modStream == NULL || modStream->lastBuf == NULL) modStream = modStreamInSaved; if (modStream == NULL || modStream->lastBuf == NULL) { PTRACE(1, name << " HandlePacket lastBuf == NULL"); modStream = NULL; } else if (modStream->ModPars.msgType != type_of_msg) { myPTRACE(1, name << " HandlePacket modStream->ModPars.msgType(" << modStream->ModPars.msgType << ") != type_of_msg(" << type_of_msg << ")"); modStream->PutEof(diagOutOfOrder | diagNoCarrier); modStream = NULL; } if (ifp.HasOptionalField(T38_IFPPacket::e_data_field)) { PINDEX count = ifp.m_data_field.GetSize(); for (PINDEX i = 0 ; i < count ; i++) { PTRACE_IF(4, modStream == NULL, name << " HandlePacket modStream == NULL"); const T38_DATA_FIELD &Data_Field = ifp.m_data_field[i]; switch (Data_Field.m_field_type) { // Handle data case T38F(e_hdlc_data): case T38F(e_t4_non_ecm_data): case T38F(e_hdlc_sig_end): case T38F(e_hdlc_fcs_OK): case T38F(e_hdlc_fcs_BAD): case T38F(e_hdlc_fcs_OK_sig_end): case T38F(e_hdlc_fcs_BAD_sig_end): case T38F(e_t4_non_ecm_sig_end): if (Data_Field.HasOptionalField(T38_Data_Field_subtype::e_field_data)) { int size = Data_Field.m_field_data.GetSize(); if(modStream != NULL) modStream->PutData(Data_Field.m_field_data, size); #if PTRACING if (!countIn) timeBeginIn = PTime(); #endif countIn += size; } break; default: myPTRACE(1, name << " HandlePacket field_type bad !!! " << setprecision(2) << ifp); } switch (Data_Field.m_field_type) { // Handle fcs case T38F(e_hdlc_fcs_BAD): case T38F(e_hdlc_fcs_BAD_sig_end): if(modStream != NULL) modStream->SetDiag(diagBadFcs); myPTRACE(1, name << " HandlePacket bad FCS"); case T38F(e_hdlc_fcs_OK): case T38F(e_hdlc_fcs_OK_sig_end): if(modStream != NULL) { modStream->PutEof(); modStream->PushBuf(); } break; } switch( Data_Field.m_field_type ) { // Handle sig_end case T38F(e_t4_non_ecm_sig_end): #if PTRACING if (myCanTrace(2)) { PInt64 msTime = (PTime() - timeBeginIn).GetMilliSeconds(); myPTRACE(2, name << " Received " << countIn << " bytes in " << msTime << " ms (" << (PInt64(countIn) * 8 * 1000)/(msTime ? msTime : 1) << " bits/s)"); } #endif case T38F(e_hdlc_fcs_OK_sig_end): case T38F(e_hdlc_fcs_BAD_sig_end): case T38F(e_hdlc_sig_end): if(modStream != NULL) { modStream->PutEof(diagNoCarrier); modStream = NULL; } isCarrierIn = 0; if (stateModem == stmInWaitSilence) { stateModem = stmIdle; ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } break; } } } if (stateModem == stmInRecvData) { ModemCallbackWithUnlock(callbackParamIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } break; } default: myPTRACE(1, name << " HandlePacket Tag is bad !!! " << setprecision(2) << ifp); } if (!firstIn) { firstIn = FALSE; ModemCallbackWithUnlock(cbpUpdateState); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } return TRUE; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/pmutils.cxx0000664000076400007640000001353010537231227016250 0ustar frolovfrolov00000000000000/* * pmutils.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2006 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmutils.cxx,v $ * Revision 1.13 2006/12/11 10:27:35 vfrolov * Disabled renaming thread if no PTRACING * * Revision 1.12 2005/02/03 11:32:12 vfrolov * Fixed MSVC compile warnings * * Revision 1.11 2004/10/20 14:15:09 vfrolov * Added reset of signal counter to WaitDataReady() * * Revision 1.10 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.9 2004/03/09 17:23:11 vfrolov * Added PROCESS_PER_THREAD ifdef * * Revision 1.8 2004/02/17 13:23:14 vfrolov * Fixed MSVC compile errors * * Revision 1.7 2003/12/04 13:22:28 vfrolov * Removed ambiguous isEof() * Improved memory usage in DataStream * Fixed myPTRACE * * Revision 1.6 2003/01/08 16:37:25 vfrolov * Changed class DataStream: * members moved to private section and added isEof() * added threshold and isFull() * * Revision 1.5 2002/12/30 12:49:36 vfrolov * Added tracing thread's CPU usage (Linux only) * * Revision 1.4 2002/12/20 10:12:57 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.3 2002/03/07 07:55:18 vfrolov * Fixed endless recursive call SignalChildStop(). Possible there is * a bug in gcc version 2.95.4 20010902 (Debian prerelease). * Markus Storm reported the promlem. * Added Copyright header. * * Revision 1.2 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #include #include "pmutils.h" #define new PNEW /////////////////////////////////////////////////////////////// ModemThread::ModemThread() : PThread(30000, NoAutoDeleteThread, NormalPriority), stop(FALSE), childstop(FALSE) { } void ModemThread::SignalChildStop() { childstop = TRUE; SignalDataReady(); } void ModemThread::SignalStop() { stop = TRUE; SignalDataReady(); } void ModemThread::WaitDataReady() { do { dataReadySyncPoint.Wait(); } while(!dataReadySyncPoint.WillBlock()); } /////////////////////////////////////////////////////////////// ModemThreadChild::ModemThreadChild(ModemThread &_parent) : parent(_parent) { } void ModemThreadChild::SignalStop() { ModemThread::SignalStop(); parent.SignalChildStop(); } /////////////////////////////////////////////////////////////// int ChunkStream::write(const void *pBuf, PINDEX count) { int len = sizeof(data) - last; if (!len) return -1; if (len > count) len = count; memcpy(data + last, pBuf, len); last += len; return len; } int ChunkStream::read(void *pBuf, PINDEX count) { if (sizeof(data) == first) return -1; int len = last - first; if (len > count) len = count; memcpy(pBuf, data + first, len); first += len; return len; } /////////////////////////////////////////////////////////////// int DataStream::PutData(const void *_pBuf, PINDEX count) { if (eof) return -1; int done = 0; const BYTE *pBuf = (const BYTE *)_pBuf; while (count) { if (!lastBuf) { lastBuf = new ChunkStream(); bufQ.Enqueue(lastBuf); } int len = lastBuf->write(pBuf, count); if (len < 0) { lastBuf = NULL; } else { pBuf += len; count -= len; done += len; } } busy += done; return done; } int DataStream::GetData(void *_pBuf, PINDEX count) { if (!busy) { if (eof) return -1; else return 0; } int done = 0; BYTE *pBuf = (BYTE *)_pBuf; while (count) { if (!firstBuf) { firstBuf = bufQ.Dequeue(); if (!firstBuf) { lastBuf = NULL; break; } } int len = firstBuf->read(pBuf, count); if (len < 0) { delete firstBuf; firstBuf = NULL; } else { if (!len) break; pBuf += len; count -= len; done += len; } } busy -= done; return done; } void DataStream::Clean() { ChunkStream *buf; while ((buf = bufQ.Dequeue()) != NULL) delete buf; if (firstBuf) delete firstBuf; firstBuf = lastBuf = NULL; busy = 0; eof = FALSE; diag = 0; } /////////////////////////////////////////////////////////////// #if PTRACING void RenameCurrentThread(const PString &newname) { PString oldname = PThread::Current()->GetThreadName(); PThread::Current()->SetThreadName(PString(newname) #if defined(PROCESS_PER_THREAD) + ":" + PString((unsigned)getpid()) #else #if defined(P_PTHREADS) + ":" + PString((unsigned)pthread_self()) #else + ":%0x" #endif #endif ); PTRACE(2, "RenameCurrentThread old ThreadName=" << oldname); } #endif /* PTRACING */ /////////////////////////////////////////////////////////////// #ifdef PROCESS_PER_THREAD #include static double clktck = sysconf(_SC_CLK_TCK); const PString GetThreadTimes(const char *head, const char *tail) { struct tms t; if (clktck && times(&t) != -1) { return psprintf("%suser=%.3f, system=%.3f%s", head, t.tms_utime/clktck, t.tms_stime/clktck, tail); } return ""; } #endif /////////////////////////////////////////////////////////////// t38modem-2.0.0/dle.cxx0000664000076400007640000001057011061726064015321 0ustar frolovfrolov00000000000000/* * dle.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: dle.cxx,v $ * Revision 1.11 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.10 2007/03/23 09:48:23 vfrolov * Added deleting DLE shielded codes * * Revision 1.9 2007/03/22 16:26:04 vfrolov * Fixed compiler warnings * * Revision 1.8 2004/10/27 13:18:03 vfrolov * Fixed compiler warning * * Revision 1.7 2004/10/22 13:34:20 vfrolov * Fixed buffer overflow * * Revision 1.6 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.5 2003/12/04 13:22:22 vfrolov * Removed ambiguous isEof() * Improved memory usage in DataStream * Fixed myPTRACE * * Revision 1.4 2003/01/08 16:37:22 vfrolov * Changed class DataStream: * members moved to private section and added isEof() * added threshold and isFull() * * Revision 1.3 2002/04/19 14:29:26 vfrolov * Added Copyright header * * Revision 1.2 2002/01/10 06:10:02 craigs * Added MPL header * * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #include #include "dle.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// static BYTE BitRevTable[256]; static PBoolean initBitRevTable() { for( unsigned i = 0 ; i < sizeof(BitRevTable) ; i++ ) { unsigned in = i, out = 0; for( int j = 0 ; j < 8 ; j++ ) { out <<= 1; out += in & 1; in >>= 1; } BitRevTable[i] = (BYTE)out; } return TRUE; } static const PBoolean ___InitBitRevTable = initBitRevTable(); /////////////////////////////////////////////////////////////// enum { ETX = 0x03, DLE = 0x10, }; /////////////////////////////////////////////////////////////// int DLEData::PutDleData(const void *pBuf, PINDEX count) { if (PutData(NULL, 0) < 0) return -1; PINDEX cRest = count; const BYTE *p = (const BYTE *)pBuf; while( cRest > 0 ) { const BYTE *pScan = p; PINDEX cScan = cRest; if (dle) { dle = FALSE; if (*p != DLE) { if (*p == ETX) { PutEof(); cRest--; break; } p++; cRest--; } pScan++; cScan--; } const BYTE *pDle = (const BYTE *)memchr(pScan, DLE, cScan); PINDEX cPut; PINDEX cDone; if( pDle ) { dle = TRUE; cPut = PINDEX(pDle - p); cDone = cPut + 1; // skip DLE } else { cDone = cPut = cRest; } if( cPut ) { if( bitRev ) { BYTE tmp[1024]; while( cPut ) { PINDEX cTmp = cPut > 1024 ? 1024 : cPut; for( PINDEX i = 0 ; i < cTmp ; i++ ) { tmp[i] = BitRevTable[p[i]]; } PutData(tmp, cTmp); cPut -= cTmp; } } else { PutData(p, cPut); } } p += cDone; cRest -= cDone; } return count - cRest; } int DLEData::GetDleData(void *pBuf, PINDEX count) { if (recvEtx) return -1; BYTE *p = (BYTE *)pBuf; int done; for (done = 0 ; (count - done) >= 4 ; done = int(p - (BYTE *)pBuf)) { PINDEX cGet = (count - done - 2) / 2; BYTE tmp[1024]; if (cGet > (PINDEX)sizeof(tmp)) cGet = sizeof(tmp); switch( cGet = GetData(tmp, cGet) ) { case -1: *p++ = DLE; *p++ = ETX; recvEtx = TRUE; return int(p - (BYTE *)pBuf); case 0: return int(p - (BYTE *)pBuf); default: for( PINDEX i = 0 ; i < cGet ; i++ ) { BYTE b = bitRev ? BitRevTable[tmp[i]] : tmp[i]; if( b == DLE ) *p++ = DLE; *p++ = b; } } } return done; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/enginebase.h0000664000076400007640000001512511455110341016273 0ustar frolovfrolov00000000000000/* * enginebase.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: enginebase.h,v $ * Revision 1.14 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.13 2010/10/08 06:04:59 vfrolov * Added diagErrorMask * * Revision 1.12 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.11 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.10 2010/09/22 15:07:45 vfrolov * Added ResetModemState() and OnResetModemState() * * Revision 1.9 2010/09/08 17:22:23 vfrolov * Redesigned modem engine (continue) * * Revision 1.8 2010/07/07 08:09:47 vfrolov * Added IsAttached() * * Revision 1.7 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.6 2009/11/19 14:48:28 vfrolov * Moved common code to class EngineBase * * Revision 1.5 2009/11/19 11:14:04 vfrolov * Added OnUserInput * * Revision 1.4 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.3 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.2 2007/04/09 08:07:12 vfrolov * Added symbolic logging ModemCallbackParam * * Revision 1.1 2007/03/23 09:54:45 vfrolov * Initial revision * */ #ifndef _ENGINEBASE_H #define _ENGINEBASE_H /////////////////////////////////////////////////////////////// class DataStream; /////////////////////////////////////////////////////////////// class ReferenceObject : public PObject { PCLASSINFO(ReferenceObject, PObject); public: ReferenceObject() : referenceCount(1) {} void AddReference() { PWaitAndSignal mutex(referenceCountMutex); ++referenceCount; } static void DelPointer(ReferenceObject * object) { PBoolean doDelele; object->referenceCountMutex.Wait(); doDelele = (--object->referenceCount == 0); object->referenceCountMutex.Signal(); if (doDelele) delete object; } private: PMutex referenceCountMutex; unsigned referenceCount; }; /////////////////////////////////////////////////////////////// class EngineBase : public ReferenceObject { PCLASSINFO(EngineBase, ReferenceObject); private: class OWNERIN { int unused; }; class OWNEROUT { int unused; }; public: typedef const OWNERIN *HOWNERIN; typedef const OWNEROUT *HOWNEROUT; public: /**@name Construction */ //@{ EngineBase(const PString &_name = ""); virtual ~EngineBase(); //@} enum { diagNoCarrier = 0x0001, diagOutOfOrder = 0x0100, diagDiffSig = 0x0400, // a different signal is detected diagBadFcs = 0x0800, diagError = 0x8000, // bad usage diagErrorMask = 0xFF00, }; enum DataType { dtNone, dtRing, dtBusy, dtCed, dtCng, dtSilence, dtHdlc, dtRaw, }; enum ModemCallbackParam { cbpUserDataMask = 0xFF, cbpOutBufNoFull = 256, cbpUpdateState = 257, cbpReset = -1, cbpOutBufEmpty = -2, cbpUserInput = -3, }; enum ModemClass { mcUndefined, mcAudio, mcFax, }; /**@name Modem API */ //@{ const PString &Name() const { return name; } PBoolean Attach(const PNotifier &callback); void Detach(const PNotifier &callback); void ResetModemState(); void OpenIn(HOWNERIN hOwner, PBoolean fake = FALSE); void OpenOut(HOWNEROUT hOwner, PBoolean fake = FALSE); void CloseIn(HOWNERIN hOwner); void CloseOut(HOWNEROUT hOwner); void EnableFakeIn(PBoolean enable = TRUE); void EnableFakeOut(PBoolean enable = TRUE); PBoolean IsOpenIn() const { return hOwnerIn != NULL; } PBoolean IsOpenOut() const { return hOwnerOut != NULL; } PBoolean TryLockModemCallback(); void UnlockModemCallback(); void ChangeModemClass(ModemClass newModemClass); void WriteUserInput(const PString & value); int RecvUserInput(void * pBuf, PINDEX count); virtual void SendOnIdle(DataType /*_dataType*/) {} virtual PBoolean SendStart(DataType _dataType, int param) = 0; virtual int Send(const void *pBuf, PINDEX count) = 0; virtual PBoolean SendStop(PBoolean moreFrames, int _callbackParam) = 0; virtual PBoolean isOutBufFull() const = 0; virtual PBoolean SendingNotCompleted() const { return FALSE; } virtual void RecvOnIdle(DataType /*_dataType*/) {} virtual PBoolean RecvWait(DataType _dataType, int param, int _callbackParam, PBoolean &done) = 0; virtual PBoolean RecvStart(int _callbackParam) = 0; virtual int Recv(void *pBuf, PINDEX count) = 0; virtual void RecvStop() = 0; virtual int RecvDiag() const { return 0; }; //@} protected: PBoolean IsModemOpen() const { return !modemCallback.IsNULL(); } virtual void OnAttach(); virtual void OnDetach(); virtual void OnResetModemState(); virtual void OnChangeModemClass(); virtual void OnUserInput(const PString & value); virtual void OnOpenIn(); virtual void OnOpenOut(); virtual void OnCloseIn(); virtual void OnCloseOut(); virtual void OnChangeEnableFakeIn(); virtual void OnChangeEnableFakeOut(); const PString name; DataStream *volatile recvUserInput; ModemClass modemClass; volatile HOWNERIN hOwnerIn; volatile HOWNEROUT hOwnerOut; PBoolean firstIn; PBoolean firstOut; PBoolean isFakeOwnerIn; PBoolean isFakeOwnerOut; PBoolean isEnableFakeIn; PBoolean isEnableFakeOut; void ModemCallbackWithUnlock(INT extra); PNotifier modemCallback; PTimedMutex MutexModemCallback; PMutex MutexModem; PMutex MutexIn; PMutex MutexOut; PMutex Mutex; }; #if PTRACING ostream & operator<<(ostream & out, EngineBase::DataType dataType); ostream & operator<<(ostream & out, EngineBase::ModemCallbackParam param); ostream & operator<<(ostream & out, EngineBase::ModemClass modemClass); #endif /////////////////////////////////////////////////////////////// #endif // _ENGINEBASE_H t38modem-2.0.0/g711.c0000664000076400007640000002040410616333154014650 0ustar frolovfrolov00000000000000/* * This source code is a product of Sun Microsystems, Inc. and is provided * for unrestricted use. Users may copy or modify this source code without * charge. * * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun source code is provided with no support and without any obligation on * the part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ /* * g711.c * * u-law, A-law and linear PCM conversions. */ /* * December 30, 1994: * Functions linear2alaw, linear2ulaw have been updated to correctly * convert unquantized 16 bit values. * Tables for direct u- to A-law and A- to u-law conversions have been * corrected. * Borge Lindberg, Center for PersonKommunikation, Aalborg University. * bli@cpk.auc.dk * */ #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ #define QUANT_MASK (0xf) /* Quantization field mask. */ #define NSEGS (8) /* Number of A-law segments. */ #define SEG_SHIFT (4) /* Left shift for segment number. */ #define SEG_MASK (0x70) /* Segment field mask. */ static int seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF}; static int seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF}; #if 0 /* copy from CCITT G.711 specifications */ unsigned char u2a[128] = { /* u- to A-law conversions */ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 29, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* corrected: 81, 82, 83, 84, 85, 86, 87, 88, should be: */ 80, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128}; unsigned char a2u[128] = { /* A- to u-law conversions */ 1, 3, 5, 7, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, /* corrected: 73, 74, 75, 76, 77, 78, 79, 79, should be: */ 73, 74, 75, 76, 77, 78, 79, 80, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; #endif static int search( int val, /* changed from "short" *drago* */ int * table, int size) /* changed from "short" *drago* */ { int i; /* changed from "short" *drago* */ for (i = 0; i < size; i++) { if (val <= *table++) return (i); } return (size); } /* * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law * * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. * * Linear Input Code Compressed Code * ------------------------ --------------- * 0000000wxyza 000wxyz * 0000001wxyza 001wxyz * 000001wxyzab 010wxyz * 00001wxyzabc 011wxyz * 0001wxyzabcd 100wxyz * 001wxyzabcde 101wxyz * 01wxyzabcdef 110wxyz * 1wxyzabcdefg 111wxyz * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ int linear2alaw(int pcm_val) /* 2's complement (16-bit range) */ /* changed from "short" *drago* */ { int mask; /* changed from "short" *drago* */ int seg; /* changed from "short" *drago* */ int aval; pcm_val = pcm_val >> 3; if (pcm_val >= 0) { mask = 0xD5; /* sign (7th) bit = 1 */ } else { mask = 0x55; /* sign bit = 0 */ pcm_val = -pcm_val - 1; } /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_aend, 8); /* Combine the sign, segment, and quantization bits. */ if (seg >= 8) /* out of range, return maximum value. */ return (0x7F ^ mask); else { aval = seg << SEG_SHIFT; if (seg < 2) aval |= (pcm_val >> 1) & QUANT_MASK; else aval |= (pcm_val >> seg) & QUANT_MASK; return (aval ^ mask); } } /* * alaw2linear() - Convert an A-law value to 16-bit linear PCM * */ int alaw2linear(int a_val) { int t; /* changed from "short" *drago* */ int seg; /* changed from "short" *drago* */ a_val ^= 0x55; t = (a_val & QUANT_MASK) << 4; seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; switch (seg) { case 0: t += 8; break; case 1: t += 0x108; break; default: t += 0x108; t <<= seg - 1; } return ((a_val & SIGN_BIT) ? t : -t); } #define BIAS (0x84) /* Bias for linear code. */ #define CLIP 8159 /* * linear2ulaw() - Convert a linear PCM value to u-law * * In order to simplify the encoding process, the original linear magnitude * is biased by adding 33 which shifts the encoding range from (0 - 8158) to * (33 - 8191). The result can be seen in the following encoding table: * * Biased Linear Input Code Compressed Code * ------------------------ --------------- * 00000001wxyza 000wxyz * 0000001wxyzab 001wxyz * 000001wxyzabc 010wxyz * 00001wxyzabcd 011wxyz * 0001wxyzabcde 100wxyz * 001wxyzabcdef 101wxyz * 01wxyzabcdefg 110wxyz * 1wxyzabcdefgh 111wxyz * * Each biased linear code has a leading 1 which identifies the segment * number. The value of the segment number is equal to 7 minus the number * of leading 0's. The quantization interval is directly available as the * four bits wxyz. * The trailing bits (a - h) are ignored. * * Ordinarily the complement of the resulting code word is used for * transmission, and so the code word is complemented before it is returned. * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ int linear2ulaw( int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; int uval; /* Get the sign and the magnitude of the value. */ pcm_val = pcm_val >> 2; if (pcm_val < 0) { pcm_val = -pcm_val; mask = 0x7F; } else { mask = 0xFF; } if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */ pcm_val += (BIAS >> 2); /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_uend, 8); /* * Combine the sign, segment, quantization bits; * and complement the code word. */ if (seg >= 8) /* out of range, return maximum value. */ return (0x7F ^ mask); else { uval = (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); return (uval ^ mask); } } /* * ulaw2linear() - Convert a u-law value to 16-bit linear PCM * * First, a biased linear code is derived from the code word. An unbiased * output can then be obtained by subtracting 33 from the biased code. * * Note that this function expects to be passed the complement of the * original code word. This is in keeping with ISDN conventions. */ int ulaw2linear( int u_val) { int t; /* Complement to obtain normal u-law value. */ u_val = ~u_val; /* * Extract and bias the quantization bits. Then * shift up by the segment number and subtract out the bias. */ t = ((u_val & QUANT_MASK) << 3) + BIAS; t <<= (u_val & SEG_MASK) >> SEG_SHIFT; return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); } #if 0 /* A-law to u-law conversion */ static int alaw2ulaw (int aval) { aval &= 0xff; return ((aval & 0x80) ? (0xFF ^ a2u[aval ^ 0xD5]) : (0x7F ^ a2u[aval ^ 0x55])); } /* u-law to A-law conversion */ static int ulaw2alaw (int uval) { uval &= 0xff; return ((uval & 0x80) ? (0xD5 ^ (u2a[0xFF ^ uval] - 1)) : (0x55 ^ (u2a[0x7F ^ uval] - 1))); } #endif t38modem-2.0.0/main_process.cxx0000664000076400007640000001473711335211150017235 0ustar frolovfrolov00000000000000/* * main_process.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: main_process.cxx,v $ * Revision 1.9 2010/02/12 08:33:44 vfrolov * Moved PTrace::Initialise() to beginning * * Revision 1.8 2009/07/29 10:39:04 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.7 2009/07/15 13:23:19 vfrolov * Added Descriptions(args) * * Revision 1.6 2009/07/03 09:18:11 vfrolov * Included opal/buildopts.h * Added workarounds for race condition on exit * Suppressed version tracing on help output * * Revision 1.5 2009/04/03 12:04:36 vfrolov * Added versions of used libs to output * * Revision 1.4 2009/01/21 12:25:12 vfrolov * Added tracing of options * * Revision 1.3 2008/09/11 16:04:47 frolov * Added list of libs to output * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/17 08:32:44 vfrolov * Moved class T38Modem from main.h and main.cxx to main_process.cxx * */ #include #ifdef USE_OPAL #include #endif #include "version.h" #ifdef USE_OPAL #include "opal/manager.h" #else #include "h323lib/h323ep.h" #endif #define new PNEW ///////////////////////////////////////////////////////////////////////////// static PString GetListOfLibs() { return #ifdef USE_OPAL PString("OPAL-") + PString(OPAL_VERSION) + PString("/") + OpalGetVersion() #else #if OPENH323_MAJOR < 1 || (OPENH323_MAJOR == 1 && OPENH323_MINOR <= 19) PString("OpenH323-") + PString(OPENH323_VERSION) #else PString("H323plus-") + PString(OPENH323_VERSION) #endif #endif #ifdef PTLIB_VERSION + PString(", PTLIB-") + PString(PTLIB_VERSION) #if PTLIB_MAJOR > 2 || (PTLIB_MAJOR == 2 && PTLIB_MINOR >= 6) + PString("/") + PProcess::GetLibVersion() #endif #endif #ifdef PWLIB_VERSION + PString(", PWLIB-") + PString(PWLIB_VERSION) #endif ; } ///////////////////////////////////////////////////////////////////////////// class T38Modem : public PProcess { PCLASSINFO(T38Modem, PProcess) public: T38Modem(); void Main(); protected: PBoolean Initialise(); }; PCREATE_PROCESS(T38Modem); /////////////////////////////////////////////////////////////// T38Modem::T38Modem() : PProcess("Vyacheslav Frolov", "T38Modem", MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER) { } void T38Modem::Main() { cout << GetName() << " Version " << GetVersion(TRUE) << "\n" << " (" << GetListOfLibs() << ")" << " by " << GetManufacturer() << " on " << GetOSClass() << ' ' << GetOSName() << " (" << GetOSVersion() << '-' << GetOSHardware() << ")\n" << endl; if (!Initialise()) { PThread::Sleep(100); // workaround for race condition return; } for (;;) PThread::Sleep(5000); } PBoolean T38Modem::Initialise() { PConfigArgs args(GetArguments()); args.Parse( #ifdef USE_OPAL MyManager::ArgSpec() + #else MyH323EndPoint::ArgSpec() + #endif "h-help." "v-version." #if PMEMORY_CHECK "-setallocationbreakpoint:" #endif #if PTRACING "t-trace." "o-output:" #endif "-save." , FALSE); #if PMEMORY_CHECK if (args.HasOption("setallocationbreakpoint")) PMemoryHeap::SetAllocationBreakpoint(args.GetOptionString("setallocationbreakpoint").AsInteger()); #endif #if PTRACING PTrace::Initialise(args.GetOptionCount('t'), args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL, PTrace::DateAndTime | PTrace::Thread | PTrace::Blocks); #endif if (args.HasOption('h')) { cout << "Usage:\n" " " << GetName() << " [options]\n" "\n" "Options:\n" #if PTRACING " -t --trace : Enable trace, use multiple times for more detail.\n" " -o --output file : File for trace output, default is stderr.\n" #endif " --save : Save arguments in configuration file and exit.\n" " -v --version : Display version.\n" " -h --help : Display this help message.\n" "\n"; PStringArray descriptions = #ifdef USE_OPAL MyManager::Descriptions(); #else MyH323EndPoint::Descriptions(); #endif for (PINDEX i = 0 ; i < descriptions.GetSize() ; i++) cout << descriptions[i] << endl; return FALSE; } PStringArray info = #ifdef USE_OPAL MyManager::Descriptions(args); #else MyH323EndPoint::Descriptions(args); #endif if (info.GetSize() > 0) { for (PINDEX i = 0 ; i < info.GetSize() ; i++) cout << info[i] << endl; return FALSE; } if (args.HasOption('v')) return FALSE; if (args.HasOption("save")) { args.Save("save"); cout << "Arguments were saved in configuration file" << endl; return FALSE; } PTRACE(1, GetName() << " Version " << GetVersion(TRUE) << " (" << GetListOfLibs() << ")" << " on " << GetOSClass() << " " << GetOSName() << " (" << GetOSVersion() << '-' << GetOSHardware() << ")"); #if PTRACING if (PTrace::CanTrace(3)) { PTRACE(3, "Options: " << args); const PConfig config; const PStringArray keys = config.GetKeys(); if (!keys.IsEmpty()) { PTRACE(3, "Config:"); for (PINDEX iK = 0 ; iK < keys.GetSize() ; iK++) { const PStringArray values = config.GetString(keys[iK]).Lines(); for (PINDEX iV = 0 ; iV < values.GetSize() ; iV++) { PTRACE(3, " --" << keys[iK] << "=" << values[iV]); } } } } #endif #ifdef USE_OPAL MyManager *manager = new MyManager(); if (!manager->Initialise(args)) return FALSE; #else if (!MyH323EndPoint::Create(args)) return FALSE; #endif return TRUE; } ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/.cvsignore0000664000076400007640000000007011062150526016016 0ustar frolovfrolov00000000000000obj_* Debug Release NoTrace *.ncb *.vcproj.*.user *.suo t38modem-2.0.0/Changes.txt0000664000076400007640000001170111551246122016133 0ustar frolovfrolov00000000000000/* * CHANGES * * T38FAX Pseudo Modem * * Original author: Vyacheslav Frolov * */ Changelog for t38modem 2.0.0 (Apr 13 2011) * (OPAL) Implemented G.711 fallback to fax pass-through mode. * (OPAL) Added ability to disable T.38 fax mode. * (OPAL) Implemented multiple per-route options to override command line options (NOTE: the route comma delimiter in command line was deprecated for this reason). * Added ability to set Q.931 bearer capability information element for H.323 connections. * Enabled dialing characters # and * in extension number. * Replaced hardcoded timeouts by S7 value (V.250). * (OPAL) Implemented fake audio codecs G.722, G.722.1, G.722.2, G.723.1, G.723.1(5.3k), G.723.1A(5.3k), G.723.1A(6.3k), G.726-16K, G.726-24K, G.726-32K, G.726-40K, G.728, G.729, G.729A, G.729A/B, G.729B, GSM-06.10, GSM-AMR, iLBC. * (OPAL) Restored and improved ability to set error recovery redundancy for IFP packets. * (OPAL) Restored and improved ability to continuously re-send last UDPTL packet. * (OPAL) Implemented fallback to alternate route option. * (OPAL) Added ability to build with OPAL configured to disable H.323 or SIP protocol support. * Allowed "+ A B C D" dialing digits in called number. * Added ignoring unrecognized dialing digits (V.250). * Implemented dial string terminated by a semicolon ("ATD;[...]"). * Implemented fake Ring Tone and Busy Tone generation for voice-modem mode. * Replaced hardcoded workaround for mgetty-voice by conditional-compiling one (ALAW_132_BIT_REVERSE). * Added ability to put called number to Caller ID info with NAME or NMBR tags. * Fixed some other bugs and incompatibilities. Made some other improvements. Added some other options. Changelog for t38modem 1.2.0 (Oct 8 2009) * Enabled dialing characters # and *. * Added ability to dial extension numbers. * Dial modifiers 'T' and 'P' can be used instead 'D'. * Fixed incompatibilities with some voice applications (V.253). * Added ability to dial after call answer. * Added semicolon concatenating of AT commands. * Added ability to dial and switch to answering mode ("ATD;A"). * Fixed incompatibilities with some OS. * (OPAL) Added ability to use route pattern "modem:.*@tty". * (OPAL) Added ability to enable/disadle/reorder audio formats. * (OPAL) Removed --h323-old-asn and --sip-old-asn options. * Fixed some other bugs. Changelog for t38modem 1.1.0 (Apr 16 2009) * Fixed 100% CPU usage at the begining of T.38 session. * Fixed transmission fail with ECM. * Added H323Plus support. * Added OPAL v3.6 Lalande support. * (OPAL) Added STUN support. * (OPAL) Added ability to set outbound proxy for SIP. * (OPAL) Added ability to set registrar for SIP. * (OPAL) options --h323-redundancy, --h323-repeat, --sip-redundancy and --sip-repeat are disabled (because no support in OPAL). Oct 6 2008 - t38modem moved to http://sourceforge.net/projects/t38modem/. Changelog for t38modem 1.0.0 (Aug 29 2008) * Added OPAL support. * Implemented voice mode functionality. * The code optimized for original ASN.1 sequence in T.38 (06/98) Annex A (it's optimal if you use --old-asn option). Compiling with OPTIMIZE_CORRIGENDUM_IFP will optimize for CORRIGENDUM No. 1 fix (it's optimal if you do not use --old-asn option). * Added ability to continuously re-send last UDPTL packet. * Added Unix98 PTY support. Changelog for t38modem 0.8.4 (Mar 22 2007) * Fixed incompatibilities with some endpoints. * Fixed big file descriptors problem. * Fixed some possible dead locks. * Implemented AT#HCLR command. * Added ability to set range of IP ports to use. NOTE: To compile t38modem since version 0.8.4 you need openh323 since version 1.19.1. Changelog for t38modem 0.8.3 (Jun 29 2006) * Fixed race condition on start up. * Added -v option to keep Debian packaging happy. Changelog for t38modem 0.8.2 (Sep 14 2005) * Fixed incompatibilities with some endpoints. * Fixed UDPTL size. Changelog for t38modem 0.8.1 (Mar 31 2005) * Fixed incompatibilities with some fax applications. * Implemented some AT commands. * Fixed some bugs. Changelog for t38modem 0.8.0 (Oct 29 2004) * Ported to Windows. Changelog for t38modem 0.7.0 (Jul 7 2004) * Implemented FCS generation. The DCE transfers FCS octets after the HDLC data to the DTE (as it should be). HylaFax Users: the config parameter "Class1FrameOverhead: 2" should be discarded if it was used. * Implemented ECM support. Jun 2003 - t38modem moved to http://openh323.sourceforge.net/. Jun 6 2003 - t38modem 0.6.3 released. Jan 15 2003 - t38modem 0.6.2 released. Jan 6 2003 - t38modem 0.6.1 released. Dec 12 2002 - t38modem 0.6.0 released. Nov 14 2002 - t38modem 0.5.0 released. Aug 16 2002 - t38modem 0.4.2 released. Jul 9 2002 - t38modem 0.4.1 released. Mar 26 2002 - t38modem 0.4.0 released. Feb 12 2002 - t38modem 0.1.0 released. Jan 1 2002 - t38modem commited to http://www.openh323.org/. Dec 2001 - t38modem can send faxes. Oct 2001 - t38modem can receive faxes. Sep 2001 - t38modem project started. t38modem-2.0.0/ReadMe.txt0000664000076400007640000002550711515616302015732 0ustar frolovfrolov00000000000000/* * README * * T38FAX Pseudo Modem * * Original author: Vyacheslav Frolov * */ 1. Introduction --------------- What is t38modem? From your fax or voice application view point it's a fax/voice modem pool. From IP network view point it's a H.323/SIP endpoint with T.38 fax support. From your view point it's a gateway between an application and IP network. The homepage for t38modem project is http://t38modem.sourceforge.net/. 2. Building ----------- 2.1. Building for Unix ---------------------- Building with Open H323 Library: $ export PWLIBDIR=$path_to_libs/pwlib $ export OPENH323DIR=$path_to_libs/openh323 $ make NO_PBOOLEAN=1 opt Building with H323 Plus Library: $ export PTLIBDIR=$path_to_libs/ptlib $ export OPENH323DIR=$path_to_libs/h323plus $ make opt Building with Open Phone Abstraction Library (OPAL): $ export PTLIBDIR=$path_to_libs/ptlib $ export OPALDIR=$path_to_libs/opal $ make USE_OPAL=1 opt 2.2. Building for Windows ------------------------- Building with Open H323 Library: Start Microsoft Visual C++ 2005 with h323lib\t38modem_openh323_2005.vcproj file. Set Active Configuration to "t38modem - Win32 Release". Build t38modem.exe. Building with H323 Plus Library: Start Microsoft Visual C++ 2005 with h323lib\t38modem_2005.vcproj file. Set Active Configuration to "t38modem - Win32 Release". Build t38modem.exe. Building with Open Phone Abstraction Library (OPAL): Start Microsoft Visual C++ 2005 with opal\t38modem_2005.vcproj file. Set Active Configuration to "t38modem - Win32 Release". Build t38modem.exe. 3. Examples ----------- To get info about t38modem command line syntax enter: $ t38modem --help 3.1. Starting ------------- Starting with Open H323 Library or H323 Plus Library: $ t38modem -n -o trace.log -p ttyx0,ttyx1 --old-asn \ --route 0@127.0.0.1 --route all@172.16.33.21 This will create two modems (/dev/ttyx0 and /dev/ttyx1) and H.323 endpoint. If dialed number begins with '0' then it will be routed to a local host (leading '0' will be discarded). Other dialed numbers will be routed to 172.16.33.21. Starting with OPAL: $ t38modem -n -o trace.log -p ttyx0,ttyx1 \ --route "modem:0.*=h323:@127.0.0.1" \ --route "modem:1.*=sip:@172.16.33.20" \ --route "modem:2.*=h323:@172.16.33.21" \ --route "h323:.*=modem:" \ --route "sip:.*=modem:" This will create two modems (/dev/ttyx0 and /dev/ttyx1) and H.323 and SIP endpoints. If dialed number begins with '0' then it will be routed to a local host. If dialed number begins with '1' then it will be routed to SIP endpoint 172.16.33.20. If dialed number begins with '2' then it will be routed to H.323 endpoint 172.16.33.21. Leading '0', '1' and '2' will be discarded. Q. I try to use T38modem, but after run "t38modem -p ttyx0" I get a message "Could not open /dev/ptyx0: No such file or directory". A. Looks like you don't have legacy PTY devices compiled in your kernel. You need to re-compile the kernel with 'Legacy PTY Support'. Alternatively, you can build t38modem with USE_UNIX98_PTY=1 option and use -p +/dev/ttyx0,+/dev/ttyx1 instead of -p ttyx0,ttyx1. FreeBSD Users: You need to use -p ttypa,ttypb instead of -p ttyx0,ttyx1. Remember to replace ttyx0 with ttypa and ttyx1 with ttypb when following the rest of these instructions. This will create two modems /dev/ttypa and /dev/ttypb. Windows Users: You need two COM ports connected via Null-modem cable to create one modem. If your COM1 connected to COM2 and COM3 connected to COM4 you need to use -p \\.\COM2,\\.\COM4 instead of -p ttyx0,ttyx1. This will create two modems COM1 and COM3. Q. How to use t38modem without additional COM port hardware on Windows? A. Replace a pair of physical COM ports with a pair of virtual COM ports. See http://com0com.sourceforge.net/ project for details. Q. What model of modem to select in Add Hardware Wizard? A. Select "Standard 1440 bps Modem". Cisco Users: Possible additionaly you will need to use --h245tunneldisable option. 3.2. Testing (you need two consoles) ------------------------------------ (FreeBSD users - remeber to use /dev/ttypa and /dev/ttypb with 'cu -l') (Windows users - use COM1 and COM3 with HyperTerminal) $ cu -l /dev/ttyx0 $ cu -l /dev/ttyx1 Connected. Connected. <-- at <-- at --> OK --> OK (wait at least 10 secs) <-- atdt012345 --> --> RING --> --> RING <-- ati9 --> NDID = 12345 --> OK --> --> RING --> --> RING <-- ata --> CONNECT --> CONNECT <-- x --> OK <-- ath --> OK --> --> ERROR <-- at --> OK <-- at --> OK ... ... 3.3. Example of Cisco config (loopback) --------------------------------------- 10.0.2.12 --> Cisco port 2:D --E1-cable--> Cisco port 3:D --> 10.0.2.12 dial-peer voice 3340 voip incoming called-number 3334.... codec g711alaw fax rate 14400 fax protocol t38 ls-redundancy 0 hs-redundancy 0 dial-peer voice 3341 pots destination-pattern 3334.... port 2:D forward-digits 7 dial-peer voice 3342 pots incoming called-number 334.... direct-inward-dial port 3:D exit dial-peer voice 3343 voip destination-pattern 334.... session target ipv4:10.0.2.12 codec g711alaw fax rate 14400 fax protocol t38 ls-redundancy 0 hs-redundancy 0 exit 3.4. Example of HylaFAX modem config files ------------------------------------------ Copy HylaFAX/etc/config.ttyx to HylaFAX's etc directory Create simbolic links: config.ttyx0 -> config.ttyx config.ttyx1 -> config.ttyx Start HylaFAX with new modems: $ .../faxgetty -D ttyx0 $ .../faxgetty -D ttyx1 (FreeBSD users - don't forget we are using ttypa and ttypb) 4. AT commands specific to t38modem ----------------------------------- 4.1. AT#CID command ------------------- 4.1.1 Reporting Caller ID and DID after the first RING ------------------------------------------------------ #CID=0 - disable Caller ID and DID reporting (default). #CID=1 - enable only Caller ID reporting. #CID=10 - enable Caller ID and DID reporting. #CID=11 - enable only DID reporting. Example: <-- AT#CID=10 --> OK --> RING --> DATE = ; Caller ID --> TIME = ; Caller ID --> NMBR = ; Caller ID --> NAME = ; Caller ID --> NDID = ; DID --> RING --> RING 4.2. ATI command ---------------- 4.2.1 calling/called number reporting ------------------------------------- I8 - reports calling number for last incoming call. I9 - reports called number for last incoming call. Example: <-- ATI8I9 --> NMBR = --> NDID = --> OK 4.3. ATD command ---------------- 4.3.1 Fax mode modifiers ------------------------ F - force fax mode (T.38 or G.711 pass-trough) after dialing (with OPAL can be overriden by route option OPAL-Force-Fax-Mode=false). V - do not force fax mode after dialing (default) (with OPAL can be overriden by route option OPAL-Force-Fax-Mode=true). Examples: <-- ATDF force fax mode after dialing but user can override it by inserting V into . <-- ATDV do not force fax mode after dialing and user can't override it by inserting F into . 4.3.2 calling/called number modifiers ------------------------------------- L - purge calling number and start dialing it. T,P,D - continue to dial of called number if dialing of calling number was started. If calling number is empty after processing ATD command then t38modem's local party number used. The calling number is a string of 0 or more of the characters: "0 1 2 3 4 5 6 7 8 9 * #" Note: dialing of calling number is not allowed in online command state. The called number is a string of 0 or more of the characters: "0 1 2 3 4 5 6 7 8 9 * # + A B C D" Note: '+' will be ignored in online command state or after '@' modifier. Examples: <-- ATD calling number is t38modem's local party number but user can override it by inserting L into <-- ATDL calling number is t38modem's local party number and user can't override it by inserting L into <-- ATDLT calling number is but user can override it by inserting L into <-- ATDL calling number is and user can't override it by inserting L into 4.3.3 Wait for answer modifier ------------------------------ @ - dial the called number part collected before first @, wait till the call answered and play the called number part collected after last @. Implicitly '@' will continue dialing of called number (see 4.3.2). Examples: <-- ATS8? --> 002 --> OK <-- ATD12345@,,,4321 dial number 12345, wait till the call answered, wait 6 seconds (3 commas * 2 seconds), play digits 4321. <-- ATD12345@,,,4321@ dial number 12345. 4.4. AT#DFRMC command --------------------- 4.4.1 Set delay for CONNECT result code for AT+FRM command ---------------------------------------------------------- #DFRMC=0 - disable delay (default). #DFRMC=25 - set delay to 250 ms. 4.5. AT#HCLR command -------------------- 4.5.1 Set clear call mode for ATH command ----------------------------------------- #HCLR=0 - ATH command will not clear not answered incoming call (default). #HCLR=1 - ATH command will clear not answered incoming call. 4.6. AT#CIDFMT command ---------------------- AT#CIDFMT command allows to set value format for NAME and NMBR tags of Caller ID. Syntax: #CIDFMT=[][,[]] If a format element is empty then its value will be untouched. Subparameter : 0 - report "NAME = " (default) 1 - report "NAME = " 2 - report "NAME = " 3 - report "NAME = <- " Subparameter : 0 - report "NMBR = " (default) 1 - report "NMBR = " 2 - report "NMBR = " 3 - report "NMBR = #" t38modem-2.0.0/hdlc.cxx0000664000076400007640000002207211453532357015474 0ustar frolovfrolov00000000000000/* * hdlc.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2003-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: hdlc.cxx,v $ * Revision 1.8 2010/10/08 06:06:39 vfrolov * Added diagErrorMask * * Revision 1.7 2009/11/09 19:12:57 vfrolov * Changed T38Engine to EngineBase * * Revision 1.6 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.5 2005/02/03 11:32:11 vfrolov * Fixed MSVC compile warnings * * Revision 1.4 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.3 2004/03/09 12:52:39 vfrolov * Fixed compile warning * * Revision 1.2 2004/02/17 13:23:07 vfrolov * Fixed MSVC compile errors * * Revision 1.1 2003/12/04 13:38:33 vfrolov * Initial revision * */ #include #include "hdlc.h" #include "enginebase.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// void HDLC::pack(const void *_pBuf, PINDEX count, PBoolean flag) { WORD w = WORD((WORD)rawByte << 8); const BYTE *pBuf = (const BYTE *)_pBuf; for (PINDEX i = 0 ; i < count ; i++) { w |= *(pBuf++) & 0xFF; for (PINDEX j = 0 ; j < 8 ; j++) { w <<= 1; if (++rawByteLen == 8) { rawByte = BYTE(w >> 8); outData.PutData(&rawByte, 1); rawByteLen = 0; } if (!flag) { if (w & 0x100) { if (++rawOnes == 5) { w = WORD((w & 0xFF00) << 1 | (w & 0xFF)); if (++rawByteLen == 8) { rawByte = BYTE(w >> 8); outData.PutData(&rawByte, 1); rawByteLen = 0; } rawOnes = 0; } } else rawOnes = 0; } } } rawByte = BYTE(w >> 8); if (flag) rawOnes = 0; } PBoolean HDLC::sync(BYTE b) { WORD w = WORD(((WORD)rawByte << 8) | (b & 0xFF)); PINDEX j = 8 - rawByteLen; w <<= j; for ( ; j <= 8 ; j++, w <<= 1) { if ((w >> 8) == 0x7E) { rawByte = BYTE(w >> j); rawByteLen = 8 - j; return TRUE; } } rawByte = BYTE(w >> j); rawByteLen = 7; return FALSE; } PBoolean HDLC::skipFlag(BYTE b) { WORD w = WORD(((WORD)rawByte << 8) | (b & 0xFF)); PINDEX j = 8 - rawByteLen; w <<= j; if ((w >> 8) == 0x7E) { rawByte = BYTE(w >> j); return TRUE; } return FALSE; } PBoolean HDLC::unpack(BYTE b) { //myPTRACE(1, "unpack det " << hex << (WORD)b); WORD w = WORD(((WORD)rawByte << 8) | (b & 0xFF)); PINDEX j = 8 - rawByteLen; w <<= j; for ( ; j <= 8 ; j++, w <<= 1) { if ((w >> 8) == 0x7E) { rawByte = BYTE(w >> j); rawByteLen = 8 - j; return FALSE; } if (w & 0x8000) { hdlcChunk <<= 1; hdlcChunk |= 1; hdlcChunkLen++; rawOnes++; } else { if (rawOnes != 5) { hdlcChunk <<= 1; hdlcChunkLen++; } rawOnes = 0; } if (hdlcChunkLen == 24) { BYTE b = BYTE(hdlcChunk >> 16); outData.PutData(&b, 1); //myPTRACE(1, "unpack put " << hex << (WORD)hdlcChunk); hdlcChunkLen = 16; } } rawByte = BYTE(w >> j); rawByteLen = 7; return TRUE; } /////////////////////////////////////////////////////////////// int HDLC::GetInData(void *pBuf, PINDEX count) { if (!inData) return -1; int len = inData->GetData(pBuf, count); if (len > 0) { rawCount += len; lastChar = ((BYTE *)pBuf)[len - 1]; } else if (len < 0) { inData = NULL; return -1; } return len; } int HDLC::GetRawData(void *_pBuf, PINDEX count) { BYTE *pBuf = (BYTE *)_pBuf; switch (inDataType) { case EngineBase::dtHdlc: break; case EngineBase::dtRaw: return GetInData(pBuf, count); default: return 0; } int outLen = outData.GetData(pBuf, count); if (outLen < 0) return -1; int len = 0; if (outLen > 0) { pBuf += outLen; count -= outLen; len += outLen; } do { if (inData) { BYTE Buf[256]; int inLen = inData->GetData(Buf, sizeof(Buf)); if (inLen < 0) { Buf[0] = BYTE(fcs >> 8); Buf[1] = BYTE(fcs & 0xFF); if (inData->GetDiag() & EngineBase::diagErrorMask) Buf[0]++; pack(Buf, 2); pack("\x7e\x7e", 2, TRUE); outData.PutEof(); inData = NULL; } else if (inLen > 0) { lastChar = Buf[inLen - 1]; fcs.build(Buf, inLen); pack(Buf, inLen); } else break; } int outLen = outData.GetData(pBuf, count); if (outLen < 0) { if (len > 0) break; return -1; } else if (outLen > 0) { pBuf += outLen; count -= outLen; len += outLen; } } while (count); if (len > 0) rawCount += len; return len; } int HDLC::GetHdlcData(void *_pBuf, PINDEX count) { int len; BYTE *pBuf = (BYTE *)_pBuf; switch (inDataType) { case EngineBase::dtHdlc: len = GetInData(pBuf, count); if (len > 0) fcs.build(pBuf, len); return len; case EngineBase::dtRaw: break; default: return 0; } if (!inData || hdlcState == stEof) { len = outData.GetData(pBuf, count); if (len > 0) fcs.build(pBuf, len); } else if (!count && hdlcState == stData) { if (outData.GetData(NULL, 0) < 0 || inData->GetData(NULL, 0) < 0) return -1; return 0; } else { BYTE b; int res; len = 0; for ( ; (res = inData->GetData(&b, 1)) != 0 ; ) { if (res < 0) { outData.PutEof(); inData = NULL; hdlcState = stEof; //myPTRACE(1, "hdlcState=stEof EOF"); } else { lastChar = b; rawCount++; switch (hdlcState) { case stSync: if (sync(b)) { hdlcState = stSkipFlags; //myPTRACE(1, "hdlcState=stSkipFlags " << hex << (int)b); } break; case stSkipFlags: if (skipFlag(b)) break; hdlcState = stData; //myPTRACE(1, "hdlcState=stData " << hex << (int)b); case stData: if (!unpack(b)) { outData.PutEof(); hdlcState = stEof; //myPTRACE(1, "hdlcState=stEof " << hex << (int)b); } break; default: myPTRACE(1, "HDLC::GetHdlcData(): unexpected hdlcState=" << hdlcState); } } if (hdlcState == stEof || hdlcState == stData) { int outLen = outData.GetData(pBuf, count); if (outLen < 0) { if (len > 0) break; return -1; } else if (outLen > 0) { fcs.build(pBuf, outLen); pBuf += outLen; count -= outLen; len += outLen; } if (!count || hdlcState == stEof) break; } } } return len; } /////////////////////////////////////////////////////////////// HDLC::HDLC() : inDataType(EngineBase::dtNone), outDataType(EngineBase::dtNone), inData(NULL), lastChar(-1), rawCount(0), rawByteLen(0), rawOnes(0), hdlcState(stEof) { } PBoolean HDLC::isFcsOK() { if (hdlcChunkLen != 16) { myPTRACE(1, "isFcsOK(): hdlcChunkLen(" << hdlcChunkLen << ") != 16"); return FALSE; } if ((WORD)hdlcChunk != fcs) { myPTRACE(1, "isFcsOK(): hdlcChunk(" << hex << (WORD)hdlcChunk << ") != fcs(" << (WORD)fcs << ")"); return FALSE; } return TRUE; } void HDLC::PutRawData(DataStream *_inData) { inDataType = EngineBase::dtRaw; inData = _inData; lastChar = -1; } void HDLC::PutHdlcData(DataStream *_inData) { inDataType = EngineBase::dtHdlc; inData = _inData; lastChar = -1; } void HDLC::GetRawStart(PINDEX flags) { outDataType = EngineBase::dtRaw; outData.Clean(); fcs = FCS(); if (inDataType == EngineBase::dtHdlc) { while (flags--) pack("\x7e", 1, TRUE); } } void HDLC::GetHdlcStart(PBoolean sync) { outDataType = EngineBase::dtHdlc; outData.Clean(); fcs = FCS(); if (inDataType == EngineBase::dtRaw) { hdlcChunkLen = 0; rawOnes = 0; hdlcState = sync ? stSync : stSkipFlags; } else { if (!sync) rawCount += 4; // count FCS, flags, zeros } //myPTRACE(1, "hdlcState=" << (sync ? "stSync" : "stSkipFlags") << " START"); } int HDLC::GetData(void *pBuf, PINDEX count) { switch (outDataType) { case EngineBase::dtHdlc: return GetHdlcData(pBuf, count); case EngineBase::dtRaw: return GetRawData(pBuf, count); default: myPTRACE(1, "HDLC::GetData bad outDataType=" << outDataType); } return -1; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/pmodem.h0000664000076400007640000000753311453124673015473 0ustar frolovfrolov00000000000000/* * pmodem.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmodem.h,v $ * Revision 1.11 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.10 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.9 2009/07/08 18:43:44 vfrolov * Added PseudoModem::ttyName() * * Revision 1.8 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.7 2007/03/23 10:14:35 vfrolov * Implemented voice mode functionality * * Revision 1.6 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.5 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * Revision 1.4 2002/05/15 16:17:49 vfrolov * Implemented per modem routing for I/C calls * * Revision 1.3 2002/03/05 12:31:58 vfrolov * Added Copyright header * Changed class hierarchy * PseudoModem is abstract * PseudoModemBody is child of PseudoModem * Added PseudoModemQ::CreateModem() to create instances * * Revision 1.2 2002/01/10 06:10:02 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #ifndef _PMODEM_H #define _PMODEM_H #include "pmutils.h" /////////////////////////////////////////////////////////////// class T38Engine; class AudioEngine; class EngineBase; class PseudoModem : public ModemThread { PCLASSINFO(PseudoModem, ModemThread); public: /**@name Construction */ //@{ PseudoModem(const PString &_tty) : ttyname(_tty), valid(FALSE) {}; //@} /**@name Operations */ virtual PBoolean IsReady() const = 0; virtual PBoolean CheckRoute(const PString &number) const = 0; virtual PBoolean Request(PStringToString &request) const = 0; virtual T38Engine *NewPtrT38Engine() const = 0; virtual AudioEngine *NewPtrAudioEngine() const = 0; virtual EngineBase *NewPtrUserInputEngine() const = 0; const PString &ttyName() const { return ttyname; } const PString &ptyName() const { return ptyname; } const PString &modemToken() const { return ptyname; } PBoolean IsValid() const { return valid; } //@} protected: Comparison Compare(const PObject & obj) const; PString ttyname; PString ptyname; PBoolean valid; }; /////////////////////////////////////////////////////////////// class PseudoModemList; PQUEUE(_PseudoModemQ, PseudoModem); class PseudoModemQ : protected _PseudoModemQ { PCLASSINFO(PseudoModemQ, _PseudoModemQ); public: /**@name Construction */ //@{ PseudoModemQ(); ~PseudoModemQ(); //@} /**@name Operations */ PBoolean CreateModem( const PString &tty, const PString &route, const PConfigArgs &args, const PNotifier &callbackEndPoint ); void Enqueue(PseudoModem *modem); PBoolean Enqueue(const PString &modemToken); PseudoModem *DequeueWithRoute(const PString &number); PseudoModem *Dequeue(const PString &modemToken); //@} protected: PseudoModem *Find(const PString &modemToken) const; PseudoModemList *pmodem_list; PMutex Mutex; }; /////////////////////////////////////////////////////////////// #endif // _PMODEM_H t38modem-2.0.0/t30tone.h0000664000076400007640000000337511455110341015473 0ustar frolovfrolov00000000000000/* * t30tone.h * * T38FAX Pseudo Modem * * Copyright (c) 2002-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: t30tone.h,v $ * Revision 1.6 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.5 2010/09/10 18:00:44 vfrolov * Cleaned up code * * Revision 1.4 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.3 2007/03/23 10:14:36 vfrolov * Implemented voice mode functionality * * Revision 1.2 2004/07/07 07:52:07 vfrolov * Moved ptlib.h including to *.cxx for precompiling * * Revision 1.1 2002/04/30 10:59:10 vfrolov * Initial revision * */ #ifndef _T30TONE_H #define _T30TONE_H /////////////////////////////////////////////////////////////// class T30ToneDetect : public PObject { PCLASSINFO(T30ToneDetect, PObject); public: T30ToneDetect(); ~T30ToneDetect(); PBoolean Write(const void * buffer, PINDEX len); protected: long *cng_filter_buf; PINDEX index; long power; int cng_on_count; int cng_off_count; int cng_phase; }; /////////////////////////////////////////////////////////////// #endif // _T30TONE_H t38modem-2.0.0/drivers.cxx0000664000076400007640000001232011061726064016226 0ustar frolovfrolov00000000000000/* * drivers.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2004-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: drivers.cxx,v $ * Revision 1.4 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.3 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.2 2004/07/07 13:45:17 vfrolov * Added C0C driver for Windows * * Revision 1.1 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * */ #include #include "pmodemi.h" #include "drivers.h" #include "drv_pty.h" #include "drv_c0c.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// static int numDrivers = 0; /////////////////////////////////////////////////////////////// #define MAX_DRIVERS 10 static struct { const char *name; PBoolean (*CheckTty)( const PString &_tty ); PString (*ArgSpec)(); PStringArray (*Description)(); PseudoModem *(*CreatePseudoModem)( const PString &tty, const PString &route, const PConfigArgs &args, const PNotifier &callbackEndPoint ); } drivers[MAX_DRIVERS]; static int addDriver( const char *name, PBoolean (*CheckTty)( const PString &_tty ), PString (*ArgSpec)(), PStringArray (*Description)(), PseudoModem *(*CreatePseudoModem)( const PString &tty, const PString &route, const PConfigArgs &args, const PNotifier &callbackEndPoint ) ) { if (numDrivers >= MAX_DRIVERS) return -1; drivers[numDrivers].name = name; drivers[numDrivers].CheckTty = CheckTty; drivers[numDrivers].ArgSpec = ArgSpec; drivers[numDrivers].Description = Description; drivers[numDrivers].CreatePseudoModem = CreatePseudoModem; return numDrivers++; } /////////////////////////////////////////////////////////////// #define DECLARE_MODEM_DRIVER(name, suffix) \ static PseudoModem *CreatePseudoModem##suffix( \ const PString &tty, \ const PString &route, \ const PConfigArgs &args, \ const PNotifier &callbackEndPoint) \ { \ return new \ PseudoModem##suffix(tty, route, args, callbackEndPoint);\ } \ static const int ___addDriver##suffix = addDriver( \ name, \ &PseudoModem##suffix::CheckTty, \ &PseudoModem##suffix::ArgSpec, \ &PseudoModem##suffix::Description, \ &CreatePseudoModem##suffix \ ); \ /////////////////////////////////////////////////////////////// #ifdef MODEM_DRIVER_Pty DECLARE_MODEM_DRIVER("PTY", Pty) #endif #ifdef MODEM_DRIVER_C0C DECLARE_MODEM_DRIVER("C0C", C0C) #endif /////////////////////////////////////////////////////////////// PseudoModem *PseudoModemDrivers::CreateModem( const PString &tty, const PString &route, const PConfigArgs &args, const PNotifier &callbackEndPoint ) { PseudoModem *modem = NULL; for (int i = 0 ; i < numDrivers ; i++) { if (drivers[i].CheckTty(tty)) { modem = drivers[i].CreatePseudoModem(tty, route, args, callbackEndPoint); if (!modem) { myPTRACE(1, "PseudoModemDrivers::CreateModem " << tty << " was not created"); return NULL; } break; } } if (!modem) { myPTRACE(1, "PseudoModemDrivers::CreateModem " << tty << " is not a valid value"); return NULL; } if (!modem->IsValid()) { myPTRACE(1, "PseudoModemDrivers::CreateModem " << tty << " is not valid"); delete modem; return NULL; } return modem; } PString PseudoModemDrivers::ArgSpec() { PString argSpec; for (int i = 0 ; i < numDrivers ; i++) argSpec += drivers[i].ArgSpec(); return argSpec; } PStringArray PseudoModemDrivers::Descriptions() { PStringArray descriptions; for (int i = 0 ; i < numDrivers ; i++) { descriptions.Append(new PString(drivers[i].name)); PStringArray description = drivers[i].Description(); for (PINDEX j = 0 ; j < description.GetSize() ; j++) descriptions.Append(new PString(PString(" ") + description[j])); } return descriptions; } /////////////////////////////////////////////////////////////// t38modem-2.0.0/pmodeme.h0000664000076400007640000000506311453124673015634 0ustar frolovfrolov00000000000000/* * pmodeme.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmodeme.h,v $ * Revision 1.8 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.7 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.6 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.5 2007/03/23 10:14:35 vfrolov * Implemented voice mode functionality * * Revision 1.4 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * Revision 1.3 2002/04/19 14:29:33 vfrolov * Added Copyright header * * Revision 1.2 2002/01/10 06:10:02 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #ifndef _PMODEME_H #define _PMODEME_H #include "pmodemi.h" /////////////////////////////////////////////////////////////// class ModemEngineBody; class PseudoModemBody; class ModemEngine : public ModemThreadChild { PCLASSINFO(ModemEngine, ModemThreadChild); public: /**@name Construction */ //@{ ModemEngine(PseudoModemBody &_parent); ~ModemEngine(); //@} /**@name Operations */ //@{ PBoolean IsReady() const; PBoolean Request(PStringToString &request) const; virtual T38Engine *NewPtrT38Engine() const; virtual AudioEngine *NewPtrAudioEngine() const; virtual EngineBase *NewPtrUserInputEngine() const; const PString &ptyName() const { return Parent().ptyName(); } const PString &modemToken() const { return Parent().modemToken(); } //@} protected: PseudoModemBody &Parent() const { return (PseudoModemBody &)parent; } virtual void Main(); void ToPtyQ(const void *buf, PINDEX count) const { Parent().ToOutPtyQ(buf, count); } ModemEngineBody *body; }; /////////////////////////////////////////////////////////////// #endif // _PMODEME_H t38modem-2.0.0/audio.cxx0000664000076400007640000003311611506625435015663 0ustar frolovfrolov00000000000000/* * audio.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: audio.cxx,v $ * Revision 1.18 2010/12/29 12:35:41 vfrolov * Fixed mutex locking * * Revision 1.17 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.16 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.15 2010/09/22 15:23:48 vfrolov * Added OnResetModemState() * * Revision 1.14 2010/09/10 18:00:44 vfrolov * Cleaned up code * * Revision 1.13 2010/07/09 04:44:27 vfrolov * Added tracing targetTimeFakeOut * * Revision 1.12 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.11 2009/11/20 16:37:27 vfrolov * Fixed audio class application blocking by forced T.38 mode * * Revision 1.10 2009/11/19 11:14:04 vfrolov * Added OnUserInput * * Revision 1.9 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.8 2009/10/05 15:01:08 vfrolov * Fixed possible memory leak * * Revision 1.7 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.6 2007/08/27 10:52:13 vfrolov * Fixed typo * * Revision 1.5 2007/05/28 12:44:24 vfrolov * Added adaptive delay restarting * Fixed SendOnIdle() * * Revision 1.4 2007/04/24 16:28:53 vfrolov * Added more tracing * * Revision 1.3 2007/04/04 09:52:57 vfrolov * Added missing lastWriteCount setting * * Revision 1.2 2007/03/23 14:54:19 vfrolov * Fixed compiler warnings * * Revision 1.1 2007/03/23 09:54:45 vfrolov * Initial revision * */ #include #include "pmutils.h" #include "t30tone.h" #include "tone_gen.h" #include "audio.h" #define new PNEW /////////////////////////////////////////////////////////////// typedef PInt16 SIMPLE_TYPE; #define BYTES_PER_SIMPLE sizeof(SIMPLE_TYPE) #define SIMPLES_PER_SEC 8000 #define BYTES_PER_MSEC ((SIMPLES_PER_SEC*BYTES_PER_SIMPLE)/1000) /////////////////////////////////////////////////////////////// class FakeReadThread : public PThread { PCLASSINFO(FakeReadThread, PThread); public: FakeReadThread(AudioEngine &engine) : PThread(30000) , audioEngine(engine) { PTRACE(3, audioEngine.Name() << " FakeReadThread"); audioEngine.AddReference(); } ~FakeReadThread() { PTRACE(3, audioEngine.Name() << " ~FakeReadThread"); ReferenceObject::DelPointer(&audioEngine); } protected: virtual void Main(); AudioEngine &audioEngine; }; void FakeReadThread::Main() { PTRACE(3, audioEngine.Name() << " FakeReadThread::Main started"); audioEngine.OpenOut(EngineBase::HOWNEROUT(this), TRUE); static BYTE buf[BYTES_PER_MSEC*20]; #if PTRACING unsigned long count = 0; #endif for (;;) { if (!audioEngine.Read(EngineBase::HOWNEROUT(this), buf, sizeof(buf))) break; #if PTRACING count++; #endif } audioEngine.CloseOut(EngineBase::HOWNEROUT(this)); PTRACE(3, audioEngine.Name() << " FakeReadThread::Main stopped, faked out " << count*(sizeof(buf)/BYTES_PER_MSEC) << " ms"); } /////////////////////////////////////////////////////////////// class FakeWriteThread : public PThread { PCLASSINFO(FakeWriteThread, PThread); public: FakeWriteThread(AudioEngine &engine) : PThread(30000) , audioEngine(engine) { PTRACE(3, audioEngine.Name() << " FakeWriteThread"); audioEngine.AddReference(); } ~FakeWriteThread() { PTRACE(3, audioEngine.Name() << " ~FakeWriteThread"); ReferenceObject::DelPointer(&audioEngine); } protected: virtual void Main(); AudioEngine &audioEngine; }; void FakeWriteThread::Main() { PTRACE(3, audioEngine.Name() << " FakeWriteThread::Main started"); audioEngine.OpenIn(EngineBase::HOWNERIN(this), TRUE); #if PTRACING unsigned long count = 0; #endif for (;;) { if (!audioEngine.Write(EngineBase::HOWNERIN(this), NULL, BYTES_PER_MSEC*20)) break; #if PTRACING count++; #endif } audioEngine.CloseIn(EngineBase::HOWNERIN(this)); PTRACE(3, audioEngine.Name() << " FakeWriteThread::Main stopped, faked out " << count*20 << " ms"); } /////////////////////////////////////////////////////////////// static ToneGenerator::ToneType dt2tt(EngineBase::DataType dataType) { switch (dataType) { case EngineBase::dtCng: return ToneGenerator::ttCng; case EngineBase::dtCed: return ToneGenerator::ttCed; case EngineBase::dtRing: return ToneGenerator::ttRing; case EngineBase::dtBusy: return ToneGenerator::ttBusy; default: break; } return ToneGenerator::ttSilence; } /////////////////////////////////////////////////////////////// AudioEngine::AudioEngine(const PString &_name) : EngineBase(_name + " AudioEngine") , callbackParam(cbpReset) , sendAudio(NULL) , recvAudio(NULL) , pToneIn(NULL) , pToneOut(NULL) , t30ToneDetect(NULL) { PTRACE(2, name << " AudioEngine"); } AudioEngine::~AudioEngine() { PTRACE(2, name << " ~AudioEngine"); delete sendAudio; delete recvAudio; delete pToneIn; delete pToneOut; delete t30ToneDetect; } void AudioEngine::OnAttach() { EngineBase::OnAttach(); } void AudioEngine::OnDetach() { EngineBase::OnDetach(); } void AudioEngine::OnResetModemState() { EngineBase::OnResetModemState(); if (sendAudio) { if (hOwnerOut != NULL) { sendAudio->PutEof(); } else { delete sendAudio; sendAudio = NULL; } } if (recvAudio) { delete recvAudio; recvAudio = NULL; } if (pToneIn) { delete pToneIn; pToneIn = NULL; } if (pToneOut) { delete pToneOut; pToneOut = NULL; } } void AudioEngine::OnChangeModemClass() { EngineBase::OnChangeModemClass(); if (modemClass == mcAudio) { if (!t30ToneDetect) t30ToneDetect = new T30ToneDetect; } else { if (t30ToneDetect) { delete t30ToneDetect; t30ToneDetect = NULL; } } } /////////////////////////////////////////////////////////////// void AudioEngine::OnOpenOut() { EngineBase::OnOpenOut(); } void AudioEngine::OnCloseOut() { EngineBase::OnCloseOut(); } void AudioEngine::OnChangeEnableFakeOut() { EngineBase::OnChangeEnableFakeOut(); if (IsOpenOut() || !isEnableFakeOut) return; if (!sendAudio) return; (new FakeReadThread(*this))->Resume(); } PBoolean AudioEngine::Read(HOWNEROUT hOwner, void * buffer, PINDEX amount) { if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; PWaitAndSignal mutexOutWait(MutexOut); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; { PWaitAndSignal mutexWait(Mutex); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; if (firstOut) { firstOut = FALSE; ModemCallbackWithUnlock(cbpUpdateState); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; readDelay.Restart(); } } readDelay.Delay(amount/BYTES_PER_MSEC); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; if (sendAudio) { PBoolean wasFull = sendAudio->isFull(); int count = sendAudio->GetData(buffer, amount); if (count < 0) { count = 0; delete sendAudio; sendAudio = NULL; ModemCallbackWithUnlock(callbackParam); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; } else { if (wasFull && !sendAudio->isFull()) { ModemCallbackWithUnlock(cbpOutBufNoFull); if (hOwnerOut != hOwner || !IsModemOpen()) return FALSE; } } if (amount > count) memset((BYTE *)buffer + count, 0, amount - count); } else { if (pToneOut) pToneOut->Read(buffer, amount); else memset(buffer, 0, amount); } return TRUE; } void AudioEngine::SendOnIdle(DataType _dataType) { PTRACE(2, name << " SendOnIdle " << _dataType); PWaitAndSignal mutexWaitModem(MutexModem); PWaitAndSignal mutexWait(Mutex); ToneGenerator::ToneType toneType = dt2tt(_dataType); if (pToneOut && pToneOut->Type() != toneType) { delete pToneOut; pToneOut = NULL; } if (pToneOut == NULL && toneType != ToneGenerator::ttSilence) pToneOut = new ToneGenerator(toneType); } PBoolean AudioEngine::SendStart(DataType PTRACE_PARAM(_dataType), int PTRACE_PARAM(param)) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); if (sendAudio) delete sendAudio; sendAudio = new DataStream(1024 * BYTES_PER_SIMPLE); PTRACE(3, name << " SendStart _dataType=" << _dataType << " param=" << param); return TRUE; } int AudioEngine::Send(const void *pBuf, PINDEX count) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return -1; PWaitAndSignal mutexWait(Mutex); if (sendAudio) sendAudio->PutData(pBuf, count); return count; } PBoolean AudioEngine::SendStop(PBoolean PTRACE_PARAM(moreFrames), int _callbackParam) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); callbackParam = _callbackParam; if (sendAudio) sendAudio->PutEof(); PTRACE(3, name << " SendStop moreFrames=" << moreFrames << " callbackParam=" << callbackParam); return TRUE; } PBoolean AudioEngine::isOutBufFull() const { PWaitAndSignal mutexWait(Mutex); if (!sendAudio) return FALSE; return sendAudio->isFull(); } /////////////////////////////////////////////////////////////// void AudioEngine::OnOpenIn() { EngineBase::OnOpenIn(); } void AudioEngine::OnCloseIn() { EngineBase::OnCloseIn(); } void AudioEngine::OnChangeEnableFakeIn() { EngineBase::OnChangeEnableFakeIn(); if (IsOpenIn() || !isEnableFakeIn) return; if (!recvAudio) return; (new FakeWriteThread(*this))->Resume(); } PBoolean AudioEngine::Write(HOWNERIN hOwner, const void * buffer, PINDEX len) { if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; PWaitAndSignal mutexInWait(MutexIn); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; { PWaitAndSignal mutexWait(Mutex); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; if (firstIn) { firstIn = FALSE; ModemCallbackWithUnlock(cbpUpdateState); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; writeDelay.Restart(); } if (buffer) { if (recvAudio && !recvAudio->isFull()) { recvAudio->PutData(buffer, len); ModemCallbackWithUnlock(callbackParam); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } if (t30ToneDetect && t30ToneDetect->Write(buffer, len)) { OnUserInput('c'); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } } else { if (recvAudio && !recvAudio->isFull()) { for (PINDEX rest = len ; rest > 0 ;) { BYTE buf[64*BYTES_PER_SIMPLE]; PINDEX lenChank = rest; if (lenChank > (PINDEX)sizeof(buf)) lenChank = (PINDEX)sizeof(buf); if (pToneIn) pToneIn->Read(buf, lenChank); else memset(buf, 0, lenChank); recvAudio->PutData(buf, lenChank); rest -= lenChank; } ModemCallbackWithUnlock(callbackParam); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; } } } writeDelay.Delay(len/BYTES_PER_MSEC); if (hOwnerIn != hOwner || !IsModemOpen()) return FALSE; return TRUE; } void AudioEngine::RecvOnIdle(DataType _dataType) { PTRACE(2, name << " RecvOnIdle " << _dataType); PWaitAndSignal mutexWaitModem(MutexModem); PWaitAndSignal mutexWait(Mutex); ToneGenerator::ToneType toneType = dt2tt(_dataType); if (pToneIn && pToneIn->Type() != toneType) { delete pToneIn; pToneIn = NULL; } if (pToneIn == NULL && toneType != ToneGenerator::ttSilence) pToneIn = new ToneGenerator(toneType); } PBoolean AudioEngine::RecvWait(DataType /*_dataType*/, int /*param*/, int /*_callbackParam*/, PBoolean &done) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); if (recvAudio) delete recvAudio; recvAudio = new DataStream(1024 * BYTES_PER_SIMPLE); done = TRUE; return TRUE; } PBoolean AudioEngine::RecvStart(int _callbackParam) { PWaitAndSignal mutexWaitModem(MutexModem); if (!IsModemOpen()) return FALSE; PWaitAndSignal mutexWait(Mutex); if (!recvAudio) return FALSE; callbackParam = _callbackParam; return TRUE; } int AudioEngine::Recv(void * pBuf, PINDEX count) { PWaitAndSignal mutexWaitModem(MutexModem); if (!recvAudio) return -1; PWaitAndSignal mutexWait(Mutex); return recvAudio->GetData(pBuf, count); } void AudioEngine::RecvStop() { PWaitAndSignal mutexWaitModem(MutexModem); if(!IsModemOpen()) return; PWaitAndSignal mutexWait(Mutex); if (recvAudio) { delete recvAudio; recvAudio = NULL; } } /////////////////////////////////////////////////////////////// t38modem-2.0.0/pmodemi.cxx0000664000076400007640000001422111453124673016207 0ustar frolovfrolov00000000000000/* * pmodemi.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmodemi.cxx,v $ * Revision 1.17 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.16 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.15 2009/07/08 18:43:44 vfrolov * Added PseudoModem::ttyName() * * Revision 1.14 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.13 2007/03/23 10:14:35 vfrolov * Implemented voice mode functionality * * Revision 1.12 2005/02/03 11:32:12 vfrolov * Fixed MSVC compile warnings * * Revision 1.11 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * Revision 1.10 2004/07/07 07:49:19 vfrolov * Included ptlib.h for precompiling * * Revision 1.9 2004/03/01 17:18:46 vfrolov * Increased MAX_qBUF * * Revision 1.8 2003/12/04 12:10:16 vfrolov * Tuned max delay for buffer full condition * * Revision 1.7 2002/12/30 12:49:33 vfrolov * Added tracing thread's CPU usage (Linux only) * * Revision 1.6 2002/12/20 10:12:54 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.5 2002/05/15 16:17:55 vfrolov * Implemented per modem routing for I/C calls * * Revision 1.4 2002/03/05 12:40:27 vfrolov * Changed class hierarchy * PseudoModem is abstract * PseudoModemBody is child of PseudoModem * Added PseudoModemQ::CreateModem() to create instances * * Revision 1.3 2002/03/01 08:53:12 vfrolov * Added Copyright header * Some OS specific code moved from pmodemi.cxx to pty.cxx * Added error code string to log * Fixed race condition with fast close and open slave tty * Some other changes * * Revision 1.2 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #include #include "pmodemi.h" #include "pmodeme.h" #define new PNEW /////////////////////////////////////////////////////////////// PseudoModemBody::PseudoModemBody(const PString &_tty, const PString &_route, const PNotifier &_callbackEndPoint) : PseudoModem(_tty), route(_route), callbackEndPoint(_callbackEndPoint), engine(NULL) { } PseudoModemBody::~PseudoModemBody() { PseudoModemBody::StopAll(); } PBoolean PseudoModemBody::IsReady() const { PWaitAndSignal mutexWait(Mutex); return engine && engine->IsReady(); } PBoolean PseudoModemBody::CheckRoute(const PString &number) const { return route.IsEmpty() || number.Find(route) == 0; } PBoolean PseudoModemBody::Request(PStringToString &request) const { PWaitAndSignal mutexWait(Mutex); return engine && engine->Request(request); } T38Engine *PseudoModemBody::NewPtrT38Engine() const { PWaitAndSignal mutexWait(Mutex); if (engine == NULL) return NULL; return engine->NewPtrT38Engine(); } AudioEngine *PseudoModemBody::NewPtrAudioEngine() const { PWaitAndSignal mutexWait(Mutex); if (engine == NULL) return NULL; return engine->NewPtrAudioEngine(); } EngineBase *PseudoModemBody::NewPtrUserInputEngine() const { PWaitAndSignal mutexWait(Mutex); if (engine == NULL) return NULL; return engine->NewPtrUserInputEngine(); } void PseudoModemBody::ToPtyQ(const void *buf, PINDEX count, PBoolean OutQ) { if( count == 0 ) return; PBYTEArrayQ &PtyQ = OutQ ? outPtyQ : inPtyQ; for( int delay = 10 ;; delay *= 2 ) { static const PINDEX MAX_qBUF = 1024*2; static const int MAX_delay = ((MAX_qBUF/2)*8*1000)/14400; PINDEX busy = PtyQ.GetCount(); if( busy < MAX_qBUF ) { PINDEX free = MAX_qBUF - busy; PINDEX len = count; if( len > free ) len = free; PtyQ.Enqueue(new PBYTEArray((const BYTE *)buf, len)); buf = (const BYTE *)buf + len; count -= len; } { PWaitAndSignal mutexWait(Mutex); ModemThreadChild *notify = OutQ ? GetPtyNotifier() : (ModemThreadChild *)engine; if (notify == NULL) { myPTRACE(1, "PseudoModemBody::ToPtyQ notify == NULL"); PtyQ.Clean(); return; } notify->SignalDataReady(); } if( count == 0 ) return; if (stop) break; if (delay > MAX_delay) { delay = MAX_delay; myPTRACE(2, "PseudoModemBody::ToPtyQ(" << (OutQ ? "outPtyQ" : "inPtyQ") << ")" << " busy=" << busy << " count=" << count << " delay=" << delay); } PThread::Sleep(delay); if( stop ) break; } } PBoolean PseudoModemBody::StartAll() { if (engine) return TRUE; if ((engine = new ModemEngine(*this)) != NULL) { engine->Resume(); return TRUE; } PseudoModemBody::StopAll(); return FALSE; } void PseudoModemBody::StopAll() { if (engine) { engine->SignalStop(); engine->WaitForTermination(); PWaitAndSignal mutexWait(Mutex); delete engine; engine = NULL; } outPtyQ.Clean(); inPtyQ.Clean(); childstop = FALSE; } PBoolean PseudoModemBody::AddModem() const { PStringToString request; request.SetAt("modemtoken", modemToken()); request.SetAt("command", "addmodem"); callbackEndPoint(request, 10); return request("response") == "confirm"; } void PseudoModemBody::Main() { RenameCurrentThread(ptyName() + "(b)"); myPTRACE(2, "Started for " << ttyPath() << " (accepts " << (route.IsEmpty() ? PString("all") : route) << ")"); MainLoop(); myPTRACE(2, "Stopped " << GetThreadTimes(", CPU usage: ")); } /////////////////////////////////////////////////////////////// t38modem-2.0.0/drv_pty.h0000664000076400007640000000527711061726064015701 0ustar frolovfrolov00000000000000/* * drv_pty.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: drv_pty.h,v $ * Revision 1.4 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.3 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.2 2004/07/19 08:31:06 vfrolov * Fixed "friend declaration requires class-key" * * Revision 1.1 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * * Log: pty.h,v * * Revision 1.3 2002/04/19 14:29:37 vfrolov * Added Copyright header * * Revision 1.2 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #ifndef _DRV_PTY_H #define _DRV_PTY_H #ifndef _WIN32 #define MODEM_DRIVER_Pty #endif #ifdef MODEM_DRIVER_Pty #include "pmodemi.h" /////////////////////////////////////////////////////////////// class InPty; class OutPty; class PseudoModemPty : public PseudoModemBody { PCLASSINFO(PseudoModemPty, PseudoModemBody); public: /**@name Construction */ //@{ PseudoModemPty( const PString &_tty, const PString &_route, const PConfigArgs &args, const PNotifier &_callbackEndPoint ); ~PseudoModemPty(); //@} /**@name static functions */ //@{ static PBoolean CheckTty(const PString &_tty); static PString ArgSpec(); static PStringArray Description(); //@} protected: /**@name Overrides from class PseudoModemBody */ //@{ const PString &ttyPath() const; ModemThreadChild *GetPtyNotifier(); PBoolean StartAll(); void StopAll(); void MainLoop(); //@} private: PBoolean OpenPty(); void ClosePty(); PBoolean IsOpenPty() const { return hPty >= 0; } int hPty; InPty *inPty; OutPty *outPty; PString ptypath; PString ttypath; friend class InPty; friend class OutPty; }; /////////////////////////////////////////////////////////////// #endif // MODEM_DRIVER_Pty #endif // _DRV_PTY_H t38modem-2.0.0/fcs.h0000664000076400007640000000243310014412662014745 0ustar frolovfrolov00000000000000/* * fcs.h * * T38FAX Pseudo Modem * * Copyright (c) 2003 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: fcs.h,v $ * Revision 1.2 2004/02/17 13:22:58 vfrolov * Fixed MSVC compile errors * * Revision 1.1 2003/12/04 13:38:26 vfrolov * Initial revision * * */ #ifndef _FCS_H #define _FCS_H #include "pmutils.h" /////////////////////////////////////////////////////////////// class FCS { public: FCS() : fcs(0xFFFF) {} void build(const void *pBuf, PINDEX count); operator WORD() const { return WORD(~fcs); } protected: DWORD fcs; }; /////////////////////////////////////////////////////////////// #endif // _FCS_H t38modem-2.0.0/opal/0000775000076400007640000000000011551247020014753 5ustar frolovfrolov00000000000000t38modem-2.0.0/opal/modemep.cxx0000664000076400007640000011373311525202063017134 0ustar frolovfrolov00000000000000/* * modemep.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2011 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: modemep.cxx,v $ * Revision 1.32 2011/02/11 09:41:07 vfrolov * Added more tracing * * Revision 1.31 2011/01/17 17:17:38 vfrolov * Disabled compiling with OPAL version != 3.9.0 (use SVN TRUNK 24174) * * Revision 1.30 2011/01/14 20:42:36 vfrolov * Added "srcname" to incoming call request * Thanks Dmitry (gorod225) * * Revision 1.29 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.28 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.27 2010/07/09 13:18:13 vfrolov * Fixed help message * * Revision 1.26 2010/07/09 07:12:36 vfrolov * Added "Trying alternate route ..." message * * Revision 1.25 2010/07/09 04:51:55 vfrolov * Implemented alternate route option (OPAL-Try-Next) * Added timeout option for SetUpPhase (OPAL-Set-Up-Phase-Timeout) * * Revision 1.24 2010/07/07 08:11:44 vfrolov * Fixed race condition with engine attaching * * Revision 1.23 2010/03/23 07:51:28 vfrolov * Added ability to disable auto fax mode forcing with OPAL-Force-Fax-Mode=false * * Revision 1.22 2010/03/19 08:36:05 vfrolov * Added forcing fax mode (to send CNG) if used fake audio encoder * * Revision 1.21 2010/03/15 13:40:28 vfrolov * Removed unused code * * Revision 1.20 2010/02/16 16:21:25 vfrolov * Added --force-fax-mode and --no-force-t38-mode options * * Revision 1.19 2010/02/02 08:41:56 vfrolov * Implemented ringing indication for voice class dialing * * Revision 1.18 2010/01/29 07:15:52 vfrolov * Fixed a "glare" condition with switcing to the fax passthrough * mode and receiving a T.38 mode request at the same time * * Revision 1.17 2010/01/13 09:59:19 vfrolov * Fixed incompatibility with OPAL trunk * Fixed incorrect codec selection for the incoming offer * * Revision 1.16 2009/12/23 17:50:57 vfrolov * Added missing calls to OnPatchMediaStream() * * Revision 1.15 2009/12/08 15:06:22 vfrolov * Fixed incompatibility with OPAL trunk * * Revision 1.14 2009/11/15 18:21:18 vfrolov * Replaced AutoStartMediaStreams() by more adequate code * * Revision 1.13 2009/11/11 18:05:00 vfrolov * Added ability to apply options for incoming connections * * Revision 1.12 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.11 2009/11/02 18:02:19 vfrolov * Removed pre v3.7 compatibility code * * Revision 1.10 2009/10/16 19:22:42 vfrolov * Fixed race condition * * Revision 1.9 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.8 2009/07/13 15:08:17 vfrolov * Ported to OPAL SVN trunk * * Revision 1.7 2009/07/08 18:50:52 vfrolov * Added ability to use route pattern "modem:.*@tty" * * Revision 1.6 2009/04/09 10:02:19 vfrolov * Fixed ignoring tone for H.323 because duration always is zero * * Revision 1.5 2009/04/08 14:34:28 vfrolov * Replaced SendUserInputString() by SendUserInputTone() for compatibility * with OPAL SVN trunk * * Revision 1.4 2009/01/14 16:35:55 vfrolov * Added Calling-Party-Number for SIP * * Revision 1.3 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.2 2007/07/20 14:30:25 vfrolov * Moved GetPartyName() to opalutils.cxx * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #include #include ///////////////////////////////////////////////////////////////////////////// #define PACK_VERSION(major, minor, build) (((((major) << 8) + (minor)) << 8) + (build)) #if !(PACK_VERSION(OPAL_MAJOR, OPAL_MINOR, OPAL_BUILD) == PACK_VERSION(3, 9, 0)) #error *** Uncompatible OPAL version (required == 3.9.0, use SVN TRUNK 24174) *** #endif #undef PACK_VERSION ///////////////////////////////////////////////////////////////////////////// #include #include "../enginebase.h" #include "../pmodem.h" #include "../drivers.h" #include "modemstrm.h" #include "modemep.h" #include "opalutils.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// class ModemConnection : public OpalConnection { PCLASSINFO(ModemConnection, OpalConnection); public: ModemConnection( OpalCall & call, ModemEndPoint & ep, const PString & token, const PString & remoteParty, void *userData, StringOptions * stringOptions ); ~ModemConnection(); virtual OpalMediaStream * CreateMediaStream( const OpalMediaFormat & mediaFormat, /// Media format for stream unsigned sessionID, /// Session number for stream PBoolean isSource /// Is a source stream ); virtual bool IsNetworkConnection() const { return true; } virtual void OnReleased(); virtual PBoolean SetUpConnection(); virtual PBoolean SetAlerting( const PString & calleeName, /// Name of endpoint being alerted. PBoolean withMedia /// Open media with alerting ); virtual PBoolean SetConnected(); virtual OpalMediaFormatList GetMediaFormats() const; virtual void OnConnected(); virtual void OnEstablished(); virtual void AcceptIncoming(); virtual PBoolean SendUserInputTone( char tone, ///< DTMF tone code unsigned duration = 0 ///< Duration of tone in milliseconds ); enum PseudoModemMode { pmmUnknown, pmmAny, pmmFax, pmmFaxNoForce, }; bool RequestMode( PseudoModemMode mode ); protected: bool UpdateMediaStreams(OpalConnection &other); PDECLARE_NOTIFIER(PThread, ModemConnection, RequestMode); const PNotifier requestMode; PseudoModem *pmodem; EngineBase *userInputEngine; PseudoModemMode requestedMode; bool isPartyA; PDECLARE_NOTIFIER(PTimer, ModemConnection, OnPhaseTimeout); PTimer phaseTimer; Phases phaseTimerPhase; bool phaseWasTimeout; }; ///////////////////////////////////////////////////////////////////////////// // // Implementation // ///////////////////////////////////////////////////////////////////////////// #if PTRACING static ostream & operator<<(ostream & out, ModemConnection::PseudoModemMode mode) { switch (mode) { case ModemConnection::pmmAny: return out << "any"; case ModemConnection::pmmFax: return out << "fax"; case ModemConnection::pmmFaxNoForce: return out << "fax-no-force"; default: return out << "unknown" << INT(mode); } } #endif ///////////////////////////////////////////////////////////////////////////// ModemEndPoint::ModemEndPoint(OpalManager & mgr, const char * prefix) : OpalEndPoint(mgr, prefix, CanTerminateCall) { myPTRACE(1, "ModemEndPoint::ModemEndPoint"); pmodem_pool = new PseudoModemQ(); } PString ModemEndPoint::ArgSpec() { return PseudoModemDrivers::ArgSpec() + "-no-modem." "p-ptty:" "-force-fax-mode." "-no-force-t38-mode." ; } PStringArray ModemEndPoint::Descriptions() { PStringArray descriptions = PString( "Modem options:\n" " --no-modem : Disable MODEM protocol.\n" " -p --ptty [num@]tty[,...] : Pseudo ttys. Can be used multiple times.\n" " If tty prefixed by num@ then tty will\n" " accept incoming calls only\n" " for numbers with prefix num.\n" " Use none@tty to disable incoming calls.\n" " See Modem drivers section for tty format.\n" " --force-fax-mode : Use OPAL-Force-Fax-Mode=true route option by\n" " default.\n" " --no-force-t38-mode : Use OPAL-No-Force-T38-Mode=true route option by\n" " default.\n" "Modem route options:\n" " OPAL-Set-Up-Phase-Timeout=secs\n" " Set timeout for outgoing call Set-Up phase to secs seconds.\n" " OPAL-Try-Next=dst\n" " Set alternate incoming destination address for outgoing calls to dst. This\n" " address will be used to re-route if outgoing call Set-Up phase fails.\n" " OPAL-Force-Fax-Mode={true|false}\n" " Enable or disable forcing fax mode (T.38 or G.711 pass-trough).\n" " OPAL-No-Force-T38-Mode={true|false}\n" " Not enable or not disable forcing T.38 mode.\n" "Modem drivers:\n" ).Lines(); PStringArray ds; ds = PseudoModemDrivers::Descriptions(); for (PINDEX i = 0 ; i < ds.GetSize() ; i++) descriptions.Append(new PString(PString(" ") + ds[i])); return descriptions; } PStringArray ModemEndPoint::Descriptions(const PConfigArgs & /*args*/) { PStringArray descriptions; return descriptions; } PBoolean ModemEndPoint::Create(OpalManager & mgr, const PConfigArgs & args) { if (args.HasOption("no-modem")) { cout << "Disabled MODEM protocol" << endl; return TRUE; } if ((new ModemEndPoint(mgr))->Initialise(args)) return TRUE; return FALSE; } PBoolean ModemEndPoint::Initialise(const PConfigArgs & args) { if (args.HasOption("ptty")) { PString tty = args.GetOptionString("ptty"); PStringArray ttys = tty.Tokenise(",\r\n ", FALSE); for( PINDEX i = 0 ; i < ttys.GetSize() ; i++ ) { tty = ttys[i]; PStringArray atty = tty.Tokenise("@", FALSE); PString r = ""; if (atty.GetSize() == 2) { r = atty[0]; tty = atty[1]; } else if (atty.GetSize() == 1) { tty = atty[0]; } if (!pmodem_pool->CreateModem(tty, r, args, PCREATE_NOTIFIER(OnMyCallback))) cerr << "Can't create modem for " << tty << endl; } } if (args.HasOption("force-fax-mode")) defaultStringOptions.SetAt("Force-Fax-Mode", "true"); if (args.HasOption("no-force-t38-mode")) defaultStringOptions.SetAt("No-Force-T38-Mode", "true"); return TRUE; } void ModemEndPoint::OnMyCallback(PObject &from, INT myPTRACE_PARAM(extra)) { if (PIsDescendant(&from, PStringToString) ) { PStringToString &request = (PStringToString &)from; PString command = request("command"); myPTRACE(1, "ModemEndPoint::OnMyCallback command=" << command << " extra=" << extra); PString modemToken = request("modemtoken"); PString response = "reject"; if (command == "dial") { PseudoModem *modem = pmodem_pool->Dequeue(modemToken); if (modem != NULL) { PString partyA = PString("modem:") + request("localpartyname"); PString partyB = request("number") + "@" + modem->ttyName(); PString originalPartyB; long tries = request("trynextcount").AsInteger(); if (tries++ > 0) { originalPartyB = request("originalpartyb"); cout << "Trying alternate route " << tries << " to " << partyB << " instead " << originalPartyB << endl; PTRACE(1, "Trying alternate route " << tries << " to " << partyB << " instead " << originalPartyB); } else { originalPartyB = partyB; } myPTRACE(1, "MyManager::OnMyCallback SetUpCall(" << partyA << ", " << partyB << ")"); PSafePtr call = GetManager().SetUpCall(partyA, partyB, modem); if (call != NULL) { PSafePtr pConn = call->GetConnection(0); if (pConn != NULL) { OpalConnection::StringOptions newOptions; newOptions.SetAt("Try-Next-Count", tries); newOptions.SetAt("Original-Party-B", originalPartyB); pConn->SetStringOptions(newOptions, false); request.SetAt("calltoken", pConn->GetToken()); response = "confirm"; modem = NULL; } } if (modem != NULL) pmodem_pool->Enqueue(modem); } } else if (command == "answer") { PSafePtr pConn = PSafePtrCast(GetConnectionWithLock(request("calltoken"), PSafeReference)); if (pConn != NULL) { pConn->AcceptIncoming(); response = "confirm"; } } else if (command == "requestmode") { PSafePtr pConn = PSafePtrCast(GetConnectionWithLock(request("calltoken"), PSafeReference)); if (pConn != NULL) { const PString &newModeString = request("mode"); ModemConnection::PseudoModemMode mode; if (newModeString == "fax") { mode = ModemConnection::pmmFax; } else if (newModeString == "fax-no-force") { mode = ModemConnection::pmmFaxNoForce; } else { mode = ModemConnection::pmmUnknown; } if (mode != ModemConnection::pmmUnknown) { if (pConn->RequestMode(mode)) response = "confirm"; } else { myPTRACE(1, "ModemEndPoint::OnMyCallback: unknown mode " << newModeString); } } } else if (command == "clearcall") { PSafePtr pConn = PSafePtrCast(GetConnectionWithLock(request("calltoken"), PSafeReference)); if (pConn != NULL) { pConn->ClearCall(); response = "confirm"; } } else if (command == "addmodem") { if (pmodem_pool->Enqueue(modemToken)) response = "confirm"; } request.SetAt("response", response); myPTRACE(1, "ModemEndPoint::OnMyCallback request={\n" << request << "}"); } else { myPTRACE(1, "ModemEndPoint::OnMyCallback unknown class " << from.GetClass() << " extra=" << extra); } } PseudoModem * ModemEndPoint::PMAlloc(const PString &number) const { return pmodem_pool->DequeueWithRoute(number); } void ModemEndPoint::PMFree(PseudoModem *pmodem) const { if (pmodem != NULL) pmodem_pool->Enqueue(pmodem); } PSafePtr ModemEndPoint::MakeConnection( OpalCall & call, const PString & remoteParty, void *userData, unsigned int /*options*/, OpalConnection::StringOptions * stringOptions ) { myPTRACE(1, "ModemEndPoint::MakeConnection " << remoteParty); OpalConnection::StringOptions localOptions; if (stringOptions == NULL) stringOptions = &localOptions; PINDEX iParams = remoteParty.Find(';'); PString remotePartyAddress = remoteParty.Left(iParams); if (iParams != P_MAX_INDEX) { PStringToString params; PURL::SplitVars(remoteParty(iParams + 1, P_MAX_INDEX), params, ';', '='); for (PINDEX i = 0; i < params.GetSize(); i++) { PCaselessString key = params.GetKeyAt(i); if (key.NumCompare("OPAL-") == EqualTo) { stringOptions->SetAt(key.Mid(5), params.GetDataAt(i)); } } } PWaitAndSignal wait(inUseFlag); PString token; for (int i = 0 ; i < 10000 ; i++) { token = remotePartyAddress + "/" + call.GetToken() + "/" + PString(i); if (!connectionsActive.Contains(token)) { ModemConnection * connection = new ModemConnection(call, *this, token, remotePartyAddress, userData, stringOptions); PTRACE(6, "ModemEndPoint::MakeConnection new " << connection->GetClass() << ' ' << (void *)connection); OpalConnection::StringOptions newOptions; for (PINDEX i = 0 ; i < defaultStringOptions.GetSize() ; i++) { if (!connection->GetStringOptions().Contains(defaultStringOptions.GetKeyAt(i))) newOptions.SetAt(defaultStringOptions.GetKeyAt(i), defaultStringOptions.GetDataAt(i)); } connection->SetStringOptions(newOptions, false); return AddConnection(connection); } } return AddConnection(NULL); } OpalMediaFormatList ModemEndPoint::GetMediaFormats() const { myPTRACE(1, "ModemEndPoint::GetMediaFormats"); OpalMediaFormatList formats; formats += OpalPCM16; formats += OpalT38; formats += OpalRFC2833; return formats; } ///////////////////////////////////////////////////////////////////////////// ModemConnection::ModemConnection( OpalCall & call, ModemEndPoint & ep, const PString & token, const PString & remoteParty, void *userData, StringOptions * stringOptions) : OpalConnection(call, ep, token, 0, stringOptions) #ifdef _MSC_VER #pragma warning(disable:4355) // warning C4355: 'this' : used in base member initializer list #endif , requestMode(PCREATE_NOTIFIER(RequestMode)) #ifdef _MSC_VER #pragma warning(default:4355) #endif , pmodem((PseudoModem *)userData) , userInputEngine(NULL) , requestedMode(pmmAny) , isPartyA(userData != NULL) , phaseTimerPhase(NumPhases) , phaseWasTimeout(false) { remotePartyNumber = GetPartyName(remoteParty); remotePartyAddress = remoteParty; myPTRACE(4, "ModemConnection::ModemConnection " << *this); phaseTimer.SetNotifier(PCREATE_NOTIFIER(OnPhaseTimeout)); } ModemConnection::~ModemConnection() { myPTRACE(4, "ModemConnection::~ModemConnection " << *this << " " << GetCallEndReason()); if (pmodem != NULL) { PseudoModem *pmodemTmp = pmodem; ((ModemEndPoint &)GetEndPoint()).PMFree(pmodem); pmodem = NULL; PStringToString request; request.SetAt("command", "clearcall"); request.SetAt("calltoken", GetToken()); if (isPartyA) { switch (GetCallEndReason()) { case EndedByLocalUser: if (!phaseWasTimeout) break; case EndedByQ931Cause: case EndedByConnectFail: case EndedByGatekeeper: case EndedByNoBandwidth: case EndedByCapabilityExchange: case EndedByCallForwarded: case EndedByNoEndPoint: case EndedByHostOffline: case EndedByUnreachable: case EndedByTransportFail: if (GetStringOptions().Contains("Try-Next")) { PString num = GetStringOptions()("Try-Next"); myPTRACE(1, "ModemConnection::~ModemConnection: Try-Next=" << num); request.SetAt("trynextcommand", "dial"); request.SetAt("number", num); request.SetAt("localpartyname", remotePartyNumber); request.SetAt("trynextcount", GetStringOptions().Contains("Try-Next-Count") ? GetStringOptions()("Try-Next-Count") : "1"); request.SetAt("originalpartyb", GetStringOptions().Contains("Original-Party-B") ? GetStringOptions()("Original-Party-B") : "unknown"); } break; default: break; } } if (!pmodemTmp->Request(request)) { myPTRACE(1, "ModemConnection::~ModemConnection error request={\n" << request << "}"); } } if (userInputEngine != NULL) ReferenceObject::DelPointer(userInputEngine); phaseTimer.Stop(); } void ModemConnection::OnPhaseTimeout(PTimer &, INT) { PTRACE(4, "ModemConnection::OnPhaseTimeout: for " << phaseTimerPhase << " on " << GetPhase()); if (phaseTimerPhase == GetPhase()) { PTRACE(4, "ModemConnection::OnPhaseTimeout: clearing call"); phaseWasTimeout = true; ClearCall(); } } OpalMediaStream * ModemConnection::CreateMediaStream( const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource) { myPTRACE(2, "ModemConnection::CreateMediaStream " << *this << " mediaFormat=" << mediaFormat << " sessionID=" << sessionID << " isSource=" << isSource); if (mediaFormat == OpalT38) { if (pmodem != NULL) { T38Engine *t38engine = pmodem->NewPtrT38Engine(); if (t38engine != NULL) return new T38ModemMediaStream(*this, sessionID, isSource, t38engine); } } else if (mediaFormat == OpalPCM16) { if (pmodem != NULL) { AudioEngine *audioEngine = pmodem->NewPtrAudioEngine(); if (audioEngine != NULL) return new AudioModemMediaStream(*this, sessionID, isSource, audioEngine); } } return OpalConnection::CreateMediaStream(mediaFormat, sessionID, isSource); } void ModemConnection::OnReleased() { myPTRACE(1, "ModemConnection::OnReleased " << *this); OpalConnection::OnReleased(); } PBoolean ModemConnection::SetUpConnection() { myPTRACE(1, "ModemConnection::SetUpConnection " << *this); SetPhase(SetUpPhase); if (GetCall().GetConnection(0) == this) { OpalConnection::StringOptions stringOptions; if (!remotePartyNumber.IsEmpty()) stringOptions.SetAt(OPAL_OPT_CALLING_PARTY_NUMBER, remotePartyNumber); if (!OnIncomingConnection(0, &stringOptions)) { Release(EndedByCallerAbort); return FALSE; } PTRACE(2, "Outgoing call routed to " << GetCall().GetPartyB() << " for " << *this); if (!GetCall().OnSetUp(*this)) { Release(EndedByNoAccept); return FALSE; } if (GetStringOptions().Contains("Set-Up-Phase-Timeout")) { long secs = GetStringOptions()("Set-Up-Phase-Timeout").AsInteger(); PTRACE(4, "ModemConnection::SetUpConnection: Set-Up-Phase-Timeout=" << secs); if (secs > 0) { phaseTimerPhase = SetUpPhase; phaseTimer.SetInterval(0, secs); } } return TRUE; } PString srcNum; PString srcName; { PSafePtr other = GetOtherPartyConnection(); if (other == NULL) return FALSE; srcNum = other->GetRemotePartyNumber(); srcName = other->GetRemotePartyName(); } PString dstNum = GetRemotePartyNumber(); myPTRACE(1, "ModemConnection::SetUpConnection" << " dstNum=" << dstNum << " srcNum=" << srcNum << " srcName=" << srcName << " ..."); ModemEndPoint &ep = (ModemEndPoint &)GetEndPoint(); PseudoModem *_pmodem = ep.PMAlloc(dstNum); if (_pmodem == NULL) { myPTRACE(1, "... denied (all modems busy)"); Release(EndedByLocalUser); return FALSE; } if (pmodem != NULL) { myPTRACE(1, "... denied (internal error)"); ep.PMFree(_pmodem); Release(EndedByLocalUser); return FALSE; } pmodem = _pmodem; PStringToString request; request.SetAt("command", "call"); request.SetAt("calltoken", GetToken()); request.SetAt("srcnum", srcNum); request.SetAt("srcname", srcName); request.SetAt("dstnum", dstNum); if (!pmodem->Request(request)) { myPTRACE(1, "... denied (modem is not ready)"); // or we can try other modem Release(EndedByLocalUser); return FALSE; } if (request("response") != "confirm") { myPTRACE(1, "... denied (no confirm)"); Release(EndedByLocalUser); return FALSE; } myPTRACE(1, "... Ok"); SetPhase(AlertingPhase); OnAlerting(); return TRUE; } PBoolean ModemConnection::SetAlerting( const PString & myPTRACE_PARAM(calleeName), PBoolean myPTRACE_PARAM(withMedia)) { myPTRACE(1, "ModemConnection::SetAlerting " << *this << " " << calleeName << " " << withMedia); SetPhase(AlertingPhase); PAssert(pmodem != NULL, "pmodem is NULL"); PStringToString request; request.SetAt("command", "alerting"); request.SetAt("calltoken", GetToken()); if (!pmodem->Request(request)) { myPTRACE(1, "ModemConnection::SetAlerting error request={\n" << request << "}"); } return TRUE; } PBoolean ModemConnection::SetConnected() { myPTRACE(1, "ModemConnection::SetConnected " << *this); return OpalConnection::SetConnected(); } OpalMediaFormatList ModemConnection::GetMediaFormats() const { PTRACE(4, "ModemConnection::GetMediaFormats " << *this); OpalMediaFormatList mediaFormats = endpoint.GetMediaFormats(); switch (requestedMode) { case pmmFax: for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { if (mediaFormats[i].GetMediaType() != OpalMediaType::Fax()) { PTRACE(3, "ModemConnection::GetMediaFormats Remove " << mediaFormats[i]); mediaFormats -= mediaFormats[i]; i--; } } break; default: break; } return mediaFormats; } void ModemConnection::OnConnected() { myPTRACE(1, "ModemConnection::OnConnected " << *this); OpalConnection::OnConnected(); } void ModemConnection::OnEstablished() { myPTRACE(1, "ModemConnection::OnEstablished " << *this); PAssert(pmodem != NULL, "pmodem is NULL"); PStringToString request; request.SetAt("command", "established"); request.SetAt("calltoken", GetToken()); if (!pmodem->Request(request)) { myPTRACE(1, "ModemConnection::OnEstablished error request={\n" << request << "}"); } OpalConnection::OnEstablished(); } void ModemConnection::AcceptIncoming() { myPTRACE(1, "ModemConnection::AcceptIncoming " << *this); SetPhase(ConnectedPhase); OnConnected(); } PBoolean ModemConnection::SendUserInputTone(char tone, unsigned PTRACE_PARAM(duration)) { PTRACE(4, "ModemConnection::SendUserInputTone " << tone << " " << duration); if (tone == ' ') return false; if (userInputEngine == NULL) { if (pmodem != NULL) userInputEngine = pmodem->NewPtrUserInputEngine(); if (userInputEngine == NULL) return false; } userInputEngine->WriteUserInput(tone); return true; } void ModemConnection::RequestMode(PThread &, INT faxMode) { PSafePtr other = GetOtherPartyConnection(); if (other != NULL) { if (!LockReadWrite()) { myPTRACE(1, "ModemConnection::RequestMode " << *this << " Can't lock"); return; } bool done = false; if (faxMode) { OpalMediaFormatList otherMediaFormats = other->GetMediaFormats(); other->AdjustMediaFormats(false, otherMediaFormats, NULL); PTRACE(4, "ModemConnection::RequestMode: other connection formats: \n" << setfill('\n') << otherMediaFormats << setfill(' ')); if (!otherMediaFormats.HasType(OpalMediaType::Fax())) { PTRACE(3, "ModemConnection::RequestMode: other connection has not fax type"); faxMode = false; done = UpdateMediaStreams(*other); } else if (GetStringOptions().GetBoolean("No-Force-T38-Mode")) { PTRACE(3, "ModemConnection::RequestMode: No-Force-T38-Mode=true"); faxMode = false; done = UpdateMediaStreams(*other); } } if (!done && !other->SwitchFaxMediaStreams(faxMode)) { myPTRACE(1, "ModemConnection::RequestMode " << *this << " Change to mode " << (faxMode ? "fax" : "audio") << " failed"); } UnlockReadWrite(); } } bool ModemConnection::RequestMode(PseudoModemMode mode) { myPTRACE(1, "ModemConnection::RequestMode: " << *this << " " << mode); PTRACE(4, "ModemConnection::RequestMode: options: \n" << setfill('\n') << GetStringOptions() << setfill(' ')); PseudoModemMode oldMode = requestedMode; requestedMode = mode; for (;;) { switch (requestedMode) { case pmmAny: break; case pmmFax: if (!GetStringOptions().GetBoolean("Force-Fax-Mode", true)) { PTRACE(3, "ModemConnection::RequestMode: Force-Fax-Mode=false"); requestedMode = mode; break; } PTRACE(3, "ModemConnection::RequestMode: force fax mode for other connection"); PThread::Create(requestMode, (INT)true); break; case pmmFaxNoForce: { PSafePtr other = GetOtherPartyConnection(); if (other != NULL) { OpalMediaFormatList otherMediaFormats = other->GetMediaFormats(); other->AdjustMediaFormats(false, otherMediaFormats, NULL); PTRACE(4, "ModemConnection::RequestMode: other connection formats: \n" << setfill('\n') << otherMediaFormats << setfill(' ')); if (!otherMediaFormats.HasType(OpalMediaType::Fax())) { PTRACE(3, "ModemConnection::RequestMode: other connection has not fax type"); requestedMode = pmmFax; continue; } if (GetStringOptions().GetBoolean("Force-Fax-Mode")) { PTRACE(3, "ModemConnection::RequestMode: Force-Fax-Mode=true"); requestedMode = pmmFax; continue; } bool force = false; OPAL_DEFINE_MEDIA_COMMAND(SearchForFakeTranscoder, "search_for_fake_transcoder"); static const SearchForFakeTranscoder cmdSearchForFakeTranscoder; for (OpalMediaStreamPtr stream = GetMediaStream(OpalMediaType::Audio(), true) ; stream != NULL ; stream = GetMediaStream(OpalMediaType::Audio(), true, stream)) { if (!stream->IsOpen()) continue; OpalMediaPatch *patch = stream->GetPatch(); if (patch == NULL) continue; OpalMediaStreamPtr otherStream = patch->GetSink(); if (otherStream == NULL) continue; if (!otherStream->IsOpen()) continue; if (!stream->ExecuteCommand(cmdSearchForFakeTranscoder)) { myPTRACE(4, "ModemConnection::RequestMode: found non-fake patch " << *patch); force = false; break; } myPTRACE(4, "ModemConnection::RequestMode: found fake patch " << *patch); force = true; } if (force) { myPTRACE(3, "ModemConnection::RequestMode: non-fake audio source not found"); requestedMode = pmmFax; continue; } } break; } default: myPTRACE(1, "ModemConnection::RequestMode: " << *this << " unknown mode " << requestedMode); requestedMode = oldMode; return false; } break; } return true; } static OpalMediaStreamPtr FindMediaStream( const OpalConnection &connection, const OpalMediaFormatList &allowedFormats, bool source) { for (OpalMediaStreamPtr stream = connection.GetMediaStream(OpalMediaType(), source) ; stream != NULL ; stream = connection.GetMediaStream(OpalMediaType(), source, stream)) { if (!stream->IsOpen()) continue; if (allowedFormats.HasFormat(stream->GetMediaFormat())) return stream; } return NULL; } static OpalMediaStreamPtr GetAllowedOtherStream( const OpalMediaStream &stream, const OpalMediaFormatList &allowedFormats) { OpalMediaPatch *patch = stream.GetPatch(); if (patch == NULL) return NULL; OpalMediaStreamPtr otherStream = (stream.IsSource()) ? patch->GetSink() : (OpalMediaStreamPtr)&patch->GetSource(); if (otherStream == NULL) return NULL; if (!otherStream->IsOpen()) return NULL; if (!allowedFormats.HasFormat(otherStream->GetMediaFormat())) return NULL; return otherStream; } static OpalMediaFormatList ReorderMediaFormats( const OpalMediaFormatList &mediaFormats, const OpalMediaFormat &mediaFormat) { OpalMediaFormatList formats = mediaFormats; PStringArray order; order += mediaFormat.GetName(); order += '@' + mediaFormat.GetMediaType(); formats.Reorder(order); return formats; } bool ModemConnection::UpdateMediaStreams(OpalConnection &other) { OpalMediaFormatList otherMediaFormats = other.GetMediaFormats(); other.AdjustMediaFormats(true, otherMediaFormats, NULL); OpalMediaFormatList thisMediaFormats = GetMediaFormats(); AdjustMediaFormats(true, thisMediaFormats, NULL); other.AdjustMediaFormats(true, thisMediaFormats, this); PTRACE(3, "ModemConnection::UpdateMediaStreams:\n" "patching " << setfill(',') << thisMediaFormats << setfill(' ') << "\n" "<------> " << setfill(',') << otherMediaFormats << setfill(' ')); if (!thisMediaFormats.HasType(OpalMediaType::Audio())) { for (PINDEX i = 0 ; i < otherMediaFormats.GetSize() ; i++) { if (otherMediaFormats[i] != OpalG711uLaw && otherMediaFormats[i] != OpalG711ALaw && otherMediaFormats[i].GetMediaType() != OpalMediaType::Fax()) { otherMediaFormats -= otherMediaFormats[i]; i--; } } } else { for (PINDEX i = 0 ; i < otherMediaFormats.GetSize() ; i++) { if (otherMediaFormats[i].GetMediaType() != OpalMediaType::Audio() && otherMediaFormats[i].GetMediaType() != OpalMediaType::Fax()) { otherMediaFormats -= otherMediaFormats[i]; i--; } } } PTRACE(4, "ModemConnection::UpdateMediaStreams:\n" "patching " << setfill(',') << thisMediaFormats << setfill(' ') << "\n" "<------> " << setfill(',') << otherMediaFormats << setfill(' ')); if (otherMediaFormats.GetSize() < 1) { PTRACE(2, "ModemConnection::UpdateMediaStreams: other connection has no capable media formats"); return false; } OpalMediaStreamPtr otherSink = FindMediaStream(other, otherMediaFormats, false); if (otherSink == NULL) { PTRACE(2, "ModemConnection::UpdateMediaStreams: other connection has no capable sink media streams"); return false; } unsigned otherSinkSessionID = otherSink->GetSessionID(); OpalMediaFormat otherSinkFormat = otherSink->GetMediaFormat(); PTRACE(3, "ModemConnection::UpdateMediaStreams: " "otherSink=" << *otherSink << " " "otherSinkFormat=" << otherSinkFormat << " " "otherSinkSessionID=" << otherSinkSessionID); OpalMediaStreamPtr otherSource = FindMediaStream(other, otherMediaFormats, true); if (otherSource == NULL) { PTRACE(2, "ModemConnection::UpdateMediaStreams: other connection has no capable source media streams"); return false; } OpalMediaFormat otherSourceFormat = otherSource->GetMediaFormat(); PTRACE(3, "ModemConnection::UpdateMediaStreams: " "otherSource=" << *otherSource << " " "otherSourceFormat=" << otherSourceFormat); OpalMediaStreamPtr thisSource = GetAllowedOtherStream(*otherSink, thisMediaFormats); OpalMediaFormat thisSourceFormat; if (thisSource != NULL) { PTRACE(4, "ModemConnection::UpdateMediaStreams: no need to replace " << *thisSource << " --> " << *otherSink); } else { if (!GetCall().SelectMediaFormats( otherSinkFormat.GetMediaType(), ReorderMediaFormats(thisMediaFormats, otherSinkFormat), otherSinkFormat, GetLocalMediaFormats(), thisSourceFormat, otherSinkFormat)) { PTRACE(3, "ModemConnection::UpdateMediaStreams: can't select source format for sink " << otherSink); return false; } PTRACE(3, "ModemConnection::UpdateMediaStreams: selected source format " << thisSourceFormat << " for sink " << otherSinkFormat); } OpalMediaStreamPtr thisSink = GetAllowedOtherStream(*otherSource, thisMediaFormats); OpalMediaFormat thisSinkFormat; if (thisSink != NULL) { PTRACE(4, "ModemConnection::UpdateMediaStreams: no need to replace " << *thisSink << " <-- " << *otherSource); } else { if (!GetCall().SelectMediaFormats( otherSourceFormat.GetMediaType(), otherSourceFormat, ReorderMediaFormats(thisMediaFormats, otherSourceFormat), GetLocalMediaFormats(), otherSourceFormat, thisSinkFormat)) { PTRACE(3, "ModemConnection::UpdateMediaStreams: can't select sink format for source " << otherSource); return false; } PTRACE(3, "ModemConnection::UpdateMediaStreams: selected sink format " << thisSinkFormat << " for source " << otherSourceFormat); } if (thisSource == NULL) { OpalMediaPatch *patch = otherSink->GetPatch(); if (patch != NULL) { otherSink->RemovePatch(patch); patch->GetSource().Close(); } PTRACE(4, "ModemConnection::UpdateMediaStreams: opening source for sink " << *otherSink); thisSource = OpenMediaStream(thisSourceFormat, otherSinkSessionID, true); if (thisSource == NULL) { PTRACE(3, "ModemConnection::UpdateMediaStreams: can't open source stream"); return false; } PTRACE(4, "ModemConnection::UpdateMediaStreams: creating patch for source " << *thisSource); patch = GetEndPoint().GetManager().CreateMediaPatch(*thisSource, otherSink->RequiresPatchThread(thisSource) && thisSource->RequiresPatchThread(otherSink)); if (patch == NULL) { PTRACE(3, "ModemConnection::UpdateMediaStreams: can't create patch for " << *thisSource); return false; } PTRACE(4, "ModemConnection::UpdateMediaStreams: adding otherSink to patch " << *patch); patch->AddSink(otherSink); other.OnPatchMediaStream(false, *patch); OnPatchMediaStream(true, *patch); PTRACE(3, "ModemConnection::UpdateMediaStreams: created patch " << *patch); } if (thisSink == NULL) { OpalMediaPatch *patch = otherSource->GetPatch(); if (patch != NULL) otherSource->RemovePatch(patch); // NOTE: Both sinks must have the same session ID for T.38 <-> PCM transcoding !!! PTRACE(4, "ModemConnection::UpdateMediaStreams: opening sink for source " << *otherSource); thisSink = OpenMediaStream(thisSinkFormat, otherSinkSessionID, false); if (thisSink == NULL) { PTRACE(3, "ModemConnection::UpdateMediaStreams: can't open sink stream"); return false; } PTRACE(4, "ModemConnection::UpdateMediaStreams: creating patch for source " << *otherSource); patch = GetEndPoint().GetManager().CreateMediaPatch(*otherSource, thisSink->RequiresPatchThread(otherSource) && otherSource->RequiresPatchThread(thisSink)); if (patch == NULL) { PTRACE(3, "ModemConnection::UpdateMediaStreams: can't create patch for " << *otherSource); return false; } PTRACE(4, "ModemConnection::UpdateMediaStreams: adding thisSink to patch " << *patch); patch->AddSink(thisSink); other.OnPatchMediaStream(true, *patch); OnPatchMediaStream(false, *patch); PTRACE(3, "ModemConnection::UpdateMediaStreams: created patch " << *patch); } StartMediaStreams(); PTRACE(3, "ModemConnection::UpdateMediaStreams: OK"); return true; } ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/opal/manager.cxx0000664000076400007640000002606111514777315017134 0ustar frolovfrolov00000000000000/* * manager.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2011 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: manager.cxx,v $ * Revision 1.16 2011/01/17 08:33:17 vfrolov * Added --displayname option * * Revision 1.15 2010/07/09 13:18:13 vfrolov * Fixed help message * * Revision 1.14 2010/07/08 11:40:18 vfrolov * Fixed route message for sip * Added call end reason to call cleared message * Added support for multiple per route * * Revision 1.13 2010/03/15 13:40:27 vfrolov * Removed unused code * * Revision 1.12 2010/02/24 14:20:10 vfrolov * Added variant of patch #2954967 "opal sip/h323 build-time detection" * Thanks Mariusz Mazur * * Revision 1.11 2010/02/12 08:55:07 vfrolov * Implemented fake codecs * * Revision 1.10 2009/12/23 17:53:00 vfrolov * Deprecated route comma delimiter * * Revision 1.9 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.8 2009/07/31 17:34:40 vfrolov * Removed --h323-old-asn and --sip-old-asn options * * Revision 1.7 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.6 2009/07/15 13:23:20 vfrolov * Added Descriptions(args) * * Revision 1.5 2009/01/26 15:25:36 vfrolov * Added --stun option * * Revision 1.4 2009/01/15 08:46:34 vfrolov * Fixed OnRouteConnection() be able to compile with OPAL trunk since 21925 * * Revision 1.3 2008/09/11 07:45:09 frolov * Fixed compiler warnings * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #include #include #include "../pmutils.h" #if OPAL_H323 #include "h323ep.h" #endif #if OPAL_SIP #include "sipep.h" #endif #include "modemep.h" #include "manager.h" #include "fake_codecs.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// MyManager::MyManager() { //autoStartTransmitFax = TRUE; } PString MyManager::ArgSpec() { return #if OPAL_H323 MyH323EndPoint::ArgSpec() + #endif #if OPAL_SIP MySIPEndPoint::ArgSpec() + #endif ModemEndPoint::ArgSpec() + "-ports:" "-route:" "u-username:" "-displayname:" "-stun:" "-fake-audio:" ; } PStringArray MyManager::Descriptions() { PStringArray descriptions = PString( "Common options:\n" " --ports T:B-M[,...] : For (T)ype set (B)ase and (M)ax ports to use.\n" " T is 'udp', 'rtp' or 'tcp'. B and M are numbers.\n" " --route pat=dst[;option[=value][;...]]\n" " : Route the calls with incoming destination address\n" " matching the regexp pat to the outgoing\n" " destination address dst.\n" " All '' meta-strings found in dst or in\n" " following route options will be replaced by all\n" " valid consecutive E.164 digits from the incoming\n" " destination address. To strip N first digits use\n" " '' meta-string.\n" " If the specification is of the form @filename,\n" " then the file is read with each line consisting\n" " of a pat=dst[;...] route specification.\n" " -u --username str : Set the default username to str.\n" " --displayname str : Set the default display name to str.\n" " Can be overriden by route option\n" " OPAL-" OPAL_OPT_CALLING_DISPLAY_NAME "=str\n" " --stun server : Set STUN server.\n" " --fake-audio [!]wildcard[,[!]...]\n" " : Register the fake audio format(s) matching the\n" " wildcard(s). The '*' character match any\n" " substring. The leading '!' character indicates\n" " a negative test.\n" " May be used multiple times.\n" ).Lines(); PStringArray arr[] = { #if OPAL_H323 MyH323EndPoint::Descriptions(), #endif #if OPAL_SIP MySIPEndPoint::Descriptions(), #endif ModemEndPoint::Descriptions(), }; for (PINDEX i = 0 ; i < PINDEX(sizeof(arr)/sizeof(arr[0])) ; i++) { if (arr[i].GetSize() > 0) { descriptions.Append(new PString("")); descriptions += arr[i]; } } return descriptions; } PStringArray MyManager::Descriptions(const PConfigArgs & args) { if (args.HasOption("fake-audio")) { PStringStream s; s << setfill(',') << args.GetOptionString("fake-audio").Lines(); FakeCodecs::RegisterFakeAudioFormats(s.Tokenise(",", FALSE)); } PStringArray descriptions; PBoolean first = TRUE; PStringArray arr[] = { #if OPAL_H323 MyH323EndPoint::Descriptions(args), #endif #if OPAL_SIP MySIPEndPoint::Descriptions(args), #endif ModemEndPoint::Descriptions(args), }; for (PINDEX i = 0 ; i < PINDEX(sizeof(arr)/sizeof(arr[0])) ; i++) { if (arr[i].GetSize() > 0) { if (!first) descriptions.Append(new PString("")); else first = FALSE; descriptions += arr[i]; } } return descriptions; } PBoolean MyManager::Initialise(const PConfigArgs & args) { DisableDetectInBandDTMF(TRUE); silenceDetectParams.m_mode = OpalSilenceDetector::NoSilenceDetection; if (args.HasOption("ports")) { PString p = args.GetOptionString("ports"); PStringArray ports = p.Tokenise(",\r\n", FALSE); for (PINDEX i = 0 ; i < ports.GetSize() ; i++) { p = ports[i]; PStringArray ps = p.Tokenise(":-", FALSE); if (ps.GetSize() == 3) { if (ps[0] == "udp") SetUDPPorts(ps[1].AsUnsigned(), ps[2].AsUnsigned()); else if (ps[0] == "rtp") SetRtpIpPorts(ps[1].AsUnsigned(), ps[2].AsUnsigned()); else if (ps[0] == "tcp") SetTCPPorts(ps[1].AsUnsigned(), ps[2].AsUnsigned()); } } PTRACE(1, "UDP ports: " << GetUDPPortBase() << "-" << GetUDPPortMax()); PTRACE(1, "RTP ports: " << GetRtpIpPortBase() << "-" << GetRtpIpPortMax()); PTRACE(1, "TCP ports: " << GetTCPPortBase() << "-" << GetTCPPortMax()); } SetDefaultUserName( args.HasOption("username") ? args.GetOptionString("username") : PProcess::Current().GetName() + " v" + PProcess::Current().GetVersion() ); if (args.HasOption("displayname")) SetDefaultDisplayName(args.GetOptionString("displayname")); if (args.HasOption("stun")) SetSTUNServer(args.GetOptionString("stun")); if (stun != NULL) { cout << "STUN server \"" << stun->GetServer() << "\" replies " << stun->GetNatTypeName(); PIPSocket::Address externalAddress; if (stun->GetExternalAddress(externalAddress)) cout << ", external IP " << externalAddress; cout << endl; } if (!ModemEndPoint::Create(*this, args)) return FALSE; #if OPAL_H323 if (!MyH323EndPoint::Create(*this, args)) return FALSE; #endif #if OPAL_SIP if (!MySIPEndPoint::Create(*this, args)) return FALSE; #endif if (args.HasOption("route")) { SetRouteTable(args.GetOptionString("route").Tokenise("\r\n", FALSE)); cout << "Route table:" << endl; const RouteTable &routeTable = GetRouteTable(); for (PINDEX i=0 ; i < routeTable.GetSize() ; i++) { cout << " " << routeTable[i].pattern << "=" << routeTable[i].destination << endl; } } return TRUE; } bool MyManager::OnRouteConnection(PStringSet & routesTried, const PString & a_party, const PString & b_party, OpalCall & call, unsigned options, OpalConnection::StringOptions * stringOptions) { const PString &token = call.GetToken(); if (!OpalManager::OnRouteConnection(routesTried, a_party, b_party, call, options, stringOptions)) { cout << "Call[" << token << "] from " << a_party << " to " << b_party << ", no route!" << endl; PTRACE(1, "Call[" << token << "] from " << a_party << " to " << b_party << ", no route!"); return false; } PString dst; PSafePtr dst_conn = call.GetConnection(1); if (dst_conn != NULL) { if (dst_conn->GetPrefixName().NumCompare("sip") == EqualTo) dst = dst_conn->GetRemotePartyURL(); else dst = dst_conn->GetRemotePartyAddress(); if (dst.NumCompare(dst_conn->GetPrefixName() + ":") != EqualTo) dst = dst_conn->GetPrefixName() + ":" + dst; } cout << "Call[" << token << "] from " << a_party << " to " << b_party << ", route to " << dst << endl; PTRACE(1, "Call[" << token << "] from " << a_party << " to " << b_party << ", route to " << dst); return true; } void MyManager::OnClearedCall(OpalCall & call) { cout << "Call[" << call.GetToken() << "] cleared (" << call.GetCallEndReasonText() << ")" << endl; PTRACE(1, "Call[" << call.GetToken() << "] cleared (" << call.GetCallEndReason() << ")"); OpalManager::OnClearedCall(call); } PBoolean MyManager::OnOpenMediaStream(OpalConnection & connection, OpalMediaStream & stream) { OpalCall &call = connection.GetCall(); cout << "Open " << stream << " for Call[" << call.GetToken() << "]" << endl; return OpalManager::OnOpenMediaStream(connection, stream); } void MyManager::OnClosedMediaStream(const OpalMediaStream & stream) { OpalCall &call = stream.GetConnection().GetCall(); cout << "Close " << stream << " for Call[" << call.GetToken() << "]" << endl; OpalManager::OnClosedMediaStream(stream); } PString MyManager::ApplyRouteTable(const PString & proto, const PString & addr, PINDEX & routeIndex) { PString destination = OpalManager::ApplyRouteTable(proto, addr, routeIndex); PINDEX pos = 0; while ((pos = destination.Find("') { PINDEX strip_num = (PINDEX)destination.Mid(pos + 4, strip_num_len).AsInteger(); destination.Splice(addr.Left((PINDEX)::strspn(addr, "0123456789*#")).Mid(strip_num), pos, 4 + strip_num_len + 1); } else { pos++; } } return destination; } ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/opal/modemep.h0000664000076400007640000000665711347434314016577 0ustar frolovfrolov00000000000000/* * modemep.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: modemep.h,v $ * Revision 1.8 2010/03/15 13:40:28 vfrolov * Removed unused code * * Revision 1.7 2010/02/16 16:21:25 vfrolov * Added --force-fax-mode and --no-force-t38-mode options * * Revision 1.6 2010/02/08 17:30:31 vfrolov * Disabled OPAL version < 3.8.0 * * Revision 1.5 2009/11/02 18:02:19 vfrolov * Removed pre v3.7 compatibility code * * Revision 1.4 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.3 2009/07/13 15:08:17 vfrolov * Ported to OPAL SVN trunk * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #ifndef _MODEM_EP_H #define _MODEM_EP_H ///////////////////////////////////////////////////////////////////////////// #define PACK_VERSION(major, minor, build) (((((major) << 8) + (minor)) << 8) + (build)) #if !(PACK_VERSION(OPAL_MAJOR, OPAL_MINOR, OPAL_BUILD) >= PACK_VERSION(3, 8, 0)) #error *** Uncompatible OPAL version (required >= 3.8.0) *** #endif #undef PACK_VERSION ///////////////////////////////////////////////////////////////////////////// #include ///////////////////////////////////////////////////////////////////////////// class PseudoModem; class PseudoModemQ; class ModemEndPoint : public OpalEndPoint { PCLASSINFO(ModemEndPoint, OpalEndPoint); public: /**@name Construction */ //@{ /**Create a new endpoint. */ ModemEndPoint( OpalManager & manager, ///< Manager of all endpoints. const char * prefix = "modem" ///< Prefix for URL style address strings ); //@} static PString ArgSpec(); static PStringArray Descriptions(); static PStringArray Descriptions(const PConfigArgs & args); static PBoolean Create(OpalManager & mgr, const PConfigArgs & args); PBoolean Initialise(const PConfigArgs & args); PseudoModem * PMAlloc(const PString &number) const; void PMFree(PseudoModem *pmodem) const; /**@name Overrides from OpalEndPoint */ //@{ virtual PSafePtr MakeConnection( OpalCall & call, ///< Owner of connection const PString & party, ///< Remote party to call void * userData = NULL, ///< Arbitrary data to pass to connection unsigned int options = 0, ///< Options to pass to conneciton OpalConnection::StringOptions * stringOptions = NULL ); virtual OpalMediaFormatList GetMediaFormats() const; //@} protected: PStringToString defaultStringOptions; PseudoModemQ *pmodem_pool; PDECLARE_NOTIFIER(PObject, ModemEndPoint, OnMyCallback); }; ///////////////////////////////////////////////////////////////////////////// #endif // _MODEM_EP_H t38modem-2.0.0/opal/opalutils.cxx0000664000076400007640000000255211061726064017525 0ustar frolovfrolov00000000000000/* * opalutils.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: opalutils.cxx,v $ * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/07/20 14:26:40 vfrolov * Initial revision * */ #include #include #include "opalutils.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// PString GetPartyName(const PString & party) { PINDEX beg = party.Find(':'); if (beg != P_MAX_INDEX) beg++; PINDEX end = party.Find('@'); if (end != P_MAX_INDEX) end--; return party(beg, end); } ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/opal/h323ep.h0000664000076400007640000000613711347434313016145 0ustar frolovfrolov00000000000000/* * h323ep.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: h323ep.h,v $ * Revision 1.7 2010/03/15 13:40:27 vfrolov * Removed unused code * * Revision 1.6 2010/01/21 16:05:33 vfrolov * Changed --h323-audio to accept multiple wildcards * Implemented OPAL-Enable-Audio route option * Renamed route option OPAL-H323-Bearer-Capability to OPAL-Bearer-Capability * * Revision 1.5 2009/12/23 17:54:24 vfrolov * Implemented --h323-bearer-capability option * * Revision 1.4 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.3 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #ifndef _MY_H323EP_H #define _MY_H323EP_H #include ///////////////////////////////////////////////////////////////////////////// // // myH323EndPoint // creates MyH323Connection // class OpalMediaFormat; class MyH323EndPoint : public H323EndPoint { PCLASSINFO(MyH323EndPoint, H323EndPoint); public: /**@name Construction */ //@{ /**Create a new endpoint. */ MyH323EndPoint( OpalManager & manager) : H323EndPoint(manager) {} //@} static PString ArgSpec(); static PStringArray Descriptions(); static PStringArray Descriptions(const PConfigArgs & args); static PBoolean Create(OpalManager & mgr, const PConfigArgs & args); PBoolean Initialise(const PConfigArgs & args); virtual H323Connection * CreateConnection( OpalCall & call, ///< Call object to attach the connection to const PString & token, ///< Call token for new connection void * userData, ///< Arbitrary user data from MakeConnection OpalTransport & transport, ///< Transport for connection const PString & alias, ///< Alias for outgoing call const H323TransportAddress & address, ///< Address for outgoing call H323SignalPDU * setupPDU, ///< Setup PDU for incoming call unsigned options = 0, OpalConnection::StringOptions * stringOptions = NULL ///< complex string options ); protected: PStringToString defaultStringOptions; }; ///////////////////////////////////////////////////////////////////////////// #endif // _MY_H323EP_H t38modem-2.0.0/opal/t38modem_2005.vcproj0000664000076400007640000002557711455110342020325 0ustar frolovfrolov00000000000000 t38modem-2.0.0/opal/sipep.cxx0000664000076400007640000004753411525202063016633 0ustar frolovfrolov00000000000000/* * sipep.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2011 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: sipep.cxx,v $ * Revision 1.29 2011/02/11 09:41:07 vfrolov * Added more tracing * * Revision 1.28 2011/01/19 11:41:17 vfrolov * Replaced deprecated ApplyStringOptions() by OnApplyStringOptions() * * Revision 1.27 2011/01/13 06:39:08 vfrolov * Disabled OPAL version < 3.9.0 * Added route options help topic * * Revision 1.26 2010/03/15 14:32:02 vfrolov * Added options * --sip-t38-udptl-redundancy * --sip-t38-udptl-keep-alive-interval * * Revision 1.25 2010/02/24 14:20:10 vfrolov * Added variant of patch #2954967 "opal sip/h323 build-time detection" * Thanks Mariusz Mazur * * Revision 1.24 2010/02/12 08:55:07 vfrolov * Implemented fake codecs * * Revision 1.23 2010/02/08 17:30:31 vfrolov * Disabled OPAL version < 3.8.0 * * Revision 1.22 2010/01/22 11:19:38 vfrolov * Added --sip-disable-t38-mode option * * Revision 1.21 2010/01/22 09:29:38 vfrolov * Added workaround to allow switching codecs to g711alaw if disabled g711ulaw * * Revision 1.20 2010/01/21 16:00:54 vfrolov * Changed --sip-audio to accept multiple wildcards * Implemented OPAL-Enable-Audio route option * * Revision 1.19 2010/01/21 08:28:09 vfrolov * Removed previously added workaround (now switching codecs fixed in OPAL) * * Revision 1.18 2010/01/15 11:53:31 vfrolov * Added workaround for switching codecs from non-G.711 to G.711 * * Revision 1.17 2010/01/13 09:59:19 vfrolov * Fixed incompatibility with OPAL trunk * Fixed incorrect codec selection for the incoming offer * * Revision 1.16 2010/01/11 14:26:49 vfrolov * Duplicated code moved to ApplyStringOptions() * * Revision 1.15 2009/12/09 13:27:22 vfrolov * Fixed Disable-T38-Mode * * Revision 1.14 2009/12/08 15:06:22 vfrolov * Fixed incompatibility with OPAL trunk * * Revision 1.13 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.12 2009/10/28 17:30:41 vfrolov * Fixed uncompatibility with OPAL trunk * * Revision 1.11 2009/10/06 17:13:10 vfrolov * Fixed uncompatibility with OPAL trunk * * Revision 1.10 2009/07/31 17:34:40 vfrolov * Removed --h323-old-asn and --sip-old-asn options * * Revision 1.9 2009/07/22 17:26:54 vfrolov * Added ability to enable other audio formats * * Revision 1.8 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.7 2009/07/15 18:25:53 vfrolov * Added reordering of formats * * Revision 1.6 2009/07/06 08:30:59 vfrolov * Fixed typo. Thanks Dmitry (gorod225) * * Revision 1.5 2009/05/29 13:01:40 vfrolov * Ported to OPAL trunk * * Revision 1.4 2009/04/07 12:49:18 vfrolov * Implemented --sip-proxy and --sip-register options * * Revision 1.3 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.2 2007/07/20 14:34:45 vfrolov * Added setting of calling number of an outgoing connection * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #include #include #if OPAL_SIP ///////////////////////////////////////////////////////////////////////////// #define PACK_VERSION(major, minor, build) (((((major) << 8) + (minor)) << 8) + (build)) #if !(PACK_VERSION(OPAL_MAJOR, OPAL_MINOR, OPAL_BUILD) >= PACK_VERSION(3, 9, 0)) #error *** Uncompatible OPAL version (required >= 3.9.0) *** #endif #undef PACK_VERSION ///////////////////////////////////////////////////////////////////////////// #include #include "sipep.h" #include "fake_codecs.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// class MySIPConnection : public SIPConnection { PCLASSINFO(MySIPConnection, SIPConnection); public: /**@name Construction */ //@{ MySIPConnection( OpalCall & call, ///< Owner call for connection SIPEndPoint & endpoint, ///< Owner endpoint for connection const PString & token, ///< token to identify the connection const SIPURL & address, ///< Destination address for outgoing call OpalTransport * transport, ///< Transport INVITE came in on unsigned int options = 0, ///< Connection options OpalConnection::StringOptions * stringOptions = NULL ///< complex string options ) : SIPConnection(call, endpoint, token, address, transport, options, stringOptions) , switchingToFaxMode(false) {} //@} virtual PBoolean SetUpConnection(); virtual void OnApplyStringOptions(); virtual bool SwitchFaxMediaStreams( bool enableFax ///< Enable FAX or return to audio mode ); virtual void OnSwitchedFaxMediaStreams( bool enabledFax ///< Enabled FAX or audio mode ); virtual PBoolean OnOpenMediaStream( OpalMediaStream & stream ///< New media stream being opened ); virtual OpalMediaFormatList GetMediaFormats() const; virtual OpalMediaFormatList GetLocalMediaFormats(); virtual void AdjustMediaFormats( bool local, ///< Media formats a local ones to be presented to remote OpalMediaFormatList & mediaFormats, ///< Media formats to use OpalConnection * otherConnection ///< Other connection we are adjusting media for ) const; protected: mutable OpalMediaFormatList mediaFormatList; bool switchingToFaxMode; }; ///////////////////////////////////////////////////////////////////////////// // // Implementation // ///////////////////////////////////////////////////////////////////////////// PString MySIPEndPoint::ArgSpec() { return "-no-sip." "-sip-audio:" "-sip-audio-list." "-sip-disable-t38-mode." "-sip-t38-udptl-redundancy:" "-sip-t38-udptl-keep-alive-interval:" "-sip-proxy:" "-sip-register:" "-sip-listen:" "-sip-no-listen." ; } PStringArray MySIPEndPoint::Descriptions() { PStringArray descriptions = PString( "SIP options:\n" " --no-sip : Disable SIP protocol.\n" " --sip-audio str : Use OPAL-Enable-Audio=str route option by\n" " default. May be used multiple times.\n" " --sip-audio-list : Display available audio formats.\n" " --sip-disable-t38-mode : Use OPAL-Disable-T38-Mode=true route option by\n" " default.\n" " --sip-t38-udptl-redundancy str\n" " : Use OPAL-T38-UDPTL-Redundancy=str route option by\n" " default.\n" " --sip-t38-udptl-keep-alive-interval ms\n" " : Use OPAL-T38-UDPTL-Keep-Alive-Interval=ms route\n" " option by default.\n" " --sip-proxy [user:[pwd]@]host\n" " : Proxy information.\n" " --sip-register [user@]registrar[,pwd[,contact[,realm[,authID]]]]\n" " : Registration information. Can be used multiple\n" " times.\n" " --sip-listen iface : Interface/port(s) to listen for SIP requests\n" " : '*' is all interfaces (default tcp$*:5060 and\n" " : udp$*:5060).\n" " --sip-no-listen : Disable listen for incoming calls.\n" "\n" "SIP route options:\n" " OPAL-Enable-Audio=[!]wildcard[,[!]...]\n" " Enable the audio format(s) matching the wildcard(s). The '*' character\n" " match any substring. The leading '!' character indicates a negative test.\n" " Default: " OPAL_G711_ULAW_64K "," OPAL_G711_ALAW_64K ".\n" " OPAL-Disable-T38-Mode={true|false}\n" " Enable or disable T.38 fax mode.\n" " Default: false (enable T.38 fax mode).\n" " OPAL-T38-UDPTL-Redundancy=[maxsize:redundancy[,maxsize:redundancy...]]\n" " Set error recovery redundancy for IFP packets dependent from their size.\n" " For example the string '2:I,9:L,32767:H' (where I, L and H are numbers)\n" " sets redundancy for (I)ndication, (L)ow speed and (H)igh speed IFP packets.\n" " Default: empty string (no redundancy).\n" " OPAL-T38-UDPTL-Redundancy-Interval=ms\n" " Continuously resend last UDPTL packet each ms milliseconds on idle till it\n" " contains IFP packets not sent redundancy times.\n" " Default: 50.\n" " OPAL-T38-UDPTL-Keep-Alive-Interval=ms\n" " Continuously resend last UDPTL packet each ms milliseconds on idle.\n" " Default: 0 (no resend).\n" " OPAL-T38-UDPTL-Optimise-On-Retransmit={true|false}\n" " Optimize UDPTL packets on resending in accordance with required redundancy\n" " (exclude redundancy IFP packets sent redundancy times).\n" " Default: true (optimize).\n" ).Lines(); return descriptions; } PStringArray MySIPEndPoint::Descriptions(const PConfigArgs & args) { PStringArray descriptions; if (args.HasOption("sip-audio-list")) descriptions += FakeCodecs::GetAvailableAudioFormatsDescription("SIP", "sip"); return descriptions; } PBoolean MySIPEndPoint::Create(OpalManager & mgr, const PConfigArgs & args) { if (args.HasOption("no-sip")) { cout << "Disabled SIP protocol" << endl; return TRUE; } if ((new MySIPEndPoint(mgr))->Initialise(args)) return TRUE; return FALSE; } PBoolean MySIPEndPoint::Initialise(const PConfigArgs & args) { if (args.HasOption("sip-audio")) { PStringStream s; s << setfill(',') << args.GetOptionString("sip-audio").Lines(); defaultStringOptions.SetAt("Enable-Audio", s); } if (args.HasOption("sip-disable-t38-mode")) defaultStringOptions.SetAt("Disable-T38-Mode", "true"); defaultStringOptions.SetAt("T38-UDPTL-Redundancy-Interval", "50"); defaultStringOptions.SetAt("T38-UDPTL-Optimise-On-Retransmit", "true"); defaultStringOptions.SetAt("T38-UDPTL-Redundancy", args.HasOption("sip-t38-udptl-redundancy") ? args.GetOptionString("sip-t38-udptl-redundancy") : ""); defaultStringOptions.SetAt("T38-UDPTL-Keep-Alive-Interval", args.HasOption("sip-t38-udptl-keep-alive-interval") ? args.GetOptionString("sip-t38-udptl-keep-alive-interval") : "0"); if (!args.HasOption("sip-no-listen")) { PStringArray listeners; if (args.HasOption("sip-listen")) listeners = args.GetOptionString("sip-listen").Lines(); else listeners = GetDefaultListeners(); if (!StartListeners(listeners)) { cerr << "Could not open any SIP listener from " << setfill(',') << listeners << endl; return FALSE; } cout << "Waiting for incoming SIP calls from " << setfill(',') << listeners << endl; } if (args.HasOption("sip-proxy")) SetProxy(args.GetOptionString("sip-proxy")); if (args.HasOption("sip-register")) { PString r = args.GetOptionString("sip-register"); PStringArray regs = r.Tokenise("\r\n", FALSE); for (PINDEX i = 0 ; i < regs.GetSize() ; i++) { PStringArray prms = regs[i].Tokenise(",", TRUE); PAssert(prms.GetSize() >= 1, "empty registration information"); if (prms.GetSize() >= 1) { SIPRegister::Params params; params.m_addressOfRecord = prms[0]; if (prms.GetSize() >= 2) { params.m_password = prms[1]; if (prms.GetSize() >= 3) { params.m_contactAddress = prms[2]; if (prms.GetSize() >= 4) { params.m_realm = prms[3]; if (prms.GetSize() >= 5) { params.m_authID = prms[4]; } } } } params.m_expire = 300; if (!Register(params, regs[i])) { cerr << "Could not start SIP registration to " << params.m_addressOfRecord << endl; return FALSE; } } } } return TRUE; } SIPConnection * MySIPEndPoint::CreateConnection( OpalCall & call, const PString & token, void * /*userData*/, const SIPURL & destination, OpalTransport * transport, SIP_PDU * /*invite*/, unsigned int options, OpalConnection::StringOptions * stringOptions) { PTRACE(2, "MySIPEndPoint::CreateConnection for " << call); MySIPConnection * connection = new MySIPConnection(call, *this, token, destination, transport, options, stringOptions); PTRACE(6, "MySIPEndPoint::CreateConnection new " << connection->GetClass() << ' ' << (void *)connection); OpalConnection::StringOptions newOptions; for (PINDEX i = 0 ; i < defaultStringOptions.GetSize() ; i++) { if (!connection->GetStringOptions().Contains(defaultStringOptions.GetKeyAt(i))) newOptions.SetAt(defaultStringOptions.GetKeyAt(i), defaultStringOptions.GetDataAt(i)); } connection->SetStringOptions(newOptions, false); return connection; } ///////////////////////////////////////////////////////////////////////////// PBoolean MySIPConnection::SetUpConnection() { PTRACE(2, "MySIPConnection::SetUpConnection " << *this << " name=" << GetLocalPartyName()); PSafePtr conn = GetCall().GetConnection(0); if (conn != NULL && conn != this) { // Set the calling number of an outgoing connection PString name = conn->GetRemotePartyNumber(); if (!name.IsEmpty() && name != "*") { SetLocalPartyName(name); PTRACE(1, "MySIPConnection::SetUpConnection new name=" << GetLocalPartyName()); } } return SIPConnection::SetUpConnection(); } void MySIPConnection::OnApplyStringOptions() { SIPConnection::OnApplyStringOptions(); if (LockReadWrite()) { mediaFormatList = OpalMediaFormatList(); if (GetStringOptions().Contains("Enable-Audio")) { const PStringArray wildcards = GetStringOptions()("Enable-Audio").Tokenise(",", FALSE); OpalMediaFormatList list = endpoint.GetMediaFormats(); for (PINDEX w = 0 ; w < wildcards.GetSize() ; w++) { OpalMediaFormatList::const_iterator f; while ((f = list.FindFormat(wildcards[w], f)) != list.end()) { if (f->GetMediaType() == OpalMediaType::Audio() && f->IsValidForProtocol("sip") && f->IsTransportable()) mediaFormatList += *f; if (++f == list.end()) break; } } } else { mediaFormatList += OpalG711_ULAW_64K; mediaFormatList += OpalG711_ALAW_64K; } if (GetStringOptions().GetBoolean("Disable-T38-Mode")) { PTRACE(3, "MySIPConnection::OnApplyStringOptions: Disable-T38-Mode=true"); } else { mediaFormatList += OpalT38; } mediaFormatList += OpalRFC2833; PTRACE(4, "MySIPConnection::OnApplyStringOptions Enabled formats (in preference order):\n" << setfill('\n') << mediaFormatList << setfill(' ')); UnlockReadWrite(); } } bool MySIPConnection::SwitchFaxMediaStreams(bool enableFax) { PTRACE(3, "MySIPConnection::SwitchFaxMediaStreams: " << (enableFax ? "fax" : "audio")); bool res = false; if (!enableFax) { OpalMediaFormatList::iterator i = mediaFormatList.begin(); while (i != mediaFormatList.end()) { if (i->GetMediaType() != OpalMediaType::Audio() || *i == OpalG711_ULAW_64K || *i == OpalG711_ALAW_64K) ++i; else mediaFormatList -= *i++; } } OpalMediaFormatList mediaFormats = GetMediaFormats(); AdjustMediaFormats(true, mediaFormats, NULL); PTRACE(3, "MySIPConnection::SwitchFaxMediaStreams:\n" << setfill('\n') << mediaFormats << setfill(' ')); const OpalMediaType &mediaType = enableFax ? OpalMediaType::Fax() : OpalMediaType::Audio(); for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { if (mediaFormats[i].GetMediaType() == mediaType) { res = SIPConnection::SwitchFaxMediaStreams(switchingToFaxMode = enableFax); break; } } PTRACE(3, "MySIPConnection::SwitchFaxMediaStreams: " << (res ? "OK" : "FAIL")); return res; } void MySIPConnection::OnSwitchedFaxMediaStreams(bool enabledFax) { PTRACE(3, "MySIPConnection::OnSwitchedFaxMediaStreams: " << (enabledFax == switchingToFaxMode ? "" : "NOT ") << "switched to " << (switchingToFaxMode ? "fax" : "audio")); SIPConnection::OnSwitchedFaxMediaStreams(enabledFax); if (switchingToFaxMode && !enabledFax) { PTRACE(3, "MySIPConnection::OnSwitchedFaxMediaStreams: fallback to audio"); mediaFormatList -= OpalT38; SwitchFaxMediaStreams(false); } } PBoolean MySIPConnection::OnOpenMediaStream(OpalMediaStream & stream) { PTRACE(4, "MySIPConnection::OnOpenMediaStream: " << stream); RTP_Session *session = GetSession(stream.GetSessionID()); if (session) RTP_Session::EncodingLock(*session)->ApplyStringOptions(GetStringOptions()); return SIPConnection::OnOpenMediaStream(stream); } OpalMediaFormatList MySIPConnection::GetMediaFormats() const { OpalMediaFormatList mediaFormats = SIPConnection::GetMediaFormats(); PTRACE(4, "MySIPConnection::GetMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { PBoolean found = FALSE; for (PINDEX j = 0 ; j < mediaFormatList.GetSize() ; j++) { if (mediaFormats[i] == mediaFormatList[j]) { found = TRUE; break; } } if (!found) { PTRACE(3, "MySIPConnection::GetMediaFormats Remove " << mediaFormats[i]); mediaFormats -= mediaFormats[i]; i--; } } PTRACE(4, "MySIPConnection::GetMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); return mediaFormats; } OpalMediaFormatList MySIPConnection::GetLocalMediaFormats() { OpalMediaFormatList mediaFormats = SIPConnection::GetLocalMediaFormats(); PTRACE(4, "MySIPConnection::GetLocalMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { PBoolean found = FALSE; for (PINDEX j = 0 ; j < mediaFormatList.GetSize() ; j++) { if (mediaFormats[i] == mediaFormatList[j]) { found = TRUE; break; } } if (!found) { PTRACE(3, "MySIPConnection::GetLocalMediaFormats Remove " << mediaFormats[i]); mediaFormats -= mediaFormats[i]; i--; } } PTRACE(4, "MySIPConnection::GetLocalMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); return mediaFormats; } void MySIPConnection::AdjustMediaFormats( bool local, OpalMediaFormatList & mediaFormats, OpalConnection * otherConnection) const { PTRACE(4, "MySIPConnection::AdjustMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); SIPConnection::AdjustMediaFormats(local, mediaFormats, otherConnection); if (local) { PStringArray order; for (PINDEX j = 0 ; j < mediaFormatList.GetSize() ; j++) order += mediaFormatList[j].GetName(); mediaFormats.Reorder(order); PTRACE(4, "MySIPConnection::AdjustMediaFormats: reordered"); } PTRACE(4, "MySIPConnection::AdjustMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); } ///////////////////////////////////////////////////////////////////////////// #endif // OPAL_SIP ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/opal/modemstrm.cxx0000664000076400007640000002335611453124673017530 0ustar frolovfrolov00000000000000/* * modemstrm.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: modemstrm.cxx,v $ * Revision 1.12 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.11 2010/03/15 13:46:46 vfrolov * Added resetting prepare packet timeout on start media patch for fax sink * * Revision 1.10 2010/01/14 18:32:51 vfrolov * Added ignoring packets with mismatched payload type and fake packets * * Revision 1.9 2009/12/08 15:06:22 vfrolov * Fixed incompatibility with OPAL trunk * * Revision 1.8 2009/11/26 07:24:22 vfrolov * Added adjusting sequence numbers * * Revision 1.7 2009/11/20 16:37:27 vfrolov * Fixed audio class application blocking by forced T.38 mode * * Revision 1.6 2009/11/10 08:13:38 vfrolov * Fixed race condition on re-opening T38Engine * * Revision 1.5 2009/10/27 19:03:50 vfrolov * Added ability to re-open T38Engine * Added ability to prepare IFP packets with adaptive delay/period * * Revision 1.4 2009/07/31 17:34:40 vfrolov * Removed --h323-old-asn and --sip-old-asn options * * Revision 1.3 2009/07/27 16:21:24 vfrolov * Moved h323lib specific code to h323lib directory * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #include #include #include #include #include "../audio.h" #include "../t38engine.h" #include "modemstrm.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// AudioModemMediaStream::AudioModemMediaStream( OpalConnection & conn, unsigned sessionID, PBoolean isSource, AudioEngine *engine) : OpalMediaStream(conn, OpalPCM16, sessionID, isSource) , audioEngine(engine) { PTRACE(4, "AudioModemMediaStream::AudioModemMediaStream " << *this); PAssert(audioEngine != NULL, "audioEngine is NULL"); } AudioModemMediaStream::~AudioModemMediaStream() { ReferenceObject::DelPointer(audioEngine); } PBoolean AudioModemMediaStream::Open() { if (isOpen) return TRUE; PTRACE(3, "AudioModemMediaStream::Open " << *this); if (IsSink()) audioEngine->OpenIn(EngineBase::HOWNERIN(this)); else audioEngine->OpenOut(EngineBase::HOWNEROUT(this)); return OpalMediaStream::Open(); } PBoolean AudioModemMediaStream::Close() { if (isOpen) { PTRACE(3, "AudioModemMediaStream::Close " << *this); if (IsSink()) audioEngine->CloseIn(EngineBase::HOWNERIN(this)); else audioEngine->CloseOut(EngineBase::HOWNEROUT(this)); } return OpalMediaStream::Close(); } PBoolean AudioModemMediaStream::ReadData(BYTE * data, PINDEX size, PINDEX & length) { if (!isOpen || !audioEngine->Read(EngineBase::HOWNEROUT(this), data, size)) { length = 0; return false; } length = size; return true; } PBoolean AudioModemMediaStream::WriteData(const BYTE * data, PINDEX length, PINDEX & written) { if (!isOpen || !audioEngine->Write(EngineBase::HOWNERIN(this), data, length)) { written = 0; return false; } written = length; return true; } ///////////////////////////////////////////////////////////////////////////// T38ModemMediaStream::T38ModemMediaStream( OpalConnection & conn, unsigned sessionID, PBoolean isSource, T38Engine *engine) : OpalMediaStream(conn, OpalT38, sessionID, isSource) , t38engine(engine) { PTRACE(4, "T38ModemMediaStream::T38ModemMediaStream " << *this); PAssert(t38engine != NULL, "t38engine is NULL"); PTRACE(4, "T38ModemMediaStream::T38ModemMediaStream DataSize=" << GetDataSize()); } T38ModemMediaStream::~T38ModemMediaStream() { ReferenceObject::DelPointer(t38engine); } PBoolean T38ModemMediaStream::Open() { if (isOpen) return TRUE; PTRACE(3, "T38ModemMediaStream::Open " << *this); currentSequenceNumber = 0; #if PTRACING totallost = 0; #endif if (IsSink()) t38engine->OpenIn(EngineBase::HOWNERIN(this)); else t38engine->OpenOut(EngineBase::HOWNEROUT(this)); return OpalMediaStream::Open(); } PBoolean T38ModemMediaStream::Close() { if (isOpen) { PTRACE(3, "T38ModemMediaStream::Close " << *this); if (IsSink()) { PTRACE(2, "T38ModemMediaStream::Close Send statistics:" " sequence=" << currentSequenceNumber << " lost=" << totallost); t38engine->CloseIn(EngineBase::HOWNERIN(this)); } else { PTRACE(2, "T38ModemMediaStream::Close Receive statistics:" " sequence=" << currentSequenceNumber); t38engine->CloseOut(EngineBase::HOWNEROUT(this)); } } return OpalMediaStream::Close(); } void T38ModemMediaStream::OnStartMediaPatch() { if (isSource) { if (mediaPatch != NULL) { OpalMediaStreamPtr sink = mediaPatch->GetSink(); if (sink != NULL) { OpalMediaFormat format = sink->GetMediaFormat(); if (format.IsValid()) { if (format.GetMediaType() != OpalMediaType::Fax()) { myPTRACE(3, "T38ModemMediaStream::OnStartMediaPatch: use timeout=0, period=20 for sink " << *sink); t38engine->SetPreparePacketTimeout(EngineBase::HOWNEROUT(this), 0, 20); } else { myPTRACE(3, "T38ModemMediaStream::OnStartMediaPatch: use timeout=-1 for sink " << *sink); t38engine->SetPreparePacketTimeout(EngineBase::HOWNEROUT(this), -1); } } else { myPTRACE(1, "T38ModemMediaStream::OnStartMediaPatch: format is invalid !!!"); } } else { myPTRACE(1, "T38ModemMediaStream::OnStartMediaPatch: sink is NULL !!!"); } } else { myPTRACE(1, "T38ModemMediaStream::OnStartMediaPatch: mediaPatch is NULL !!!"); } } } PBoolean T38ModemMediaStream::ReadPacket(RTP_DataFrame & packet) { if (!isOpen) return FALSE; T38_IFP ifp; int res; packet.SetTimestamp(timestamp); timestamp += 160; do { //PTRACE(4, "T38ModemMediaStream::ReadPacket ..."); res = t38engine->PreparePacket(EngineBase::HOWNEROUT(this), ifp); } while (currentSequenceNumber == 0 && res < 0); packet[0] = 0x80; packet.SetPayloadType(mediaFormat.GetPayloadType()); if (res > 0) { PTRACE(4, "T38ModemMediaStream::ReadPacket ifp = " << setprecision(2) << ifp); PASN_OctetString ifp_packet; ifp_packet.EncodeSubType(ifp); packet.SetPayloadSize(ifp_packet.GetDataLength()); memcpy(packet.GetPayloadPtr(), ifp_packet.GetPointer(), ifp_packet.GetDataLength()); packet.SetSequenceNumber(WORD(currentSequenceNumber++ & 0xFFFF)); } else if (res < 0) { // send a "repeated" packet with a "fake" payload of one byte of 0xFF //packet.SetPayloadSize(1); //packet.GetPayloadPtr()[0] = 0xFF; packet.SetPayloadSize(0); packet.SetSequenceNumber(WORD((currentSequenceNumber - 1) & 0xFFFF)); } else { return FALSE; } PTRACE(5, "T38ModemMediaStream::ReadPacket" " packet " << packet.GetSequenceNumber() << " size=" << packet.GetPayloadSize() << " type=" << packet.GetPayloadType() << " ts=" << packet.GetTimestamp()); return TRUE; } PBoolean T38ModemMediaStream::WritePacket(RTP_DataFrame & packet) { if (!isOpen) return FALSE; PTRACE(5, "T38ModemMediaStream::WritePacket " " packet " << packet.GetSequenceNumber() << " size=" << packet.GetPayloadSize() << " " << packet.GetPayloadType()); if (mediaFormat.GetPayloadType() != packet.GetPayloadType()) { PTRACE(5, "T38ModemMediaStream::WritePacket: ignored packet with mismatched payload type"); return TRUE; } long packedSequenceNumber = (packet.GetSequenceNumber() & 0xFFFF) + (currentSequenceNumber & ~0xFFFFL); long lost = packedSequenceNumber - currentSequenceNumber; if (lost < -0x10000L/2) { lost += 0x10000L; packedSequenceNumber += 0x10000L; } else if (lost > 0x10000L/2) { lost -= 0x10000L; packedSequenceNumber -= 0x10000L; } if (lost < 0) { PTRACE(lost == -1 ? 5 : 3, "T38ModemMediaStream::WritePacket: " << (packet.GetPayloadSize() == 0 ? "Fake" : "Repeated") << " packet " << packedSequenceNumber << " (expected " << currentSequenceNumber << ")"); if (lost > -10) return TRUE; } if (packet.GetPayloadSize() == 0) { PTRACE(5, "T38ModemMediaStream::WritePacket: ignored fake packet"); return TRUE; } PASN_OctetString ifp_packet((const char *)packet.GetPayloadPtr(), packet.GetPayloadSize()); T38_IFP ifp; if (!ifp_packet.DecodeSubType(ifp)) { PTRACE(2, "T38ModemMediaStream::WritePacket " T38_IFP_NAME " decode failure: " << PRTHEX(PBYTEArray(ifp_packet)) << "\n ifp = " << setprecision(2) << ifp); return TRUE; } if (lost != 0) { if (lost < 0 || lost > 10) lost = 1; #if PTRACING totallost += lost; #endif if (!t38engine->HandlePacketLost(EngineBase::HOWNERIN(this), lost)) return FALSE; PTRACE(3, "T38ModemMediaStream::WritePacket: adjusting sequence number to " << packedSequenceNumber); } currentSequenceNumber = packedSequenceNumber + 1; return t38engine->HandlePacket(EngineBase::HOWNERIN(this), ifp); } ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/opal/t38modem_2005.sln0000664000076400007640000000205011443412262017576 0ustar frolovfrolov00000000000000Microsoft Visual Studio Solution File, Format Version 9.00 # Visual C++ Express 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "t38modem", "t38modem_2005.vcproj", "{5684EA1E-9DEC-4701-A81E-FBF8133A77B5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 No Trace|Win32 = No Trace|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Debug|Win32.ActiveCfg = Debug|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Debug|Win32.Build.0 = Debug|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.No Trace|Win32.ActiveCfg = No Trace|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.No Trace|Win32.Build.0 = No Trace|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Release|Win32.ActiveCfg = Release|Win32 {5684EA1E-9DEC-4701-A81E-FBF8133A77B5}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal t38modem-2.0.0/opal/manager.h0000664000076400007640000000510711347434313016547 0ustar frolovfrolov00000000000000/* * manager.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: manager.h,v $ * Revision 1.6 2010/03/15 13:40:27 vfrolov * Removed unused code * * Revision 1.5 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.4 2009/07/15 13:23:20 vfrolov * Added Descriptions(args) * * Revision 1.3 2009/01/15 08:46:34 vfrolov * Fixed OnRouteConnection() be able to compile with OPAL trunk since 21925 * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #ifndef _PM_MANAGER_H #define _PM_MANAGER_H #include ///////////////////////////////////////////////////////////////////////////// class MyManager : public OpalManager { PCLASSINFO(MyManager, OpalManager); public: MyManager(); static PString ArgSpec(); static PStringArray Descriptions(); static PStringArray Descriptions(const PConfigArgs & args); PBoolean Initialise(const PConfigArgs & args); virtual bool OnRouteConnection( PStringSet & routesTried, ///< Set of routes already tried const PString & a_party, ///< Source local address const PString & b_party, ///< Destination indicated by source OpalCall & call, ///< Call for new connection unsigned options, ///< Options for new connection (can't use default as overrides will fail) OpalConnection::StringOptions * stringOptions ); virtual void OnClearedCall(OpalCall & call); virtual PBoolean OnOpenMediaStream(OpalConnection & connection, OpalMediaStream & stream); virtual void OnClosedMediaStream(const OpalMediaStream & stream); virtual PString ApplyRouteTable(const PString & proto, const PString & addr, PINDEX & entry); }; ///////////////////////////////////////////////////////////////////////////// #endif // _PM_MANAGER_H t38modem-2.0.0/opal/sipep.h0000664000076400007640000000537411347434314016264 0ustar frolovfrolov00000000000000/* * sipep.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: sipep.h,v $ * Revision 1.6 2010/03/15 13:40:28 vfrolov * Removed unused code * * Revision 1.5 2010/01/21 16:00:55 vfrolov * Changed --sip-audio to accept multiple wildcards * Implemented OPAL-Enable-Audio route option * * Revision 1.4 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.3 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #ifndef _MY_SIPEP_H #define _MY_SIPEP_H #include ///////////////////////////////////////////////////////////////////////////// class MySIPEndPoint : public SIPEndPoint { PCLASSINFO(MySIPEndPoint, SIPEndPoint); public: /**@name Construction */ //@{ MySIPEndPoint( OpalManager & manager ) : SIPEndPoint(manager) {} //@} static PString ArgSpec(); static PStringArray Descriptions(); static PStringArray Descriptions(const PConfigArgs & args); static PBoolean Create(OpalManager & mgr, const PConfigArgs & args); PBoolean Initialise(const PConfigArgs & args); virtual SIPConnection * CreateConnection( OpalCall & call, ///< Owner of connection const PString & token, ///< token used to identify connection void * userData, ///< User data for connection const SIPURL & destination, ///< Destination for outgoing call OpalTransport * transport, ///< Transport INVITE has been received on SIP_PDU * invite, ///< Original INVITE pdu unsigned int options = 0, ///< connection options OpalConnection::StringOptions * stringOptions = NULL ///< complex string options ); protected: PStringToString defaultStringOptions; }; ///////////////////////////////////////////////////////////////////////////// #endif // _MY_SIPEP_H t38modem-2.0.0/opal/fake_codecs.h0000664000076400007640000000261311335207472017363 0ustar frolovfrolov00000000000000/* * fake_codecs.h * * T38FAX Pseudo Modem * * Copyright (c) 2010 Vyacheslav Frolov * * t38modem Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is t38modem. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: fake_codecs.h,v $ * Revision 1.1 2010/02/12 08:20:10 vfrolov * Initial revision * */ #ifndef _FAKE_CODECS_H #define _FAKE_CODECS_H ///////////////////////////////////////////////////////////////////////////// namespace FakeCodecs { ///////////////////////////////////////////////////////////////////////////// PStringArray GetAvailableAudioFormatsDescription(const char *name, const char *protocol); void RegisterFakeAudioFormats(const PStringArray &wildcards); ///////////////////////////////////////////////////////////////////////////// } // namespace FakeCodecs ///////////////////////////////////////////////////////////////////////////// #endif // _FAKE_CODECS_H t38modem-2.0.0/opal/.cvsignore0000664000076400007640000000007011062150404016744 0ustar frolovfrolov00000000000000obj_* Debug Release NoTrace *.ncb *.vcproj.*.user *.suo t38modem-2.0.0/opal/opalutils.h0000664000076400007640000000170110650143040017133 0ustar frolovfrolov00000000000000/* * opalutils.h * * T38FAX Pseudo Modem * * Copyright (c) 2007 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: opalutils.h,v $ * Revision 1.1 2007/07/20 14:26:40 vfrolov * Initial revision * * */ #ifndef _OPALUTILS_H #define _OPALUTILS_H extern PString GetPartyName(const PString & party); #endif // _OPALUTILS_H t38modem-2.0.0/opal/modemstrm.h0000664000076400007640000000752111453124673017151 0ustar frolovfrolov00000000000000/* * modemstrm.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: modemstrm.h,v $ * Revision 1.6 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.5 2009/12/08 15:06:22 vfrolov * Fixed incompatibility with OPAL trunk * * Revision 1.4 2009/11/20 16:37:27 vfrolov * Fixed audio class application blocking by forced T.38 mode * * Revision 1.3 2009/10/27 19:03:50 vfrolov * Added ability to re-open T38Engine * Added ability to prepare IFP packets with adaptive delay/period * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #ifndef _MY_MODEM_MEDIA_STREAM_H #define _MY_MODEM_MEDIA_STREAM_H #include ///////////////////////////////////////////////////////////////////////////// class AudioEngine; class AudioModemMediaStream : public OpalMediaStream { PCLASSINFO(AudioModemMediaStream, OpalMediaStream); public: /**@name Construction */ //@{ /**Construct a new media stream. */ AudioModemMediaStream( OpalConnection & conn, unsigned sessionID, ///< Session number for stream PBoolean isSource, ///< Is a source stream AudioEngine *engine ); ~AudioModemMediaStream(); //@} /**@name Overrides of OpalRawMediaStream class */ //@{ virtual PBoolean Open(); virtual PBoolean Close(); virtual PBoolean ReadData( BYTE * data, ///< Data buffer to read to PINDEX size, ///< Size of buffer PINDEX & length ///< Length of data actually read ); virtual PBoolean WriteData( const BYTE * data, ///< Data to write PINDEX length, ///< Length of data to read. PINDEX & written ///< Length of data actually written ); virtual PBoolean IsSynchronous() const { return FALSE; } //@} protected: AudioEngine *audioEngine; }; ///////////////////////////////////////////////////////////////////////////// class T38Engine; class T38ModemMediaStream : public OpalMediaStream { PCLASSINFO(T38ModemMediaStream, OpalMediaStream); public: /**@name Construction */ //@{ /**Construct a new media stream. */ T38ModemMediaStream( OpalConnection & conn, unsigned sessionID, ///< Session number for stream PBoolean isSource, ///< Is a source stream T38Engine *engine ); ~T38ModemMediaStream(); //@} /**@name Overrides of OpalMediaStream class */ //@{ virtual PBoolean Open(); virtual PBoolean Close(); virtual void OnStartMediaPatch(); virtual PBoolean ReadPacket( RTP_DataFrame & packet ); virtual PBoolean WritePacket( RTP_DataFrame & packet ); virtual PBoolean IsSynchronous() const { return FALSE; } //@} protected: long currentSequenceNumber; #if PTRACING int totallost; #endif T38Engine * t38engine; }; ///////////////////////////////////////////////////////////////////////////// #endif // _MY_MODEM_MEDIA_STREAM_H t38modem-2.0.0/opal/h323ep.cxx0000664000076400007640000005452011525202063016510 0ustar frolovfrolov00000000000000/* * h323ep.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2011 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: h323ep.cxx,v $ * Revision 1.26 2011/02/11 09:41:07 vfrolov * Added more tracing * * Revision 1.25 2011/01/19 11:41:17 vfrolov * Replaced deprecated ApplyStringOptions() by OnApplyStringOptions() * * Revision 1.24 2011/01/13 06:39:08 vfrolov * Disabled OPAL version < 3.9.0 * Added route options help topic * * Revision 1.23 2010/03/15 14:31:30 vfrolov * Added options * --h323-t38-udptl-redundancy * --h323-t38-udptl-keep-alive-interval * * Revision 1.22 2010/02/24 14:20:09 vfrolov * Added variant of patch #2954967 "opal sip/h323 build-time detection" * Thanks Mariusz Mazur * * Revision 1.21 2010/02/12 08:55:07 vfrolov * Implemented fake codecs * * Revision 1.20 2010/02/08 17:30:31 vfrolov * Disabled OPAL version < 3.8.0 * * Revision 1.19 2010/01/22 11:20:20 vfrolov * Added --h323-disable-t38-mode option * * Revision 1.18 2010/01/21 16:05:33 vfrolov * Changed --h323-audio to accept multiple wildcards * Implemented OPAL-Enable-Audio route option * Renamed route option OPAL-H323-Bearer-Capability to OPAL-Bearer-Capability * * Revision 1.17 2010/01/21 09:22:45 vfrolov * Fixed tracing typo * * Revision 1.16 2010/01/13 09:59:19 vfrolov * Fixed incompatibility with OPAL trunk * Fixed incorrect codec selection for the incoming offer * * Revision 1.15 2009/12/23 17:54:24 vfrolov * Implemented --h323-bearer-capability option * * Revision 1.14 2009/12/09 13:27:22 vfrolov * Fixed Disable-T38-Mode * * Revision 1.13 2009/12/08 15:06:22 vfrolov * Fixed incompatibility with OPAL trunk * * Revision 1.12 2009/11/10 11:30:57 vfrolov * Implemented G.711 fallback to fax pass-through mode * * Revision 1.11 2009/10/28 17:30:41 vfrolov * Fixed uncompatibility with OPAL trunk * * Revision 1.10 2009/10/06 17:13:10 vfrolov * Fixed uncompatibility with OPAL trunk * * Revision 1.9 2009/07/31 17:34:40 vfrolov * Removed --h323-old-asn and --sip-old-asn options * * Revision 1.8 2009/07/22 17:26:54 vfrolov * Added ability to enable other audio formats * * Revision 1.7 2009/07/22 14:42:49 vfrolov * Added Descriptions(args) to endpoints * * Revision 1.6 2009/07/15 18:25:53 vfrolov * Added reordering of formats * * Revision 1.5 2009/05/29 13:01:41 vfrolov * Ported to OPAL trunk * * Revision 1.4 2008/09/24 14:39:21 frolov * Removed capabilities adding * * Revision 1.3 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.2 2007/07/20 14:34:45 vfrolov * Added setting of calling number of an outgoing connection * * Revision 1.1 2007/05/28 12:47:52 vfrolov * Initial revision * */ #include #include #if OPAL_H323 ///////////////////////////////////////////////////////////////////////////// #define PACK_VERSION(major, minor, build) (((((major) << 8) + (minor)) << 8) + (build)) #if !(PACK_VERSION(OPAL_MAJOR, OPAL_MINOR, OPAL_BUILD) >= PACK_VERSION(3, 9, 0)) #error *** Uncompatible OPAL version (required >= 3.9.0) *** #endif #undef PACK_VERSION ///////////////////////////////////////////////////////////////////////////// #include #include "h323ep.h" #include "fake_codecs.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// // // MyH323Connection // removes local capabilities for not allowed media formats // class MyH323Connection : public H323Connection { PCLASSINFO(MyH323Connection, H323Connection); public: /**@name Construction */ //@{ /**Create a new connection. */ MyH323Connection( OpalCall & call, ///< Call object connection belongs to H323EndPoint & endpoint, ///< H323 End Point object const PString & token, ///< Token for new connection const PString & alias, ///< Alias for outgoing call const H323TransportAddress & address, ///< Address for outgoing call unsigned options = 0, ///< Connection option bits OpalConnection::StringOptions * stringOptions = NULL ///< complex string options ) : H323Connection(call, endpoint, token, alias, address, options, stringOptions) , switchingToFaxMode(false) {} //@} virtual PBoolean SetUpConnection(); virtual void OnApplyStringOptions(); virtual PBoolean OnSendSignalSetup( H323SignalPDU & setupPDU ///< Setup PDU to send ); virtual AnswerCallResponse OnAnswerCall( const PString & callerName, ///< Name of caller const H323SignalPDU & setupPDU, ///< Received setup PDU H323SignalPDU & connectPDU, ///< Connect PDU to send H323SignalPDU & progressPDU ///< Progress PDU to send ); virtual bool SwitchFaxMediaStreams( bool enableFax ///< Enable FAX or return to audio mode ); virtual void OnSwitchedFaxMediaStreams( bool enabledFax ///< Enabled FAX or audio mode ); virtual PBoolean OnOpenMediaStream( OpalMediaStream & stream ///< New media stream being opened ); virtual OpalMediaFormatList GetMediaFormats() const; virtual OpalMediaFormatList GetLocalMediaFormats(); virtual void AdjustMediaFormats( bool local, ///< Media formats a local ones to be presented to remote OpalMediaFormatList & mediaFormats, ///< Media formats to use OpalConnection * otherConnection ///< Other connection we are adjusting media for ) const; protected: mutable OpalMediaFormatList mediaFormatList; PIntArray bearerCapability; bool switchingToFaxMode; }; ///////////////////////////////////////////////////////////////////////////// // // Implementation // ///////////////////////////////////////////////////////////////////////////// PString MyH323EndPoint::ArgSpec() { return "-no-h323." "-h323-audio:" "-h323-audio-list." "-h323-disable-t38-mode." "-h323-t38-udptl-redundancy:" "-h323-t38-udptl-keep-alive-interval:" "F-fastenable." "T-h245tunneldisable." "-h323-listen:" "-h323-no-listen." "g-gatekeeper:" "n-no-gatekeeper." "-require-gatekeeper." "-h323-bearer-capability:" ; } PStringArray MyH323EndPoint::Descriptions() { PStringArray descriptions = PString( "H.323 options:\n" " --no-h323 : Disable H.323 protocol.\n" " --h323-audio str : Use OPAL-Enable-Audio=str route option by\n" " default. May be used multiple times.\n" " --h323-audio-list : Display available audio formats.\n" " --h323-disable-t38-mode : Use OPAL-Disable-T38-Mode=true route option by\n" " default.\n" " --h323-t38-udptl-redundancy str\n" " : Use OPAL-T38-UDPTL-Redundancy=str route option by\n" " default.\n" " --h323-t38-udptl-keep-alive-interval ms\n" " : Use OPAL-T38-UDPTL-Keep-Alive-Interval=ms route\n" " option by default.\n" " -F --fastenable : Enable fast start.\n" " -T --h245tunneldisable : Disable H245 tunnelling.\n" " --h323-listen iface : Interface/port(s) to listen for H.323 requests\n" " : '*' is all interfaces, (default tcp$*:1720).\n" " --h323-no-listen : Disable listen for incoming calls.\n" " -g --gatekeeper host : Specify gatekeeper host.\n" " -n --no-gatekeeper : Disable gatekeeper discovery.\n" " --require-gatekeeper : Exit if gatekeeper discovery fails.\n" " --h323-bearer-capability str\n" " : Use OPAL-Bearer-Capability=str route option by\n" " default.\n" "\n" "H.323 route options:\n" " OPAL-Enable-Audio=[!]wildcard[,[!]...]\n" " Enable the audio format(s) matching the wildcard(s). The '*' character\n" " match any substring. The leading '!' character indicates a negative test.\n" " Default: " OPAL_G711_ULAW_64K "," OPAL_G711_ALAW_64K ".\n" " OPAL-Disable-T38-Mode={true|false}\n" " Enable or disable T.38 fax mode.\n" " Default: false (enable T.38 fax mode).\n" " OPAL-T38-UDPTL-Redundancy=[maxsize:redundancy[,maxsize:redundancy...]]\n" " Set error recovery redundancy for IFP packets dependent from their size.\n" " For example the string '2:I,9:L,32767:H' (where I, L and H are numbers)\n" " sets redundancy for (I)ndication, (L)ow speed and (H)igh speed IFP packets.\n" " Default: empty string (no redundancy).\n" " OPAL-T38-UDPTL-Redundancy-Interval=ms\n" " Continuously resend last UDPTL packet each ms milliseconds on idle till it\n" " contains IFP packets not sent redundancy times.\n" " Default: 50.\n" " OPAL-T38-UDPTL-Keep-Alive-Interval=ms\n" " Continuously resend last UDPTL packet each ms milliseconds on idle.\n" " Default: 0 (no resend).\n" " OPAL-T38-UDPTL-Optimise-On-Retransmit={true|false}\n" " Optimize UDPTL packets on resending in accordance with required redundancy\n" " (exclude redundancy IFP packets sent redundancy times).\n" " Default: true (optimize).\n" " OPAL-Bearer-Capability=S:C:R:P\n" " Set bearer capability information element (Q.931) with\n" " S - coding standard (0-3)\n" " C - information transfer capability (0-31)\n" " R - information transfer rate (1-127)\n" " P - user information layer 1 protocol (2-5).\n" ).Lines(); return descriptions; } PStringArray MyH323EndPoint::Descriptions(const PConfigArgs & args) { PStringArray descriptions; if (args.HasOption("h323-audio-list")) descriptions += FakeCodecs::GetAvailableAudioFormatsDescription("H.323", "h323"); return descriptions; } PBoolean MyH323EndPoint::Create(OpalManager & mgr, const PConfigArgs & args) { if (args.HasOption("no-h323")) { cout << "Disabled H.323 protocol" << endl; return TRUE; } if ((new MyH323EndPoint(mgr))->Initialise(args)) return TRUE; return FALSE; } PBoolean MyH323EndPoint::Initialise(const PConfigArgs & args) { if (args.HasOption("h323-audio")) { PStringStream s; s << setfill(',') << args.GetOptionString("h323-audio").Lines(); defaultStringOptions.SetAt("Enable-Audio", s); } if (args.HasOption("h323-disable-t38-mode")) defaultStringOptions.SetAt("Disable-T38-Mode", "true"); DisableFastStart(!args.HasOption("fastenable")); DisableH245Tunneling(args.HasOption("h245tunneldisable")); defaultStringOptions.SetAt("T38-UDPTL-Redundancy-Interval", "50"); defaultStringOptions.SetAt("T38-UDPTL-Optimise-On-Retransmit", "true"); defaultStringOptions.SetAt("T38-UDPTL-Redundancy", args.HasOption("h323-t38-udptl-redundancy") ? args.GetOptionString("h323-t38-udptl-redundancy") : ""); defaultStringOptions.SetAt("T38-UDPTL-Keep-Alive-Interval", args.HasOption("h323-t38-udptl-keep-alive-interval") ? args.GetOptionString("h323-t38-udptl-keep-alive-interval") : "0"); if (args.HasOption("h323-bearer-capability")) defaultStringOptions.SetAt("Bearer-Capability", args.GetOptionString("h323-bearer-capability")); if (!args.HasOption("h323-no-listen")) { PStringArray listeners; if (args.HasOption("h323-listen")) listeners = args.GetOptionString("h323-listen").Lines(); else listeners = GetDefaultListeners(); if (!StartListeners(listeners)) { cerr << "Could not open any H.323 listener from " << setfill(',') << listeners << endl; return FALSE; } cout << "Waiting for incoming H.323 calls from " << setfill(',') << listeners << endl; } if (args.HasOption("gatekeeper")) { PString gkName = args.GetOptionString("gatekeeper"); if (SetGatekeeper(gkName)) cout << "Gatekeeper set: " << *GetGatekeeper() << endl; else { cerr << "Error registering with gatekeeper at \"" << gkName << '"' << endl; return FALSE; } } else if (!args.HasOption("no-gatekeeper")) { cout << "Searching for gatekeeper..." << flush; if (DiscoverGatekeeper()) cout << "\nGatekeeper found: " << *GetGatekeeper() << endl; else { cerr << "\nNo gatekeeper found." << endl; if (args.HasOption("require-gatekeeper")) return FALSE; } } return TRUE; } H323Connection * MyH323EndPoint::CreateConnection( OpalCall & call, const PString & token, void * /*userData*/, OpalTransport & /*transport*/, const PString & alias, const H323TransportAddress & address, H323SignalPDU * /*setupPDU*/, unsigned options, OpalConnection::StringOptions * stringOptions) { PTRACE(2, "MyH323EndPoint::CreateConnection for " << call); MyH323Connection *connection = new MyH323Connection(call, *this, token, alias, address, options, stringOptions); PTRACE(6, "MyH323EndPoint::CreateConnection new " << connection->GetClass() << ' ' << (void *)connection); OpalConnection::StringOptions newOptions; for (PINDEX i = 0 ; i < defaultStringOptions.GetSize() ; i++) { if (!connection->GetStringOptions().Contains(defaultStringOptions.GetKeyAt(i))) newOptions.SetAt(defaultStringOptions.GetKeyAt(i), defaultStringOptions.GetDataAt(i)); } connection->SetStringOptions(newOptions, false); return connection; } ///////////////////////////////////////////////////////////////////////////// PBoolean MyH323Connection::SetUpConnection() { PTRACE(2, "MyH323Connection::SetUpConnection " << *this << " name=" << GetLocalPartyName()); PSafePtr conn = GetCall().GetConnection(0); if (conn != NULL && conn != this) { // Set the calling number of an outgoing connection PString name = conn->GetRemotePartyNumber(); if (!name.IsEmpty() && name != "*") { SetLocalPartyName(name); PTRACE(1, "MyH323Connection::SetUpConnection new name=" << GetLocalPartyName()); } } return H323Connection::SetUpConnection(); } void MyH323Connection::OnApplyStringOptions() { H323Connection::OnApplyStringOptions(); if (LockReadWrite()) { mediaFormatList = OpalMediaFormatList(); if (GetStringOptions().Contains("Enable-Audio")) { const PStringArray wildcards = GetStringOptions()("Enable-Audio").Tokenise(",", FALSE); OpalMediaFormatList list = endpoint.GetMediaFormats(); for (PINDEX w = 0 ; w < wildcards.GetSize() ; w++) { OpalMediaFormatList::const_iterator f; while ((f = list.FindFormat(wildcards[w], f)) != list.end()) { if (f->GetMediaType() == OpalMediaType::Audio() && f->IsValidForProtocol("h323") && f->IsTransportable()) mediaFormatList += *f; if (++f == list.end()) break; } } } else { mediaFormatList += OpalG711_ULAW_64K; mediaFormatList += OpalG711_ALAW_64K; } if (GetStringOptions().GetBoolean("Disable-T38-Mode")) { PTRACE(3, "MyH323Connection::OnApplyStringOptions: Disable-T38-Mode=true"); } else { mediaFormatList += OpalT38; } mediaFormatList += OpalRFC2833; PTRACE(4, "MyH323Connection::OnApplyStringOptions Enabled formats (in preference order):\n" << setfill('\n') << mediaFormatList << setfill(' ')); if (GetStringOptions().Contains("Bearer-Capability")) { PString bc = GetStringOptions()["Bearer-Capability"]; PStringArray sBC = bc.Tokenise(":", FALSE); PIntArray iBC(4); if (sBC.GetSize() == iBC.GetSize()) { for (PINDEX i = 0 ; i < iBC.GetSize() ; i++) iBC[i] = sBC[i].AsUnsigned(); if (iBC[0] >= 0 && iBC[0] <= 3 && iBC[1] >= 0 && iBC[1] <= 31 && iBC[2] >= 1 && iBC[2] <= 127 && iBC[3] >= 2 && iBC[3] <= 5) { PTRACE(3, "MyH323Connection::OnApplyStringOptions: Bearer-Capability=" << bc); bearerCapability = iBC; } else { iBC[0] = -1; } } else { iBC[0] = -1; } if (iBC[0] < 0) { PTRACE(3, "MyH323Connection::OnApplyStringOptions: Wrong Bearer-Capability=" << bc << " (ignored)"); } } UnlockReadWrite(); } } PBoolean MyH323Connection::OnSendSignalSetup(H323SignalPDU & setupPDU) { if (!bearerCapability.IsEmpty()) { PTRACE(3, "MyH323Connection::OnSendSignalSetup: Set Bearer capability '" << bearerCapability << "'"); setupPDU.GetQ931().SetBearerCapabilities( Q931::InformationTransferCapability(bearerCapability[1]), bearerCapability[2], bearerCapability[0], bearerCapability[3]); } return H323Connection::OnSendSignalSetup(setupPDU); } H323Connection::AnswerCallResponse MyH323Connection::OnAnswerCall( const PString & caller, const H323SignalPDU & setupPDU, H323SignalPDU & connectPDU, H323SignalPDU & progressPDU) { if (!bearerCapability.IsEmpty()) { PTRACE(3, "MyH323Connection::OnAnswerCall: Set Bearer capability '" << bearerCapability << "'"); connectPDU.GetQ931().SetBearerCapabilities( Q931::InformationTransferCapability(bearerCapability[1]), bearerCapability[2], bearerCapability[0], bearerCapability[3]); progressPDU.GetQ931().SetBearerCapabilities( Q931::InformationTransferCapability(bearerCapability[1]), bearerCapability[2], bearerCapability[0], bearerCapability[3]); } return H323Connection::OnAnswerCall(caller, setupPDU, connectPDU, progressPDU); } bool MyH323Connection::SwitchFaxMediaStreams(bool enableFax) { OpalMediaFormatList mediaFormats = GetMediaFormats(); AdjustMediaFormats(true, mediaFormats, NULL); PTRACE(3, "MyH323Connection::SwitchFaxMediaStreams:\n" << setfill('\n') << mediaFormats << setfill(' ')); const OpalMediaType &mediaType = enableFax ? OpalMediaType::Fax() : OpalMediaType::Audio(); for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { if (mediaFormats[i].GetMediaType() == mediaType) { switchingToFaxMode = enableFax; return H323Connection::SwitchFaxMediaStreams(enableFax); } } PTRACE(3, "MyH323Connection::SwitchFaxMediaStreams: " << mediaType << " is not supported"); return false; } void MyH323Connection::OnSwitchedFaxMediaStreams(bool enabledFax) { PTRACE(3, "MyH323Connection::OnSwitchedFaxMediaStreams: " << (enabledFax == switchingToFaxMode ? "" : "NOT ") << "switched to " << (switchingToFaxMode ? "fax" : "audio")); H323Connection::OnSwitchedFaxMediaStreams(enabledFax); if (switchingToFaxMode && !enabledFax) { PTRACE(3, "MyH323Connection::OnSwitchedFaxMediaStreams: fallback to audio"); mediaFormatList -= OpalT38; SwitchFaxMediaStreams(false); } } PBoolean MyH323Connection::OnOpenMediaStream(OpalMediaStream & stream) { PTRACE(4, "MyH323Connection::OnOpenMediaStream: " << stream); RTP_Session *session = GetSession(stream.GetSessionID()); if (session) RTP_Session::EncodingLock(*session)->ApplyStringOptions(GetStringOptions()); return H323Connection::OnOpenMediaStream(stream); } OpalMediaFormatList MyH323Connection::GetMediaFormats() const { OpalMediaFormatList mediaFormats = H323Connection::GetMediaFormats(); PTRACE(4, "MyH323Connection::GetMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { PBoolean found = FALSE; for (PINDEX j = 0 ; j < mediaFormatList.GetSize() ; j++) { if (mediaFormats[i] == mediaFormatList[j]) { found = TRUE; break; } } if (!found) { PTRACE(3, "MyH323Connection::GetMediaFormats Remove " << mediaFormats[i]); mediaFormats -= mediaFormats[i]; i--; } } PTRACE(4, "MyH323Connection::GetMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); return mediaFormats; } OpalMediaFormatList MyH323Connection::GetLocalMediaFormats() { OpalMediaFormatList mediaFormats = H323Connection::GetLocalMediaFormats(); PTRACE(4, "MyH323Connection::GetLocalMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); for (PINDEX i = 0 ; i < mediaFormats.GetSize() ; i++) { PBoolean found = FALSE; for (PINDEX j = 0 ; j < mediaFormatList.GetSize() ; j++) { if (mediaFormats[i] == mediaFormatList[j]) { found = TRUE; break; } } if (!found) { PTRACE(3, "MyH323Connection::GetLocalMediaFormats Remove " << mediaFormats[i]); mediaFormats -= mediaFormats[i]; i--; } } PTRACE(4, "MyH323Connection::GetLocalMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); return mediaFormats; } void MyH323Connection::AdjustMediaFormats( bool local, OpalMediaFormatList & mediaFormats, OpalConnection * otherConnection) const { PTRACE(4, "MyH323Connection::AdjustMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); H323Connection::AdjustMediaFormats(local, mediaFormats, otherConnection); if (local) { PStringArray order; for (PINDEX j = 0 ; j < mediaFormatList.GetSize() ; j++) order += mediaFormatList[j].GetName(); mediaFormats.Reorder(order); PTRACE(4, "MyH323Connection::AdjustMediaFormats: reordered"); } PTRACE(4, "MyH323Connection::AdjustMediaFormats:\n" << setfill('\n') << mediaFormats << setfill(' ')); } ///////////////////////////////////////////////////////////////////////////// #endif // OPAL_H323 ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/opal/fake_codecs.cxx0000664000076400007640000002045611352366775017756 0ustar frolovfrolov00000000000000/* * fake_codecs.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2010 Vyacheslav Frolov * * t38modem Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is t38modem. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: fake_codecs.cxx,v $ * Revision 1.3 2010/03/24 10:48:29 vfrolov * Fixed incompatibility with OPAL trunk * * Revision 1.2 2010/03/19 08:27:52 vfrolov * Added "search_for_fake_transcoder" command * * Revision 1.1 2010/02/12 08:20:10 vfrolov * Initial revision * */ #include #include ///////////////////////////////////////////////////////////////////////////// #define PACK_VERSION(major, minor, build) (((((major) << 8) + (minor)) << 8) + (build)) #if !(PACK_VERSION(OPAL_MAJOR, OPAL_MINOR, OPAL_BUILD) >= PACK_VERSION(3, 8, 1)) #error *** Uncompatible OPAL version (required >= 3.8.1) *** #endif #undef PACK_VERSION ///////////////////////////////////////////////////////////////////////////// #include #include "fake_codecs.h" #define new PNEW ///////////////////////////////////////////////////////////////////////////// #define FAKE_AUDIO_FORMATS(pattern) \ pattern(G722) \ pattern(G7221) \ pattern(G7222) \ pattern(G726_40K) \ pattern(G726_32K) \ pattern(G726_24K) \ pattern(G726_16K) \ pattern(G728) \ pattern(G729) \ pattern(G729A) \ pattern(G729B) \ pattern(G729AB) \ pattern(G7231_6k3) \ pattern(G7231_5k3) \ pattern(G7231A_6k3) \ pattern(G7231A_5k3) \ pattern(GSM0610) \ pattern(GSMAMR) \ pattern(iLBC) \ ///////////////////////////////////////////////////////////////////////////// namespace FakeCodecs { ///////////////////////////////////////////////////////////////////////////// class FakeFramedAudioTranscoder : public OpalFramedTranscoder { PCLASSINFO(FakeFramedAudioTranscoder, OpalFramedTranscoder); public: FakeFramedAudioTranscoder(const char * inFormat, const char * outFormat) : OpalFramedTranscoder(inFormat, outFormat) {} PBoolean ExecuteCommand(const OpalMediaCommand & command) { if (command.GetName() == "search_for_fake_transcoder") { PTRACE(4, "FakeFramedAudioTranscoder::ExecuteCommand: found fake transcoder " << *this); return true; } return OpalFramedTranscoder::ExecuteCommand(command); } PBoolean ConvertFrame(const BYTE *, PINDEX &, BYTE *, PINDEX &outLen) { outLen = 0; return PTrue; } }; ///////////////////////////////////////////////////////////////////////////// #define DECLARE_FAKE_AUDIO_TRANSCODER(inFormat, outFormat) \ class Fake_##inFormat##_##outFormat##_Transcoder : public FakeFramedAudioTranscoder \ { \ public: \ Fake_##inFormat##_##outFormat##_Transcoder() \ : FakeFramedAudioTranscoder(OPAL_##inFormat, OPAL_##outFormat) {} \ } \ #define DECLARE_FAKE_AUDIO_CODEC(format) \ DECLARE_FAKE_AUDIO_TRANSCODER(format, PCM16); \ DECLARE_FAKE_AUDIO_TRANSCODER(PCM16, format); \ ///////////////////////////////////////////////////////////////////////////// #define REGISTER_FAKE_TRANSCODER(inFormat, outFormat) \ new OpalTranscoderFactory::Worker( \ MakeOpalTranscoderKey(OPAL_##inFormat, OPAL_##outFormat), false) \ #define REGISTER_FAKE_AUDIO_CODEC(format) \ { \ REGISTER_FAKE_TRANSCODER(format, PCM16); \ REGISTER_FAKE_TRANSCODER(PCM16, format); \ } \ ///////////////////////////////////////////////////////////////////////////// FAKE_AUDIO_FORMATS(DECLARE_FAKE_AUDIO_CODEC); ///////////////////////////////////////////////////////////////////////////// static bool IsMatch(const PString &str, const PStringArray &wildcards) { for (PINDEX i = 0 ; i < wildcards.GetSize() ; i++) { PString pattern = wildcards[i]; if (pattern.IsEmpty()) continue; bool negative; if (pattern[0] == '!') { negative = true; pattern = pattern.Mid(1); if (pattern.IsEmpty()) return true; } else { negative = false; } pattern = PRegularExpression::EscapeString(pattern); pattern.Replace("\\*", ".*", true); PRegularExpression regexp; if (!regexp.Compile(pattern, PRegularExpression::IgnoreCase|PRegularExpression::Extended)) { PTRACE(1, "FakeCodecs::IsMatch: " "Wildcard \"" << wildcards[i] << "\" ignored" ", \"" << pattern << "\" - " << regexp.GetErrorText()); cerr << "Wildcard \"" << wildcards[i] << "\" ignored" << endl; continue; } if (str.MatchesRegEx(regexp) != negative) return true; } return false; } ///////////////////////////////////////////////////////////////////////////// static OpalMediaFormatList GetNormalMediaFormats() { static const OpalMediaFormatList *pFormats = NULL; if (pFormats == NULL) pFormats = new OpalMediaFormatList(OpalMediaFormat::GetAllRegisteredMediaFormats()); return *pFormats; } ///////////////////////////////////////////////////////////////////////////// static OpalMediaFormatList GetFakeAudioFormats(const PStringArray &wildcards = PStringArray()) { GetNormalMediaFormats(); // init list of normal formats static OpalMediaFormatList formats; const OpalMediaFormatList registered = OpalMediaFormat::GetAllRegisteredMediaFormats(); #define ADD_FORMAT(format) \ if (!registered.HasFormat(OPAL_##format) && IsMatch(OPAL_##format, wildcards)) { \ REGISTER_FAKE_AUDIO_CODEC(format); \ formats += Opal##format; \ PTRACE(3, "FakeCodecs::GetFakeAudioFormats: Registered fake audio format " << OPAL_##format); \ } \ FAKE_AUDIO_FORMATS(ADD_FORMAT); #undef ADD_FORMAT return formats; } ///////////////////////////////////////////////////////////////////////////// static PStringArray GetNotRegisteredFakeAudioFormatNames() { PStringArray names; const OpalMediaFormatList registered = OpalMediaFormat::GetAllRegisteredMediaFormats(); #define ADD_FORMAT(format) \ if (!registered.HasFormat(OPAL_##format)) { \ names += OPAL_##format; \ } \ FAKE_AUDIO_FORMATS(ADD_FORMAT); #undef ADD_FORMAT return names; } ///////////////////////////////////////////////////////////////////////////// PStringArray GetAvailableAudioFormatsDescription(const char *name, const char *protocol) { PStringArray descriptions; descriptions.Append(new PString(PString("Available audio formats for ") + name + ":")); OpalMediaFormatList formats; formats = GetNormalMediaFormats(); for (OpalMediaFormatList::iterator f = formats.begin(); f != formats.end(); ++f) { if (f->GetMediaType() == OpalMediaType::Audio() && f->IsTransportable() && f->IsValidForProtocol(protocol)) descriptions.Append(new PString(PString(" ") + f->GetName())); } formats = GetFakeAudioFormats(); for (OpalMediaFormatList::iterator f = formats.begin(); f != formats.end(); ++f) { if (f->GetMediaType() == OpalMediaType::Audio() && f->IsTransportable() && f->IsValidForProtocol(protocol)) descriptions.Append(new PString(PString(" fake ") + f->GetName())); } PStringArray names = GetNotRegisteredFakeAudioFormatNames(); for (PINDEX i = 0 ; i < names.GetSize() ; i++) { descriptions.Append(new PString(PString(" not registered fake ") + names[i])); } return descriptions; } ///////////////////////////////////////////////////////////////////////////// void RegisterFakeAudioFormats(const PStringArray &wildcards) { GetFakeAudioFormats(wildcards); } ///////////////////////////////////////////////////////////////////////////// } // namespace FakeCodecs ///////////////////////////////////////////////////////////////////////////// t38modem-2.0.0/pmodemi.h0000664000076400007640000000677111453124673015647 0ustar frolovfrolov00000000000000/* * pmodemi.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmodemi.h,v $ * Revision 1.10 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.9 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.8 2009/07/08 18:43:44 vfrolov * Added PseudoModem::ttyName() * * Revision 1.7 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.6 2007/03/23 10:14:36 vfrolov * Implemented voice mode functionality * * Revision 1.5 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * Revision 1.4 2002/05/15 16:18:00 vfrolov * Implemented per modem routing for I/C calls * * Revision 1.3 2002/03/05 12:32:02 vfrolov * Added Copyright header * Changed class hierarchy * PseudoModem is abstract * PseudoModemBody is child of PseudoModem * Added PseudoModemQ::CreateModem() to create instances * * Revision 1.2 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #ifndef _PMODEMI_H #define _PMODEMI_H #include "pmodem.h" /////////////////////////////////////////////////////////////// class ModemEngine; class PseudoModemBody : public PseudoModem { PCLASSINFO(PseudoModemBody, PseudoModem); public: /**@name Construction */ //@{ PseudoModemBody(const PString &_tty, const PString &_route, const PNotifier &_callbackEndPoint); ~PseudoModemBody(); //@} /**@name Operations */ //@{ PBYTEArray *FromInPtyQ() { return inPtyQ.Dequeue(); } void ToOutPtyQ(const void *buf, PINDEX count) { ToPtyQ(buf, count, TRUE); }; //@} virtual PBoolean IsReady() const; PBoolean CheckRoute(const PString &number) const; PBoolean Request(PStringToString &request) const; virtual T38Engine *NewPtrT38Engine() const; virtual AudioEngine *NewPtrAudioEngine() const; virtual EngineBase *NewPtrUserInputEngine() const; const PNotifier &GetCallbackEndPoint() const { return callbackEndPoint; } protected: virtual const PString &ttyPath() const = 0; virtual ModemThreadChild *GetPtyNotifier() = 0; virtual PBoolean StartAll(); virtual void StopAll(); virtual void MainLoop() = 0; PBoolean AddModem() const; PBYTEArray *FromOutPtyQ() { return outPtyQ.Dequeue(); } void ToInPtyQ(PBYTEArray *buf) { inPtyQ.Enqueue(buf); } void ToInPtyQ(const void *buf, PINDEX count) { ToPtyQ(buf, count, FALSE); }; PMutex Mutex; private: void Main(); void ToPtyQ(const void *buf, PINDEX count, PBoolean OutQ); PString route; const PNotifier callbackEndPoint; ModemEngine *engine; PBYTEArrayQ outPtyQ; PBYTEArrayQ inPtyQ; }; /////////////////////////////////////////////////////////////// #endif // _PMODEMI_H t38modem-2.0.0/audio.h0000664000076400007640000000660111455110341015273 0ustar frolovfrolov00000000000000/* * audio.h * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: audio.h,v $ * Revision 1.9 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.8 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.7 2010/09/22 15:23:48 vfrolov * Added OnResetModemState() * * Revision 1.6 2010/09/08 17:22:23 vfrolov * Redesigned modem engine (continue) * * Revision 1.5 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.4 2009/11/20 16:37:27 vfrolov * Fixed audio class application blocking by forced T.38 mode * * Revision 1.3 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2007/03/23 09:54:45 vfrolov * Initial revision * */ #ifndef _PM_AUDIO_H #define _PM_AUDIO_H #include #include "enginebase.h" /////////////////////////////////////////////////////////////// class DataStream; class ToneGenerator; class T30ToneDetect; /////////////////////////////////////////////////////////////// class AudioEngine : public EngineBase { PCLASSINFO(AudioEngine, EngineBase); public: /**@name Construction */ //@{ AudioEngine(const PString &_name); ~AudioEngine(); //@} /**@name Modem API */ //@{ PBoolean Read(HOWNEROUT hOwner, void * buffer, PINDEX amount); virtual void SendOnIdle(DataType _dataType); virtual PBoolean SendStart(DataType _dataType, int param); virtual int Send(const void *pBuf, PINDEX count); virtual PBoolean SendStop(PBoolean moreFrames, int _callbackParam); virtual PBoolean isOutBufFull() const; PBoolean Write(HOWNERIN hOwner, const void * buffer, PINDEX len); virtual void RecvOnIdle(DataType _dataType); virtual PBoolean RecvWait(DataType _dataType, int param, int _callbackParam, PBoolean &done); virtual PBoolean RecvStart(int _callbackParam); virtual int Recv(void *pBuf, PINDEX count); virtual void RecvStop(); //@} protected: virtual void OnAttach(); virtual void OnDetach(); virtual void OnResetModemState(); virtual void OnChangeModemClass(); virtual void OnOpenIn(); virtual void OnOpenOut(); virtual void OnCloseIn(); virtual void OnCloseOut(); virtual void OnChangeEnableFakeIn(); virtual void OnChangeEnableFakeOut(); PAdaptiveDelay readDelay; PAdaptiveDelay writeDelay; int callbackParam; DataStream *volatile sendAudio; DataStream *volatile recvAudio; ToneGenerator *volatile pToneIn; ToneGenerator *volatile pToneOut; T30ToneDetect *volatile t30ToneDetect; }; /////////////////////////////////////////////////////////////// #endif // _PM_AUDIO_H t38modem-2.0.0/pmutils.h0000664000076400007640000001757511061726064015713 0ustar frolovfrolov00000000000000/* * pmutils.h * * T38FAX Pseudo Modem * * Copyright (c) 2001-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmutils.h,v $ * Revision 1.18 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.17 2006/12/11 10:27:35 vfrolov * Disabled renaming thread if no PTRACING * * Revision 1.16 2005/03/05 15:39:12 vfrolov * Ignore COUT_TRACE if not PTRACING * * Revision 1.15 2005/02/04 10:18:49 vfrolov * Fixed warnings for No Trace build * * Revision 1.14 2005/02/03 11:32:12 vfrolov * Fixed MSVC compile warnings * * Revision 1.13 2004/10/20 14:15:09 vfrolov * Added reset of signal counter to WaitDataReady() * * Revision 1.12 2004/07/07 07:53:58 vfrolov * Moved ptlib.h including to *.cxx for precompiling * Fixed compiler warning * * Revision 1.11 2004/03/09 17:23:19 vfrolov * Added PROCESS_PER_THREAD ifdef * * Revision 1.10 2003/12/04 13:22:35 vfrolov * Removed ambiguous isEof() * Improved memory usage in DataStream * Fixed myPTRACE * * Revision 1.9 2003/01/08 16:37:29 vfrolov * Changed class DataStream: * members moved to private section and added isEof() * added threshold and isFull() * * Revision 1.8 2002/12/30 12:49:39 vfrolov * Added tracing thread's CPU usage (Linux only) * * Revision 1.7 2002/12/20 10:13:01 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.6 2002/04/27 10:12:21 vfrolov * If defined MYPTRACE_LEVEL=N then myPTRACE() will output the trace with level N * * Revision 1.5 2002/03/07 07:30:44 vfrolov * Fixed endless recursive call SignalChildStop(). Possible there is * a bug in gcc version 2.95.4 20010902 (Debian prerelease). * Markus Storm reported the promlem. * * Revision 1.4 2002/03/01 08:17:28 vfrolov * Added Copyright header * Removed virtual modifiers * * Revision 1.3 2002/02/11 08:35:12 vfrolov * myPTRACE() outputs trace to cout only if defined COUT_TRACE * * Revision 1.2 2002/01/10 06:10:03 craigs * Added MPL header * * Revision 1.1 2002/01/01 23:06:54 craigs * Initial version * */ #ifndef _PMUTILS_H #define _PMUTILS_H /////////////////////////////////////////////////////////////// class ModemThread : public PThread { PCLASSINFO(ModemThread, PThread); public: /**@name Construction */ //@{ ModemThread(); //@} /**@name Operations */ //@{ void SignalDataReady() { dataReadySyncPoint.Signal(); } void SignalChildStop(); virtual void SignalStop(); //@} protected: virtual void Main() = 0; void WaitDataReady(); volatile PBoolean stop; // *this was requested to stop volatile PBoolean childstop; // there is a child that was requested to stop PSyncPoint dataReadySyncPoint; }; /////////////////////////////////////////////////////////////// class ModemThreadChild : public ModemThread { PCLASSINFO(ModemThreadChild, ModemThread); public: /**@name Construction */ //@{ ModemThreadChild(ModemThread &_parent); //@} /**@name Operations */ //@{ virtual void SignalStop(); //@} protected: ModemThread &parent; }; /////////////////////////////////////////////////////////////// PQUEUE(_PBYTEArrayQ, PBYTEArray); class PBYTEArrayQ : public _PBYTEArrayQ { PCLASSINFO(PBYTEArrayQ, _PBYTEArrayQ); public: PBYTEArrayQ() : count(0) {} ~PBYTEArrayQ() { Clean(); } virtual void Enqueue(PBYTEArray *buf) { PWaitAndSignal mutexWait(Mutex); count += buf->GetSize(); _PBYTEArrayQ::Enqueue(buf); } virtual PBYTEArray *Dequeue() { PWaitAndSignal mutexWait(Mutex); PBYTEArray *buf = _PBYTEArrayQ::Dequeue(); if( buf ) count -= buf->GetSize(); return buf; } PINDEX GetCount() const { return count; } void Clean() { PBYTEArray *buf; while( (buf = Dequeue()) != NULL ) { delete buf; } } protected: PINDEX count; PMutex Mutex; }; /////////////////////////////////////////////////////////////// class ChunkStream : public PObject { PCLASSINFO(ChunkStream, PObject); public: ChunkStream() : first(0), last(0) {} int write(const void *pBuf, PINDEX count); int read(void *pBuf, PINDEX count); private: BYTE data[256]; PINDEX first; PINDEX last; }; PQUEUE(ChunkStreamQ, ChunkStream); /////////////////////////////////////////////////////////////// class DataStream : public PObject { PCLASSINFO(DataStream, PObject); public: DataStream(PINDEX _threshold = 0) : firstBuf(NULL), lastBuf(NULL), busy(0), threshold(_threshold), eof(FALSE), diag(0) {} ~DataStream() { DataStream::Clean(); } int PutData(const void *pBuf, PINDEX count); int GetData(void *pBuf, PINDEX count); void PutEof() { eof = TRUE; } int GetDiag() const { return diag; } DataStream &SetDiag(int _diag) { diag = _diag; return *this; } PBoolean isFull() const { return threshold && threshold < busy; } virtual void Clean(); private: ChunkStream *firstBuf; ChunkStreamQ bufQ; ChunkStream *lastBuf; // if not NULL then it should be in bufQ or firstBuf PINDEX busy; PINDEX threshold; PBoolean eof; int diag; }; /////////////////////////////////////////////////////////////// PQUEUE(_DataStreamQ, DataStream); class DataStreamQ : public _DataStreamQ { PCLASSINFO(DataStreamQ, _DataStreamQ); public: DataStreamQ() {} ~DataStreamQ() { Clean(); } virtual void Enqueue(DataStream *buf) { PWaitAndSignal mutexWait(Mutex); _DataStreamQ::Enqueue(buf); } virtual DataStream *Dequeue() { PWaitAndSignal mutexWait(Mutex); return _DataStreamQ::Dequeue(); } void Clean() { DataStream *buf; while( (buf = Dequeue()) != NULL ) { delete buf; } } protected: PMutex Mutex; }; /////////////////////////////////////////////////////////////// #ifdef _MSC_VER // warning C4127: conditional expression is constant #pragma warning(disable:4127) #endif // _MSC_VER #if !PTRACING && defined COUT_TRACE #undef COUT_TRACE #endif #ifdef COUT_TRACE #define _myPTRACE(level, args) do { \ PTRACE(level, args); \ cout << PThread::Current()->GetThreadName() << ": " << args << endl; \ } while(0) #define myCanTrace(level) TRUE #define myPTRACE_PARAM(param) param #else #define _myPTRACE(level, args) do { PTRACE(level, args); } while(0) #define myCanTrace(level) PTrace::CanTrace(level) #define myPTRACE_PARAM(param) PTRACE_PARAM(param) #endif // COUT_TRACE #ifdef MYPTRACE_LEVEL #define myPTRACE(level, args) _myPTRACE(MYPTRACE_LEVEL, args) #else #define myPTRACE(level, args) _myPTRACE(level, args) #endif // MYPTRACE_LEVEL #define PRTHEX(data) " {\n" << setprecision(2) << hex << setfill('0') << data << dec << setfill(' ') << " }" /////////////////////////////////////////////////////////////// #if PTRACING extern void RenameCurrentThread(const PString &newname); #else #define RenameCurrentThread(newname) #endif /* PTRACING */ #ifdef PROCESS_PER_THREAD extern const PString GetThreadTimes(const char *head = "", const char *tail = ""); #else inline const PString GetThreadTimes(const char *head = "", const char *tail = ""); inline const PString GetThreadTimes(const char *, const char *) { return ""; } #endif /////////////////////////////////////////////////////////////// #endif // _PMUTILS_H t38modem-2.0.0/drivers.h0000664000076400007640000000304110557366071015662 0ustar frolovfrolov00000000000000/* * drivers.h * * T38FAX Pseudo Modem * * Copyright (c) 2004-2007 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: drivers.h,v $ * Revision 1.2 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.1 2004/07/07 12:38:32 vfrolov * The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. * * */ #ifndef _DRIVERS_H #define _DRIVERS_H /////////////////////////////////////////////////////////////// class PseudoModem; class PseudoModemDrivers : protected PObject { PCLASSINFO(PseudoModemDrivers, PObject); public: static PseudoModem *CreateModem( const PString &tty, const PString &route, const PConfigArgs &args, const PNotifier &callbackEndPoint ); static PString ArgSpec(); static PStringArray Descriptions(); }; /////////////////////////////////////////////////////////////// #endif // _DRIVERS_H t38modem-2.0.0/drv_c0c.h0000664000076400007640000000506311062145674015526 0ustar frolovfrolov00000000000000/* * drv_c0c.h * * T38FAX Pseudo Modem * * Copyright (c) 2004-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: drv_c0c.h,v $ * Revision 1.5 2008/09/11 07:41:48 frolov * Ported to OPAL SVN trunk * * Revision 1.4 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.3 2005/02/10 15:04:58 vfrolov * Disabled I/C calls for closed ports * * Revision 1.2 2004/07/19 08:31:06 vfrolov * Fixed "friend declaration requires class-key" * * Revision 1.1 2004/07/07 13:36:46 vfrolov * Initial revision * */ #ifndef _DRV_C0C_H #define _DRV_C0C_H #ifdef _WIN32 #define MODEM_DRIVER_C0C #endif #ifdef MODEM_DRIVER_C0C #include "pmodemi.h" /////////////////////////////////////////////////////////////// class InC0C; class OutC0C; class PseudoModemC0C : public PseudoModemBody { PCLASSINFO(PseudoModemC0C, PseudoModemBody); public: /**@name Construction */ //@{ PseudoModemC0C( const PString &_tty, const PString &_route, const PConfigArgs &args, const PNotifier &_callbackEndPoint ); ~PseudoModemC0C(); //@} /**@name static functions */ //@{ static PBoolean CheckTty(const PString &_tty); static PString ArgSpec(); static PStringArray Description(); //@} virtual PBoolean IsReady() const; protected: /**@name Overrides from class PseudoModemBody */ //@{ const PString &ttyPath() const; ModemThreadChild *GetPtyNotifier(); PBoolean StartAll(); void StopAll(); void MainLoop(); //@} private: BOOL OpenC0C(); void CloseC0C(); BOOL OutPnpId(); BOOL WaitReady(); BOOL IsOpenC0C() const { return hC0C != INVALID_HANDLE_VALUE; } HANDLE hC0C; InC0C *inC0C; OutC0C *outC0C; BOOL reset; BOOL ready; PString ptypath; friend class InC0C; friend class OutC0C; }; /////////////////////////////////////////////////////////////// #endif // MODEM_DRIVER_C0C #endif // _DRV_C0C_H t38modem-2.0.0/hdlc.h0000664000076400007640000000416511061726064015117 0ustar frolovfrolov00000000000000/* * hdlc.h * * T38FAX Pseudo Modem * * Copyright (c) 2003-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: hdlc.h,v $ * Revision 1.2 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.1 2003/12/04 13:38:39 vfrolov * Initial revision * */ #ifndef _HDLC_H #define _HDLC_H #include "fcs.h" /////////////////////////////////////////////////////////////// class HDLC { public: HDLC(); void PutRawData(DataStream *_inData); void PutHdlcData(DataStream *_inData); void GetRawStart(PINDEX flags = 0); void GetHdlcStart(PBoolean sync); int GetData(void *pBuf, PINDEX count); PBoolean isFcsOK(); int getLastChar() { return lastChar; } PINDEX getRawCount() { return rawCount; } void resetRawCount() { rawCount = 0; } private: void pack(const void *pBuf, PINDEX count, PBoolean flag = FALSE); PBoolean sync(BYTE b); PBoolean skipFlag(BYTE b); PBoolean unpack(BYTE b); int GetInData(void *pBuf, PINDEX count); int GetRawData(void *pBuf, PINDEX count); int GetHdlcData(void *pBuf, PINDEX count); int inDataType; int outDataType; DataStream *inData; DataStream outData; FCS fcs; int lastChar; PINDEX rawCount; BYTE rawByte; int rawByteLen; int rawOnes; DWORD hdlcChunk; int hdlcChunkLen; enum { stEof, stSync, stSkipFlags, stData, } hdlcState; }; /////////////////////////////////////////////////////////////// #endif // _HDLC_H t38modem-2.0.0/HylaFAX/0000775000076400007640000000000011551247020015254 5ustar frolovfrolov00000000000000t38modem-2.0.0/HylaFAX/config.ttyx0000664000076400007640000000226707763657114017505 0ustar frolovfrolov00000000000000# # HylaFAX configuration for a T38FAX Pseudo Modem # SessionTracing: 0x2FFF RingsBeforeAnswer: 1 ModemType: Class1 # use class 1 interface ModemFlowControl: rtscts # default ModemRevQueryCmd: AT+FREV? # # AT#CID=10 enables ANI/DNIS reporting between the # first and second RINGs in the form: # # RING # NMBR = # NDID = # RING # #ModemResetCmds: "AT#CID=10" # enable ANI/DNIS reporting #RingsBeforeAnswer: 2 # collect info between two RINGs #QualifyCID: etc/cid # CID access control list file #CIDNumber: "NMBR = " # pattern string for calling number #CIDName: "NDID = " # pattern string for called number # # T.38 dial modifiers # # F - enable T.38 mode request after dialing # V - disable T.38 mode request after dialing (remote host should do it) # # calling/called number dial modifiers # # L - reset and begin of calling number # D - continue of called number # #ModemDialCmd: ATDF%s # user can override F by dial V #ModemDialCmd: ATDV%s # user can override V by dial F #ModemDialCmd: ATD%sF # user can't override F #ModemDialCmd: ATD%sV # user can't override V #ModemDialCmd: ATD%sVL # user can't override V or calling number t38modem-2.0.0/enginebase.cxx0000664000076400007640000002471311455110341016651 0ustar frolovfrolov00000000000000/* * enginebase.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2007-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: enginebase.cxx,v $ * Revision 1.13 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.12 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.11 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.10 2010/09/22 15:07:45 vfrolov * Added ResetModemState() and OnResetModemState() * * Revision 1.9 2010/09/08 17:22:23 vfrolov * Redesigned modem engine (continue) * * Revision 1.8 2010/07/07 08:09:47 vfrolov * Added IsAttached() * * Revision 1.7 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.6 2009/11/19 14:48:28 vfrolov * Moved common code to class EngineBase * * Revision 1.5 2009/11/19 11:14:04 vfrolov * Added OnUserInput * * Revision 1.4 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.3 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.2 2007/04/09 08:07:12 vfrolov * Added symbolic logging ModemCallbackParam * * Revision 1.1 2007/03/23 09:54:45 vfrolov * Initial revision * */ #include #include "pmutils.h" #include "enginebase.h" #define new PNEW /////////////////////////////////////////////////////////////// #if PTRACING ostream & operator<<(ostream & out, EngineBase::DataType dataType) { switch (dataType) { case EngineBase::dtNone: return out << "dtNone"; case EngineBase::dtRing: return out << "dtRing"; case EngineBase::dtBusy: return out << "dtBusy"; case EngineBase::dtCed: return out << "dtCed"; case EngineBase::dtCng: return out << "dtCng"; case EngineBase::dtSilence: return out << "dtSilence"; case EngineBase::dtHdlc: return out << "dtHdlc"; case EngineBase::dtRaw: return out << "dtRaw"; } return out << "dt" << INT(dataType); } ostream & operator<<(ostream & out, EngineBase::ModemCallbackParam param) { switch (param) { case EngineBase::cbpUserDataMask: break; case EngineBase::cbpOutBufNoFull: return out << "cbpOutBufNoFull"; case EngineBase::cbpUpdateState: return out << "cbpUpdateState"; case EngineBase::cbpReset: return out << "cbpReset"; case EngineBase::cbpOutBufEmpty: return out << "cbpOutBufEmpty"; case EngineBase::cbpUserInput: return out << "cbpUserInput"; } return out << "cbp" << INT(param); } ostream & operator<<(ostream & out, EngineBase::ModemClass modemClass) { switch (modemClass) { case EngineBase::mcUndefined: return out << "mcUndefined"; case EngineBase::mcAudio: return out << "mcAudio"; case EngineBase::mcFax: return out << "mcFax"; } return out << "mc" << INT(modemClass); } #endif /////////////////////////////////////////////////////////////// EngineBase::EngineBase(const PString &_name) : name(_name) , recvUserInput(NULL) , modemClass(mcUndefined) , hOwnerIn(NULL) , hOwnerOut(NULL) , firstIn(TRUE) , firstOut(TRUE) , isFakeOwnerIn(FALSE) , isFakeOwnerOut(FALSE) , isEnableFakeIn(FALSE) , isEnableFakeOut(FALSE) { } EngineBase::~EngineBase() { if (recvUserInput) delete recvUserInput; if (hOwnerIn != NULL) myPTRACE(1, name << " ~EngineBase WARNING: (In) still open by " << hOwnerIn); if (hOwnerOut != NULL) myPTRACE(1, name << " ~EngineBase WARNING: (Out) still open by " << hOwnerOut); if (!modemCallback.IsNULL()) myPTRACE(1, name << " ~EngineBase WARNING: !modemCallback.IsNULL()"); } PBoolean EngineBase::Attach(const PNotifier &callback) { PTRACE(1, name << " Attach"); PWaitAndSignal mutexWait(Mutex); if (!modemCallback.IsNULL()) { myPTRACE(1, name << " Attach !modemCallback.IsNULL()"); return FALSE; } modemCallback = callback; OnAttach(); return TRUE; } void EngineBase::OnAttach() { PTRACE(1, name << " OnAttach Attached"); OnResetModemState(); } void EngineBase::Detach(const PNotifier &callback) { PTRACE(1, name << " Detach"); PWaitAndSignal mutexWait(Mutex); if (modemCallback.IsNULL()) { myPTRACE(1, name << " Detach Already Detached"); return; } if (modemCallback != callback) { myPTRACE(1, name << " Detach modemCallback != callback"); return; } modemCallback = NULL; modemClass = mcUndefined; OnChangeModemClass(); OnDetach(); } void EngineBase::OnDetach() { myPTRACE(1, name << " OnDetach Detached"); OnResetModemState(); } void EngineBase::ResetModemState() { PWaitAndSignal mutexWaitModem(MutexModem); PWaitAndSignal mutexWait(Mutex); OnResetModemState(); } void EngineBase::OnResetModemState() { myPTRACE(1, name << " OnResetModemState"); } void EngineBase::OpenIn(HOWNERIN hOwner, PBoolean fake) { PWaitAndSignal mutexWait(Mutex); while (hOwnerIn != NULL) { if (hOwnerIn == hOwner) { myPTRACE(1, name << " OpenIn: re-open " << hOwner); return; } if (fake) { myPTRACE(1, name << " OpenIn: disabled close " << hOwnerIn << " by fake " << hOwner); return; } myPTRACE(1, name << " OpenIn " << (isFakeOwnerIn ? ": close fake " : "WARNING: close ") << hOwnerIn << " by " << hOwner); hOwnerIn = NULL; OnCloseIn(); } myPTRACE(1, name << " OpenIn: open " << hOwner); hOwnerIn = hOwner; isFakeOwnerIn = fake; OnOpenIn(); } void EngineBase::OpenOut(HOWNEROUT hOwner, PBoolean fake) { PWaitAndSignal mutexWait(Mutex); while (hOwnerOut != NULL) { if (hOwnerOut == hOwner) { myPTRACE(1, name << " OpenOut: re-open " << hOwner); return; } if (fake) { myPTRACE(1, name << " OpenOut: disabled close " << hOwnerOut << " by fake " << hOwner); return; } myPTRACE(1, name << " OpenOut " << (isFakeOwnerOut ? ": close fake " : "WARNING: close ") << hOwnerOut << " by " << hOwner); hOwnerOut = NULL; OnCloseOut(); } myPTRACE(1, name << " OpenOut: open " << hOwner); hOwnerOut = hOwner; isFakeOwnerOut = fake; OnOpenOut(); } void EngineBase::OnOpenIn() { firstIn = TRUE; ModemCallbackWithUnlock(cbpUpdateState); } void EngineBase::OnOpenOut() { firstOut = TRUE; ModemCallbackWithUnlock(cbpUpdateState); } void EngineBase::CloseIn(HOWNERIN hOwner) { PWaitAndSignal mutexWait(Mutex); if (hOwnerIn == hOwner) { myPTRACE(1, name << " CloseIn: close " << (isFakeOwnerIn ? "fake " : "") << hOwner); if (!isFakeOwnerIn) isEnableFakeIn = FALSE; // allow re-enable fake stream hOwnerIn = NULL; OnCloseIn(); } else { myPTRACE(1, name << " CloseIn: re-close " << hOwner); } } void EngineBase::CloseOut(HOWNEROUT hOwner) { PWaitAndSignal mutexWait(Mutex); if (hOwnerOut == hOwner) { myPTRACE(1, name << " CloseOut: close " << (isFakeOwnerOut ? "fake " : "") << hOwner); if (!isFakeOwnerOut) isEnableFakeOut = FALSE; // allow re-enable fake stream hOwnerOut = NULL; OnCloseOut(); } else { myPTRACE(1, name << " CloseOut: re-close " << hOwner); } } void EngineBase::OnCloseIn() { ModemCallbackWithUnlock(cbpUpdateState); } void EngineBase::OnCloseOut() { ModemCallbackWithUnlock(cbpUpdateState); } void EngineBase::EnableFakeIn(PBoolean enable) { PWaitAndSignal mutexWait(Mutex); if (isEnableFakeIn == enable) return; myPTRACE(3, name << " EnableFakeIn: " << (enable ? "enable" : "disable")); isEnableFakeIn = enable; OnChangeEnableFakeIn(); } void EngineBase::OnChangeEnableFakeIn() { if (!isEnableFakeIn && hOwnerIn != NULL && isFakeOwnerIn) { myPTRACE(1, name << " OnChangeEnableFakeIn: close fake " << hOwnerIn); hOwnerIn = NULL; OnCloseIn(); } } void EngineBase::EnableFakeOut(PBoolean enable) { PWaitAndSignal mutexWait(Mutex); if (isEnableFakeOut == enable) return; myPTRACE(3, name << " EnableFakeOut: " << (enable ? "enable" : "disable")); isEnableFakeOut = enable; OnChangeEnableFakeOut(); } void EngineBase::OnChangeEnableFakeOut() { if (!isEnableFakeOut && hOwnerOut != NULL && isFakeOwnerOut) { myPTRACE(1, name << " OnChangeEnableFakeOut: close fake " << hOwnerOut); hOwnerOut = NULL; OnCloseOut(); } } void EngineBase::ChangeModemClass(ModemClass newModemClass) { PWaitAndSignal mutexWait(Mutex); if (modemClass == newModemClass) return; modemClass = newModemClass; myPTRACE(1, name << " ChangeModemClass to " << modemClass); OnChangeModemClass(); } void EngineBase::OnChangeModemClass() { if (modemClass == mcAudio) { if (!recvUserInput) recvUserInput = new DataStream(64); } else { if (recvUserInput) { delete recvUserInput; recvUserInput = NULL; } } myPTRACE(1, name << " OnChangeModemClass to " << modemClass); } PBoolean EngineBase::TryLockModemCallback() { MutexModem.Wait(); if (!MutexModemCallback.Wait(0)) { MutexModem.Signal(); return FALSE; } return TRUE; } void EngineBase::UnlockModemCallback() { MutexModemCallback.Signal(); MutexModem.Signal(); } void EngineBase::ModemCallbackWithUnlock(INT extra) { Mutex.Signal(); MutexModemCallback.Wait(); if (!modemCallback.IsNULL()) modemCallback(*this, extra); MutexModemCallback.Signal(); Mutex.Wait(); } void EngineBase::WriteUserInput(const PString & value) { myPTRACE(1, name << " WriteUserInput " << value); PWaitAndSignal mutexWait(Mutex); OnUserInput(value); } void EngineBase::OnUserInput(const PString & value) { PTRACE(4, name << " OnUserInput " << value); if (recvUserInput && !recvUserInput->isFull()) { recvUserInput->PutData((const char *)value, value.GetLength()); ModemCallbackWithUnlock(cbpUserInput); } } int EngineBase::RecvUserInput(void * pBuf, PINDEX count) { PWaitAndSignal mutexWaitModem(MutexModem); PWaitAndSignal mutexWait(Mutex); if (!recvUserInput) return -1; return recvUserInput->GetData(pBuf, count); } /////////////////////////////////////////////////////////////// t38modem-2.0.0/pmodeme.cxx0000664000076400007640000041210711515616302016202 0ustar frolovfrolov00000000000000/* * pmodeme.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2001-2011 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: pmodeme.cxx,v $ * Revision 1.107 2011/01/19 17:17:54 vfrolov * Extended AT#CIDFMT command for setting value format for NMBR tag * * Revision 1.106 2011/01/14 20:32:11 vfrolov * Added DATE, TIME and NAME tags to Caller ID report * Added AT#CIDFMT command * * Revision 1.105 2011/01/12 12:23:43 vfrolov * Replaced hardcoded workaround for mgetty-voice by conditional one * * Revision 1.104 2010/12/28 12:29:07 vfrolov * Disabled echo in non-command state * * Revision 1.103 2010/10/12 16:46:25 vfrolov * Implemented fake streams * * Revision 1.102 2010/10/08 12:31:03 vfrolov * Optimized Mutex usage * * Revision 1.101 2010/10/08 06:06:39 vfrolov * Added diagErrorMask * * Revision 1.100 2010/10/06 16:54:19 vfrolov * Redesigned engine opening/closing * * Revision 1.99 2010/10/06 10:13:23 vfrolov * Fixed previous fix * * Revision 1.98 2010/10/06 09:06:48 vfrolov * Fixed crash at dialing reset (reported by gorod225) * * Revision 1.97 2010/09/29 11:52:59 vfrolov * Redesigned engine attaching/detaching * * Revision 1.96 2010/09/22 15:51:13 vfrolov * Moved ResetModemState() to EngineBase * * Revision 1.95 2010/09/14 06:35:10 vfrolov * Implemented dial string terminated by a semicolon ("ATD;[...]") * * Revision 1.94 2010/09/10 18:08:07 vfrolov * Implemented +VTD command * Cleaned up code * * Revision 1.93 2010/09/10 05:34:12 vfrolov * Allowed "+ A B C D" dialing digits * Added ignoring unrecognized dialing digits (V.250) * Added missing on-hook's * * Revision 1.92 2010/09/08 17:22:23 vfrolov * Redesigned modem engine (continue) * * Revision 1.91 2010/07/09 04:46:55 vfrolov * Implemented alternate route * * Revision 1.90 2010/07/08 05:11:34 vfrolov * Redesigned modem engine (continue) * * Revision 1.89 2010/07/07 13:40:56 vfrolov * Fixed ussue with call clearing in stSend state * Added missing responce tracing * * Revision 1.88 2010/07/07 08:22:47 vfrolov * Redesigned modem engine * * Revision 1.87 2010/03/23 08:58:14 vfrolov * Fixed issues with +FTS and +FRS * * Revision 1.86 2010/03/18 08:42:17 vfrolov * Added named tracing of data types * * Revision 1.85 2010/02/05 14:55:33 vfrolov * Used S7 timeout * * Revision 1.84 2010/02/02 09:51:11 vfrolov * Added missing timerRing.Stop() * * Revision 1.83 2010/02/02 08:41:56 vfrolov * Implemented ringing indication for voice class dialing * * Revision 1.82 2010/01/28 10:30:37 vfrolov * Added cleaning user input buffers for non-audio classes * * Revision 1.81 2010/01/27 14:03:38 vfrolov * Added missing mutexes * * Revision 1.80 2010/01/22 14:11:40 vfrolov * Added missing characters # and * for extension numbers * Thanks to Dmitry * * Revision 1.79 2009/12/02 09:06:42 vfrolov * Added a short delay after transmitting of signal before call clearing * * Revision 1.78 2009/11/20 16:37:27 vfrolov * Fixed audio class application blocking by forced T.38 mode * * Revision 1.77 2009/11/19 11:18:16 vfrolov * Added handling T.38 CED indication * * Revision 1.76 2009/11/18 19:08:47 vfrolov * Moved common code to class EngineBase * * Revision 1.75 2009/11/17 11:25:48 vfrolov * Added missing delay before sending fax-no-force requestmode * Redesigned handling stConnectHandle state * * Revision 1.74 2009/11/06 10:02:29 vfrolov * Fixed typo in fax-no-force * * Revision 1.73 2009/11/02 18:06:33 vfrolov * Added fax-no-forse requestmode * * Revision 1.72 2009/10/27 18:25:22 vfrolov * Added reseting send state on detaching engines * * Revision 1.71 2009/10/01 13:31:12 vfrolov * Ported to OPAL SVN trunk * * Revision 1.70 2009/07/29 17:12:35 vfrolov * Wait audioEngine on stConnectHandle * * Revision 1.69 2009/07/10 15:23:31 vfrolov * Implicitly dial modifier '@' will continue of called number * * Revision 1.68 2009/07/10 14:04:13 vfrolov * Changed usage multiple dial modifiers '@' * each next '@' overrides previous '@' * ("ATD4444@123@456" eq "ATD4444@456", "ATD4444@123@" eq "ATD4444") * Dial modifiers (except 'D') can be used after '@' * Dial modifiers 'T' and 'P' can be used instead 'D' * * Revision 1.67 2009/07/06 08:28:44 vfrolov * Added DTMF shielding * * Revision 1.66 2009/07/03 16:38:17 vfrolov * Added more state tracing * * Revision 1.65 2009/07/03 16:22:56 vfrolov * Added missing pPlayTone deletings * Added more state tracing * * Revision 1.64 2009/07/02 15:09:48 vfrolov * Improved state tracing * * Revision 1.63 2009/07/02 06:55:30 vfrolov * Fixed +VSM=? and +VLS=? for modem type autodetecting by VentaFax * Thanks Dmitry (gorod225) * * Revision 1.62 2009/07/02 05:41:37 vfrolov * Enabled +VIT > 0 (for compatibility with some voice applications) * * Revision 1.61 2009/07/01 15:11:58 vfrolov * Fixed codec 128,"8-BIT LINEAR" * * Revision 1.60 2009/07/01 10:52:06 vfrolov * Enabled +VSM= w/o * * Revision 1.59 2009/07/01 08:20:39 vfrolov * Implemented +VIP command * * Revision 1.58 2009/06/30 13:55:27 vfrolov * Added +VSM codecs * 128,"8-BIT LINEAR",8,0,(8000),(0),(0) * 130,"UNSIGNED PCM",8,0,(8000),(0),(0) * 131,"G.711 ULAW",8,0,(8000),(0),(0) * Added +VLS * 5,"ST",00000000,00000000,00000000 * * Revision 1.57 2009/06/30 10:50:33 vfrolov * Added +VSM codecs * 0,"SIGNED PCM",8,0,(8000),(0),(0) * 1,"UNSIGNED PCM",8,0,(8000),(0),(0) * * Revision 1.56 2009/06/29 15:36:38 vfrolov * Added ability to dial in connection establised state * * Revision 1.55 2009/06/29 13:28:42 vfrolov * Added +VSM codecs * 4,"G711U",8,0,(8000),(0),(0) * 5,"G711A",8,0,(8000),(0),(0) * * Revision 1.54 2009/06/25 16:48:52 vfrolov * Added stub for +VSD command * * Revision 1.53 2009/06/25 12:46:38 vfrolov * Implemented dialing followed answering ("ATD;A") * * Revision 1.52 2009/06/24 13:12:58 vfrolov * Implemented +VEM and +VIT commands * * Revision 1.51 2009/06/24 12:48:37 vfrolov * Added stubs for +VGR and +VGT commands * * Revision 1.50 2009/06/24 12:19:01 vfrolov * Added stubs for +VRA and +VRN * * Revision 1.49 2009/06/24 08:04:46 vfrolov * Added semicolon concatenating of commands * * Revision 1.48 2009/06/22 16:05:48 vfrolov * Added ability to dial extension numbers * * Revision 1.47 2009/05/06 09:17:23 vfrolov * Enabled dialing characters # and * * * Revision 1.46 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.45 2008/09/10 07:05:06 frolov * Fixed doubled mutex lock * * Revision 1.44 2007/08/27 10:55:21 vfrolov * Added missing moreFrames = FALSE * * Revision 1.43 2007/08/24 16:12:14 vfrolov * Disabled CNG sending in voice class mode * * Revision 1.42 2007/05/04 09:58:57 vfrolov * Fixed Attach(audioEngine) * * Revision 1.41 2007/04/24 16:26:02 vfrolov * Fixed unexpected state change * * Revision 1.40 2007/04/09 08:07:12 vfrolov * Added symbolic logging ModemCallbackParam * * Revision 1.39 2007/03/30 07:56:36 vfrolov * Included g711.c * * Revision 1.38 2007/03/23 14:54:20 vfrolov * Fixed compiler warnings * * Revision 1.37 2007/03/23 10:14:35 vfrolov * Implemented voice mode functionality * * Revision 1.36 2007/03/22 16:26:04 vfrolov * Fixed compiler warnings * * Revision 1.35 2007/02/22 16:00:33 vfrolov * Implemented AT#HCLR command * * Revision 1.34 2006/12/11 11:19:48 vfrolov * Fixed race condition with modem Callback * * Revision 1.33 2006/12/07 10:53:24 vfrolov * Added OnParentStop() * * Revision 1.32 2006/12/01 13:35:25 vfrolov * Fixed modem locking after unexpected dial while connection * * Revision 1.31 2005/11/22 16:39:36 vfrolov * Fixed MSVC compile warning * * Revision 1.30 2005/03/05 15:42:39 vfrolov * Added missing check for PTRACING * Fixed typo in T38DLE trace * * Revision 1.29 2005/03/04 16:35:38 vfrolov * Implemented AT#DFRMC command * Redisigned class Profile * * Revision 1.28 2005/02/16 12:14:47 vfrolov * Send CONNECT just before data for AT+FRM command * * Revision 1.27 2005/02/10 10:35:18 vfrolov * Fixed AT prefix searching * * Revision 1.26 2005/02/03 11:32:11 vfrolov * Fixed MSVC compile warnings * * Revision 1.25 2005/02/01 11:43:46 vfrolov * Implemented ATV0 command (numeric format for result codes) * Implemented AT+FMI?, AT+FMM? and AT+FMR? commands * Added stubs for ATBn, ATX3 and AT+FCLASS=0 commands * Added stub for AT+FLO command * * Revision 1.24 2004/10/27 13:36:26 vfrolov * Decreased binary, DLE, and callback tracing * * Revision 1.23 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.22 2004/06/24 17:20:22 vfrolov * Added stub for ATXn command * * Revision 1.21 2004/05/09 07:46:11 csoutheren * Updated to compile with new PIsDescendant function * * Revision 1.20 2004/03/01 17:14:34 vfrolov * Fixed binary log in command mode * * Revision 1.19 2003/12/04 16:09:51 vfrolov * Implemented FCS generation * Implemented ECM support * * Revision 1.18 2003/01/08 16:58:58 vfrolov * Added cbpOutBufNoFull and isOutBufFull() * * Revision 1.17 2002/12/30 12:49:29 vfrolov * Added tracing thread's CPU usage (Linux only) * * Revision 1.16 2002/12/20 10:12:50 vfrolov * Implemented tracing with PID of thread (for LinuxThreads) * or ID of thread (for other POSIX Threads) * * Revision 1.15 2002/12/19 10:31:33 vfrolov * Changed usage multiple dial modifiers 'L' (for secure reasons) * each next 'L' overrides previous 'L' * ("ATD4444L123L456" eq "ATD4444L456", "ATD4444L123L" eq "ATD4444") * Added dial modifier 'D' - continue dial number * ("ATD000L123D4444" eq "ATD0004444L123") * Added mising spaces into "NMBR = " and "NDID = " * * Revision 1.14 2002/11/05 13:59:11 vfrolov * Implemented Local Party Number dial modifier 'L' * (put dial string 1234L5678 to dial 1234 from 5678) * * Revision 1.13 2002/05/15 16:10:52 vfrolov * Reimplemented AT+FTS and AT+FRS * Added workaround "Reset state stSendAckWait" * * Revision 1.12 2002/05/07 10:36:16 vfrolov * Added more code for case stResetHandle * Changed duration of CED (T.30 requires 2.6...4.0 secs) * * Revision 1.11 2002/04/19 14:06:04 vfrolov * Implemented T.38 mode request dial modifiers * F - enable * V - disable * * Revision 1.10 2002/04/03 02:45:36 vfrolov * Implemented AT#CID=10 - ANI/DNIS reporting between RINGs * * Revision 1.9 2002/03/01 14:59:48 vfrolov * Get data for Revision string from version.h * * Revision 1.8 2002/03/01 10:00:26 vfrolov * Added Copyright header * Implemented connection established handling and mode change request * Implemented ATI8 command * Fixed some deadlocks * Added some other changes * * Revision 1.7 2002/02/11 08:40:15 vfrolov * More clear call implementation * * Revision 1.6 2002/01/10 06:10:02 craigs * Added MPL header * * Revision 1.5 2002/01/06 03:48:45 craigs * Added changes to support efax 0.9 * Thanks to Vyacheslav Frolov * * Revision 1.4 2002/01/03 21:36:00 craigs * Added change to use S1 register for number of rings on answer * Thanks to Vyacheslav Frolov * * Revision 1.3 2002/01/02 04:49:37 craigs * Added support for ATS0 register * * Revision 1.2 2002/01/01 23:59:52 craigs * Lots of additional implementation thanks to Vyacheslav Frolov * */ #include #include #include "pmodemi.h" #include "pmodeme.h" #include "dle.h" #include "fcs.h" #include "t38engine.h" #include "audio.h" #include "version.h" /////////////////////////////////////////////////////////////// #include "g711.c" /////////////////////////////////////////////////////////////// static const char Manufacturer[] = "Vyacheslav Frolov"; static const char Model[] = "T38FAX"; #define _TOSTR(s) #s #define TOSTR(s) _TOSTR(s) static const char Revision[] = TOSTR(MAJOR_VERSION) "." TOSTR(MINOR_VERSION) "." TOSTR(BUILD_NUMBER); /////////////////////////////////////////////////////////////// #define DeclareStringParam(name) \ public: \ void name(const PString &_s##name) { s##name = _s##name; } \ const PString &name() const { return s##name; } \ protected: \ PString s##name; /////////////////////////////////////////////////////////////// class Profile { protected: #define PROFILE_SIZE_STD 40 #define PROFILE_SIZE_EXT 10 #define PROFILE_SIZE_VOICE 10 #define PROFILE_SIZE (PROFILE_SIZE_STD + PROFILE_SIZE_EXT + PROFILE_SIZE_VOICE) enum { MinRegStd = 0, MaxRegStd = MinRegStd + PROFILE_SIZE_STD - 1, MinRegExt = MaxRegStd + 1, MaxRegExt = MinRegExt + PROFILE_SIZE_EXT - 1, MinRegVoice = MaxRegExt + 1, MaxRegVoice = MinRegVoice + PROFILE_SIZE_VOICE - 1, MaxBit = 7, }; public: Profile(); #define DeclareRegisterBit(name, byte, bit) \ void name(PBoolean val) { SetBit(byte, bit, val); } \ PBoolean name() const { PBoolean val; GetBit(byte, bit, val); return val; } DeclareRegisterBit(Echo, 23, 0); DeclareRegisterBit(asciiResultCodes, 23, 6); DeclareRegisterBit(noResultCodes, 23, 7); #define DeclareRegisterByte(name, byte) \ void name(BYTE val) { SetReg(byte, val); } \ BYTE name() const { BYTE val; GetReg(byte, val); return val; } DeclareRegisterByte(AutoAnswer, 0); DeclareRegisterByte(RingCount, 1); DeclareRegisterByte(S7, 7); DeclareRegisterByte(DialTimeComma, 8); DeclareRegisterByte(DialTimeDTMF, 11); DeclareRegisterByte(IfcByDCE, MinRegExt + 0); DeclareRegisterByte(IfcByDTE, MinRegExt + 1); DeclareRegisterByte(ClearMode, MinRegExt + 2); DeclareRegisterByte(DelayFrmConnect, MinRegExt + 3); DeclareRegisterByte(DidMode, MinRegExt + 4); DeclareRegisterByte(CidMode, MinRegExt + 5); #if 5 >= PROFILE_SIZE_EXT #error *** The PROFILE_SIZE_EXT is too small to declare register *** #endif DeclareRegisterByte(Vtd, MinRegVoice + 0); DeclareRegisterByte(Vcml, MinRegVoice + 1); DeclareRegisterByte(Vsds, MinRegVoice + 2); DeclareRegisterByte(Vsdi, MinRegVoice + 3); DeclareRegisterByte(VgrInterval, MinRegVoice + 4); DeclareRegisterByte(VgtInterval, MinRegVoice + 5); DeclareRegisterByte(VraInterval, MinRegVoice + 6); DeclareRegisterByte(VrnInterval, MinRegVoice + 7); DeclareRegisterByte(CidNameFmt, MinRegVoice + 8); DeclareRegisterByte(CidNmbrFmt, MinRegVoice + 9); #if 9 >= PROFILE_SIZE_VOICE #error *** The PROFILE_SIZE_VOICE is too small to declare register *** #endif void Flo(BYTE val) { IfcByDTE(val); IfcByDCE(val); } BYTE Flo() const { if (IfcByDTE() == IfcByDCE()) return IfcByDTE(); return 255; } PBoolean SetBit(PINDEX r, PINDEX b, PBoolean val) { if( !ChkRB(r, b) ) return FALSE; BYTE msk = MaskB(b); if( val ) S[r] |= msk; else S[r] &= ~msk; return TRUE; } PBoolean GetBit(PINDEX r, PINDEX b, PBoolean &val) const { if (!ChkRB(r, b)) { val = 0; return FALSE; } BYTE msk = MaskB(b); val = (S[r] & msk) ? TRUE : FALSE; return TRUE; } PBoolean SetReg(PINDEX r, BYTE val) { if( !ChkR(r) ) return FALSE; S[r] = val; return TRUE; } PBoolean GetReg(PINDEX r, BYTE &val) const { if (!ChkR(r)) { val = 0; return FALSE; } val = S[r]; return TRUE; } PBoolean SetBits(PINDEX r, PINDEX bl, PINDEX bh, BYTE val) { if( !ChkRBB(r, bl, bh) ) return FALSE; BYTE msk = MaskBB(bl, bh); S[r] &= ~msk; S[r] &= (val << bl) & msk; return TRUE; } PBoolean GetBits(PINDEX r, PINDEX bl, PINDEX bh, BYTE &val) const { if (!ChkRBB(r, bl, bh)) { val = 0; return FALSE; } BYTE msk = MaskBB(bl, bh); val = BYTE((S[r] & msk) >> bl); return TRUE; } Profile &operator=(const Profile &p); Profile &SetVoiceProfile(const Profile &p); void ModemClass(const PString &_modemClass) { modemClass = _modemClass; if (modemClass == "1") { modemClassId = EngineBase::mcFax; } else if (modemClass == "8") { modemClassId = EngineBase::mcAudio; } else { modemClassId = EngineBase::mcUndefined; } } const PString &ModemClass() const { return modemClass; } EngineBase::ModemClass ModemClassId() const { return modemClassId; } protected: static PBoolean ChkR(PINDEX r) { return r < PROFILE_SIZE; } static PBoolean ChkB(PINDEX b) { return b <= MaxBit; } static PBoolean ChkRB(PINDEX r, PINDEX b) { return ChkR(r) && ChkB(b); } static PBoolean ChkRBB(PINDEX r, PINDEX bl, PINDEX bh) { return ChkR(r) && ChkB(bl) && ChkB(bh) && bl <= bh; } static BYTE MaskB(PINDEX b) { return "\x01\x02\x04\x08\x10\x20\x40\x80"[b]; } static BYTE MaskBB(PINDEX bl, PINDEX bh) { // bl <= bh return BYTE(("\x01\x03\x07\x0F\x1F\x3F\x7F\xFF"[bh - bl]) << bl); } BYTE S[PROFILE_SIZE]; // S-registers PString modemClass; EngineBase::ModemClass modemClassId; #undef PROFILE_SIZE #undef PROFILE_SIZE_VOICE #undef PROFILE_SIZE_EXT #undef PROFILE_SIZE_STD }; static const Profile Profiles[1]; /////////////////////////////////////////////////////////////// class Timeout : public PTimer { PCLASSINFO(Timeout, PTimer); public: Timeout(const PNotifier &callback, PBoolean _continuous = FALSE) : state(0), continuous(_continuous) { SetNotifier(callback); } void Start(unsigned period) { PWaitAndSignal mutexWait(Mutex); state = 1; if( continuous ) { RunContinuous(period); OnTimeout(); } else { PTimer::operator=(period); } } void Stop() { PWaitAndSignal mutexWait(Mutex); state = 0; PTimer::operator=(0); } PBoolean Get() { PWaitAndSignal mutexWait(Mutex); if (state == 2) { state = continuous ? 1 : 0; return TRUE; } return FALSE; } protected: void OnTimeout() { PWaitAndSignal mutexWait(Mutex); if( state == 1 ) state = 2; PTimer::OnTimeout(); } int state; PBoolean continuous; PMutex Mutex; }; /////////////////////////////////////////////////////////////// enum CallState { // ---------+---------------------------+--------+------------------------------------------------+ // | on sending command | on | on received command | // +---------------------------+clearing+------------------------------------------------+ // |"dial"|"answer"|"clearcall"|off_hook|"call"|"alerting"|"established"| "clearcall" | // ---------+------+--------+-----+-----+--------+------+----------+-------------+----------+-----+ // off_hook | true | true |true |false| true |false | true | true | true |false| // ---------+------+--------+-----+-----+--------+------+----------+-------------+----------+-----+ cstCleared, // )--+ <--+ <--+ )--+ <--+ <--+ | // | | | | | | | // | | | | | | | cstDialing, // <--+ )--+ )--+ )--+ | )--+ )--+ )--+ )--+ )--+ | // | | | | | | | | | cstAlerted, // )--+ )--+ )--+ | <--+ )--+ )--+ )--+ | // | | | | | | | | // | | | | | | | | cstCalled, // )--+ )--+ )--+ | <--+ | | )--+ | // | | | | | | | | cstAnswering, // <--+ )--+ )--+ )--+ )--+ )--+ )--+ | // | | | | | | | // | | | | | | | cstEstablished, // )--+ )--+ )--+ <--+ )--+ )--+ | // | | | | | | cstReleasing, // <--+ )--+ )--+ <--+ )--+ | }; // ---------+------+--------+-----+-----+--------+------+----------+-------------+----------+-----+ #if PTRACING static ostream & operator<<(ostream & out, CallState state) { switch (state) { case cstCleared: return out << "cstCleared"; case cstDialing: return out << "cstDialing"; case cstAlerted: return out << "cstAlerted"; case cstCalled: return out << "cstCalled"; case cstAnswering: return out << "cstAnswering"; case cstEstablished: return out << "cstEstablished"; case cstReleasing: return out << "cstReleasing"; } return out << "cst" << INT(state); } #endif /////////////////////////////////////////////////////////////// #if PTRACING struct CallStateAndSubState { CallStateAndSubState(CallState s, int ss) : state(s), subState(ss) {} CallState state; int subState; }; static ostream & operator<<(ostream & out, const CallStateAndSubState &stateAndSubState) { out << stateAndSubState.state; if (stateAndSubState.state == cstReleasing) out << "." << stateAndSubState.subState; return out; } #endif /////////////////////////////////////////////////////////////// enum State { stCommand, stDial, stConnectWait, stConnectHandle, stReqModeAckWait, stReqModeAckHandle, stSend, stSendBufEmptyHandle, stSendAckWait, stSendAckHandle, stRecvBegWait, stRecvBegHandle, stRecv, }; #if PTRACING static ostream & operator<<(ostream & out, State state) { switch (state) { case stCommand: return out << "stCommand"; case stDial: return out << "stDial"; case stConnectWait: return out << "stConnectWait"; case stConnectHandle: return out << "stConnectHandle"; case stReqModeAckWait: return out << "stReqModeAckWait"; case stReqModeAckHandle: return out << "stReqModeAckHandle"; case stSend: return out << "stSend"; case stSendBufEmptyHandle: return out << "stSendBufEmptyHandle"; case stSendAckWait: return out << "stSendAckWait"; case stSendAckHandle: return out << "stSendAckHandle"; case stRecvBegWait: return out << "stRecvBegWait"; case stRecvBegHandle: return out << "stRecvBegHandle"; case stRecv: return out << "stRecv"; } return out << "st" << INT(state); } #endif /////////////////////////////////////////////////////////////// enum SubStateConnectHandle { chConnected, chWaitAudioEngine, chAudioEngineAttached, chWaitPlayTone, chTonePlayed, chConnectionEstablishDelay, chConnectionEstablished, }; #if PTRACING static ostream & operator<<(ostream & out, SubStateConnectHandle subState) { switch (subState) { case chConnected: return out << "chConnected"; case chWaitAudioEngine: return out << "chWaitAudioEngine"; case chAudioEngineAttached: return out << "chAudioEngineAttached"; case chWaitPlayTone: return out << "chWaitPlayTone"; case chTonePlayed: return out << "chTonePlayed"; case chConnectionEstablishDelay: return out << "chConnectionEstablishDelay"; case chConnectionEstablished: return out << "chConnectionEstablished"; } return out << "ch" << INT(subState); } #endif /////////////////////////////////////////////////////////////// #if PTRACING struct StateAndSubState { StateAndSubState(State s, int ss) : state(s), subState(ss) {} State state; int subState; }; static ostream & operator<<(ostream & out, const StateAndSubState &stateAndSubState) { out << stateAndSubState.state; if (stateAndSubState.state == stConnectHandle) out << "." << SubStateConnectHandle(stateAndSubState.subState); return out; } #endif /////////////////////////////////////////////////////////////// enum ModemClassEngine { mceAudio, mceT38, mceNumberOfItems, }; #if PTRACING static ostream & operator<<(ostream & out, ModemClassEngine mce) { switch (mce) { case mceAudio: return out << "mceAudio"; case mceT38: return out << "mceT38"; default: break; } return out << "mce" << INT(mce); } #endif /////////////////////////////////////////////////////////////// #define DeclareResultCode(name, v0, v1) \ PString name() const { return P.asciiResultCodes() ? (v1) : (v0); } /////////////////////////////////////////////////////////////// class ModemEngineBody : public PObject { PCLASSINFO(ModemEngineBody, PObject); public: /**@name Construction */ //@{ ModemEngineBody(ModemEngine &_parent, const PNotifier &_callbackEndPoint); ~ModemEngineBody(); //@} /**@name Operations */ //@{ PBoolean Request(PStringToString &request); EngineBase *NewPtrEngine(ModemClassEngine mce); void OnParentStop(); void HandleData(const PBYTEArray &buf, PBYTEArray &bresp); void CheckState(PBYTEArray &bresp); void CheckStatePost(); PBoolean IsReady() const { PWaitAndSignal mutexWait(Mutex); return state == stCommand && !off_hook && callState == cstCleared && (PTime() - lastOnHookActivity) > 5*1000; } PBoolean isOutBufFull() const { PWaitAndSignal mutexWait(Mutex); return currentClassEngine && currentClassEngine->isOutBufFull(); } //@} protected: PBoolean Echo() const { return P.Echo(); } PBoolean HandleClass1Cmd(const char **ppCmd, PString &resp, PBoolean &ok, PBoolean &crlf); PBoolean HandleClass8Cmd(const char **ppCmd, PString &resp, PBoolean &ok, PBoolean &crlf); PBoolean Answer(); void HandleCmd(PString &resp); void HandleCmdRest(PString &resp); unsigned EstablishmentTimeout() const { return unsigned(P.S7() ? P.S7() : Profiles[0].S7()) * 1000; } void ResetDleData() { dleData.Clean(); dataCount = 0; moreFrames = FALSE; } PBoolean SetBitRevDleData() { switch (P.ModemClassId()) { case EngineBase::mcAudio: dleData.BitRev( #ifdef ALAW_132_BIT_REVERSE P.Vcml() == 132 ? TRUE : #endif FALSE); break; case EngineBase::mcFax: dleData.BitRev(TRUE); break; default: return FALSE; } return TRUE; } PBoolean SendSilence(int ms) { if (P.ModemClassId() == EngineBase::mcFax && currentClassEngine) { dataType = EngineBase::dtSilence; SetState(stSend); if (currentClassEngine->SendStart(dataType, ms)) { SetState(stSendAckWait); if (currentClassEngine->SendStop(FALSE, NextSeq())) return TRUE; } } SetState(stCommand); return FALSE; } PBoolean _SendStart(EngineBase::DataType dt, int br, PString &resp) { dataType = dt; ResetDleData(); SetBitRevDleData(); SetState(stSend); if (!currentClassEngine || !currentClassEngine->SendStart(dataType, br)) { SetState(stCommand); return FALSE; } resp = RC_CONNECT(); return TRUE; } PBoolean SendStart(EngineBase::DataType dt, int br, PString &resp) { PWaitAndSignal mutexWait(Mutex); return _SendStart(dt, br, resp); } PBoolean RecvStart(EngineBase::DataType dt, int br) { PBoolean done = FALSE; PWaitAndSignal mutexWait(Mutex); dataType = dt; SetBitRevDleData(); SetState(stRecvBegWait); timeout.Start(60000); if (!currentClassEngine || !currentClassEngine->RecvWait(dataType, br, NextSeq(), done)) { SetState(stCommand); timeout.Stop(); return FALSE; } if (done) { SetState(stRecvBegHandle); timeout.Stop(); parent.SignalDataReady(); } return TRUE; } void _AttachEngine(ModemClassEngine mce); void _DetachEngine(ModemClassEngine mce); void _ClearCall(); int NextSeq() { return seq = ++seq & EngineBase::cbpUserDataMask; } ModemEngine &parent; EngineBase *activeEngines[mceNumberOfItems]; EngineBase *currentClassEngine; PBoolean enableFakeIn[mceNumberOfItems]; PBoolean enableFakeOut[mceNumberOfItems]; const PNotifier callbackEndPoint; PDECLARE_NOTIFIER(PObject, ModemEngineBody, OnEngineCallback); PDECLARE_NOTIFIER(PObject, ModemEngineBody, OnTimerCallback); const PNotifier engineCallback; const PNotifier timerCallback; Timeout timerRing; Timeout timerBusy; Timeout timeout; PTime lastOnHookActivity; int seq; PBoolean forceFaxMode; PBoolean connectionEstablished; PBoolean off_hook; int lockReleasingState; enum CallDirection { cdUndefined, cdOutgoing, cdIncoming, }; CallDirection callDirection; CallState callState; int callSubState; State state; int subState; #define TRACE_STATE(level, header) \ PTRACE(level, header \ " " << (off_hook ? "off" : "on") << "-hook" \ " " << CallStateAndSubState(callState, callSubState) << \ " " << StateAndSubState(state, subState)) PBoolean OffHook() { if (!off_hook) { off_hook = TRUE; TRACE_STATE(4, "ModemEngineBody::OffHook:"); return TRUE; } return FALSE; } void OnHook(); void SetCallState(CallState newState) { if (callState != newState || callSubState != 0) { callState = newState; callSubState = 0; TRACE_STATE(4, "ModemEngineBody::SetCallState:"); } } void SetCallSubState(int newSubState) { if (callSubState != newSubState) { callSubState = newSubState; TRACE_STATE(4, "ModemEngineBody::SetCallSubState:"); } } void SetState(State newState, int newSubState = 0) { if (state != newState || subState != newSubState) { state = newState; subState = newSubState; TRACE_STATE(4, "ModemEngineBody::SetState:"); } } void SetSubState(int newSubState) { if (subState != newSubState) { subState = newSubState; TRACE_STATE(4, "ModemEngineBody::SetSubState:"); } } void OnChangeModemClass() { for (int i = 0 ; i < mceNumberOfItems ; i++) { if (activeEngines[i]) activeEngines[i]->ChangeModemClass(P.ModemClassId()); } switch (P.ModemClassId()) { case EngineBase::mcAudio: currentClassEngine = activeEngines[mceAudio]; break; case EngineBase::mcFax: currentClassEngine = activeEngines[mceT38]; break; default: currentClassEngine = NULL; } } void SendOnIdle(EngineBase::DataType dt) { if (sendOnIdle != dt) { sendOnIdle = dt; PTRACE(4, "ModemEngineBody::SendOnIdle " << sendOnIdle); for (int i = 0 ; i < mceNumberOfItems ; i++) { if (activeEngines[i]) activeEngines[i]->SendOnIdle(sendOnIdle); } } } int param; PStringToString params; PString cmd; PString cmdRest; EngineBase::DataType dataType; EngineBase::DataType sendOnIdle; PDTMFEncoder *pPlayTone; DLEData dleData; PINDEX dataCount; PBoolean moreFrames; FCS fcs; Profile P; PMutex Mutex; PTime callTime; DeclareStringParam(CallToken) DeclareStringParam(SrcNum) DeclareStringParam(SrcName) DeclareStringParam(DstNum) DeclareResultCode(RC_PREF, "", "\r\n") DeclareResultCode(RC_OK, "0\r", "OK\r\n") DeclareResultCode(RC_CONNECT, "1\r", "CONNECT\r\n") DeclareResultCode(RC_RING, "2\r", "RING\r\n") DeclareResultCode(RC_NO_CARRIER, "3\r", "NO CARRIER\r\n") DeclareResultCode(RC_ERROR, "4\r", "ERROR\r\n") DeclareResultCode(RC_CONNECT_1200, "5\r", "CONNECT 1200\r\n") DeclareResultCode(RC_NO_DIALTONE, "6\r", "NO DIALTONE\r\n") DeclareResultCode(RC_BUSY, "7\r", "BUSY\r\n") DeclareResultCode(RC_NO_ANSWER, "8\r", "NO ANSWER\r\n") DeclareResultCode(RC_RINGING, "9\r", "RINGING\r\n") DeclareResultCode(RC_FCERROR, "+F4\r", "+FCERROR\r\n") }; /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// ModemEngine::ModemEngine(PseudoModemBody &_parent) : ModemThreadChild(_parent) { body = new ModemEngineBody(*this, Parent().GetCallbackEndPoint()); } ModemEngine::~ModemEngine() { if( body ) delete body; } PBoolean ModemEngine::IsReady() const { return body && body->IsReady(); } T38Engine *ModemEngine::NewPtrT38Engine() const { if (!body) return NULL; EngineBase *engine = body->NewPtrEngine(mceT38); PAssert(engine == NULL || PIsDescendant(engine, T38Engine), PInvalidCast); return (T38Engine *)engine; } AudioEngine *ModemEngine::NewPtrAudioEngine() const { if (!body) return NULL; EngineBase *engine = body->NewPtrEngine(mceAudio); PAssert(engine == NULL || PIsDescendant(engine, AudioEngine), PInvalidCast); return (AudioEngine *)engine; } EngineBase *ModemEngine::NewPtrUserInputEngine() const { if (!body) return NULL; return body->NewPtrEngine(mceAudio); } PBoolean ModemEngine::Request(PStringToString &request) const { return body && body->Request(request); } void ModemEngine::Main() { RenameCurrentThread(ptyName() + "(e)"); myPTRACE(1, "<-> Started"); if( !body ) { myPTRACE(1, "<-> no body" << ptyName()); SignalStop(); return; } for(;;) { PBYTEArray bresp; if (stop) break; body->CheckState(bresp); if (stop) break; while( !body->isOutBufFull() ) { PBYTEArray *buf = Parent().FromInPtyQ(); if (buf) { body->HandleData(*buf, bresp); delete buf; if (stop) break; } else break; } if (stop) break; if (bresp.GetSize()) { ToPtyQ(bresp, bresp.GetSize()); } if (stop) break; body->CheckStatePost(); if (stop) break; WaitDataReady(); } body->OnParentStop(); myPTRACE(1, "<-> Stopped" << GetThreadTimes(", CPU usage: ")); } /////////////////////////////////////////////////////////////// Profile::Profile() { for (PINDEX r = 0 ; r < PINDEX(sizeof(S)/sizeof(S[0])) ; r++) S[r] = 0; S7(60); DialTimeComma(2); DialTimeDTMF(70); Echo(TRUE); VraInterval(50); VrnInterval(10); Vsds(128); Vsdi(50); Vcml(132); Vtd(100); asciiResultCodes(TRUE); noResultCodes(FALSE); ModemClass("1"); } Profile &Profile::operator=(const Profile &p) { for (PINDEX r = 0 ; r <= PINDEX(sizeof(S)/sizeof(S[0])) ; r++) S[r] = p.S[r]; ModemClass(p.ModemClass()); return *this; } Profile &Profile::SetVoiceProfile(const Profile &p) { for (PINDEX r = MinRegVoice ; r <= MaxRegVoice ; r++) S[r] = p.S[r]; return *this; } /////////////////////////////////////////////////////////////// ModemEngineBody::ModemEngineBody(ModemEngine &_parent, const PNotifier &_callbackEndPoint) : parent(_parent), currentClassEngine(NULL), callbackEndPoint(_callbackEndPoint), #ifdef _MSC_VER #pragma warning(disable:4355) // warning C4355: 'this' : used in base member initializer list #endif engineCallback(PCREATE_NOTIFIER(OnEngineCallback)), timerCallback(PCREATE_NOTIFIER(OnTimerCallback)), #ifdef _MSC_VER #pragma warning(default:4355) #endif timerRing(timerCallback, TRUE), timerBusy(timerCallback, TRUE), timeout(timerCallback), seq(0), forceFaxMode(FALSE), connectionEstablished(FALSE), off_hook(FALSE), lockReleasingState(0), callDirection(cdUndefined), callState(cstCleared), state(stCommand), dataType(EngineBase::dtNone), sendOnIdle(EngineBase::dtNone), pPlayTone(NULL) { for (int i = 0 ; i < mceNumberOfItems ; i++) { activeEngines[i] = NULL; enableFakeIn[i] = FALSE; enableFakeOut[i] = FALSE; } } ModemEngineBody::~ModemEngineBody() { PWaitAndSignal mutexWait(Mutex); OnHook(); timeout.Stop(); timerRing.Stop(); timerBusy.Stop(); } void ModemEngineBody::OnParentStop() { PWaitAndSignal mutexWait(Mutex); OnHook(); } void ModemEngineBody::OnHook() { lastOnHookActivity = PTime(); if (off_hook) { for (int i = 0 ; i < mceNumberOfItems ; i++) { enableFakeIn[i] = FALSE; enableFakeOut[i] = FALSE; } timerBusy.Stop(); timeout.Stop(); off_hook = FALSE; callDirection = cdUndefined; forceFaxMode = FALSE; state = stCommand; subState = 0; _DetachEngine(mceT38); _DetachEngine(mceAudio); TRACE_STATE(4, "ModemEngineBody::OnHook:"); } _ClearCall(); } void ModemEngineBody::_ClearCall() { if (callState == cstCleared) return; lockReleasingState++; if (callState != cstReleasing) { SetCallState(cstReleasing); timerRing.Stop(); connectionEstablished = FALSE; if (pPlayTone) { delete pPlayTone; pPlayTone = NULL; } if (off_hook) { timerBusy.Start(1000); if (!activeEngines[mceAudio]) _AttachEngine(mceAudio); for (int i = 0 ; i < mceNumberOfItems ; i++) { enableFakeIn[i] = TRUE; enableFakeOut[i] = TRUE; } if (activeEngines[mceAudio]) activeEngines[mceAudio]->RecvOnIdle(EngineBase::dtBusy); } } if (!CallToken().IsEmpty()) { PStringToString request; request.SetAt("modemtoken", parent.modemToken()); request.SetAt("command", "clearcall"); request.SetAt("calltoken", CallToken()); CallToken(""); Mutex.Signal(); callbackEndPoint(request, 1); Mutex.Wait(); } if (--lockReleasingState == 0 && !off_hook) SetCallState(cstCleared); parent.SignalDataReady(); } PBoolean ModemEngineBody::Request(PStringToString &request) { myPTRACE(3, "ModemEngineBody::Request: " << state << " request={\n" << request << "}"); PString command = request("command"); request.SetAt("response", "reject"); if( command == "call" ) { PWaitAndSignal mutexWait(Mutex); if (callState != cstCleared) { myPTRACE(1, "ModemEngineBody::Request: call already in " << callState << " state"); } else if (off_hook) { myPTRACE(1, "ModemEngineBody::Request: line already in off-hook state"); } else { SetCallState(cstCalled); CallToken(request("calltoken")); SrcNum(request("srcnum")); SrcName(request("srcname")); DstNum(request("dstnum")); callTime = PTime(); P.RingCount(0); timerRing.Start(5000); request.SetAt("response", "confirm"); } } else if (command == "alerting") { PWaitAndSignal mutexWait(Mutex); if (callState != cstDialing) { myPTRACE(1, "ModemEngineBody::Request: call already in " << callState << " state"); } else if (!off_hook) { myPTRACE(1, "ModemEngineBody::Request: line already in on-hook state"); } else if (CallToken().IsEmpty() || CallToken() == request("calltoken")) { SetCallState(cstAlerted); if (state == stConnectWait && !pPlayTone && P.ModemClassId() == EngineBase::mcAudio) { SetState(stConnectHandle, chConnected); timerRing.Start(5000); if (!activeEngines[mceAudio]) _AttachEngine(mceAudio); enableFakeIn[mceAudio] = TRUE; enableFakeOut[mceAudio] = TRUE; if (activeEngines[mceAudio]) activeEngines[mceAudio]->RecvOnIdle(EngineBase::dtRing); parent.SignalDataReady(); request.SetAt("response", "confirm"); } } else { myPTRACE(1, "ModemEngineBody::Request: line already in use by " << CallToken()); } } else if (command == "established") { PWaitAndSignal mutexWait(Mutex); if (callState != cstDialing && callState != cstAlerted && callState != cstAnswering) { myPTRACE(1, "ModemEngineBody::Request: call already in " << callState << " state"); } else if (!off_hook) { myPTRACE(1, "ModemEngineBody::Request: line already in on-hook state"); } else if (CallToken().IsEmpty() || CallToken() == request("calltoken")) { timerRing.Stop(); if (activeEngines[mceAudio]) activeEngines[mceAudio]->RecvOnIdle(EngineBase::dtNone); SetCallState(cstEstablished); if (state == stConnectWait) { SetState(stConnectHandle, chConnected); parent.SignalDataReady(); request.SetAt("response", "confirm"); } } else { myPTRACE(1, "ModemEngineBody::Request: line already in use by " << CallToken()); } } else if (command == "clearcall") { PWaitAndSignal mutexWait(Mutex); if (callState == cstCleared || callState == cstReleasing) { myPTRACE(1, "ModemEngineBody::Request: call already in " << callState << " state"); } else if (CallToken().IsEmpty() || CallToken() == request("calltoken")) { CallToken(""); if (callState == cstDialing && state == stConnectWait && request("trynextcommand") == "dial") { SetCallState(cstCleared); SetState(stDial); params = request; params.RemoveAt("command"); params.RemoveAt("calltoken"); params.RemoveAt("trynextcommand"); parent.SignalDataReady(); } else { _ClearCall(); } request.SetAt("response", "confirm"); } else { myPTRACE(1, "ModemEngineBody::Request: line already in use by " << CallToken()); } } else { myPTRACE(1, "ModemEngineBody::Request: unknown request " << command); } return TRUE; } EngineBase *ModemEngineBody::NewPtrEngine(ModemClassEngine mce) { PAssert(mce == mceT38 || mce == mceAudio, "mce is not valid"); PWaitAndSignal mutexWait(Mutex); _AttachEngine(mce); if (activeEngines[mce]) { activeEngines[mce]->AddReference(); myPTRACE(1, "ModemEngineBody::NewPtrEngine created pointer for engine " << mce); } return activeEngines[mce]; } void ModemEngineBody::_AttachEngine(ModemClassEngine mce) { PAssert(mce == mceT38 || mce == mceAudio, "mce is not valid"); if (activeEngines[mce] == NULL) { EngineBase *engine; switch (mce) { case mceT38: engine = new T38Engine(parent.ptyName()); break; case mceAudio: engine = new AudioEngine(parent.ptyName()); break; default: myPTRACE(1, parent.ptyName() << " ModemEngineBody::_AttachEngine Invalid mce " << mce); return; } if (engine->TryLockModemCallback()) { if (!engine->Attach(engineCallback)) { myPTRACE(1, parent.ptyName() << " ModemEngineBody::_AttachEngine Can't attach engineCallback to " << mce); engine->UnlockModemCallback(); ReferenceObject::DelPointer(engine); return; } engine->UnlockModemCallback(); activeEngines[mce] = engine; } else { myPTRACE(1, parent.ptyName() << " ModemEngineBody::_AttachEngine Can't lock ModemCallback for " << mce); ReferenceObject::DelPointer(engine); return; } } activeEngines[mce]->ChangeModemClass(P.ModemClassId()); activeEngines[mce]->SendOnIdle(sendOnIdle); switch (mce) { case mceT38: if (P.ModemClassId() == EngineBase::mcFax) currentClassEngine = activeEngines[mce]; if (state == stReqModeAckWait) { SetState(stReqModeAckHandle); timeout.Stop(); parent.SignalDataReady(); } break; case mceAudio: if (P.ModemClassId() == EngineBase::mcAudio) currentClassEngine = activeEngines[mce]; if (state == stConnectHandle && subState == chWaitAudioEngine) { SetSubState(chAudioEngineAttached); timeout.Stop(); parent.SignalDataReady(); } break; default: break; } myPTRACE(1, "ModemEngineBody::_AttachEngine Attached " << mce); } void ModemEngineBody::_DetachEngine(ModemClassEngine mce) { PAssert(mce == mceT38 || mce == mceAudio, "mce is not valid"); if (activeEngines[mce] == NULL) return; if (!CallToken().IsEmpty() && activeEngines[mce]->SendingNotCompleted()) { Mutex.Signal(); myPTRACE(2, "ModemEngineBody::_DetachEngine: sending is not completed for " << mce); PThread::Sleep(100); Mutex.Wait(); if (activeEngines[mce] == NULL) return; } for (;;) { if (activeEngines[mce]->TryLockModemCallback()) { activeEngines[mce]->Detach(engineCallback); activeEngines[mce]->UnlockModemCallback(); ReferenceObject::DelPointer(activeEngines[mce]); activeEngines[mce] = NULL; break; } Mutex.Signal(); PThread::Sleep(20); Mutex.Wait(); if (activeEngines[mce] == NULL) return; } switch (mce) { case mceT38: if (P.ModemClassId() == EngineBase::mcFax) { currentClassEngine = NULL; parent.SignalDataReady(); } break; case mceAudio: if (P.ModemClassId() == EngineBase::mcAudio) { currentClassEngine = NULL; parent.SignalDataReady(); } break; default: break; } myPTRACE(1, "ModemEngineBody::_DetachEngine Detached " << mce); } void ModemEngineBody::OnEngineCallback(PObject & PTRACE_PARAM(from), INT extra) { PTRACE(extra < 0 ? 2 : 4, "ModemEngineBody::OnEngineCallback " << from.GetClass() << " " << EngineBase::ModemCallbackParam(extra) << " (" << seq << ", " << state << ")"); switch (extra) { case EngineBase::cbpOutBufEmpty: { PWaitAndSignal mutexWait(Mutex); switch (state) { case stSend: SetState(stSendBufEmptyHandle); break; default: break; } break; } case EngineBase::cbpReset: case EngineBase::cbpOutBufNoFull: case EngineBase::cbpUpdateState: case EngineBase::cbpUserInput: break; default: { PWaitAndSignal mutexWait(Mutex); if (extra == seq) { switch (state) { case stSendAckWait: SetState(stSendAckHandle); timeout.Stop(); break; case stRecvBegWait: SetState(stRecvBegHandle); timeout.Stop(); break; case stConnectHandle: if (subState == chWaitPlayTone) { SetSubState(chTonePlayed); timeout.Stop(); } break; default: break; } } else { myPTRACE(1, "ModemEngineBody::OnEngineCallback extra(" << extra << ") != seq(" << seq << ")"); } } } parent.SignalDataReady(); } void ModemEngineBody::OnTimerCallback(PObject & PTRACE_PARAM(from), INT PTRACE_PARAM(extra)) { PTRACE(2, "ModemEngineBody::OnTimerCallback " << state << " " << from.GetClass() << " " << extra); parent.SignalDataReady(); } static int ParseNum(const char **ppCmd, PINDEX minDigits = 1, PINDEX maxDigits = 3, int maxNum = 255, int defNum = 0) { const char *pEnd = *ppCmd; int num = 0; for( ; isdigit(*pEnd) ; pEnd++ ) { num = (num * 10) + (*pEnd - '0'); } PINDEX len = PINDEX(pEnd - *ppCmd); *ppCmd = pEnd; if (len < minDigits || len > maxDigits || num > maxNum) return -1; else if (!len) return defNum; return num; } PBoolean ModemEngineBody::HandleClass1Cmd(const char **ppCmd, PString &resp, PBoolean &ok, PBoolean &crlf) { PBoolean T; switch (*(*ppCmd - 2)) { case 'T': T = TRUE; break; case 'R': T = FALSE; break; default: return FALSE; } EngineBase::DataType dt; switch (*(*ppCmd - 1)) { case 'S': dt = EngineBase::dtSilence; break; case 'M': dt = EngineBase::dtRaw; break; case 'H': dt = EngineBase::dtHdlc; break; default: return FALSE; } if (dt == EngineBase::dtSilence) { switch( *(*ppCmd)++ ) { case '=': switch( **ppCmd ) { case '?': (*ppCmd)++; resp += "\r\n0-255"; crlf = TRUE; break; default: if (P.ModemClassId() == EngineBase::mcFax) { int dms = ParseNum(ppCmd); if( dms >= 0 ) { ok = FALSE; PWaitAndSignal mutexWait(Mutex); if (T) { if (!SendSilence(dms*10)) return FALSE; } else { PBoolean done = FALSE; timeout.Start(60000); dataType = dt; param = dms*10; SetState(stRecvBegWait); if (currentClassEngine && currentClassEngine->RecvWait(dataType, 0, NextSeq(), done)) { if (!done) break; timeout.Stop(); if (!SendSilence(param)) return FALSE; } else { timeout.Stop(); SetState(stCommand); return FALSE; } } } else { return FALSE; } } else { return FALSE; } } break; default: return FALSE; } } else { switch( *(*ppCmd)++ ) { case '=': switch( **ppCmd ) { case '?': (*ppCmd)++; if (dt == EngineBase::dtRaw) resp += "\r\n24,48,72,73,74,96,97,98,121,122,145,146"; else resp += "\r\n3" /*",24,48,72,73,74,96,97,98,121,122,145,146"*/; crlf = TRUE; break; default: if (P.ModemClassId() == EngineBase::mcFax) { int br = ParseNum(ppCmd); switch( br ) { case 3: if (dt == EngineBase::dtRaw) return FALSE; case 24: case 48: case 72: case 73: case 74: case 96: case 97: case 98: case 121: case 122: case 145: case 146: ok = FALSE; { PString _resp; PBoolean res = T ? SendStart(dt, br, _resp) : RecvStart(dt, br); if (_resp.GetLength()) { if (crlf) { resp += "\r\n"; crlf = FALSE; } else { resp += RC_PREF(); } resp += _resp; } if (!res) PThread::Sleep(100); // workaround return res; } default: return FALSE; } } else { return FALSE; } } break; default: return FALSE; } } return TRUE; } PBoolean ModemEngineBody::HandleClass8Cmd(const char **ppCmd, PString &resp, PBoolean &ok, PBoolean &crlf) { PBoolean T; switch (*(*ppCmd - 2)) { case 'T': T = TRUE; break; case 'R': T = FALSE; break; default: return FALSE; } #define TONE_FREQUENCY_MIN PDTMFEncoder::MinFrequency #define TONE_FREQUENCY_MAX (8000/4) #define TONE_DMS_MAX 500 #define TONE_VOLUME 15 switch (*(*ppCmd - 1)) { case 'S': switch (*(*ppCmd)++) { case '=': switch (**ppCmd) { case '?': { (*ppCmd)++; resp += PString(PString::Printf, "\r\n(0,%u-%u),(0,%u-%u),(0-%u)", (unsigned)TONE_FREQUENCY_MIN, (unsigned)TONE_FREQUENCY_MAX, (unsigned)TONE_FREQUENCY_MIN, (unsigned)TONE_FREQUENCY_MAX, (unsigned)TONE_DMS_MAX); crlf = TRUE; break; } default: if (P.ModemClassId() == EngineBase::mcAudio) { ok = FALSE; PDTMFEncoder tone; for (;;) { int dms = P.Vtd(); switch (**ppCmd) { case '[': { (*ppCmd)++; int f1 = 0; int f2 = 0; f1 = ParseNum(ppCmd, 0, 5, TONE_FREQUENCY_MAX, f1); if (f1 && f1 < TONE_FREQUENCY_MIN) { myPTRACE(1, "Parse error: wrong f1 before " << *ppCmd); return FALSE; } if (**ppCmd == ',') { (*ppCmd)++; f2 = ParseNum(ppCmd, 0, 5, TONE_FREQUENCY_MAX, f2); if (f2 && f2 < TONE_FREQUENCY_MIN) { myPTRACE(1, "Parse error: wrong f2 before " << *ppCmd); return FALSE; } if (**ppCmd == ',') { (*ppCmd)++; dms = ParseNum(ppCmd, 0, 5, TONE_DMS_MAX, dms); if (dms < 0) { myPTRACE(1, "Parse error: wrong dms before " << *ppCmd); return FALSE; } } } if (**ppCmd != ']') { myPTRACE(1, "Parse error: no ']' before " << *ppCmd); return FALSE; } if (dms) { unsigned ms = dms*10; char op; if (f1 && f2) { op = '+'; } else if (f1) { op = '-'; } else if (f2) { op = '-'; f1 = f2; f2 = 0; } else { op = ' '; } if (!tone.Generate(op, f1, f2, ms, TONE_VOLUME)) { myPTRACE(1, "Cannot encode tone \"" << f1 << op << f2 << ":" << ms << "\""); return FALSE; } myPTRACE(2, "Encoded tone \"" << f1 << op << f2 << ":" << ms << "\", size=" << tone.GetSize()); } (*ppCmd)++; break; } case '{': { (*ppCmd)++; char dtmf = **ppCmd; if (isdigit(dtmf) || (dtmf >= 'A' && dtmf <= 'C') || dtmf == '*' || dtmf == '#') (*ppCmd)++; else dtmf = ' '; if (**ppCmd == ',') { (*ppCmd)++; dms = ParseNum(ppCmd, 0, 5, TONE_DMS_MAX, dms); if (dms < 0) { myPTRACE(1, "Parse error: wrong dms before " << *ppCmd); return FALSE; } } if (**ppCmd != '}') { myPTRACE(1, "Parse error: no '}' before " << *ppCmd); return FALSE; } if (dms) { unsigned ms = dms*10; if (dtmf == ' ') { if (!tone.Generate(' ', 0, 0, ms)) { myPTRACE(1, "Cannot encode tone \"0 0:" << ms << "\""); return FALSE; } myPTRACE(2, "Encoded tone \"0 0:" << ms << "\", size=" << tone.GetSize()); } else { tone.AddTone(dtmf, ms); myPTRACE(2, "Encoded DTMF tone \"" << dtmf << ":" << ms << "\", size=" << tone.GetSize()); } } (*ppCmd)++; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case '*': case '#': { char dtmf = *(*ppCmd)++; unsigned ms = dms*10; tone.AddTone(dtmf, ms); myPTRACE(2, "Encoded DTMF tone \"" << dtmf << ":" << ms << "\", size=" << tone.GetSize()); break; } case ',': { unsigned ms = dms*10; if (!tone.Generate(' ', 0, 0, ms)) { myPTRACE(1, "Cannot encode tone \"0 0:" << ms << "\""); return FALSE; } myPTRACE(2, "Encoded tone \"0 0:" << ms << "\", size=" << tone.GetSize()); break; } default: break; } if (**ppCmd != ',') break; (*ppCmd)++; } PWaitAndSignal mutexWait(Mutex); dataType = EngineBase::dtRaw; moreFrames = FALSE; SetState(stSend); if (currentClassEngine && currentClassEngine->SendStart(dataType, 0)) { PINDEX len = tone.GetSize(); if (len) { const PInt16 *ps = tone.GetPointer(); currentClassEngine->Send(ps, len*sizeof(*ps)); } SetState(stSendAckWait); if (!currentClassEngine->SendStop(FALSE, NextSeq())) { SetState(stCommand); return FALSE; } } else { SetState(stCommand); return FALSE; } } else { return FALSE; } } break; default: return FALSE; } break; case 'X': if (P.ModemClassId() == EngineBase::mcAudio) { ok = FALSE; PString _resp; PBoolean res = T ? SendStart(EngineBase::dtRaw, 0, _resp) : RecvStart(EngineBase::dtRaw, 0); if (_resp.GetLength()) { if (crlf) { resp += "\r\n"; crlf = FALSE; } else { resp += RC_PREF(); } resp += _resp; } if (!res) PThread::Sleep(100); // workaround return res; } else { return FALSE; } default: return FALSE; } return TRUE; } PBoolean ModemEngineBody::Answer() { OffHook(); timerRing.Stop(); forceFaxMode = TRUE; if (!connectionEstablished) { PBoolean ok; if (callState == cstCalled) { SetCallState(cstAnswering); SetState(stConnectWait); timeout.Start(EstablishmentTimeout()); PStringToString request; request.SetAt("modemtoken", parent.modemToken()); request.SetAt("command", "answer"); request.SetAt("calltoken", CallToken()); Mutex.Signal(); callbackEndPoint(request, 2); Mutex.Wait(); ok = (request("response") == "confirm"); } else { ok = FALSE; } if (!ok) { OnHook(); return FALSE; } } else { timeout.Stop(); SetState(stConnectHandle, chConnectionEstablished); parent.SignalDataReady(); } return TRUE; } void ModemEngineBody::HandleCmd(PString &resp) { PINDEX i; for (i = 0 ; ; i++) { i = cmd.FindOneOf("Aa", i); if (i == P_MAX_INDEX) { myPTRACE(1, "--> " << cmd.GetLength() << " bytes of binary"); cmd.MakeEmpty(); return; } PString at = cmd.Mid(i, 2); if (at == "AT" || at == "at") break; } #if PTRACING if (i) { PBYTEArray bin((const BYTE *)(const char *)cmd, i); myPTRACE(1, "--> " << PRTHEX(bin)); } #endif const char *pCmd; pCmd = ((const char *)cmd) + i; myPTRACE(1, "--> " << pCmd); pCmd += 2; // skip AT cmdRest = PString(pCmd).ToUpper(); cmd.MakeEmpty(); HandleCmdRest(resp); } #define ToSBit(funk) \ switch (ParseNum(&pCmd, 0, 1, 1)) { \ case 0: \ P.funk(FALSE); \ break; \ case 1: \ P.funk(TRUE); \ break; \ default: \ err = TRUE; \ } void ModemEngineBody::HandleCmdRest(PString &resp) { PString tmp = cmdRest; cmdRest.MakeEmpty(); const char *pCmd = tmp; PBoolean err = FALSE; PBoolean ok = TRUE; PBoolean crlf = FALSE; while (state == stCommand && !err && *pCmd) { switch( *pCmd++ ) { case ' ': case ';': break; case 'A': // Accept incoming call ok = FALSE; { PWaitAndSignal mutexWait(Mutex); callDirection = cdIncoming; if (!Answer()) err = TRUE; } break; case 'B': // Turn ITU-T V.22/BELL 212A if (ParseNum(&pCmd, 0, 1) >= 0) { } else { err = TRUE; } break; case 'D': // Dial ok = FALSE; { PWaitAndSignal mutexWait(Mutex); PBoolean wasOnHook = OffHook(); PString num; PString numTone; PBoolean addNumTone; PString LocalPartyName; PBoolean local = FALSE; PBoolean setForceFaxMode = FALSE; CallDirection setCallDirection = cdOutgoing; if (!CallToken().IsEmpty()) { addNumTone = TRUE; } else { addNumTone = FALSE; if (pPlayTone) { myPTRACE(1, "ModemEngineBody::HandleCmd pPlayTone is not NULL"); delete pPlayTone; pPlayTone = NULL; } } while (!err) { char ch = *pCmd++; if (ch == 0) { pCmd--; break; } if (ch == ';') { setCallDirection = cdUndefined; cmdRest = PString(pCmd); break; } switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '*': case '#': if (local) { if (!CallToken().IsEmpty()) { // Dialing of calling number is not // allowed in online command state err = TRUE; continue; } LocalPartyName += ch; continue; } break; case '+': if (local) continue; if (addNumTone) continue; if (!num.IsEmpty()) continue; break; case 'A': case 'B': case 'C': if (local) continue; break; case 'D': if (local) { local = FALSE; continue; } break; case ',': if (!addNumTone) continue; break; case 'F': setForceFaxMode = TRUE; continue; case 'V': setForceFaxMode = FALSE; continue; case 'L': LocalPartyName = ""; local = TRUE; continue; case 'T': case 'P': local = FALSE; continue; case '@': local = FALSE; addNumTone = TRUE; numTone.MakeEmpty(); continue; default: // V.250 // Any characters appearing in the dial string that // the DCE does not recognize ... shall be ignored. continue; } if (!addNumTone) num += ch; else numTone += ch; } myPTRACE(2, "Dial string: " << num << "@" << numTone << "L" << LocalPartyName << (setForceFaxMode ? "F" : "V") << (err ? "" : " - OK")); if (!err) { PDTMFEncoder playTone; for (PINDEX i = 0 ; i < numTone.GetLength() ; i++) { char ch = numTone[i]; switch (ch) { case ',': { unsigned ms = unsigned(P.DialTimeComma()) * 1000; playTone.Generate(' ', 0, 0, ms); myPTRACE(2, "Encoded tone \"0 0:" << ms << "\", size=" << playTone.GetSize()); break; } default: { unsigned ms = P.DialTimeDTMF(); playTone.AddTone(ch, ms); myPTRACE(2, "Encoded DTMF tone \"" << ch << ":" << ms << "\", size=" << playTone.GetSize()); playTone.Generate(' ', 0, 0, ms); myPTRACE(2, "Encoded tone \"0 0:" << ms << "\", size=" << playTone.GetSize()); break; } } } if (!CallToken().IsEmpty()) { if (connectionEstablished) { callDirection = setCallDirection; forceFaxMode = (forceFaxMode || setForceFaxMode); SetState(stConnectHandle, chConnected); parent.SignalDataReady(); if (numTone.GetLength()) { if (!pPlayTone) pPlayTone = new PDTMFEncoder(); pPlayTone->Concatenate(playTone); } break; } OnHook(); if (crlf) { resp += "\r\n"; crlf = FALSE; } else { resp += RC_PREF(); } resp += RC_BUSY(); break; } callDirection = setCallDirection; forceFaxMode = setForceFaxMode; timerRing.Stop(); SetState(stDial); if (numTone.GetLength()) { if (!pPlayTone) pPlayTone = new PDTMFEncoder(); pPlayTone->Concatenate(playTone); } timeout.Start(EstablishmentTimeout()); params.RemoveAll(); params.SetAt("number", num); params.SetAt("localpartyname", LocalPartyName); parent.SignalDataReady(); // try to Dial w/o delay } else { if (wasOnHook) OnHook(); } } break; case 'E': { // Turn Echo on/off PWaitAndSignal mutexWait(Mutex); ToSBit(Echo); break; } case 'H': // On/Off-hook if( ParseNum(&pCmd, 0, 1, 0) >= 0 ) { // ATH & ATH0 PWaitAndSignal mutexWait(Mutex); P.ModemClass("1"); OnChangeModemClass(); if (off_hook || P.ClearMode()) OnHook(); } else { err = TRUE; } break; case 'I': // Information { int val = ParseNum(&pCmd, 0, 1); switch (val) { case 0: resp += "\r\n" + PString(Model); crlf = TRUE; break; case 3: resp += "\r\n" + PString(Manufacturer); crlf = TRUE; break; case 8: resp += "\r\nNMBR = " + SrcNum(); crlf = TRUE; break; case 9: resp += "\r\nNDID = " + DstNum(); crlf = TRUE; break; default: if( val < 0 ) err = TRUE; } } break; case 'L': // Turn Speaker on/off if( ParseNum(&pCmd, 0, 1) >= 0 ) { } else { err = TRUE; } break; case 'M': // Turn Speaker on/off if( ParseNum(&pCmd, 0, 1) >= 0 ) { } else { err = TRUE; } break; case 'N': // Ring volume if( ParseNum(&pCmd, 0, 1) >= 0 ) { } else { err = TRUE; } break; case 'O': // Go online err = TRUE; break; case 'Q': { // Turn result codes on/off PWaitAndSignal mutexWait(Mutex); ToSBit(noResultCodes); break; } case 'S': // Set/Get Register { int r = ParseNum(&pCmd); if (r >= 0) { switch (*pCmd++) { case '=': { int val = ParseNum(&pCmd); PWaitAndSignal mutexWait(Mutex); if( val < 0 || !P.SetReg(r, (BYTE)val) ) { err = TRUE; } } break; case '?': { BYTE val; PWaitAndSignal mutexWait(Mutex); if (P.GetReg(r, val)) { resp.sprintf("\r\n%3.3u", (unsigned)val); crlf = TRUE; } else { err = TRUE; } } break; case '.': { int b = ParseNum(&pCmd, 1, 1, 7); if (b >= 0) { switch (*pCmd++) { case '=': { int val = ParseNum(&pCmd, 1, 1, 1); if( val < 0 || !P.SetBit(r, b, (PBoolean)val) ) { err = TRUE; } } break; case '?': { PBoolean val; if (P.GetBit(r, b, val)) { resp.sprintf("\r\n%u", (unsigned)val); crlf = TRUE; } else { err = TRUE; } } break; default: err = TRUE; } } else { err = TRUE; } } break; default: err = TRUE; } } else { err = TRUE; } } break; case 'V': { // Numeric or ASCII result codes PWaitAndSignal mutexWait(Mutex); ToSBit(asciiResultCodes); break; } case 'X': // Which result codes if (ParseNum(&pCmd, 0, 1) >= 3) { } else { err = TRUE; } break; case 'Z': // Load Registers from Profile { int val = ParseNum(&pCmd, 0, 1, sizeof(Profiles)/sizeof(Profiles[0]) - 1); if( val >= 0 ) { PWaitAndSignal mutexWait(Mutex); P = Profiles[val]; OnChangeModemClass(); if (off_hook) // clear call only in off-hook state OnHook(); } else { err = TRUE; } } break; case '+': switch( *pCmd++ ) { case 'F': // FAX switch( *pCmd++ ) { case 'A': if (*pCmd == 'A') { // +FAA pCmd += 1; switch( *pCmd++ ) { case '=': switch( *pCmd ) { case '?': pCmd++; resp += "\r\n0"; crlf = TRUE; break; default: switch( ParseNum(&pCmd) ) { case 0: break; default: err = TRUE; } } break; case '?': resp += "\r\n0"; crlf = TRUE; break; default: err = TRUE; } } else { err = TRUE; } break; case 'C': if( strncmp(pCmd, "LASS", 4) == 0 ) { // +FCLASS pCmd += 4; switch( *pCmd++ ) { case '=': switch( *pCmd ) { case '?': pCmd++; resp += "\r\n1,8"; crlf = TRUE; break; default: { const char *modemClass; switch (ParseNum(&pCmd)) { case 0: modemClass = "0"; break; case 1: modemClass = "1"; break; case 8: modemClass = "8"; break; default: modemClass = NULL; err = TRUE; } if (modemClass) { PWaitAndSignal mutexWait(Mutex); P.ModemClass(modemClass); OnChangeModemClass(); } } } break; case '?': resp += "\r\n" + P.ModemClass(); crlf = TRUE; break; default: err = TRUE; } } else { err = TRUE; } break; case 'L': switch (*pCmd++) { case 'O': // +FLO switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n0-2"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); switch (val) { case 0: case 1: case 2: { PWaitAndSignal mutexWait(Mutex); P.Flo((BYTE)val); break; } default: err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.Flo()); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'M': switch (*pCmd++) { case 'D': switch (*pCmd++) { case 'L': // +FMDL switch (*pCmd++) { case '?': resp += "\r\n" + PString(Model); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'F': switch (*pCmd++) { case 'R': // +FMFR switch (*pCmd++) { case '?': resp += "\r\n" + PString(Manufacturer); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'I': // +FMI switch (*pCmd++) { case '?': resp += "\r\n" + PString(Manufacturer); crlf = TRUE; break; default: err = TRUE; } break; case 'M': // +FMM switch (*pCmd++) { case '?': resp += "\r\n" + PString(Model); crlf = TRUE; break; default: err = TRUE; } break; case 'R': // +FMR switch (*pCmd++) { case '?': resp += "\r\n" + PString(Revision); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'R': switch( *pCmd ) { case 'M': // +FRM case 'H': // +FRH case 'S': // +FRS pCmd++; if (!HandleClass1Cmd(&pCmd, resp, ok, crlf)) err = TRUE; break; default: if( strncmp(pCmd, "EV", 2) == 0 ) { // +FREV pCmd += 2; switch( *pCmd++ ) { case '?': resp += "\r\n" + PString(Revision); crlf = TRUE; break; default: err = TRUE; } } else { err = TRUE; } } break; case 'T': switch( *pCmd ) { case 'M': // +FTM case 'H': // +FTH case 'S': // +FTS pCmd++; if (!HandleClass1Cmd(&pCmd, resp, ok, crlf)) err = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'I': // DTE-DCE interface commands switch (*pCmd++) { case 'F': switch (*pCmd++) { case 'C': // +IFC switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n+IFC:(0-2),(0-2)"; crlf = TRUE; break; default: { int valByDTE = ParseNum(&pCmd, 1, 1, 2); if (valByDTE < 0) { err = TRUE; break; } int valByDCE; if (*pCmd == ',') { pCmd++; valByDCE = ParseNum(&pCmd); if (valByDCE < 0) { err = TRUE; break; } } else { valByDCE = P.IfcByDCE(); } PWaitAndSignal mutexWait(Mutex); P.IfcByDTE((BYTE)valByDTE); P.IfcByDCE((BYTE)valByDCE); } } break; case '?': resp.sprintf("\r\n+IFC:%u,%u", (unsigned)P.IfcByDTE(), (unsigned)P.IfcByDCE()); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; default: err = TRUE; } break; case 'V': // Voice switch (*pCmd++) { case 'C': if (strncmp(pCmd, "ID", 2) == 0) { // +VCID pCmd += 2; switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0,1)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); switch (val) { case 0: case 1: { PWaitAndSignal mutexWait(Mutex); P.CidMode((BYTE)val); break; } default: err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.CidMode()); crlf = TRUE; break; default: err = TRUE; } } else { err = TRUE; } break; case 'E': switch (*pCmd++) { case 'M': // +VEM switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0)"; crlf = TRUE; break; default: if (ParseNum(&pCmd) != 0) err = TRUE; } break; case '?': resp.sprintf("\r\n0"); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'G': switch (*pCmd++) { case 'R': // +VGR switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0-255)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); if (val >= 0) { PWaitAndSignal mutexWait(Mutex); P.VgrInterval((BYTE)val); } else { err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.VgrInterval()); crlf = TRUE; break; default: err = TRUE; } break; case 'T': // +VGT switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0-255)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); if (val >= 0) { PWaitAndSignal mutexWait(Mutex); P.VgtInterval((BYTE)val); } else { err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.VgtInterval()); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'I': switch (*pCmd++) { case 'P': // +VIP { int val; if (*pCmd == '=') { pCmd++; if (*pCmd == '?') { pCmd++; resp.sprintf("\r\n(0-%u)", sizeof(Profiles)/sizeof(Profiles[0]) - 1); crlf = TRUE; break; } val = ParseNum(&pCmd); if( val < 0 || val > ((int)(sizeof(Profiles)/sizeof(Profiles[0])) - 1)) { err = TRUE; break; } } else { val = 0; } PWaitAndSignal mutexWait(Mutex); P.SetVoiceProfile(Profiles[val]); } break; case 'T': // +VIT switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0)"; crlf = TRUE; break; default: if (ParseNum(&pCmd) < 0) err = TRUE; } break; case '?': resp.sprintf("\r\n0"); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'L': switch (*pCmd++) { case 'S': // +VLS switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n0,\"\",00000000,00000000,00000000"; resp += "\r\n1,\"T\",00000000,00000000,00000000"; resp += "\r\n5,\"ST\",00000000,00000000,00000000"; resp += "\r\n7,\"MST\",00000000,00000000,00000000"; crlf = TRUE; break; default: switch (ParseNum(&pCmd)) { case 0: { PWaitAndSignal mutexWait(Mutex); if (off_hook || P.ClearMode()) OnHook(); break; } case 1: case 5: case 7: ok = FALSE; { PWaitAndSignal mutexWait(Mutex); callDirection = cdUndefined; if (!Answer()) err = TRUE; } break; default: err = TRUE; } } break; case '?': resp += off_hook ? "\r\n1" : "\r\n0"; crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'R': switch (*pCmd++) { case 'A': // +VRA switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0-255)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); if (val >= 0) { PWaitAndSignal mutexWait(Mutex); P.VraInterval((BYTE)val); } else { err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.VraInterval()); crlf = TRUE; break; default: err = TRUE; } break; case 'N': // +VRN switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0-255)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); if (val >= 0) { PWaitAndSignal mutexWait(Mutex); P.VrnInterval((BYTE)val); } else { err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.VrnInterval()); crlf = TRUE; break; default: err = TRUE; } break; case 'X': // +VRX if (!HandleClass8Cmd(&pCmd, resp, ok, crlf)) err = TRUE; break; default: err = TRUE; } break; case 'S': switch (*pCmd++) { case 'D': // +VSD switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0-255),(0-255)"; crlf = TRUE; break; default: { int sds = ParseNum(&pCmd); if (sds < 0 || *pCmd != ',') { err = TRUE; break; } pCmd++; int sdi = ParseNum(&pCmd); if (sdi < 0) { err = TRUE; break; } PWaitAndSignal mutexWait(Mutex); P.Vsds((BYTE)sds); P.Vsdi((BYTE)sdi); } } break; case '?': resp.sprintf("\r\n%u,%u", (unsigned)P.Vsds(), (unsigned)P.Vsdi()); crlf = TRUE; break; default: err = TRUE; } break; case 'M': // +VSM switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n0,\"SIGNED PCM\",8,0,(8000),(0),(0)"; resp += "\r\n1,\"UNSIGNED PCM\",8,0,(8000),(0),(0)"; resp += "\r\n4,\"G.711U\",8,0,(8000),(0),(0)"; resp += "\r\n5,\"G.711A\",8,0,(8000),(0),(0)"; resp += "\r\n128,\"8-BIT LINEAR\",8,0,(8000),(0),(0)"; resp += "\r\n129,\"ADPCM (NOT IMPLEMENTED)\",0,0,(0),(0),(0)"; resp += "\r\n130,\"UNSIGNED PCM\",8,0,(8000),(0),(0)"; resp += "\r\n131,\"G.711 ULAW\",8,0,(8000),(0),(0)"; resp += "\r\n132,\"G.711 ALAW\",8,0,(8000),(0),(0)"; crlf = TRUE; break; default: { int cml = ParseNum(&pCmd); switch (cml) { case 0: case 1: case 4: case 5: case 128: case 130: case 131: case 132: break; default: err = TRUE; } if (err) break; if (*pCmd == ',') { pCmd++; int vsr = ParseNum(&pCmd, 4, 4, 8000); if (vsr != 8000) { err = TRUE; break; } int scs = 0; int sel = 0; if (*pCmd == ',') { pCmd++; scs = ParseNum(&pCmd); if (scs != 0) { err = TRUE; break; } if (*pCmd == ',') { pCmd++; sel = ParseNum(&pCmd); if (sel != 0) { err = TRUE; break; } } } } PWaitAndSignal mutexWait(Mutex); P.Vcml((BYTE)cml); } } break; case '?': resp.sprintf("\r\n%u,8000,0,0", (unsigned)P.Vcml()); crlf = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; case 'T': switch (*pCmd++) { case 'D': // +VTD switch (*pCmd++) { case '=': switch (*pCmd) { case '?': pCmd++; resp += "\r\n(0-255)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); if (val >= 0) { PWaitAndSignal mutexWait(Mutex); P.Vtd((BYTE)val); } else { err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.Vtd()); crlf = TRUE; break; default: err = TRUE; } break; case 'X': // +VTX case 'S': // +VTS if (!HandleClass8Cmd(&pCmd, resp, ok, crlf)) err = TRUE; break; default: err = TRUE; } break; default: err = TRUE; } break; default: err = TRUE; } break; case '&': switch( *pCmd++ ) { case 'C': // &C { int val = ParseNum(&pCmd, 0, 1, 1); if( val >= 0 ) { } else { err = TRUE; } } break; case 'D': // &D { int val = ParseNum(&pCmd, 0, 1, 3); if( val >= 0 ) { } else { err = TRUE; } } break; case 'F': // &F { PWaitAndSignal mutexWait(Mutex); P = Profiles[0]; OnChangeModemClass(); if (off_hook) // clear call only in off-hook state OnHook(); } break; case 'H': // &H { int val = ParseNum(&pCmd, 0, 1, 7); if( val >= 0 ) { PWaitAndSignal mutexWait(Mutex); P.SetBits(27, 3, 5, (BYTE)val); } else { err = TRUE; } } break; default: err = TRUE; } break; case '#': if (strncmp(pCmd, "CID", 3) == 0) { // #CID pCmd += 3; switch( *pCmd++ ) { case '=': switch( *pCmd ) { case '?': pCmd++; resp += "\r\n(0,1,10,11)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); PWaitAndSignal mutexWait(Mutex); switch (val) { case 0: P.CidMode(0); P.DidMode(0); break; case 1: P.CidMode(1); P.DidMode(0); break; case 10: P.CidMode(1); P.DidMode(1); break; case 11: P.CidMode(0); P.DidMode(1); break; default: err = TRUE; } } } break; case '?': { unsigned cid; if (P.CidMode()) { if (P.DidMode()) { cid = 10; } else { cid = 1; } } else { if (P.DidMode()) { cid = 11; } else { cid = 0; } } resp.sprintf("\r\n%u", cid); crlf = TRUE; break; } case 'F': if (strncmp(pCmd, "MT", 2) == 0) { // #CIDFMT pCmd += 2; switch (*pCmd++) { case '=': { int name_format = P.CidNameFmt(); int nmbr_format = P.CidNmbrFmt(); name_format = ParseNum(&pCmd, 0, 1, 3, name_format); if (name_format < 0) { err = TRUE; break; } if (*pCmd == ',') { pCmd++; nmbr_format = ParseNum(&pCmd, 0, 1, 3, nmbr_format); if (nmbr_format < 0) { err = TRUE; break; } } PWaitAndSignal mutexWait(Mutex); P.CidNameFmt((BYTE)name_format); P.CidNmbrFmt((BYTE)nmbr_format); break; } case '?': resp.sprintf("\r\n%u,%u", (unsigned)P.CidNameFmt(), (unsigned)P.CidNmbrFmt()); crlf = TRUE; break; default: err = TRUE; } } else { err = TRUE; } break; default: err = TRUE; } } else if (strncmp(pCmd, "DFRMC", 5) == 0) { // #DFRMC pCmd += 5; switch( *pCmd++ ) { case '=': switch( *pCmd ) { case '?': pCmd++; resp += "\r\n(0-255)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); if (val >= 0) { PWaitAndSignal mutexWait(Mutex); P.DelayFrmConnect((BYTE)val); } else { err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.DelayFrmConnect()); crlf = TRUE; break; default: err = TRUE; } } else if (strncmp(pCmd, "HCLR", 4) == 0) { // #HCLR pCmd += 4; switch( *pCmd++ ) { case '=': switch( *pCmd ) { case '?': pCmd++; resp += "\r\n(0,1)"; crlf = TRUE; break; default: { int val = ParseNum(&pCmd); switch (val) { case 0: case 1: { PWaitAndSignal mutexWait(Mutex); P.ClearMode((BYTE)val); break; } default: err = TRUE; } } } break; case '?': resp.sprintf("\r\n%u", (unsigned)P.ClearMode()); crlf = TRUE; break; default: err = TRUE; } } else { err = TRUE; } break; default: err = TRUE; } } if (crlf) resp += "\r\n"; if (err) { if (!crlf) resp += RC_PREF(); resp += RC_ERROR(); } else if (ok) { if (!crlf) resp += RC_PREF(); resp += RC_OK(); } } void ModemEngineBody::HandleData(const PBYTEArray &buf, PBYTEArray &bresp) { int len = buf.GetSize(); const BYTE *pBuf = buf; while (len > 0) { switch (state) { case stCommand: if (!off_hook) { PWaitAndSignal mutexWait(Mutex); lastOnHookActivity = PTime(); } while (state == stCommand && len > 0) { const BYTE *pEnd = (const BYTE *)memchr(pBuf, '\r', len); if (pEnd == NULL) { cmd += PString((const char *)pBuf, len); if( Echo() ) bresp.Concatenate(PBYTEArray(pBuf, len)); len = 0; } else { int rlen = int(pEnd - pBuf); if( rlen ) { cmd += PString((const char *)pBuf, rlen); if( Echo() ) { bresp.Concatenate(PBYTEArray(pBuf, rlen)); } len -= rlen; pBuf += rlen; } len--; pBuf++; if (Echo()) bresp.Concatenate(PBYTEArray((const BYTE *)"\r", 1)); PString resp; HandleCmd(resp); if (resp.GetLength()) { PBYTEArray _bresp((const BYTE *)(const char *)resp, resp.GetLength()); myPTRACE(1, "<-- " << PRTHEX(_bresp)); bresp.Concatenate(_bresp); } } } break; case stSend: { int lendone = dleData.PutDleData(pBuf, len); if (lendone > 0) { PTRACE(4, "--> DLE " << lendone << " bytes"); len -= lendone; pBuf += lendone; } int dt = dataType; BYTE Buf[1024]; for(;;) { int count = dleData.GetData(Buf, sizeof(Buf)); PWaitAndSignal mutexWait(Mutex); switch (count) { case -1: SetState(stSendAckWait); if (!currentClassEngine || !currentClassEngine->SendStop(moreFrames, NextSeq())) { SetState(stSendAckHandle); timeout.Stop(); parent.SignalDataReady(); // try to SendAckHandle w/o delay } break; case 0: break; default: switch( dt ) { case EngineBase::dtHdlc: if (dataCount < 2 && (dataCount + count) >= 2 && (Buf[1 - dataCount] & 0x08) == 0) { moreFrames = TRUE; } } dataCount += count; if (P.ModemClassId() == EngineBase::mcAudio) { if (currentClassEngine) { const signed char *pb = (const signed char *)Buf; PInt16 Buf2[sizeof(Buf)]; PInt16 *ps = Buf2; switch (P.Vcml()) { case 0: while (count--) *ps++ = (PInt16)((PInt16)(*pb++)*256); break; case 1: case 128: case 130: while (count--) *ps++ = (PInt16)((PInt16)(*pb++)*256 - 0x8000); break; case 4: case 131: while (count--) *ps++ = (PInt16)ulaw2linear(*pb++); break; case 5: case 132: while (count--) *ps++ = (PInt16)alaw2linear(*pb++); break; } count = int(pb - (const signed char *)Buf); currentClassEngine->Send(Buf2, count*sizeof(*ps)); } } else if (P.ModemClassId() == EngineBase::mcFax) { if (currentClassEngine) currentClassEngine->Send(Buf, count); } } if( count <= 0 ) break; } } break; case stSendAckWait: myPTRACE(1, "Reset state stSendAckWait"); { PWaitAndSignal mutexWait(Mutex); SetState(stCommand); timeout.Stop(); if (currentClassEngine) currentClassEngine->ResetModemState(); } break; default: myPTRACE(1, "Reset state " << state); len--; pBuf++; { PWaitAndSignal mutexWait(Mutex); timeout.Stop(); if (state == stRecv && (dataCount || P.ModemClassId() == EngineBase::mcAudio)) { PBYTEArray _bresp((const BYTE *)"\x10\x03", 2); // add myPTRACE(1, "<-- " << PRTHEX(_bresp)); bresp.Concatenate(_bresp); } SetState(stCommand); PString resp = RC_PREF(); if (currentClassEngine || P.ModemClassId() == EngineBase::mcAudio) { if (currentClassEngine) currentClassEngine->ResetModemState(); resp += RC_OK(); } else { OnHook(); resp += RC_NO_CARRIER(); } PBYTEArray _bresp((const BYTE *)(const char *)resp, resp.GetLength()); myPTRACE(1, "<-- " << PRTHEX(_bresp)); bresp.Concatenate(_bresp); } } } } void ModemEngineBody::CheckState(PBYTEArray & bresp) { PString resp; PWaitAndSignal mutexWait(Mutex); if (cmd.IsEmpty()) { if (timerBusy.Get()) { if (P.ModemClassId() == EngineBase::mcAudio) { PBYTEArray _bresp((const BYTE *)"\x10" "b", 2); // b bresp.Concatenate(_bresp); myPTRACE(2, "<-- DLE " << PRTHEX(_bresp)); } } if (timerRing.Get()) { if (off_hook && !pPlayTone && P.ModemClassId() == EngineBase::mcAudio) { BYTE b[2] = {'\x10', 'r'}; PBYTEArray _bresp(b, sizeof(b)); bresp.Concatenate(_bresp); myPTRACE(2, "<-- DLE " << PRTHEX(_bresp)); } else if (!off_hook && callState == cstCalled) { resp = RC_RING(); BYTE ringCount = P.RingCount(); if (!ringCount) { if (P.CidMode() || P.DidMode()) { if (P.CidMode()) { resp += "DATE = " + callTime.AsString("MMdd") + "\r\n"; resp += "TIME = " + callTime.AsString("hhmm") + "\r\n"; switch (P.CidNmbrFmt()) { case 0: default: resp += "NMBR = " + SrcNum() + "\r\n"; break; case 1: resp += "NMBR = \r\n"; break; case 2: resp += "NMBR = " + DstNum() + "\r\n"; break; case 3: resp += "NMBR = " + DstNum() + "#" + SrcNum() + "\r\n"; break; } switch (P.CidNameFmt()) { case 0: default: resp += "NAME = " + SrcName() + "\r\n"; break; case 1: resp += "NAME = \r\n"; break; case 2: resp += "NAME = " + DstNum() + "\r\n"; break; case 3: resp += "NAME = " + DstNum() + " <- " + SrcName() + "\r\n"; break; } } if (P.DidMode()) resp += "NDID = " + DstNum() + "\r\n"; resp += RC_RING(); } } P.RingCount(++ringCount); if (P.AutoAnswer() > 0 && (ringCount >= P.AutoAnswer())) { callDirection = cdIncoming; Answer(); } } } } if (timeout.Get()) { switch( state ) { case stReqModeAckWait: case stRecvBegWait: resp = RC_NO_CARRIER(); SetState(stCommand); if (currentClassEngine) currentClassEngine->ResetModemState(); else OnHook(); break; case stConnectHandle: if (subState == chConnectionEstablishDelay) { SetSubState(chConnectionEstablished); break; } case stConnectWait: resp = RC_NO_CARRIER(); OnHook(); break; default: break; } } for (int i = 0 ; i < mceNumberOfItems ; i++) { EngineBase *engine = activeEngines[i]; if (engine == NULL) continue; for(;;) { BYTE c; if (engine->RecvUserInput(&c, 1) <= 0) break; if (!P.ModemClassId() == EngineBase::mcAudio) continue; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case '*': case '#': { // '/'c'~' BYTE b[6] = {'\x10', '/', '\x10', c, '\x10', '~'}; PBYTEArray _bresp(b, sizeof(b)); bresp.Concatenate(_bresp); myPTRACE(2, "<-- DLE " << PRTHEX(_bresp)); break; } default: { // c BYTE b[2] = {'\x10', c}; PBYTEArray _bresp(b, sizeof(b)); bresp.Concatenate(_bresp); myPTRACE(2, "<-- DLE " << PRTHEX(_bresp)); break; } } } } if (off_hook && callState == cstReleasing && callSubState == 0) { SetCallSubState(1); switch (state) { case stCommand: break; case stDial: case stConnectWait: case stConnectHandle: case stReqModeAckWait: case stReqModeAckHandle: if (callDirection == cdOutgoing) resp = RC_BUSY(); else resp = RC_ERROR(); timeout.Stop(); OnHook(); break; case stSend: case stSendBufEmptyHandle: case stSendAckWait: case stSendAckHandle: case stRecvBegWait: case stRecvBegHandle: case stRecv: break; default: resp = RC_ERROR(); SetState(stCommand); } } switch (state) { case stDial: if (callState == cstCleared) { // we can't dial after releasing w/o on-hook SetCallState(cstDialing); SetState(stConnectWait); timeout.Start(EstablishmentTimeout()); PStringToString request = params; request.SetAt("modemtoken", parent.modemToken()); request.SetAt("command", "dial"); Mutex.Signal(); callbackEndPoint(request, 5); Mutex.Wait(); if (request("response") == "confirm") { CallToken(request("calltoken")); break; } else { timeout.Stop(); } } OnHook(); resp = RC_BUSY(); break; case stSendBufEmptyHandle: { SetState(stSendAckWait); if (!currentClassEngine || !currentClassEngine->SendStop(moreFrames, NextSeq())) { SetState(stSendAckHandle); timeout.Stop(); parent.SignalDataReady(); // try to SendAckHandle w/o delay } } break; case stConnectHandle: { switch(subState) { case chConnected: if (!activeEngines[mceAudio]) { SetSubState(chWaitAudioEngine); timeout.Start(60000); break; } SetSubState(chAudioEngineAttached); case chAudioEngineAttached: if (pPlayTone) { if (!activeEngines[mceAudio]) { SetSubState(chWaitAudioEngine); break; } if (!activeEngines[mceAudio]->IsOpenOut()) break; PBoolean err = FALSE; if (activeEngines[mceAudio]->SendStart(EngineBase::dtRaw, 0)) { PINDEX len = pPlayTone->GetSize(); if (len) { const PInt16 *ps = pPlayTone->GetPointer(); activeEngines[mceAudio]->Send(ps, len*sizeof(*ps)); } SetSubState(chWaitPlayTone); if (activeEngines[mceAudio]->SendStop(FALSE, NextSeq())) { timeout.Start(60000); } else { SetSubState(chAudioEngineAttached); err = TRUE; } } else { err = TRUE; } if (err) { resp = RC_ERROR(); SetState(stCommand); OnHook(); } if (pPlayTone) { delete pPlayTone; pPlayTone = NULL; } break; } SetSubState(chTonePlayed); case chTonePlayed: if (!connectionEstablished && P.ModemClassId() == EngineBase::mcFax) { SetSubState(chConnectionEstablishDelay); timeout.Start(1000); // wait 1 sec before request mode break; } SetSubState(chConnectionEstablished); case chConnectionEstablished: if (callDirection == cdOutgoing && P.ModemClassId() == EngineBase::mcFax) SendOnIdle(EngineBase::dtCng); connectionEstablished = TRUE; enableFakeIn[mceAudio] = TRUE; enableFakeOut[mceAudio] = TRUE; if (P.ModemClassId() == EngineBase::mcAudio || callDirection == cdUndefined) { SetState(stCommand); timeout.Stop(); PString _resp; HandleCmdRest(_resp); PBYTEArray _bresp((const BYTE *)(const char *)_resp, _resp.GetLength()); myPTRACE(1, "<-- " << PRTHEX(_bresp)); bresp.Concatenate(_bresp); } else if (activeEngines[mceT38]) { SetState(stReqModeAckHandle); timeout.Stop(); parent.SignalDataReady(); } else { SetState(stReqModeAckWait); timeout.Start(forceFaxMode ? 10000 : 60000); PStringToString request; request.SetAt("modemtoken", parent.modemToken()); request.SetAt("command", "requestmode"); request.SetAt("calltoken", CallToken()); request.SetAt("mode", forceFaxMode ? "fax" : "fax-no-force"); Mutex.Signal(); callbackEndPoint(request, 4); Mutex.Wait(); PString response = request("response"); if (forceFaxMode && response != "confirm") { SetState(stCommand); timeout.Stop(); resp = RC_NO_CARRIER(); } } break; } } break; case stReqModeAckHandle: { switch( callDirection ) { case cdIncoming: dataType = EngineBase::dtCed; SetState(stSend); if (activeEngines[mceT38] && activeEngines[mceT38]->SendStart(dataType, 3000)) { SetState(stSendAckWait); if (!activeEngines[mceT38]->SendStop(FALSE, NextSeq())) { resp = RC_ERROR(); SetState(stCommand); } } else { resp = RC_NO_CARRIER(); SetState(stCommand); } break; case cdOutgoing: if (!RecvStart(EngineBase::dtHdlc, 3)) resp = RC_ERROR(); break; default: resp = RC_NO_CARRIER(); SetState(stCommand); } } break; case stSendAckHandle: switch (dataType) { case EngineBase::dtCed: if (!_SendStart(EngineBase::dtHdlc, 3, resp)) resp = RC_ERROR(); break; case EngineBase::dtSilence: resp = RC_OK(); SetState(stCommand); break; case EngineBase::dtHdlc: case EngineBase::dtRaw: { if (moreFrames) { resp = RC_CONNECT(); SetState(stSend); } else { resp = RC_OK(); SetState(stCommand); } ResetDleData(); } break; default: myPTRACE(1, "Unexpected dataType=" << dataType); } break; case stRecvBegHandle: { SendOnIdle(EngineBase::dtNone); if (dataType == EngineBase::dtSilence) { if (!SendSilence(param)) resp = RC_ERROR(); } else if (!currentClassEngine || !currentClassEngine->RecvStart(NextSeq())) { resp = RC_ERROR(); SetState(stCommand); } else if (P.ModemClassId() == EngineBase::mcAudio) { resp = RC_CONNECT(); SetState(stRecv); parent.SignalDataReady(); // try to Recv w/o delay } else if (P.ModemClassId() == EngineBase::mcFax) { if ((currentClassEngine->RecvDiag() & EngineBase::diagDiffSig) == 0) { if (dataType != EngineBase::dtRaw) { resp = RC_CONNECT(); if (dataType == EngineBase::dtHdlc) fcs = FCS(); } SetState(stRecv); parent.SignalDataReady(); // try to Recv w/o delay } else { currentClassEngine->RecvStop(); resp = RC_FCERROR(); SetState(stCommand); } } else { resp = RC_ERROR(); SetState(stCommand); } ResetDleData(); } break; case stRecv: { BYTE Buf[1024]; int count; for(;;) { if (!currentClassEngine) { if (P.ModemClassId() != EngineBase::mcAudio) dleData.SetDiag(EngineBase::diagError); dleData.PutEof(); count = -1; break; } count = currentClassEngine->Recv(Buf, sizeof(Buf)); switch (count) { case -1: if (P.ModemClassId() == EngineBase::mcAudio) { dleData.PutEof(); } else { int diag = currentClassEngine->RecvDiag(); if (dataType == EngineBase::dtHdlc) { Buf[0] = BYTE(fcs >> 8); Buf[1] = BYTE(fcs); if (diag & EngineBase::diagErrorMask) Buf[0]++; dleData.PutData(Buf, 2); } dleData.SetDiag(diag).PutEof(); } currentClassEngine->RecvStop(); if (dataCount == 0 && P.ModemClassId() == EngineBase::mcFax) dleData.GetDleData(Buf, sizeof(Buf)); // discard ... break; case 0: break; default: if (P.ModemClassId() == EngineBase::mcFax) { dleData.PutData(Buf, count); switch (dataType) { case EngineBase::dtHdlc: fcs.build(Buf, count); break; case EngineBase::dtRaw: if (!dataCount) { int dms = P.DelayFrmConnect(); if (dms) { Mutex.Signal(); PThread::Sleep(dms * 10); Mutex.Wait(); } // send CONNECT just before data for AT+FRM command PString _resp = RC_PREF() + RC_CONNECT(); PBYTEArray _bresp((const BYTE *)(const char *)_resp, _resp.GetLength()); myPTRACE(1, "<-- " << PRTHEX(_bresp)); bresp.Concatenate(_bresp); } break; default: myPTRACE(1, "Unexpected dataType=" << dataType); } } else { const PInt16 *ps = (const PInt16 *)Buf; signed char *pb = (signed char *)Buf; count /= sizeof(*ps); switch (P.Vcml()) { case 0: while (count--) *pb++ = (signed char)((*ps++)/256); break; case 1: case 128: case 130: while (count--) *pb++ = (signed char)((*ps++ + 0x8000)/256); break; case 4: case 131: while (count--) *pb++ = (signed char)linear2ulaw(*ps++); break; case 5: case 132: while (count--) *pb++ = (signed char)linear2alaw(*ps++); break; } count = int(pb - (signed char *)Buf); dleData.PutData(Buf, count); } dataCount += count; } if (count <= 0) break; } if (P.ModemClassId() == EngineBase::mcAudio) { if (count < 0) { PBYTEArray _bresp((const BYTE *)"\x10" "b", 2); // b bresp.Concatenate(_bresp); myPTRACE(2, "<-- DLE " << PRTHEX(_bresp)); } } for(;;) { count = dleData.GetDleData(Buf, sizeof(Buf)); switch (count) { case -1: { SetState(stCommand); if (P.ModemClassId() == EngineBase::mcAudio) { resp = RC_OK(); } else if (dataType == EngineBase::dtHdlc) { int diag = dleData.GetDiag(); if (diag == 0) resp = RC_OK(); else if (dataCount == 0 && (diag & EngineBase::diagErrorMask) == 0) resp = RC_NO_CARRIER(); else resp = RC_ERROR(); } else { resp = RC_NO_CARRIER(); } } break; case 0: break; default: { PBYTEArray _bresp(PBYTEArray(Buf, count)); #if PTRACING if (PTrace::CanTrace(4)) { if (count <= 16) { PTRACE(4, "<-- DLE " << PRTHEX(_bresp)); } else { PTRACE(4, "<-- DLE " << count << " bytes"); } } #endif bresp.Concatenate(_bresp); } } if( count <= 0 ) break; } } break; default: break; } if (resp.GetLength()) { resp = RC_PREF() + resp; PBYTEArray _bresp((const BYTE *)(const char *)resp, resp.GetLength()); myPTRACE(1, "<-- " << PRTHEX(_bresp)); bresp.Concatenate(_bresp); } } void ModemEngineBody::CheckStatePost() { PWaitAndSignal mutexWait(Mutex); if (off_hook) { for (int i = 0 ; i < (int)(sizeof(activeEngines)/sizeof(activeEngines[0])) ; i++) { EngineBase *engine = activeEngines[i]; if (!engine) continue; PBoolean enableIn = FALSE; PBoolean enableOut = FALSE; if (engine == currentClassEngine) { switch (state) { case stSend: case stSendBufEmptyHandle: case stSendAckWait: enableOut = enableFakeOut[i]; break; case stRecvBegWait: case stRecvBegHandle: case stRecv: enableIn = enableFakeIn[i]; break; default: break; } } engine->AddReference(); Mutex.Signal(); engine->EnableFakeIn(enableIn); engine->EnableFakeOut(enableOut); Mutex.Wait(); ReferenceObject::DelPointer(engine); } } if (state == stCommand) SendOnIdle(EngineBase::dtNone); } /////////////////////////////////////////////////////////////// t38modem-2.0.0/drv_c0c.cxx0000664000076400007640000005156511506353652016111 0ustar frolovfrolov00000000000000/* * drv_c0c.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2004-2010 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: drv_c0c.cxx,v $ * Revision 1.13 2010/12/28 12:27:22 vfrolov * Added fTXContinueOnXoff to prevent deadlock * * Revision 1.12 2009/07/08 18:43:44 vfrolov * Added PseudoModem::ttyName() * * Revision 1.11 2009/03/13 09:44:32 vfrolov * Fixed Segmentation fault (wrong PString usage) * * Revision 1.10 2008/09/24 14:41:06 frolov * Removed CTS monitoring * * Revision 1.9 2008/09/11 07:41:48 frolov * Ported to OPAL SVN trunk * * Revision 1.8 2007/03/30 11:01:12 vfrolov * Replaced strerror() by FormatMessage() * * Revision 1.7 2007/03/22 16:22:25 vfrolov * Added some changes * * Revision 1.6 2007/01/29 12:44:41 vfrolov * Added ability to put args to drivers * * Revision 1.5 2005/03/03 16:12:46 vfrolov * Fixed potential handle leak * Fixed compiler warnings * * Revision 1.4 2005/02/10 15:04:57 vfrolov * Disabled I/C calls for closed ports * * Revision 1.3 2004/10/20 14:00:16 vfrolov * Fixed race condition with SignalDataReady()/WaitDataReady() * * Revision 1.2 2004/08/30 12:11:33 vfrolov * Enabled input XON/XOFF control * * Revision 1.1 2004/07/07 13:36:46 vfrolov * Initial revision * */ #include #include "drv_c0c.h" #ifdef MODEM_DRIVER_C0C #define new PNEW ////////////////////////////////////////////////////////////// class UniC0C : public ModemThreadChild { PCLASSINFO(UniC0C, ModemThreadChild); public: UniC0C(PseudoModemC0C &_parent, HANDLE _hC0C); protected: PseudoModemC0C &Parent() { return (PseudoModemC0C &)parent; } HANDLE hC0C; }; /////////////////////////////////////////////////////////////// class InC0C : public UniC0C { PCLASSINFO(InC0C, UniC0C); public: InC0C(PseudoModemC0C &_parent, HANDLE _hC0C); protected: virtual void Main(); }; /////////////////////////////////////////////////////////////// class OutC0C : public UniC0C { PCLASSINFO(OutC0C, UniC0C); public: OutC0C(PseudoModemC0C &_parent, HANDLE _hC0C); protected: virtual void Main(); }; /////////////////////////////////////////////////////////////// UniC0C::UniC0C(PseudoModemC0C &_parent, HANDLE _hC0C) : ModemThreadChild(_parent), hC0C(_hC0C) { } /////////////////////////////////////////////////////////////// #if PTRACING static PString strError(DWORD err) { LPVOID pMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPTSTR) &pMsgBuf, 0, NULL); PString str((const char *)pMsgBuf); LocalFree(pMsgBuf); return str.Trim() + " (" + PString(err) + ")"; } static void TraceLastError(const PString &head) { DWORD err = ::GetLastError(); myPTRACE(1, head << " ERROR " << strError(err)); } #else #define TraceLastError(head) #endif /////////////////////////////////////////////////////////////// static void CloseEvents(int num, HANDLE *hEvents) { for (int i = 0 ; i < num ; i++) { if (hEvents[i]) { if (!::CloseHandle(hEvents[i])) { TraceLastError("CloseEvents() CloseHandle()"); } hEvents[i] = NULL; } } } static BOOL PrepareEvents(int num, HANDLE *hEvents, OVERLAPPED *overlaps) { memset(hEvents, 0, num * sizeof(HANDLE)); memset(overlaps, 0, num * sizeof(OVERLAPPED)); for (int i = 0 ; i < num ; i++) { overlaps[i].hEvent = hEvents[i] = ::CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvents[i]) { TraceLastError("PrepareEvents() CreateEvent()"); CloseEvents(i, hEvents); return FALSE; } } return TRUE; } static BOOL myClearCommError(HANDLE hC0C, DWORD *pErrors) { if (!ClearCommError(hC0C, pErrors, NULL)) { TraceLastError("ClearCommError()"); return FALSE; } return TRUE; } static BOOL myGetCommState(HANDLE hC0C, DCB *dcb) { dcb->DCBlength = sizeof(*dcb); if (!GetCommState(hC0C, dcb)) { TraceLastError("GetCommState()"); return FALSE; } return TRUE; } static BOOL mySetCommState(HANDLE hC0C, DCB *dcb) { if (!SetCommState(hC0C, dcb)) { TraceLastError("SetCommState()"); return FALSE; } return TRUE; } /////////////////////////////////////////////////////////////// InC0C::InC0C(PseudoModemC0C &_parent, HANDLE _hC0C) : UniC0C(_parent, _hC0C) { } void InC0C::Main() { RenameCurrentThread(Parent().ptyName() + "(i)"); myPTRACE(1, "--> Started"); enum { EVENT_READ, EVENT_STAT, EVENT_NUM }; HANDLE hEvents[EVENT_NUM]; OVERLAPPED overlaps[EVENT_NUM]; if (!PrepareEvents(EVENT_NUM, hEvents, overlaps)) SignalStop(); if (!SetCommMask(hC0C, EV_DSR|EV_BREAK)) { TraceLastError("SetCommMask()"); SignalStop(); } char cbuf[32]; DWORD cbufRead = 0; BOOL waitingRead = FALSE; DWORD maskStat = 0; BOOL waitingStat = FALSE; DWORD lastErrors; if (!myClearCommError(hC0C, &lastErrors)) SignalStop(); for (;;) { if (stop) break; if (!waitingRead) { DWORD undef; if (!ReadFile(hC0C, cbuf, sizeof(cbuf), &undef, &overlaps[EVENT_READ])) { DWORD err = ::GetLastError(); if (err != ERROR_IO_PENDING) { myPTRACE(1, "ReadFile() ERROR " << strError(err)); SignalStop(); break; } } waitingRead = TRUE; } if (!waitingStat) { if (!WaitCommEvent(hC0C, &maskStat, &overlaps[EVENT_STAT])) { DWORD err = ::GetLastError(); if (err != ERROR_IO_PENDING) { myPTRACE(1, "WaitCommEvent() ERROR " << strError(err)); SignalStop(); break; } } waitingStat = TRUE; DWORD stat; if (!GetCommModemStatus(hC0C, &stat)) { TraceLastError("GetCommModemStatus()"); SignalStop(); break; } if (!(stat & MS_DSR_ON)) { myPTRACE(1, "DSR is OFF"); Parent().reset = FALSE; SignalStop(); break; } DWORD errors; if (!myClearCommError(hC0C, &errors)) { SignalStop(); break; } DWORD changedErrors = lastErrors^errors; if (changedErrors & CE_BREAK) { if ((errors & CE_BREAK)) { myPTRACE(1, "BREAK is detected"); SignalStop(); break; } } lastErrors = errors; } if (waitingRead && waitingStat) { DWORD undef; switch (WaitForMultipleObjects(EVENT_NUM, hEvents, FALSE, 5000)) { case WAIT_OBJECT_0 + EVENT_READ: if (!GetOverlappedResult(hC0C, &overlaps[EVENT_READ], &cbufRead, FALSE)) { TraceLastError("GetOverlappedResult(EVENT_READ)"); SignalStop(); } waitingRead = FALSE; break; case WAIT_OBJECT_0 + EVENT_STAT: if (!GetOverlappedResult(hC0C, &overlaps[EVENT_STAT], &undef, FALSE)) { TraceLastError("GetOverlappedResult(EVENT_STAT)"); SignalStop(); } waitingStat = FALSE; PTRACE(6, "EVENT_STAT " << hex << maskStat); break; case WAIT_TIMEOUT: break; default: TraceLastError("WaitForMultipleObjects()"); SignalStop(); } if (stop) break; } if (!waitingRead && cbufRead) { PTRACE(6, "--> " << PRTHEX(PBYTEArray((const BYTE *)cbuf, cbufRead))); Parent().ToInPtyQ(cbuf, cbufRead); cbufRead = 0; } } CancelIo(hC0C); CloseEvents(EVENT_NUM, hEvents); myPTRACE(1, "--> Stopped" << GetThreadTimes(", CPU usage: ")); } /////////////////////////////////////////////////////////////// OutC0C::OutC0C(PseudoModemC0C &_parent, HANDLE _hC0C) : UniC0C(_parent, _hC0C) { } void OutC0C::Main() { RenameCurrentThread(Parent().ptyName() + "(o)"); myPTRACE(1, "<-- Started"); enum { EVENT_WRITE, EVENT_NUM }; HANDLE hEvents[EVENT_NUM]; OVERLAPPED overlaps[EVENT_NUM]; if (!PrepareEvents(EVENT_NUM, hEvents, overlaps)) SignalStop(); PBYTEArray *buf = NULL; PINDEX done = 0; DWORD written = 0; BOOL waitingWrite = FALSE; for(;;) { while (!buf) { if (stop) break; buf = Parent().FromOutPtyQ(); if (buf) { done = 0; break; } WaitDataReady(); } if (stop) break; if (!waitingWrite) { if (!WriteFile(hC0C, (const BYTE *)*buf + done, buf->GetSize() - done, &written, &overlaps[EVENT_WRITE])) { DWORD err = ::GetLastError(); if (err != ERROR_IO_PENDING) { myPTRACE(1, "WriteFile() ERROR " << strError(err)); SignalStop(); break; } waitingWrite = TRUE; } } if (waitingWrite) { switch (WaitForMultipleObjects(EVENT_NUM, hEvents, FALSE, 5000)) { case WAIT_OBJECT_0 + EVENT_WRITE: if (!GetOverlappedResult(hC0C, &overlaps[EVENT_WRITE], &written, FALSE)) { TraceLastError("GetOverlappedResult()"); SignalStop(); } waitingWrite = FALSE; break; case WAIT_TIMEOUT: myPTRACE(6, "TIMEOUT"); //#if PTRACING // if (PTrace::CanTrace(6)) { // DWORD errors; // COMSTAT comstat; // // if (ClearCommError(hC0C, &errors, &comstat)) { // PTRACE(1, // "errors=" << hex << errors << dec << " " // "fCtsHold=" << comstat.fCtsHold << " " // "fDsrHold=" << comstat.fDsrHold << " " // "fRlsdHold=" << comstat.fRlsdHold << " " // "fXoffHold=" << comstat.fXoffHold << " " // "fXoffSent=" << comstat.fXoffSent << " " // "fEof=" << comstat.fEof << " " // "fTxim=" << comstat.fTxim << " " // "cbInQue=" << comstat.cbInQue << " " // "cbOutQue=" << comstat.cbOutQue); // } // } //#endif break; default: TraceLastError("WaitForMultipleObjects()"); SignalStop(); } if (stop) break; } if (!waitingWrite && written) { PTRACE(6, "<-- " << PRTHEX(PBYTEArray((const BYTE *)*buf + done, written))); done += written; if (buf->GetSize() <= done) { if (buf->GetSize() < done) { myPTRACE(1, "<-- " << buf->GetSize() << "(size) < (done)" << done << " " << written); } delete buf; buf = NULL; } written = 0; } } CancelIo(hC0C); if (buf) { if (buf->GetSize() != done) myPTRACE(1, "<-- Not sent " << PRTHEX(PBYTEArray((const BYTE *)*buf + done, buf->GetSize() - done))); delete buf; } CloseEvents(EVENT_NUM, hEvents); myPTRACE(1, "<-- Stopped" << GetThreadTimes(", CPU usage: ")); } /////////////////////////////////////////////////////////////// PseudoModemC0C::PseudoModemC0C( const PString &_tty, const PString &_route, const PConfigArgs &/*args*/, const PNotifier &_callbackEndPoint) : PseudoModemBody(_tty, _route, _callbackEndPoint), hC0C(INVALID_HANDLE_VALUE), inC0C(NULL), outC0C(NULL), ready(FALSE) { if (CheckTty(_tty)) { ptypath = _tty; ptyname = ptypath.Mid(4); valid = TRUE; } else { myPTRACE(1, "PseudoModemC0C::PseudoModemC0C bad on " << _tty); valid = FALSE; } } PseudoModemC0C::~PseudoModemC0C() { reset = TRUE; StopAll(); CloseC0C(); } inline const char *ttyPrefix() { return "\\\\.\\"; } PBoolean PseudoModemC0C::CheckTty(const PString &_tty) { return _tty.Find(ttyPrefix()) == 0 && _tty != ttyPrefix(); } PString PseudoModemC0C::ArgSpec() { return ""; } PStringArray PseudoModemC0C::Description() { PStringArray description; description.Append(new PString("Uses serial port to communicate with fax application.")); description.Append(new PString(PString("The tty format is ") + ttyPrefix() + "port.")); return description; } PBoolean PseudoModemC0C::IsReady() const { return ready && PseudoModemBody::IsReady(); } const PString &PseudoModemC0C::ttyPath() const { return ptypath; } ModemThreadChild *PseudoModemC0C::GetPtyNotifier() { return outC0C; } PBoolean PseudoModemC0C::StartAll() { reset = TRUE; PTRACE(3, "PseudoModemC0C::StartAll"); if (IsOpenC0C() && (inC0C = new InC0C(*this, hC0C)) != NULL && (outC0C = new OutC0C(*this, hC0C)) != NULL && (PseudoModemBody::StartAll()) ) { inC0C->Resume(); outC0C->Resume(); ready = TRUE; return TRUE; } StopAll(); return FALSE; } void PseudoModemC0C::StopAll() { ready = FALSE; PTRACE(3, "PseudoModemC0C::StopAll reset=" << reset << " stop=" << stop << " childstop=" << childstop); if (inC0C) { inC0C->SignalStop(); inC0C->WaitForTermination(); PWaitAndSignal mutexWait(Mutex); delete inC0C; inC0C = NULL; } if (outC0C) { outC0C->SignalStop(); outC0C->WaitForTermination(); PWaitAndSignal mutexWait(Mutex); delete outC0C; outC0C = NULL; } if (reset) PseudoModemBody::StopAll(); else childstop = FALSE; } BOOL PseudoModemC0C::OpenC0C() { if (IsOpenC0C()) CloseC0C(); hC0C = CreateFile(ptypath, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hC0C == INVALID_HANDLE_VALUE) { TraceLastError(PString("PseudoModemBody::OpenC0C CreateFile(") + ptypath + ")"); return FALSE; } DCB dcb; if (!myGetCommState(hC0C, &dcb)) { CloseC0C(); return FALSE; } COMMPROP commProp; dcb.XonLim = 64; if (GetCommProperties(hC0C, &commProp)) { myPTRACE(1, "CurrentRxQueue=" << commProp.dwCurrentRxQueue); dcb.XoffLim = WORD(commProp.dwCurrentRxQueue - 128); } dcb.BaudRate = CBR_19200; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.fOutxCtsFlow = TRUE; dcb.fOutxDsrFlow = FALSE; dcb.fDsrSensitivity = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fTXContinueOnXoff = TRUE; dcb.fOutX = FALSE; dcb.fInX = TRUE; dcb.XonChar = 0x11; dcb.XoffChar = 0x13; dcb.fParity = FALSE; dcb.fNull = FALSE; dcb.fAbortOnError = FALSE; dcb.fErrorChar = FALSE; if (!mySetCommState(hC0C, &dcb)) { CloseC0C(); return FALSE; } COMMTIMEOUTS timeouts; if (!GetCommTimeouts(hC0C, &timeouts)) { TraceLastError("PseudoModemBody::OpenC0C GetCommTimeouts()"); CloseC0C(); return FALSE; } timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1; timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(hC0C, &timeouts)) { TraceLastError("PseudoModemBody::OpenC0C SetCommTimeouts()"); CloseC0C(); return FALSE; } myPTRACE(1, "PseudoModemBody::OpenC0C opened " << ptypath); return TRUE; } void PseudoModemC0C::CloseC0C() { if (!IsOpenC0C()) return; if (!::CloseHandle(hC0C)) { TraceLastError("PseudoModemC0C::CloseC0C() CloseHandle()"); } hC0C = INVALID_HANDLE_VALUE; } BOOL PseudoModemC0C::OutPnpId() { DCB org_dcb, dcb; if (!myGetCommState(hC0C, &org_dcb)) return FALSE; dcb = org_dcb; dcb.BaudRate = CBR_1200; dcb.ByteSize = 7; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; if (!mySetCommState(hC0C, &dcb)) return FALSE; enum { EVENT_WRITE, EVENT_NUM }; HANDLE hEvents[EVENT_NUM]; OVERLAPPED overlaps[EVENT_NUM]; if (!PrepareEvents(EVENT_NUM, hEvents, overlaps)) return FALSE; static char devID[] = "(" "\x01\x24" // PnP revision 1.0 "VAF" // EISA ID "0001" // Product ID "\\00000001" // Serial Number "\\MODEM" // Class Name "\\PNPC101" // Driver ID "\\t38modem" // User Name "??" // CheckSum ")"; static int lenDevID = sizeof(devID) - 1; devID[lenDevID - 3] = devID[lenDevID - 2] = 0; int checkSum = 0; for (int i = 0 ; i < lenDevID ; i++) { checkSum += devID[i]; } myPTRACE(1, "checkSum = 0x" << hex << (checkSum & 0xFF)); static char digs[] = "0123456789ABCDEF"; devID[lenDevID - 3] = digs[(checkSum >> 4) & 0xF]; devID[lenDevID - 2] = digs[checkSum & 0xF]; PBYTEArray *buf = new PBYTEArray((const BYTE *)devID, lenDevID); PINDEX done = 0; DWORD written = 0; BOOL waitingWrite = FALSE; if (!waitingWrite) { if (!WriteFile(hC0C, (const BYTE *)*buf + done, buf->GetSize() - done, &written, &overlaps[EVENT_WRITE])) { DWORD err = ::GetLastError(); if (err == ERROR_IO_PENDING) { waitingWrite = TRUE; } else { myPTRACE(1, "WriteFile() ERROR " << strError(err)); } } } if (waitingWrite) { switch (WaitForMultipleObjects(EVENT_NUM, hEvents, FALSE, 5000)) { case WAIT_OBJECT_0 + EVENT_WRITE: if (!GetOverlappedResult(hC0C, &overlaps[EVENT_WRITE], &written, FALSE)) { TraceLastError("GetOverlappedResult()"); } waitingWrite = FALSE; break; case WAIT_TIMEOUT: myPTRACE(6, "TIMEOUT"); break; default: TraceLastError("WaitForMultipleObjects()"); } } if (!waitingWrite && written) { myPTRACE(6, "<-- " << PRTHEX(PBYTEArray((const BYTE *)*buf + done, written))); done += written; if (buf->GetSize() <= done) { if (buf->GetSize() < done) { myPTRACE(1, "<-- " << buf->GetSize() << "(size) < (done)" << done << " " << written); } delete buf; buf = NULL; } } Sleep((lenDevID*10*1000)/1200); CancelIo(hC0C); if (buf) { if (buf->GetSize() != done) myPTRACE(1, "<-- Not sent " << PRTHEX(PBYTEArray((const BYTE *)*buf + done, buf->GetSize() - done))); delete buf; } CloseEvents(EVENT_NUM, hEvents); if (!mySetCommState(hC0C, &org_dcb)) return FALSE; return TRUE; } BOOL PseudoModemC0C::WaitReady() { enum { EVENT_STAT, EVENT_NUM }; HANDLE hEvents[EVENT_NUM]; OVERLAPPED overlaps[EVENT_NUM]; if (!PrepareEvents(EVENT_NUM, hEvents, overlaps)) return FALSE; BOOL fault = FALSE; if (!SetCommMask(hC0C, EV_CTS|EV_DSR)) { TraceLastError("SetCommMask()"); fault = TRUE; } DWORD maskStat = 0; BOOL waitingStat = FALSE; DWORD lastStat = 0; BOOL enumerator = FALSE; PTime TimeDSR; for (;;) { if (fault) break; if (!waitingStat) { if (!WaitCommEvent(hC0C, &maskStat, &overlaps[EVENT_STAT])) { DWORD err = ::GetLastError(); if (err != ERROR_IO_PENDING) { myPTRACE(1, "WaitCommEvent() ERROR " << strError(err)); fault = TRUE; break; } waitingStat = TRUE; } DWORD stat; if (!GetCommModemStatus(hC0C, &stat)) { TraceLastError("GetCommModemStatus()"); fault = TRUE; break; } DWORD changedStat = lastStat^stat; if (stat & MS_DSR_ON) { if (changedStat & MS_DSR_ON) TimeDSR = PTime(); if (stat & MS_CTS_ON) { PInt64 msSinceDSR = (PTime() - TimeDSR).GetMilliSeconds(); if (msSinceDSR > 150 && msSinceDSR < 250) { myPTRACE(1, "PnP Enumerator detected"); enumerator = TRUE; } break; } } lastStat = stat; } if (waitingStat) { DWORD undef; switch (WaitForMultipleObjects(EVENT_NUM, hEvents, FALSE, 5000)) { case WAIT_OBJECT_0 + EVENT_STAT: if (!GetOverlappedResult(hC0C, &overlaps[EVENT_STAT], &undef, FALSE)) { TraceLastError("GetOverlappedResult(EVENT_STAT)"); fault = TRUE; } waitingStat = FALSE; myPTRACE(6, "EVENT_STAT " << hex << maskStat); break; case WAIT_TIMEOUT: break; default: TraceLastError("WaitForMultipleObjects()"); fault = TRUE; } } } CancelIo(hC0C); CloseEvents(EVENT_NUM, hEvents); if (!fault && enumerator) fault = !OutPnpId(); return !fault; } void PseudoModemC0C::MainLoop() { if (AddModem() && OpenC0C()) { while (!stop && WaitReady() && StartAll()) { while (!stop && !childstop) { WaitDataReady(); } StopAll(); } CloseC0C(); } } /////////////////////////////////////////////////////////////// #endif // MODEM_DRIVER_C0C t38modem-2.0.0/tone_gen.cxx0000664000076400007640000001262511455110024016344 0ustar frolovfrolov00000000000000/* * tone_gen.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2010 Vyacheslav Frolov * * t38modem Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is t38modem. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: tone_gen.cxx,v $ * Revision 1.1 2010/10/12 16:43:00 vfrolov * Initial revision * */ #include #include #include "pmutils.h" #include "tone_gen.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// #define CNG_HZ 1100 #define CNG_MAX_DIV 100 #define CNG_AMPLITUDE 5000 #define CNG_ON_MSEC 500 #define CNG_OFF_MSEC 3000 /////////////////////////////////////////////////////////////// #define CED_HZ 2100 #define CED_MAX_DIV 100 #define CED_AMPLITUDE 5000 #define CED_ON_MSEC 3500 /////////////////////////////////////////////////////////////// #define RING_RU_HZ 425 #define RING_RU_MAX_DIV 25 #define RING_RU_AMPLITUDE 5000 #define RING_RU_ON_MSEC 1000 #define RING_RU_OFF_MSEC 4000 /////////////////////////////////////////////////////////////// #define BUSY_RU_HZ 425 #define BUSY_RU_MAX_DIV 25 #define BUSY_RU_AMPLITUDE 5000 #define BUSY_RU_ON_MSEC 350 #define BUSY_RU_OFF_MSEC 350 /////////////////////////////////////////////////////////////// typedef PInt16 SIMPLE_TYPE; #define BYTES_PER_SIMPLE sizeof(SIMPLE_TYPE) #define SIMPLES_PER_SEC 8000 /////////////////////////////////////////////////////////////// #define TWO_PI (3.1415926535897932384626433832795029L*2) static PBoolean InitTone(const void *, BYTE *pTone, size_t amount, unsigned hz, SIMPLE_TYPE ampl) { for( size_t i = 0 ; i < amount/BYTES_PER_SIMPLE ; i++ ) { double Sin = sin(double((hz*TWO_PI*i)/SIMPLES_PER_SEC)); ((SIMPLE_TYPE *)pTone)[i] = (SIMPLE_TYPE)(Sin * ampl); } return TRUE; } /////////////////////////////////////////////////////////////// #define MSEC2BYTES(ms) (((SIMPLES_PER_SEC*(ms))/1000)*BYTES_PER_SIMPLE) #define MAX_DIV2BYTES(maxdiv) ((SIMPLES_PER_SEC/(maxdiv))*BYTES_PER_SIMPLE) /////////////////////////////////////////////////////////////// ToneGenerator::ToneGenerator(ToneGenerator::ToneType tt) : type(tt) , index(0) { switch(tt) { case ttCng: { static BYTE toneOn[MAX_DIV2BYTES(CNG_MAX_DIV)]; static const PBoolean initTone = InitTone(&initTone, toneOn, sizeof(toneOn), CNG_HZ, CNG_AMPLITUDE); bytesOn = MSEC2BYTES(CNG_ON_MSEC); bytesOff = MSEC2BYTES(CNG_OFF_MSEC); pToneOn = toneOn; bytesToneOn = sizeof(toneOn); // begin with 1 sec silence #if CNG_OFF_MSEC < 1000 #error CNG_OFF_MSEC < 1000 #endif index = bytesOn + bytesOff - MSEC2BYTES(1000); break; } case ttCed: { static BYTE toneOn[MAX_DIV2BYTES(CED_MAX_DIV)]; static const PBoolean initTone = InitTone(&initTone, toneOn, sizeof(toneOn), CED_HZ, CED_AMPLITUDE); bytesOn = MSEC2BYTES(CED_ON_MSEC); bytesOff = (P_MAX_INDEX/BYTES_PER_SIMPLE)*BYTES_PER_SIMPLE - bytesOn; pToneOn = toneOn; bytesToneOn = sizeof(toneOn); // begin with 200 ms silence index = bytesOn + bytesOff - MSEC2BYTES(200); break; } case ttRing: { static BYTE toneOn[MAX_DIV2BYTES(RING_RU_MAX_DIV)]; static const PBoolean initTone = InitTone(&initTone, toneOn, sizeof(toneOn), RING_RU_HZ, RING_RU_AMPLITUDE); bytesOn = MSEC2BYTES(RING_RU_ON_MSEC); bytesOff = MSEC2BYTES(RING_RU_OFF_MSEC); pToneOn = toneOn; bytesToneOn = sizeof(toneOn); break; } case ttBusy: { static BYTE toneOn[MAX_DIV2BYTES(BUSY_RU_MAX_DIV)]; static const PBoolean initTone = InitTone(&initTone, toneOn, sizeof(toneOn), BUSY_RU_HZ, BUSY_RU_AMPLITUDE); bytesOn = MSEC2BYTES(BUSY_RU_ON_MSEC); bytesOff = MSEC2BYTES(BUSY_RU_OFF_MSEC); pToneOn = toneOn; bytesToneOn = sizeof(toneOn); break; } default: bytesOn = 0; bytesOff = (P_MAX_INDEX/BYTES_PER_SIMPLE)*BYTES_PER_SIMPLE - bytesOn; pToneOn = NULL; bytesToneOn = 0; } } void ToneGenerator::Read(void * buffer, PINDEX amount) { BYTE *pBuf = (BYTE *)buffer; PINDEX bytesOnOff = bytesOn + bytesOff; while(amount) { if (index >= bytesOnOff) index = 0; PINDEX len; if (bytesOn > index) { PINDEX i = index % bytesToneOn; len = bytesToneOn - i; if (len > amount) len = amount; memcpy(pBuf, pToneOn + i, len); } else { len = bytesOnOff - index; if (len > amount) len = amount; memset(pBuf, 0, len); } pBuf += len; amount -= len; index += len; } } /////////////////////////////////////////////////////////////// t38modem-2.0.0/Makefile0000664000076400007640000001414211513316517015470 0ustar frolovfrolov00000000000000# # Makefile # # T38FAX Pseudo Modem # # Copyright (c) 2001-2011 Vyacheslav Frolov # # t38modem Project # # The contents of this file are subject to the Mozilla Public License # Version 1.0 (the "License"); you may not use this file except in # compliance with the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. # # The Original Code is Open H323 Library. # # The Initial Developer of the Original Code is Vyacheslav Frolov # # Contributor(s): Equivalence Pty ltd # # $Log: Makefile,v $ # Revision 1.29 2011/01/12 12:23:43 vfrolov # Replaced hardcoded workaround for mgetty-voice by conditional one # # Revision 1.28 2010/10/12 16:46:25 vfrolov # Implemented fake streams # # Revision 1.27 2010/10/06 16:54:19 vfrolov # Redesigned engine opening/closing # # Revision 1.26 2010/02/12 08:35:51 vfrolov # Added fake_codecs.cxx # # Revision 1.25 2009/07/31 17:34:40 vfrolov # Removed --h323-old-asn and --sip-old-asn options # # Revision 1.24 2009/07/29 10:39:04 vfrolov # Moved h323lib specific code to h323lib directory # # Revision 1.23 2009/07/27 16:21:24 vfrolov # Moved h323lib specific code to h323lib directory # # Revision 1.22 2008/09/11 16:10:54 frolov # Ported to H323 Plus trunk # # Revision 1.21 2008/09/10 13:39:41 frolov # Fixed OBJDIR_SUFFIX for OPAL # # Revision 1.20 2008/09/10 11:15:00 frolov # Ported to OPAL SVN trunk # # Revision 1.19 2007/07/20 14:28:38 vfrolov # Added opalutils.cxx # # Revision 1.18 2007/07/17 10:03:22 vfrolov # Added Unix98 PTY support # # Revision 1.17 2007/05/28 12:52:27 vfrolov # Added OPAL support # # Revision 1.16 2007/05/17 08:32:44 vfrolov # Moved class T38Modem from main.h and main.cxx to main_process.cxx # # Revision 1.15 2007/05/03 09:21:47 vfrolov # Added compile time optimization for original ASN.1 sequence # in T.38 (06/98) Annex A or for CORRIGENDUM No. 1 fix # # Revision 1.14 2007/03/23 10:14:35 vfrolov # Implemented voice mode functionality # # Revision 1.13 2006/10/20 10:06:43 vfrolov # Added REPEAT_INDICATOR_SENDING ifdef # # Revision 1.12 2006/10/19 10:45:59 vfrolov # Added FD_TRACE_LEVEL ifdef # # Revision 1.11 2004/07/07 12:38:32 vfrolov # The code for pseudo-tty (pty) devices that communicates with fax application formed to PTY driver. # # Revision 1.10 2004/03/09 17:22:58 vfrolov # Added PROCESS_PER_THREAD ifdef # # Revision 1.9 2003/12/04 15:56:45 vfrolov # Added hdlc.cxx t30.cxx fcs.cxx # # Revision 1.8 2002/08/05 10:10:29 robertj # Normalised Makefile usage of openh323u.mak include file, fixing odd messages. # # Revision 1.7 2002/04/30 11:05:17 vfrolov # Implemented T.30 Calling Tone (CNG) generation # # Revision 1.6 2002/04/30 03:52:28 craigs # Added option for G.723.1 codec # # Revision 1.5 2002/04/27 10:17:20 vfrolov # Added checking if COUT_TRACE or MYPTRACE_LEVEL defined # Do not add -DCOUT_TRACE by default # # Revision 1.4 2002/02/11 08:35:08 vfrolov # myPTRACE() outputs trace to cout only if defined COUT_TRACE # # Revision 1.3 2002/01/10 06:10:02 craigs # Added MPL header # # PROG = t38modem SOURCES := pmutils.cxx dle.cxx pmodem.cxx pmodemi.cxx drivers.cxx \ t30tone.cxx tone_gen.cxx hdlc.cxx t30.cxx fcs.cxx \ pmodeme.cxx enginebase.cxx t38engine.cxx audio.cxx \ drv_pty.cxx \ main_process.cxx # # Build t38modem for # - Open Phone Abstraction Library if defined USE_OPAL # - Open H323 Library or H323 Plus Library if not defined USE_OPAL # (NOTE: define NO_PBOOLEAN for Open H323 Library) # ifdef USE_OPAL VPATH_CXX := opal SOURCES += \ opalutils.cxx \ modemep.cxx modemstrm.cxx \ h323ep.cxx \ sipep.cxx \ manager.cxx \ fake_codecs.cxx \ ifndef OPALDIR OPALDIR=$(HOME)/opal endif OBJDIR_SUFFIX = _opal$(OBJ_SUFFIX) STDCCFLAGS += -DUSE_OPAL include $(OPALDIR)/opal_inc.mak else VPATH_CXX := h323lib SOURCES += t38protocol.cxx audio_chan.cxx g7231_fake.cxx h323ep.cxx ifndef OPENH323DIR OPENH323DIR=$(HOME)/openh323 endif include $(OPENH323DIR)/openh323u.mak ifdef NO_PBOOLEAN STDCCFLAGS += -DPBoolean=BOOL endif endif # # If defined COUT_TRACE then enable duplicate the # output of myPTRACE() to cout # ifdef COUT_TRACE STDCCFLAGS += -DCOUT_TRACE endif # # By default the code will be optimized for original ASN.1 # sequence in T.38 (06/98) Annex A (it's optimal if you use # --old-asn option). # If defined OPTIMIZE_CORRIGENDUM_IFP then the code will be # optimized for CORRIGENDUM No. 1 fix (it's optimal if you # do not use --old-asn option). # ifdef OPTIMIZE_CORRIGENDUM_IFP STDCCFLAGS += -DOPTIMIZE_CORRIGENDUM_IFP endif # # If defined MYPTRACE_LEVEL=N then myPTRACE() will # output the trace with level N # ifdef MYPTRACE_LEVEL STDCCFLAGS += -DMYPTRACE_LEVEL=$(MYPTRACE_LEVEL) endif # # If defined FD_TRACE_LEVEL=N then myPTRACE() will # output the warnings on level N for big file descriptors # ifdef FD_TRACE_LEVEL STDCCFLAGS += -DFD_TRACE_LEVEL=$(FD_TRACE_LEVEL) endif # # If defined PROCESS_PER_THREAD then # - PID will be used in thread name rather then TID # - CPU usage will be traced # ifdef PROCESS_PER_THREAD STDCCFLAGS += -DPROCESS_PER_THREAD endif # # If defined REPEAT_INDICATOR_SENDING then t38modem # will repeat indicator sending on idle # ifdef REPEAT_INDICATOR_SENDING STDCCFLAGS += -DREPEAT_INDICATOR_SENDING endif # # If defined USE_UNIX98_PTY then t38modem will use # Unix98 scheme for pty devices. # If defined USE_LEGACY_PTY or not defined USE_UNIX98_PTY # then t38modem will use legacy scheme for pty devices. # Both schemes cen be used simultaneously. # ifdef USE_UNIX98_PTY STDCCFLAGS += -DUSE_UNIX98_PTY ifdef USE_LEGACY_PTY STDCCFLAGS += -DUSE_LEGACY_PTY endif else STDCCFLAGS += -DUSE_LEGACY_PTY endif # # If defined ALAW_132_BIT_REVERSE then t38modem # will use reversed bit order for +VSM=132 # (workaround for mgetty-voice) # ifdef ALAW_132_BIT_REVERSE STDCCFLAGS += -DALAW_132_BIT_REVERSE endif t38modem-2.0.0/tone_gen.h0000664000076400007640000000271311455110024015766 0ustar frolovfrolov00000000000000/* * tone_gen.h * * T38FAX Pseudo Modem * * Copyright (c) 2010 Vyacheslav Frolov * * t38modem Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is t38modem. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): * * $Log: tone_gen.h,v $ * Revision 1.1 2010/10/12 16:43:00 vfrolov * Initial revision * */ #ifndef _TONE_GEN_H #define _TONE_GEN_H /////////////////////////////////////////////////////////////// class ToneGenerator : public PObject { PCLASSINFO(ToneGenerator, PObject); public: enum ToneType { ttSilence, ttCng, ttCed, ttRing, ttBusy, }; ToneGenerator(ToneType tt = ttSilence); void Read(void * buffer, PINDEX amount); ToneType Type() const { return type; } protected: ToneType type; PINDEX bytesOn; PINDEX bytesOff; const BYTE *pToneOn; PINDEX bytesToneOn; PINDEX index; }; /////////////////////////////////////////////////////////////// #endif // _TONE_GEN_H t38modem-2.0.0/t30.cxx0000664000076400007640000000426711061726064015171 0ustar frolovfrolov00000000000000/* * t30.cxx * * T38FAX Pseudo Modem * * Copyright (c) 2003-2008 Vyacheslav Frolov * * Open H323 Project * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Open H323 Library. * * The Initial Developer of the Original Code is Vyacheslav Frolov * * Contributor(s): Equivalence Pty ltd * * $Log: t30.cxx,v $ * Revision 1.5 2008/09/10 11:15:00 frolov * Ported to OPAL SVN trunk * * Revision 1.4 2005/02/04 10:18:49 vfrolov * Fixed warnings for No Trace build * * Revision 1.3 2004/07/06 16:07:24 vfrolov * Included ptlib.h for precompiling * * Revision 1.2 2003/12/04 15:50:27 vfrolov * Fixed extracting ECM flag * * Revision 1.1 2003/12/04 13:38:46 vfrolov * Initial revision * */ #include #include "t30.h" /////////////////////////////////////////////////////////////// #define new PNEW /////////////////////////////////////////////////////////////// void T30::v21End(PBoolean myPTRACE_PARAM(sent)) { int size = v21frame.GetSize(); PString msg; if (size < 3) msg = "too short"; else if (v21frame[0] != 0xFF) msg = "w/o address field"; else if ((v21frame[1] & 0xF7) != 0xC0) msg = "w/o control field"; else { switch (v21frame[2]) { case 0x41: case 0x41 | 0x80: msg = "DCS"; if (v21frame.GetSize() > 3+3 && (v21frame[3+2] & 1) && (v21frame[3+3] & 0x20)) { ecm = TRUE; msg += " with ECM"; } else { ecm = FALSE; } cfr = FALSE; break; case 0x21: case 0x21 | 0x80: msg = "CFR"; cfr = TRUE; break; } } myPTRACE(2, PString(sent ? "-->" : "<--") << " v21frame " << msg << PRTHEX(v21frame)); } ///////////////////////////////////////////////////////////////