home *** CD-ROM | disk | FTP | other *** search
/ ftp.muug.mb.ca / 2014.06.ftp.muug.mb.ca.tar / ftp.muug.mb.ca / pub / openh323.tar.gz / openh323.tar / openh323 / src / vblasterlid.cxx < prev    next >
C/C++ Source or Header  |  2002-09-03  |  30KB  |  1,212 lines

  1. /*
  2.  * vblasterlid.cxx
  3.  *
  4.  * Creative Labs VOIP Blaster codec interface
  5.  *
  6.  * Open H323 Library
  7.  *
  8.  * Copyright (c) 2001 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Open H323 Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Contributor(s): ______________________________________.
  25.  *
  26.  * $Log: vblasterlid.cxx,v $
  27.  * Revision 1.7  2002/09/03 06:25:00  robertj
  28.  * Cosmetic change to formatting.
  29.  *
  30.  * Revision 1.6  2002/08/05 10:03:48  robertj
  31.  * Cosmetic changes to normalise the usage of pragma interface/implementation.
  32.  *
  33.  * Revision 1.5  2002/07/09 00:39:08  robertj
  34.  * Patches for latest fobbit driver, thanks Jian Yang
  35.  *
  36.  * Revision 1.4  2002/02/05 06:19:47  craigs
  37.  * Changed to use OPAL define rather than strings
  38.  *
  39.  * Revision 1.3  2002/01/15 07:23:10  craigs
  40.  * Added IsDevicePresent command
  41.  *
  42.  * Revision 1.2  2002/01/15 05:54:16  robertj
  43.  * Added bad implementation for GetDeviceNames()
  44.  *
  45.  * Revision 1.1  2002/01/15 04:16:52  craigs
  46.  * Initial version
  47.  *
  48.  */
  49.  
  50. #include <ptlib.h>
  51.  
  52. #ifdef __GNUC__
  53. #pragma implementation "vblasterlid.h"
  54. #endif
  55.  
  56. #include "vblasterlid.h"
  57.  
  58.  
  59. /*
  60.  
  61.   This code uses the VoIPBlaster interface as written by Dave Fobbitt (http://www.fobbit.com).
  62.   All of the information about the VoIPBlaster command set and functions was derived directly
  63.   from the code made available by Dave. This code requires Dave's USB drivers to be installed 
  64.   as per the instructions on his site. Thanks to Dave for making this code available for
  65.   general use.
  66.  
  67.   Introduction
  68.  
  69.   The interface to the VoIPBlaster (VB) is implemented using two pairs of read/write channels. These
  70.   are implemented as four named pipes for Windows, or two sockets for Unix. One channel is used
  71.   for sending commands to the VB and reading status, and the other channel is used for sending and
  72.   receiving audio data.
  73.  
  74.   Initialisation:
  75.     TBD
  76.     
  77.   Commands:
  78.     There are 27 different comands recognised by the VB. Each command is one byte.
  79.  
  80.         COMMAND_PHONE_OFF  0x01     drop loop current
  81.         COMMAND_PHONE_ON   0x02     used on startup
  82.         COMMAND_RING_ON    0x03     start ringing
  83.         COMMAND_RING_OFF   0x04     used on startup & to stop ringing
  84.         COMMAND_VOUT_START 0x05     start audio output
  85.         COMMAND_VOUT_STOP  0x06     stop audio output
  86.         COMMAND_VINP_START 0x07     start audio input
  87.         COMMAND_VINP_STOP  0x08     stop audio input
  88.         COMMAND_UNKNOWN_1  0x09     Unknown (TESTSTART)
  89.         COMMAND_UNKNOWN_2  0x0a     Unknown (TESTSTOP)
  90.         COMMAND_UNKNOWN_3  0x0b     Unknown (SENDFAXTONE)
  91.         COMMAND_0x0c       0x0c     Go offhook for headset
  92.         COMMAND_0x0d       0x0d     Go onhook for headset
  93.         COMMAND_SETUP_MODE 0x0e     Unknown(goto setup mode)
  94.         COMMAND_VOUT_DONE  0x0f     voice in/out off, report output drained
  95.         COMMAND_0x10       0x10     Unknown (used in file output, seems ok without)
  96.         COMMAND_0x11       0x11     Unknown (used in file output, seems ok without)
  97.         COMMAND_MUTE_ON    0x12     Audio mute on
  98.         COMMAND_MUTE_OFF   0x13     Audio mute off
  99.         COMMAND_VOL_0      0x34     Set volume (min)
  100.         COMMAND_VOL_1      0x35     Set volume
  101.         COMMAND_VOL_2      0x36     Set volume
  102.         COMMAND_VOL_3      0x37     Set volume (default)
  103.         COMMAND_VOL_4      0x38     Set volume
  104.         COMMAND_VOL_5      0x39     Set volume
  105.         COMMAND_VOL_6      0x3a     Set volume (max)
  106.  
  107.   Status:
  108.     There are 11 different status responses sent by the VB. Each status is one byte.
  109.         STATUS_NONE        0x00     No status
  110.         STATUS_HOOK_OFF    0x01     Offhook
  111.         STATUS_HOOK_ON     0x02     Onhook
  112.         STATUS_DEBUG       0x00     Not used (DEBUG)
  113.         STATUS_RINGDETECT  0x00     Not used (RINGDETECT)
  114.         STATUS_RINGING_ON  0x05     Ring started 
  115.         STATUS_RINGING_OFF 0x06     Ring stopped
  116.         STATUS_HEADSET_IN  0x08     Headset plugged in
  117.         STATUS_HEADSET_OUT 0x09     Headset unplugged
  118.         STATUS_0x0a        0x0a     Unknown (setup accepted?)
  119.         STATUS_VOUT_DONE   0x0c     Voice output done
  120.  
  121.   Voice:
  122.     The VB will accept audio in G.723.1 format at either 6.4kbps frames (24 bytes) or 5.3 kbps
  123.     frames (20 bytes). However, it only generates audio data at 5.3 kbps frames (20 bytes)
  124.  
  125. */
  126.  
  127. static BYTE blasterInit1[] =
  128. {
  129.     0x3b,0x00,0x40,0x8b // first 2 bytes is length, second 2 bytes is command?
  130. };
  131.  
  132. static BYTE blasterInit2[] =
  133. {
  134.     0x00,0x01,0x00,0x00,0x18,0x02,0x8f,0x00,
  135.     0x10,0x00,0x28,0x40,0x03,0x1a,0x0d,0x0c,
  136.     0xfa,0x43,0xfd,0xea,0x93,0xfe,0x1a,0x41,
  137.     0x00,0x4a,0x93,0xfe,0x2a,0x40,0x00,0x1a,
  138.     0x93,0xfe,0x3a,0x0a,0x00,0x1f,0x3c,0x00,
  139.     0x8c,0x0d,0x03,0xa3,0x23,0xa2,0xdf,0x0d,
  140.     0x0c,0x3a,0x40,0x00,0x2a,0x93,0xfe,0x4a,
  141.     0x0a,0x00,0x0f
  142. };
  143.  
  144. static u_char blasterInit3[] =
  145. {
  146.     0x75,0x58,0x9b,0x04,0x72,0x00,0x00,0x11,
  147.     0xe0,0x00,0x65,0x82,0x00,0x90,0x00,0x1c,
  148.     0x96,0xc1,0x0f,0xf2,0x3d,0x95,0x8e,0x5e,
  149.     0xe7,0x66,0xef,0xd4,0xba,0x21,0x0d,0x30,
  150.     0xcb,0x1e,0x52,0x35,0x9a,0xb6,0xff,0x7f,
  151.     0x74,0x58,0x9b,0x04,0x68,0x08,0x00,0x99,
  152.     0x52,0xfa,0x75,0xd7,0x72,0xba,0xdb,0x03,
  153.     0x3d,0xdb,0x77,0xd0,0x77,0x03,0x1f,0x05
  154. };
  155.  
  156.  
  157. /////////////////////////////////////////////////////////////////////////////
  158.  
  159. OpalVoipBlasterDevice::OpalVoipBlasterDevice()
  160.   : dtmfQueue(DTMFQueueSize)
  161. {
  162.   statusThread = NULL;
  163. }
  164.  
  165. OpalVoipBlasterDevice::~OpalVoipBlasterDevice()
  166. {
  167.   Close();
  168. }
  169.  
  170. BOOL OpalVoipBlasterDevice::Open(const PString & device)
  171. {
  172.   Close();
  173.  
  174.   int deviceIndex = device.AsInteger();
  175.  
  176.   readStopped = writeStopped = TRUE;
  177.  
  178.   // open the lowlevel device
  179.   if (!vBlaster.OpenCommand(deviceIndex)) {
  180.     PTRACE(3, "vBlaster\tCould not open VoipBlaster device \"" << device << '"');
  181.     return FALSE;
  182.   }
  183.  
  184.   // set up thread to read status pipe
  185.   statusRunning = TRUE;
  186.   statusThread = PThread::Create(PCREATE_NOTIFIER(StatusHandler), 0,
  187.                                  PThread::NoAutoDeleteThread,
  188.                                  PThread::NormalPriority,
  189.                                  "VbStatus:%x");
  190.  
  191.   // put device into setup mode
  192.   vBlaster.WriteCommand(VoipBlasterInterface::Command_SETUP_MODE); 
  193.   vBlaster.OpenData();
  194.  
  195.   // write initialisation data
  196.   vBlaster.WriteData(blasterInit1, sizeof(blasterInit1));
  197.   vBlaster.WriteData(blasterInit2, sizeof(blasterInit2));
  198.  
  199.   // remove 17 bytes from the voice channel
  200.   // if 0xa returned on status channel, then this was a serial number
  201.   // otherwise remove 3 more to make a 20 byte frame
  202.   PThread::Sleep(100);
  203.   if (firstTime) {
  204.     // read 17 bytes. This may be a serial number or not
  205.     BYTE serialNumber[17];
  206.     vBlaster.ReadData(serialNumber, sizeof(serialNumber));
  207.     PTRACE(3, "vBlaster\tGot serial number");
  208.     PError << "vBlaster\tGot serial number";
  209.   } 
  210.  
  211.   // this will close the data channel
  212.   vBlaster.CloseData();
  213.  
  214.   // send ring off command and wait for reply
  215.   PINDEX i;
  216.   ringOn = TRUE;
  217.   for (i = 0; ringOn && (i < 2); i++) {
  218.     PError << "Init " << i << endl;
  219.     vBlaster.WriteCommand(VoipBlasterInterface::Command_VOL_3);
  220.     vBlaster.WriteCommand(VoipBlasterInterface::Command_RING_OFF);
  221.     vBlaster.WriteCommand(VoipBlasterInterface::Command_PHONE_ON);
  222.  
  223.     PTimer timer(2000);
  224.     while (timer.IsRunning()) {
  225.       if (!ringOn)
  226.         break;
  227.       PThread::Sleep(100);
  228.     }
  229.   }
  230.  
  231.  
  232.   // if we did not receive a ring off, then error
  233.   if (ringOn) {
  234.     PTRACE(1, "VB\tCould not initialise VoIPBlaster");
  235.     PError << "Could not initialise VoIPBlaster" << endl;
  236.     Close();
  237.     //return FALSE;
  238.   }
  239.  
  240.   // unmute output and set default value
  241.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VOL_3);
  242.   vBlaster.WriteCommand(VoipBlasterInterface::Command_MUTE_OFF);
  243.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VOUT_STOP);
  244.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VINP_STOP);
  245.  
  246.   // save the device name
  247.   deviceName = device;
  248.   PTRACE(3, "vBlaster\tVoipBlaster device \"" << device << "\" opened");
  249.   os_handle = deviceIndex;
  250.  
  251.   return TRUE;
  252. }
  253.  
  254.  
  255. BOOL OpalVoipBlasterDevice::Close()
  256. {
  257.   PWaitAndSignal m(vbMutex);
  258.  
  259.   if (!IsOpen())
  260.     return FALSE;
  261.  
  262.   // flag the status thread to stop running on next status byte
  263.   statusRunning = FALSE;
  264.  
  265.   // send a command to the phone stat will force a status byte to return
  266.   vBlaster.WriteCommand(VoipBlasterInterface::Command_PHONE_OFF);
  267.   vBlaster.WriteCommand(VoipBlasterInterface::Command_MUTE_OFF);
  268.  
  269.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VOUT_DONE);
  270.   vBlaster.WriteData   (blasterInit3, sizeof(blasterInit3));
  271.  
  272.   // close the device - this will stop the status thread for sure!
  273.   vBlaster.CloseCommand();
  274.  
  275.   // wait for the status thread to terminate
  276.   statusThread->WaitForTermination();
  277.   delete statusThread;
  278.   statusThread = NULL;
  279.  
  280.   deviceName = PString();
  281.  
  282.   os_handle = -1;
  283.   return TRUE;
  284. }
  285.  
  286.  
  287. PString OpalVoipBlasterDevice::GetName() const
  288. {
  289.   return deviceName;
  290. }
  291.  
  292. void OpalVoipBlasterDevice::StatusHandler(PThread &, INT)
  293. {
  294.   hookState = FALSE;
  295.   headset   = FALSE;
  296.   ringOn    = FALSE;
  297.   firstTime = FALSE;
  298.  
  299.   while (statusRunning) {
  300.     int status = vBlaster.ReadStatus();
  301.     if (status != VoipBlasterInterface::Status_Empty) {
  302.       switch (status) {
  303.         case VoipBlasterInterface::Status_NONE:        // No status
  304.           break;
  305.  
  306.         case VoipBlasterInterface::Status_HOOK_OFF:    // Offhook
  307.           PTRACE(1, "VB\tHook off");
  308.           hookState = TRUE;
  309.           break;
  310.  
  311.         case VoipBlasterInterface::Status_HOOK_ON:     // Onhook
  312.           PTRACE(1, "VB\tHook on");
  313.           hookState = FALSE;
  314.           break;
  315.  
  316.         case VoipBlasterInterface::Status_RINGING_ON:  // Ring started 
  317.           PTRACE(1, "VB\tRing start");
  318.           ringOn = TRUE;
  319.           break;
  320.  
  321.         case VoipBlasterInterface::Status_RINGING_OFF: // Ring stopped
  322.           PTRACE(1, "VB\tRing end");
  323.           ringOn = FALSE;
  324.           break;
  325.  
  326.         case VoipBlasterInterface::Status_HEADSET_IN:  // Headset plugged in
  327.           PTRACE(1, "VB\tHeadset in");
  328.           headset = TRUE;
  329.           break;
  330.  
  331.         case VoipBlasterInterface::Status_HEADSET_OUT: // Headset unplugged
  332.           PTRACE(1, "VB\tHeadset off");
  333.           headset = FALSE;
  334.           break;
  335.  
  336.         case VoipBlasterInterface::Status_0x0a:        // Unknown (setup accepted?)
  337.           PTRACE(1, "VB\tStatus 0xa");
  338.           firstTime = TRUE;
  339.           break;
  340.  
  341.         case VoipBlasterInterface::Status_VOUT_DONE:   // Voice output done
  342.           PTRACE(1, "VB\tVOUT done");
  343.           break;
  344.  
  345.         case '0':
  346.         case '1':
  347.         case '2':
  348.         case '3':
  349.         case '4':
  350.         case '5':
  351.         case '6':
  352.         case '7':
  353.         case '8':
  354.         case '9':
  355.         case 'A':
  356.         case 'B':
  357.         case 'C':
  358.         case 'D':
  359.         case '*':
  360.         case '#':
  361.           PTRACE(1, "VB\tDTMF digit " << (char )status);
  362.           dtmfQueue.Enqueue((BYTE)status);
  363.           break;
  364.  
  365.         default:
  366.           PTRACE(1, "VB\tUnknown status value " << status);
  367.       }
  368.     }
  369.   }
  370. }
  371.  
  372. BOOL OpalVoipBlasterDevice::IsLineOffHook(unsigned line)
  373. {
  374.   PWaitAndSignal m(vbMutex);
  375.  
  376.   if (!IsOpen())
  377.     return FALSE;
  378.  
  379.   if (line > 0)
  380.     return FALSE;
  381.  
  382.   return hookState;
  383. }
  384.  
  385.  
  386. BOOL OpalVoipBlasterDevice::SetLineOffHook(unsigned /*line*/, BOOL /* newState*/)
  387. {
  388.   return FALSE;
  389. }
  390.  
  391.  
  392. BOOL OpalVoipBlasterDevice::HasHookFlash(unsigned /* line */)
  393. {
  394.   return FALSE;
  395. }
  396.  
  397.  
  398. BOOL OpalVoipBlasterDevice::IsLineRinging(unsigned /*line*/, DWORD * /*cadence*/)
  399. {
  400.   return FALSE;
  401. }
  402.  
  403.  
  404. BOOL OpalVoipBlasterDevice::RingLine(unsigned line, DWORD cadence)
  405. {
  406.   PWaitAndSignal m(vbMutex);
  407.  
  408.   if (!IsOpen())
  409.     return FALSE;
  410.  
  411.   if (line > 0)
  412.     return FALSE;
  413.  
  414.   // %%%%%% this really needs to use a timer to implement cadences.
  415.   if (cadence == 0)
  416.     vBlaster.WriteCommand(VoipBlasterInterface::Command_RING_OFF);
  417.   else
  418.     vBlaster.WriteCommand(VoipBlasterInterface::Command_RING_ON); 
  419.  
  420.   return TRUE;
  421. }
  422.  
  423.  
  424. BOOL OpalVoipBlasterDevice::IsLineDisconnected(unsigned /*line*/, BOOL /*checkForWink*/)
  425. {
  426.   return FALSE;
  427. }
  428.  
  429.  
  430. BOOL OpalVoipBlasterDevice::SetLineToLineDirect(unsigned /*line1 */, unsigned /*line2*/, BOOL /*connect*/)
  431. {
  432.   return FALSE;
  433. }
  434.  
  435.  
  436. BOOL OpalVoipBlasterDevice::IsLineToLineDirect(unsigned /*line1*/, unsigned /*line2*/)
  437. {
  438.   return FALSE;
  439. }
  440.  
  441.  
  442. static const struct {
  443.   const char * mediaFormat;
  444.   PINDEX frameSize;
  445. } CodecInfo[] = {
  446.   { OPAL_G7231_6k3,  24 },
  447.   { OPAL_G7231_5k3,  20 },
  448. };
  449.  
  450.  
  451. OpalMediaFormat::List OpalVoipBlasterDevice::GetMediaFormats() const
  452. {
  453.   OpalMediaFormat::List codecs;
  454.  
  455.   codecs.Append(new OpalMediaFormat(CodecInfo[0].mediaFormat));
  456.  
  457.   return codecs;
  458. }
  459.  
  460.  
  461. static PINDEX FindCodec(const OpalMediaFormat & mediaFormat)
  462. {
  463.   for (PINDEX codecType = 0; codecType < PARRAYSIZE(CodecInfo); codecType++) {
  464.     if (mediaFormat == CodecInfo[codecType].mediaFormat)
  465.       return codecType;
  466.   }
  467.  
  468.   return P_MAX_INDEX;
  469. }
  470.  
  471.  
  472. BOOL OpalVoipBlasterDevice::SetReadFormat(unsigned line, const OpalMediaFormat & mediaFormat)
  473. {
  474.   StopReadCodec(line);
  475.  
  476.   PWaitAndSignal m(vbMutex);
  477.   PWaitAndSignal mutex(readMutex);
  478.  
  479.   readCodecType = FindCodec(mediaFormat);
  480.   if (readCodecType == P_MAX_INDEX) {
  481.     PTRACE(1, "vBlaster\tUnsupported read codec requested: " << mediaFormat);
  482.     return FALSE;
  483.   }
  484.  
  485.   if (!writeStopped && readCodecType != writeCodecType) {
  486.     PTRACE(1, "vBlaster\tAsymmetric codecs requested: "
  487.               "read=" << CodecInfo[readCodecType].mediaFormat
  488.            << " write=" << CodecInfo[writeCodecType].mediaFormat);
  489.     return FALSE;
  490.   }
  491.  
  492.   PTRACE(3, "vBlaster\tSetReadFormat(" << CodecInfo[readCodecType].mediaFormat << ')');
  493.  
  494.   readFrameSize = CodecInfo[readCodecType].frameSize;
  495.  
  496.   if (writeStopped)
  497.     vBlaster.OpenData();
  498.  
  499.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VINP_START);
  500.  
  501.   readDelay.Restart();
  502.  
  503.   readStopped = FALSE;
  504.  
  505.   return TRUE;
  506. }
  507.  
  508.  
  509. BOOL OpalVoipBlasterDevice::SetWriteFormat(unsigned line, const OpalMediaFormat & mediaFormat)
  510. {
  511.   StopWriteCodec(line);
  512.  
  513.   PWaitAndSignal m(vbMutex);
  514.   PWaitAndSignal mutex(writeMutex);
  515.  
  516.   writeCodecType = FindCodec(mediaFormat);
  517.   if (writeCodecType == P_MAX_INDEX) {
  518.     PTRACE(1, "vBlaster\tUnsupported write codec requested: " << mediaFormat);
  519.     return FALSE;
  520.   }
  521.  
  522.   if (!readStopped && writeCodecType != readCodecType) {
  523.     PTRACE(1, "vBlaster\tAsymmetric codecs requested: "
  524.               "read=" << CodecInfo[readCodecType].mediaFormat
  525.            << " write=" << CodecInfo[writeCodecType].mediaFormat);
  526.     return FALSE;
  527.   }
  528.  
  529.   PTRACE(3, "vBlaster\tSetWriteFormat(" << CodecInfo[writeCodecType].mediaFormat << ')');
  530.  
  531.   writeFrameSize = CodecInfo[writeCodecType].frameSize;
  532.  
  533.   if (readStopped)
  534.     vBlaster.OpenData();
  535.  
  536.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VOUT_START);
  537.   vBlaster.WriteCommand(VoipBlasterInterface::Command_VOL_3);
  538.   vBlaster.WriteCommand(VoipBlasterInterface::Command_MUTE_OFF);
  539.  
  540.   writeDelay.Restart();
  541.  
  542.   writeStopped = FALSE;
  543.   return TRUE;
  544. }
  545.  
  546.  
  547. OpalMediaFormat OpalVoipBlasterDevice::GetReadFormat(unsigned)
  548. {
  549.   if (readCodecType == P_MAX_INDEX)
  550.     return "";
  551.   return CodecInfo[readCodecType].mediaFormat;
  552. }
  553.  
  554.  
  555. OpalMediaFormat OpalVoipBlasterDevice::GetWriteFormat(unsigned)
  556. {
  557.   if (writeCodecType == P_MAX_INDEX)
  558.     return "";
  559.   return CodecInfo[writeCodecType].mediaFormat;
  560. }
  561.  
  562.  
  563. BOOL OpalVoipBlasterDevice::SetRawCodec(unsigned)
  564. {
  565.   return FALSE;
  566. }
  567.  
  568. BOOL OpalVoipBlasterDevice::StopReadCodec(unsigned line)
  569. {
  570.   PTRACE(3, "vBlaster\tStopping read codec");
  571.  
  572.   PWaitAndSignal m(vbMutex);
  573.   readMutex.Wait();
  574.   if (!readStopped) {
  575.     readStopped = TRUE;
  576.     vBlaster.WriteCommand(VoipBlasterInterface::Command_VINP_STOP);
  577.   }
  578.  
  579.   if (writeStopped)
  580.     vBlaster.CloseData();
  581.  
  582.   readMutex.Signal();
  583.  
  584.   return OpalLineInterfaceDevice::StopReadCodec(line);
  585. }
  586.  
  587.  
  588. BOOL OpalVoipBlasterDevice::StopWriteCodec(unsigned line)
  589. {
  590.   PTRACE(3, "vBlaster\tStopping write codec");
  591.  
  592.   PWaitAndSignal m(vbMutex);
  593.   writeMutex.Wait();
  594.   if (!writeStopped) {
  595.     writeStopped = TRUE;
  596.     vBlaster.WriteCommand(VoipBlasterInterface::Command_VOUT_STOP);
  597.   }
  598.  
  599.   if (readStopped)
  600.     vBlaster.CloseData();
  601.  
  602.   writeMutex.Signal();
  603.  
  604.   return OpalLineInterfaceDevice::StopWriteCodec(line);
  605. }
  606.  
  607.  
  608. BOOL OpalVoipBlasterDevice::StopRawCodec(unsigned /*line*/)
  609. {
  610.   return FALSE;
  611. }
  612.  
  613.  
  614. PINDEX OpalVoipBlasterDevice::GetReadFrameSize(unsigned)
  615. {
  616.   return readFrameSize;
  617. }
  618.  
  619.  
  620. BOOL OpalVoipBlasterDevice::SetReadFrameSize(unsigned, PINDEX size)
  621. {
  622.   readFrameSize = size;
  623.   return TRUE;
  624. }
  625.  
  626.  
  627. PINDEX OpalVoipBlasterDevice::GetWriteFrameSize(unsigned)
  628. {
  629.   return writeFrameSize;
  630. }
  631.  
  632.  
  633. BOOL OpalVoipBlasterDevice::SetWriteFrameSize(unsigned, PINDEX size)
  634. {
  635.   writeFrameSize = size;
  636.   return TRUE;
  637. }
  638.  
  639.  
  640. BOOL OpalVoipBlasterDevice::ReadFrame(unsigned, void * buffer, PINDEX & wasRead)
  641. {
  642.   PWaitAndSignal mutex(readMutex);
  643.   if (readStopped)
  644.     return FALSE;
  645.  
  646.   readDelay.Delay(30);
  647.  
  648.   if (!hookState)
  649.     return FALSE;
  650.  
  651.   int stat;
  652.   if ((stat = vBlaster.ReadData(buffer, 20)) != 20) {
  653.     PTRACE(1, "Error reading frame - " << stat);
  654.     return FALSE;
  655.   }
  656.  
  657.   wasRead = 20;
  658.  
  659.   return TRUE;
  660. }
  661.  
  662.  
  663. BOOL OpalVoipBlasterDevice::WriteFrame(unsigned, const void * buffer, PINDEX /*count*/, PINDEX & written)
  664. {
  665.   PWaitAndSignal m(writeMutex);
  666.   if (writeStopped)
  667.     return FALSE;
  668.  
  669.   writeDelay.Delay(30);
  670.  
  671.   const BYTE * p = (const BYTE *)buffer;
  672.  
  673.   PINDEX toWrite = 0;
  674.   switch (*p & 3) {
  675.     case 0:
  676.       toWrite = 24;
  677.       vBlaster.WriteData(buffer, 24);
  678.       break;
  679.     case 1:
  680.       toWrite = 20;
  681.       vBlaster.WriteData(buffer, 20);
  682.       break;
  683.     case 2:
  684.       toWrite = 4;
  685.       break;
  686.     default:
  687.       // Check for frame erasure command
  688.       if (memcmp(buffer, "\xff\xff\xff\xff", 4) == 0)
  689.         toWrite = 24;
  690.       else
  691.         toWrite = 1;
  692.       break;
  693.   }
  694.  
  695.   written = toWrite;
  696.  
  697.   return TRUE;
  698. }
  699.  
  700.  
  701. unsigned OpalVoipBlasterDevice::GetAverageSignalLevel(unsigned, BOOL /*playback*/)
  702. {
  703.   return UINT_MAX;
  704. }
  705.  
  706.  
  707. BOOL OpalVoipBlasterDevice::EnableAudio(unsigned /*line*/, BOOL /*enable*/)
  708. {
  709.   return TRUE;
  710. }
  711.  
  712.  
  713. BOOL OpalVoipBlasterDevice::SetRecordVolume(unsigned /*line*/, unsigned /*volume*/)
  714. {
  715.   return TRUE;
  716. }
  717.  
  718.  
  719. BOOL OpalVoipBlasterDevice::SetPlayVolume(unsigned /*line*/, unsigned /*volume*/)
  720. {
  721.   return TRUE;
  722. }
  723.  
  724.  
  725. BOOL OpalVoipBlasterDevice::GetRecordVolume(unsigned, unsigned & /*volume*/)
  726. {
  727.   return TRUE;
  728. }
  729.  
  730.  
  731. BOOL OpalVoipBlasterDevice::GetPlayVolume(unsigned, unsigned & volume)
  732. {
  733.   volume = 50;
  734.   return TRUE;
  735. }
  736.  
  737.  
  738. OpalLineInterfaceDevice::AECLevels OpalVoipBlasterDevice::GetAEC(unsigned)
  739. {
  740.   return AECOff;
  741. }
  742.  
  743.  
  744. BOOL OpalVoipBlasterDevice::SetAEC(unsigned, AECLevels /*level*/)
  745. {
  746.   return FALSE;
  747. }
  748.  
  749.  
  750. BOOL OpalVoipBlasterDevice::GetVAD(unsigned)
  751. {
  752.   return FALSE;
  753. }
  754.  
  755.  
  756. BOOL OpalVoipBlasterDevice::SetVAD(unsigned, BOOL /*enable*/)
  757. {
  758.   return FALSE;
  759. }
  760.  
  761.  
  762. BOOL OpalVoipBlasterDevice::GetCallerID(unsigned, PString & /*idString*/, BOOL /*full*/)
  763. {
  764.   return FALSE;
  765. }
  766.  
  767.  
  768. BOOL OpalVoipBlasterDevice::SetCallerID(unsigned, const PString & /*idString*/)
  769. {
  770.   return FALSE;
  771. }
  772.  
  773.  
  774. BOOL OpalVoipBlasterDevice::SendCallerIDOnCallWaiting(unsigned, const PString & /*idString*/)
  775. {
  776.   return FALSE;
  777. }
  778.  
  779.  
  780. BOOL OpalVoipBlasterDevice::SendVisualMessageWaitingIndicator(unsigned /*line*/, BOOL /*isOn*/)
  781. {
  782.   return FALSE;
  783. }
  784.  
  785.  
  786. BOOL OpalVoipBlasterDevice::PlayDTMF(unsigned /*line*/,
  787.                                      const char * digits,
  788.                              DWORD /*onTime*/, DWORD /*offTime*/)
  789. {
  790.   PINDEX i;
  791.   for (i = 0; digits[i] != '\0'; i++) {
  792.     PWaitAndSignal m(vbMutex);
  793.     vBlaster.WriteCommand((VoipBlasterInterface::Command)digits[i]);
  794.   }
  795.  
  796.   return TRUE;
  797. }
  798.  
  799.  
  800. char OpalVoipBlasterDevice::ReadDTMF(unsigned)
  801. {
  802.   int v = dtmfQueue.Dequeue();
  803.   return (v < 0) ? (char)0 : (char)v;
  804. }
  805.  
  806.  
  807. BOOL OpalVoipBlasterDevice::GetRemoveDTMF(unsigned)
  808. {
  809.   return TRUE;
  810. }
  811.  
  812.  
  813. BOOL OpalVoipBlasterDevice::SetRemoveDTMF(unsigned, BOOL /*state*/)
  814. {
  815.   return TRUE;
  816. }
  817.  
  818.  
  819. unsigned OpalVoipBlasterDevice::IsToneDetected(unsigned /*line*/)
  820. {
  821.   return FALSE;
  822. }
  823.  
  824.  
  825. BOOL OpalVoipBlasterDevice::PlayTone(unsigned /*line*/, CallProgressTones /*tone*/)
  826. {
  827.   return FALSE;
  828. }
  829.  
  830. BOOL OpalVoipBlasterDevice::IsTonePlaying(unsigned)
  831. {
  832.   return FALSE;
  833. }
  834.  
  835.  
  836. BOOL OpalVoipBlasterDevice::StopTone(unsigned)
  837. {
  838.   return FALSE;
  839. }
  840.  
  841.  
  842. DWORD OpalVoipBlasterDevice::GetSerialNumber()
  843. {
  844.   return 0;
  845. }
  846.  
  847.  
  848. PStringArray OpalVoipBlasterDevice::GetDeviceNames()
  849. {
  850.   PStringArray array;
  851.  
  852.   VoipBlasterInterface blaster;
  853.   PINDEX i = 0;
  854.   while ((i < 16) && blaster.IsDevicePresent(i)) {
  855.     array[i] = i;
  856.     i++;
  857.   }
  858.  
  859.   return array;
  860. }
  861.  
  862. BOOL OpalVoipBlasterDevice::SetCountryCode(T35CountryCodes /*country*/)
  863. {
  864.   return TRUE;
  865. }
  866.  
  867.  
  868. /////////////////////////////////////////////////////////////////////////////
  869.  
  870. OpalVoipBlasterDevice::ByteQueue::ByteQueue(PINDEX _qMax)
  871.   : qMax(_qMax)
  872. {
  873.   qOut = qLen = 0;
  874.   queue.SetSize(qMax);
  875. }
  876.  
  877. int OpalVoipBlasterDevice::ByteQueue::Dequeue()
  878. {
  879.   PWaitAndSignal m(mutex);
  880.   if (qLen == 0)
  881.     return -1;
  882.  
  883.   int v = queue[qOut];
  884.  
  885.   qOut = (qOut % qMax);
  886.   qLen--;
  887.  
  888.   return v;
  889. }
  890.  
  891. BOOL OpalVoipBlasterDevice::ByteQueue::Enqueue(BYTE v)
  892. {
  893.   PWaitAndSignal m(mutex);
  894.  
  895.   if (qLen == qMax)
  896.     return FALSE;
  897.  
  898.   queue[(qOut + qLen) % qMax] = v;
  899.   qLen++;
  900.  
  901.   return TRUE;
  902. }
  903.  
  904.  
  905. /////////////////////////////////////////////////////////////////////////////
  906.  
  907. #ifdef _WIN32
  908.  
  909. #include <initguid.h>
  910. #include <setupapi.h>
  911.  
  912. // {00873FDF-61A8-11d1-AA5E-00C04FB1728B}  for VB_USB.SYS
  913.  
  914. DEFINE_GUID(GUID_CLASS_VOIP_BLASTER,
  915.   0x873fdf, 0x61a8, 0x11d1, 0xaa, 0x5e, 0x0, 0xc0, 0x4f, 0xb1, 0x72, 0x8b);
  916.  
  917. static BOOL GetDeviceInterfacePath(HDEVINFO hDevInfo,
  918.                                    PSP_DEVICE_INTERFACE_DATA pDeviceInterfaceData,
  919.                                    char *dest, DWORD dwMaxLen)
  920. {
  921.   PSP_INTERFACE_DEVICE_DETAIL_DATA pDetailData     = NULL;
  922.   ULONG                            requiredLength  =    0;
  923.  
  924.   // allocate a function class device data structure to receive the
  925.   // goods about this particular device.
  926.   SetupDiGetInterfaceDeviceDetail(hDevInfo,
  927.             pDeviceInterfaceData, //
  928.             NULL,            // probing so no output buffer yet
  929.             0,               // probing so output buffer length of zero
  930.             &requiredLength, //
  931.             NULL);           // not interested in the specific dev-node
  932.  
  933.   pDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength);
  934.   ZeroMemory(pDetailData, requiredLength );
  935.   pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  936.  
  937.   // Retrieve the information from Plug and Play.
  938.   if (!SetupDiGetDeviceInterfaceDetail(hDevInfo,
  939.                pDeviceInterfaceData,
  940.                pDetailData,
  941.                requiredLength,
  942.                NULL,
  943.                NULL)) {
  944.     PTRACE(1, "VB\tError " << GetLastError() << " in GetDeviceInterfacePath");
  945.     LocalFree(pDetailData);
  946.     return FALSE;
  947.   }
  948.  
  949.   strncpy(dest,pDetailData->DevicePath, dwMaxLen);
  950.   LocalFree(pDetailData);
  951.   return TRUE;
  952. }
  953.  
  954. static BOOL GetDevicePath( LPGUID pGuid, DWORD dwIndex, char *dest, DWORD dwMaxLen )
  955. {
  956.   
  957.   SP_DEVINFO_DATA DeviceInfoData;
  958.   SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  959.   BOOL result;
  960.  
  961.     // Create a HDEVINFO with all present devices.
  962.   HDEVINFO hDevInfoList = SetupDiGetClassDevs(
  963.         pGuid,  // this guid only
  964.         0,      // Enumerator
  965.         0,
  966.         DIGCF_PRESENT | DIGCF_INTERFACEDEVICE );
  967.     
  968.   if (hDevInfoList == INVALID_HANDLE_VALUE) {
  969.     PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiGetClassDevs");
  970.     return FALSE;
  971.   }
  972.     
  973.   // Get the Info for the specific device instance (dwIndex)
  974.   DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  975.   if (!SetupDiEnumDeviceInfo(hDevInfoList, dwIndex, &DeviceInfoData)) {
  976.     PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiEnumDeviceInfo");
  977.     SetupDiDestroyDeviceInfoList(hDevInfoList); // Cleanup
  978.     return FALSE;
  979.   }
  980.  
  981.   // for the desired interface, get the path
  982.   ZeroMemory(&DeviceInterfaceData, sizeof(DeviceInterfaceData));
  983.   DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
  984.   if(!SetupDiEnumDeviceInterfaces(hDevInfoList, &DeviceInfoData, pGuid, 0, &DeviceInterfaceData)) {
  985.     PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiEnumDeviceInterfaces");
  986.     SetupDiDestroyDeviceInfoList(hDevInfoList); // Cleanup
  987.     return FALSE;
  988.   }
  989.  
  990.   result = GetDeviceInterfacePath( hDevInfoList, &DeviceInterfaceData, dest, dwMaxLen );
  991.     
  992.   SetupDiDestroyDeviceInfoList(hDevInfoList); // Cleanup
  993.  
  994.   return result;
  995. }
  996.  
  997. static HANDLE OpenPipe(const PString & filename, DWORD dwIndex)
  998. {
  999.   char completeDeviceName[256] = "";
  1000.  
  1001.   if (!GetDevicePath((LPGUID)&GUID_CLASS_VOIP_BLASTER, dwIndex, 
  1002.         completeDeviceName, sizeof(completeDeviceName))) {
  1003.     return  INVALID_HANDLE_VALUE;
  1004.   }
  1005.  
  1006.   strcat(completeDeviceName, "\\" );
  1007.   strcat(completeDeviceName, filename);
  1008.  
  1009.   HANDLE h = CreateFile(completeDeviceName, 
  1010.                         GENERIC_WRITE | GENERIC_READ,
  1011.                         FILE_SHARE_WRITE | FILE_SHARE_READ, 
  1012.                         NULL, 
  1013.                         OPEN_EXISTING, 
  1014.                         0, 
  1015.                         NULL);
  1016.  
  1017.   if (h == INVALID_HANDLE_VALUE) {
  1018.     PTRACE(1, "Failed to open " << completeDeviceName << " : " << GetLastError());
  1019.   }
  1020.  
  1021.   return h;
  1022. }
  1023.  
  1024.  
  1025. /////////////////////////////////////////////////////////////////////////////
  1026.  
  1027. VoipBlasterInterface::VoipBlasterInterface()
  1028. {
  1029.   deviceIndex = P_MAX_INDEX;
  1030.   PINDEX i;
  1031.   for (i = 0; i < NumPipes; i++) 
  1032.     pipes[i] = INVALID_HANDLE_VALUE;
  1033.  
  1034. }
  1035.  
  1036. BOOL VoipBlasterInterface::IsDevicePresent(PINDEX deviceIndex)
  1037. {
  1038.   char completeDeviceName[256] = "";
  1039.  
  1040.   return GetDevicePath((LPGUID)&GUID_CLASS_VOIP_BLASTER, deviceIndex, 
  1041.         completeDeviceName, sizeof(completeDeviceName));
  1042. }
  1043.  
  1044.  
  1045. BOOL VoipBlasterInterface::OpenCommand(PINDEX _deviceIndex)
  1046. {
  1047.   CloseCommand();
  1048.  
  1049.   deviceIndex = _deviceIndex;
  1050.  
  1051.   // open the command and status pipes to the driver
  1052.   if (!OpenVOIPPipe(CommandPipe)) {
  1053.     PTRACE(1, "VB\tOpen of command pipe failed");
  1054.     CloseCommand();
  1055.     return FALSE;
  1056.   }
  1057.  
  1058.   if (!OpenVOIPPipe(StatusPipe)) {
  1059.     PTRACE(1, "VB\tOpen of status pipe failed");
  1060.     CloseCommand();
  1061.     return FALSE;
  1062.   }
  1063.  
  1064.   return TRUE;
  1065. }
  1066.  
  1067. BOOL VoipBlasterInterface::CloseCommand()
  1068. {
  1069.   CloseData();
  1070.  
  1071.   if (deviceIndex == P_MAX_INDEX)
  1072.     return FALSE;
  1073.  
  1074.   CloseHandle(pipes[CommandPipe]);
  1075.   pipes[CommandPipe] = INVALID_HANDLE_VALUE;
  1076.  
  1077.   CloseHandle(pipes[StatusPipe]);
  1078.   pipes[StatusPipe] = INVALID_HANDLE_VALUE;
  1079.  
  1080.   deviceIndex = P_MAX_INDEX;
  1081.  
  1082.   return TRUE;
  1083. }
  1084.  
  1085.  
  1086. BOOL VoipBlasterInterface::OpenData()
  1087. {
  1088.   if (deviceIndex == P_MAX_INDEX)
  1089.     return FALSE;
  1090.  
  1091.   // open the data pipes to the driver
  1092.   if (!OpenVOIPPipe(VoiceOutPipe)) {
  1093.     PTRACE(1, "VB\tOpen of command pipe failed");
  1094.     CloseCommand();
  1095.     return FALSE;
  1096.   }
  1097.  
  1098.   if (!OpenVOIPPipe(VoiceInPipe)) {
  1099.     PTRACE(1, "VB\tOpen of status pipe failed");
  1100.     CloseCommand();
  1101.     return FALSE;
  1102.   }
  1103.  
  1104.   return TRUE;
  1105. }
  1106.  
  1107.  
  1108. BOOL VoipBlasterInterface::CloseData()
  1109. {
  1110.   if (deviceIndex == P_MAX_INDEX)
  1111.     return FALSE;
  1112.  
  1113.   CloseHandle(pipes[VoiceOutPipe]);
  1114.   pipes[VoiceOutPipe] = INVALID_HANDLE_VALUE;
  1115.  
  1116.   CloseHandle(pipes[VoiceInPipe]);
  1117.   pipes[VoiceInPipe] = INVALID_HANDLE_VALUE;
  1118.  
  1119.   return TRUE;
  1120. }
  1121.  
  1122. BOOL VoipBlasterInterface::OpenVOIPPipe(VoipBlasterInterface::Pipe pipeIndex)
  1123. {
  1124.   if (deviceIndex == P_MAX_INDEX)
  1125.     return FALSE;
  1126.  
  1127.   return (pipes[pipeIndex] = OpenPipe(psprintf("PIPE%02i", pipeIndex), deviceIndex)) != INVALID_HANDLE_VALUE;
  1128. }
  1129.  
  1130.  
  1131. BOOL VoipBlasterInterface::WriteCommand(Command cmd)
  1132. {
  1133.   BYTE b = (BYTE)cmd;
  1134.   return WritePipe(pipes[CommandPipe], &b, 1) == 1;
  1135. }
  1136.  
  1137. VoipBlasterInterface::Status VoipBlasterInterface::ReadStatus()
  1138. {
  1139.   BYTE b;
  1140.   if (ReadPipe(pipes[StatusPipe], &b, 1) == 1)
  1141.     return (Status)b;
  1142.  
  1143.   return Status_Empty;
  1144. }
  1145.  
  1146. BOOL VoipBlasterInterface::WriteData(const void * data, PINDEX len)
  1147. {
  1148.   return WritePipe(pipes[VoiceOutPipe], data, len) == len;
  1149. }
  1150.  
  1151. int VoipBlasterInterface::ReadData(void * data, PINDEX len)
  1152. {
  1153.   return ReadPipe(pipes[VoiceInPipe], data, len);
  1154. }
  1155.  
  1156. void VoipBlasterInterface::FlushData(PTimeInterval wait)
  1157. {
  1158.   BOOL closeOnEnd = (pipes[VoiceInPipe] == INVALID_HANDLE_VALUE);
  1159.   if (closeOnEnd && !OpenVOIPPipe(VoiceInPipe)) {
  1160.     PTRACE(2, "VB\tCould not open voice in pipe for flush");
  1161.     return;
  1162.   }
  1163.  
  1164.   PTimer closeTimer;
  1165.   closeTimer.SetNotifier(PCREATE_NOTIFIER(CloseTimeout));
  1166.   closeTimer = wait;
  1167.   PINDEX count = 0;
  1168.   for (;;) {
  1169.     BYTE b;
  1170.     if (ReadPipe(pipes[VoiceInPipe], &b, 1) != 1)
  1171.       break;
  1172.     count++;
  1173.     closeTimer.Reset();
  1174.   }
  1175.  
  1176.   closeTimer.Stop();
  1177.  
  1178.   PError << "Flushed " << count << " bytes" << endl;
  1179. }
  1180.  
  1181. void VoipBlasterInterface::CloseTimeout(PTimer &, INT)
  1182. {
  1183.   if (pipes[VoiceInPipe] != INVALID_HANDLE_VALUE) {
  1184.     CloseHandle(pipes[VoiceInPipe]);
  1185.     pipes[VoiceInPipe] = INVALID_HANDLE_VALUE;
  1186.   }
  1187. }
  1188.  
  1189. int VoipBlasterInterface::WritePipe(HANDLE fd, const void *bp, DWORD len)
  1190. {
  1191.   DWORD wrote;
  1192.   if (::WriteFile(fd, bp, len, &wrote, NULL))
  1193.     return wrote; 
  1194.  
  1195.   return -1;
  1196. }
  1197.  
  1198. int VoipBlasterInterface::ReadPipe(HANDLE fd, void *bp, DWORD len)
  1199. {
  1200.   DWORD readCount;
  1201.   if (!::ReadFile(fd, bp, len, &readCount, NULL))
  1202.     return -1;
  1203.  
  1204.   return readCount;
  1205. }
  1206.  
  1207.  
  1208. #endif // _WIN32
  1209.  
  1210.  
  1211. /////////////////////////////////////////////////////////////////////////////
  1212.