4 * Creative Labs VOIP Blaster codec interface
8 * Copyright (c) 2001 Equivalence Pty. Ltd.
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/
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
20 * The Original Code is Open H323 Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.12 2005/11/14 23:25:22 csoutheren
28 * Removed obsolete constructs that cause compiler warnings in new compilers
30 * Revision 1.11 2005/06/07 07:59:11 csoutheren
31 * Applied patch 1176459 for PocketPC. Thanks to Matthias Weber
33 * Revision 1.10 2004/07/11 11:26:33 rjongbloed
34 * Fixed error in Voice Blaster tone queue, thanks German Garcia
36 * Revision 1.9 2003/12/03 06:58:00 csoutheren
37 * More vblaster implementation
39 * Revision 1.8 2003/11/10 12:38:20 csoutheren
40 * Additional fixes for Fobbit Windows driver
42 * Revision 1.7 2002/09/03 06:25:00 robertj
43 * Cosmetic change to formatting.
45 * Revision 1.6 2002/08/05 10:03:48 robertj
46 * Cosmetic changes to normalise the usage of pragma interface/implementation.
48 * Revision 1.5 2002/07/09 00:39:08 robertj
49 * Patches for latest fobbit driver, thanks Jian Yang
51 * Revision 1.4 2002/02/05 06:19:47 craigs
52 * Changed to use OPAL define rather than strings
54 * Revision 1.3 2002/01/15 07:23:10 craigs
55 * Added IsDevicePresent command
57 * Revision 1.2 2002/01/15 05:54:16 robertj
58 * Added bad implementation for GetDeviceNames()
60 * Revision 1.1 2002/01/15 04:16:52 craigs
68 #pragma implementation "vblasterlid.h"
71 #include "vblasterlid.h"
73 #define STARTUP_TIMEOUT 500
76 This code uses the VoIPBlaster interface as written by Dave Fobbitt (http://www.fobbit.com).
77 All of the information about the VoIPBlaster command set and functions was derived directly
78 from the code made available by Dave. This code requires Dave's USB drivers to be installed
79 as per the instructions on his site. Thanks to Dave for making this code available for
84 The interface to the VoIPBlaster (VB) is implemented using two pairs of read/write channels. These
85 are implemented as four named pipes for Windows, or two sockets for Unix. One channel is used
86 for sending commands to the VB and reading status, and the other channel is used for sending and
93 There are 27 different comands recognised by the VB. Each command is one byte.
95 COMMAND_PHONE_OFF 0x01 drop loop current
96 COMMAND_PHONE_ON 0x02 used on startup
97 COMMAND_RING_ON 0x03 start ringing
98 COMMAND_RING_OFF 0x04 used on startup & to stop ringing
99 COMMAND_VOUT_START 0x05 start audio output
100 COMMAND_VOUT_STOP 0x06 stop audio output
101 COMMAND_VINP_START 0x07 start audio input
102 COMMAND_VINP_STOP 0x08 stop audio input
103 COMMAND_UNKNOWN_1 0x09 Unknown (TESTSTART)
104 COMMAND_UNKNOWN_2 0x0a Unknown (TESTSTOP)
105 COMMAND_UNKNOWN_3 0x0b Unknown (SENDFAXTONE)
106 COMMAND_0x0c 0x0c Go offhook for headset
107 COMMAND_0x0d 0x0d Go onhook for headset
108 COMMAND_SETUP_MODE 0x0e Unknown(goto setup mode)
109 COMMAND_VOUT_DONE 0x0f voice in/out off, report output drained
110 COMMAND_0x10 0x10 Unknown (used in file output, seems ok without)
111 COMMAND_0x11 0x11 Unknown (used in file output, seems ok without)
112 COMMAND_MUTE_ON 0x12 Audio mute on
113 COMMAND_MUTE_OFF 0x13 Audio mute off
114 COMMAND_VOL_0 0x34 Set volume (min)
115 COMMAND_VOL_1 0x35 Set volume
116 COMMAND_VOL_2 0x36 Set volume
117 COMMAND_VOL_3 0x37 Set volume (default)
118 COMMAND_VOL_4 0x38 Set volume
119 COMMAND_VOL_5 0x39 Set volume
120 COMMAND_VOL_6 0x3a Set volume (max)
123 There are 11 different status responses sent by the VB. Each status is one byte.
124 STATUS_NONE 0x00 No status
125 STATUS_HOOK_OFF 0x01 Offhook
126 STATUS_HOOK_ON 0x02 Onhook
127 STATUS_DEBUG 0x00 Not used (DEBUG)
128 STATUS_RINGDETECT 0x00 Not used (RINGDETECT)
129 STATUS_RINGING_ON 0x05 Ring started
130 STATUS_RINGING_OFF 0x06 Ring stopped
131 STATUS_HEADSET_IN 0x08 Headset plugged in
132 STATUS_HEADSET_OUT 0x09 Headset unplugged
133 STATUS_0x0a 0x0a Unknown (setup accepted?)
134 STATUS_VOUT_DONE 0x0c Voice output done
137 The VB will accept audio in G.723.1 format at either 6.4kbps frames (24 bytes) or 5.3 kbps
138 frames (20 bytes). However, it only generates audio data at 5.3 kbps frames (20 bytes)
142 static BYTE blasterInit1
[] =
144 0x3b,0x00,0x40,0x8b // first 2 bytes is length, second 2 bytes is command?
147 static BYTE blasterInit2
[] =
149 0x00,0x01,0x00,0x00,0x18,0x02,0x8f,0x00,
150 0x10,0x00,0x28,0x40,0x03,0x1a,0x0d,0x0c,
151 0xfa,0x43,0xfd,0xea,0x93,0xfe,0x1a,0x41,
152 0x00,0x4a,0x93,0xfe,0x2a,0x40,0x00,0x1a,
153 0x93,0xfe,0x3a,0x0a,0x00,0x1f,0x3c,0x00,
154 0x8c,0x0d,0x03,0xa3,0x23,0xa2,0xdf,0x0d,
155 0x0c,0x3a,0x40,0x00,0x2a,0x93,0xfe,0x4a,
159 // this gets sent each time the voice output goes idle
160 static BYTE blasterInit3
[] =
162 0x75,0x58,0x9b,0x04,0x72,0x00,0x00,0x11,
163 0xe0,0x00,0x65,0x82,0x00,0x90,0x00,0x1c,
164 0x96,0xc1,0x0f,0xf2,0x3d,0x95,0x8e,0x5e,
165 0xe7,0x66,0xef,0xd4,0xba,0x21,0x0d,0x30,
166 0xcb,0x1e,0x52,0x35,0x9a,0xb6,0xff,0x7f,
167 0x74,0x58,0x9b,0x04,0x68,0x08,0x00,0x99,
168 0x52,0xfa,0x75,0xd7,0x72,0xba,0xdb,0x03,
169 0x3d,0xdb,0x77,0xd0,0x77,0x03,0x1f,0x05
172 // some silence data, for now only used in 'vblast_ring' to kill the dialtone
173 static BYTE silence
[] =
175 0xd5,0x14,0x0a,0x0d,0x1a,0x00,0x00,0x0a,
176 0x80,0x00,0xaf,0x06,0x00,0x00,0xfc,0x24,
177 0x9b,0x70,0xcf,0xc0,0xa1,0x53,0x18,0x00,
178 0x08,0x04,0xfe,0x50,0x8f,0x12,0x50,0x50,
179 0x4b,0x32,0x56,0x77,0x15,0xe9,0x0f,0xe8,
180 0xd5,0xc8,0xad,0x68,0xc8,0x74,0xe4,0x27,
181 0x71,0xd8,0x87,0x34,0xad,0x60,0x07,0xa8,
182 0xb6,0x64,0x0f,0x44,0x59,0x02,0xf4,0x00,
183 0x0e,0x80,0xba,0x68,0x73,0x00,0xe0,0xdf,
184 0x82,0xf4,0xa3,0x1f,0x18,0xb3,0x70,0x2e,
185 0xdd,0x1c,0xb1,0x00,0x02,0xfc,0x07,0xde,
186 0x09,0xc2,0x5e,0x82,0xaf,0xbc,0x5a,0x6c,
187 0xa2,0x02,0xbb,0xac,0x65,0x47,0x99,0x74,
188 0xfe,0xf4,0xe5,0xf7,0x70,0x90,0xe7,0x21,
189 0x15,0x33,0x56,0x8a,0x77,0x50,0xb5,0x42,
190 0x51,0x17,0xf2,0x4c,0xa8,0xfc,0x10,0x60,
191 0x82,0x1e,0x50,0x2f,0xa4,0x57,0x31,0x1e,
192 0xdd,0xc0,0xaf,0x6f,0x4d,0x1f,0xf5,0x08,
193 0x18,0x90,0x15,0x7f,0x70,0x1e,0x60,0x3e,
194 0x15,0xfb,0xa0,0x0d,0xbd,0x60,0x90,0x6d,
195 0x65,0x43,0xb1,0xdc,0xc2,0xd5,0x19,0xe5,
196 0xe1,0xe8,0x27,0xe4,0xcf,0x82,0x39,0xec,
197 0x38,0x84,0x3b,0x75,0x51,0x3b,0xb1,0x70,
198 0xde,0x68,0xba,0x06,0x79,0x0f,0x76,0x18,
199 0x74,0x53,0x45,0x4f,0x83,0x52,0xa0,0xa0,
200 0x5d,0xc1,0xed,0xc4,0x9f,0x77,0x27,0x36,
201 0x83,0xf9,0x07,0x7f,0x28,0xf3,0x41,0x41,
202 0x85,0x94,0x15,0xc0,0x45,0x13,0xee,0x30,
203 0x54,0x8c,0xea,0x37,0x03,0xf8,0xae,0x1f,
204 0x85,0xe7,0xed,0x21,0x58,0x01,0xff,0xe5,
205 0x75,0xc3,0x09,0x05,0x12,0x00,0x26,0xa7,
206 0x0b,0x26,0x9f,0x81,0x26,0x50,0x06,0x97,
207 0x31,0xa4,0x3c,0xca,0x1d,0x97,0xc2,0x9d,
208 0x42,0x6d,0xcf,0xae,0xe6,0xe5,0x27,0x04,
209 0x03,0xc2,0x93,0x5d,0xa7,0x62,0xf1,0x87,
210 0x5d,0x41,0xe5,0x0a,0x0a,0x70,0x69,0x3f,
211 0xf1,0xf4,0xfe,0x0d,0x4e,0x27,0x97,0x5b,
212 0xb0,0x0b,0xe5,0x78,0xa5,0x26,0x09,0x49,
213 0x90,0xfc,0xf1,0x77,0x52,0x0c,0xdf,0xef,
214 0x7f,0x8d,0x84,0x15,0x48,0xc7,0xff,0x5b,
217 /////////////////////////////////////////////////////////////////////////////
219 OpalVoipBlasterDevice::OpalVoipBlasterDevice()
220 : dtmfQueue(DTMFQueueSize
)
225 OpalVoipBlasterDevice::~OpalVoipBlasterDevice()
230 BOOL
OpalVoipBlasterDevice::Open(const PString
& device
)
234 int deviceIndex
= device
.AsInteger();
236 readStopped
= writeStopped
= TRUE
;
238 // open the lowlevel device
239 if (!vBlaster
.OpenCommand(deviceIndex
)) {
240 PTRACE(3, "vBlaster\tCould not open VoipBlaster device \"" << device
<< '"');
243 if (!vBlaster
.OpenData()) {
244 PTRACE(3, "vBlaster\tCould not open VoipBlaster data for device \"" << device
<< '"');
248 // put device into setup mode
249 PTRACE(3, "vBlaster\tSending setup command");
251 // put the handset on hook, which should stop all voice data
252 vBlaster
.WriteCommand(VoipBlasterInterface::Command_HS_ONHOOK
);
255 vBlaster
.Flush(STARTUP_TIMEOUT
);
257 vBlaster
.WriteCommand(VoipBlasterInterface::Command_SETUP_MODE
);
259 // setup for incoming status
262 ringOn
= TRUE
; // so we can detect ring off
266 //vBlaster.Flush(STARTUP_TIMEOUT);
268 vBlaster
.WriteData(blasterInit1
, sizeof(blasterInit1
));
270 //vBlaster.Flush(STARTUP_TIMEOUT);
272 vBlaster
.WriteData(blasterInit2
, sizeof(blasterInit2
));
274 //vBlaster.Flush(STARTUP_TIMEOUT);
276 int status
= VoipBlasterInterface::Status_Empty
;
279 for (i
= 3; ringOn
&& i
> 0; i
--) {
281 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOL_3
);
282 vBlaster
.WriteCommand(VoipBlasterInterface::Command_RING_OFF
);
283 vBlaster
.WriteCommand(VoipBlasterInterface::Command_PHONE_ON
);
288 status
= vBlaster
.ReadStatus(STARTUP_TIMEOUT
);
289 if (status
!= VoipBlasterInterface::Status_Empty
)
290 HandleStatus(status
);
291 } while (ringOn
&& timer
.IsRunning());
295 PTRACE(3, "vBlaster\tCould not initialise");
299 // set up thread to read status pipe
300 statusRunning
= TRUE
;
301 statusThread
= PThread::Create(PCREATE_NOTIFIER(StatusHandler
), 0,
302 PThread::NoAutoDeleteThread
,
303 PThread::NormalPriority
,
306 // unmute output and set default value
307 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOL_3
);
308 vBlaster
.WriteCommand(VoipBlasterInterface::Command_MUTE_OFF
);
309 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOUT_STOP
);
310 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VINP_STOP
);
312 // save the device name
314 PTRACE(3, "vBlaster\tVoipBlaster device \"" << device
<< "\" opened");
315 os_handle
= deviceIndex
;
321 BOOL
OpalVoipBlasterDevice::Close()
323 PWaitAndSignal
m(vbMutex
);
328 // flag the status thread to stop running on next status byte
329 statusRunning
= FALSE
;
331 // send a command to the phone stat will force a status byte to return
332 vBlaster
.WriteCommand(VoipBlasterInterface::Command_PHONE_OFF
);
333 vBlaster
.WriteCommand(VoipBlasterInterface::Command_MUTE_OFF
);
335 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOUT_DONE
);
336 vBlaster
.WriteData (blasterInit3
, sizeof(blasterInit3
));
338 // close the device - this will stop the status thread for sure!
339 vBlaster
.CloseCommand();
341 // wait for the status thread to terminate
342 statusThread
->WaitForTermination();
346 deviceName
= PString();
353 PString
OpalVoipBlasterDevice::GetName() const
358 void OpalVoipBlasterDevice::StatusHandler(PThread
&, INT
)
360 while (statusRunning
) {
361 int status
= vBlaster
.ReadStatus();
362 if (status
!= VoipBlasterInterface::Status_Empty
)
363 HandleStatus(status
);
367 void OpalVoipBlasterDevice::HandleStatus(int status
)
370 //case VoipBlasterInterface::Status_NONE: // No status
373 case VoipBlasterInterface::Status_HOOK_OFF
: // Offhook
374 PTRACE(1, "VB\tHook off");
379 case VoipBlasterInterface::Status_HOOK_ON
: // Onhook
380 PTRACE(1, "VB\tHook on");
385 case VoipBlasterInterface::Status_RINGING_ON
: // Ring started
386 PTRACE(1, "VB\tRing start");
390 case VoipBlasterInterface::Status_RINGING_OFF
: // Ring stopped
391 PTRACE(1, "VB\tRing end");
395 case VoipBlasterInterface::Status_HEADSET_IN
: // Headset plugged in
396 PTRACE(1, "VB\tHeadset in");
400 case VoipBlasterInterface::Status_HEADSET_OUT
: // Headset unplugged
401 PTRACE(1, "VB\tHeadset out");
405 case VoipBlasterInterface::Status_0x0a
: // Unknown (setup accepted?)
406 PTRACE(1, "VB\tStatus 0xa");
408 // read 17 byte serial number
409 BYTE serialNumber
[17];
410 memset(serialNumber
, 0, sizeof(serialNumber
));
411 vBlaster
.ReadData(serialNumber
, sizeof(serialNumber
), STARTUP_TIMEOUT
);
414 for (int i
= 0; i
< sizeof(serialNumber
); i
++)
415 str
.sprintf("%02x ", serialNumber
[i
]);
416 PTRACE(3, "vBlaster\tFirst time serial number " << str
);
421 case VoipBlasterInterface::Status_VOUT_DONE
: // Voice output done
422 PTRACE(1, "VB\tVOUT done");
441 PTRACE(1, "VB\tDTMF digit " << (char )status
);
442 dtmfQueue
.Enqueue((BYTE
)status
);
446 PTRACE(1, "VB\tUnknown status value " << status
);
451 BOOL
OpalVoipBlasterDevice::IsLineOffHook(unsigned line
)
453 PWaitAndSignal
m(vbMutex
);
465 BOOL
OpalVoipBlasterDevice::SetLineOffHook(unsigned /*line*/, BOOL
/* newState*/)
471 BOOL
OpalVoipBlasterDevice::HasHookFlash(unsigned /* line */)
477 BOOL
OpalVoipBlasterDevice::IsLineRinging(unsigned /*line*/, DWORD
* /*cadence*/)
483 BOOL
OpalVoipBlasterDevice::RingLine(unsigned line
, DWORD cadence
)
485 PWaitAndSignal
m(vbMutex
);
493 // %%%%%% this really needs to use a timer to implement cadences.
495 vBlaster
.WriteCommand(VoipBlasterInterface::Command_RING_OFF
);
497 vBlaster
.WriteCommand(VoipBlasterInterface::Command_RING_ON
);
503 BOOL
OpalVoipBlasterDevice::IsLineDisconnected(unsigned /*line*/, BOOL
/*checkForWink*/)
509 BOOL
OpalVoipBlasterDevice::SetLineToLineDirect(unsigned /*line1 */, unsigned /*line2*/, BOOL
/*connect*/)
515 BOOL
OpalVoipBlasterDevice::IsLineToLineDirect(unsigned /*line1*/, unsigned /*line2*/)
521 static const struct {
522 const char * mediaFormat
;
525 { OPAL_G7231_6k3
, 24 },
526 { OPAL_G7231_5k3
, 20 },
530 OpalMediaFormat::List
OpalVoipBlasterDevice::GetMediaFormats() const
532 OpalMediaFormat::List codecs
;
534 codecs
.Append(new OpalMediaFormat(CodecInfo
[0].mediaFormat
));
540 static PINDEX
FindCodec(const OpalMediaFormat
& mediaFormat
)
542 for (PINDEX codecType
= 0; codecType
< PARRAYSIZE(CodecInfo
); codecType
++) {
543 if (mediaFormat
== CodecInfo
[codecType
].mediaFormat
)
551 BOOL
OpalVoipBlasterDevice::SetReadFormat(unsigned line
, const OpalMediaFormat
& mediaFormat
)
555 PWaitAndSignal
m(vbMutex
);
556 PWaitAndSignal
mutex(readMutex
);
558 readCodecType
= FindCodec(mediaFormat
);
559 if (readCodecType
== P_MAX_INDEX
) {
560 PTRACE(1, "vBlaster\tUnsupported read codec requested: " << mediaFormat
);
564 if (!writeStopped
&& readCodecType
!= writeCodecType
) {
565 PTRACE(1, "vBlaster\tAsymmetric codecs requested: "
566 "read=" << CodecInfo
[readCodecType
].mediaFormat
567 << " write=" << CodecInfo
[writeCodecType
].mediaFormat
);
571 PTRACE(3, "vBlaster\tSetReadFormat(" << CodecInfo
[readCodecType
].mediaFormat
<< ')');
573 readFrameSize
= CodecInfo
[readCodecType
].frameSize
;
578 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VINP_START
);
588 BOOL
OpalVoipBlasterDevice::SetWriteFormat(unsigned line
, const OpalMediaFormat
& mediaFormat
)
590 StopWriteCodec(line
);
592 PWaitAndSignal
m(vbMutex
);
593 PWaitAndSignal
mutex(writeMutex
);
595 writeCodecType
= FindCodec(mediaFormat
);
596 if (writeCodecType
== P_MAX_INDEX
) {
597 PTRACE(1, "vBlaster\tUnsupported write codec requested: " << mediaFormat
);
601 if (!readStopped
&& writeCodecType
!= readCodecType
) {
602 PTRACE(1, "vBlaster\tAsymmetric codecs requested: "
603 "read=" << CodecInfo
[readCodecType
].mediaFormat
604 << " write=" << CodecInfo
[writeCodecType
].mediaFormat
);
608 PTRACE(3, "vBlaster\tSetWriteFormat(" << CodecInfo
[writeCodecType
].mediaFormat
<< ')');
610 writeFrameSize
= CodecInfo
[writeCodecType
].frameSize
;
615 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOUT_START
);
616 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOL_3
);
617 vBlaster
.WriteCommand(VoipBlasterInterface::Command_MUTE_OFF
);
619 writeDelay
.Restart();
621 writeStopped
= FALSE
;
626 OpalMediaFormat
OpalVoipBlasterDevice::GetReadFormat(unsigned)
628 if (readCodecType
== P_MAX_INDEX
)
630 return CodecInfo
[readCodecType
].mediaFormat
;
634 OpalMediaFormat
OpalVoipBlasterDevice::GetWriteFormat(unsigned)
636 if (writeCodecType
== P_MAX_INDEX
)
638 return CodecInfo
[writeCodecType
].mediaFormat
;
642 BOOL
OpalVoipBlasterDevice::SetRawCodec(unsigned)
647 BOOL
OpalVoipBlasterDevice::StopReadCodec(unsigned line
)
649 PTRACE(3, "vBlaster\tStopping read codec");
651 PWaitAndSignal
m(vbMutex
);
655 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VINP_STOP
);
659 vBlaster
.CloseData();
663 return OpalLineInterfaceDevice::StopReadCodec(line
);
667 BOOL
OpalVoipBlasterDevice::StopWriteCodec(unsigned line
)
669 PTRACE(3, "vBlaster\tStopping write codec");
671 PWaitAndSignal
m(vbMutex
);
675 vBlaster
.WriteCommand(VoipBlasterInterface::Command_VOUT_STOP
);
679 vBlaster
.CloseData();
683 return OpalLineInterfaceDevice::StopWriteCodec(line
);
687 BOOL
OpalVoipBlasterDevice::StopRawCodec(unsigned /*line*/)
693 PINDEX
OpalVoipBlasterDevice::GetReadFrameSize(unsigned)
695 return readFrameSize
;
699 BOOL
OpalVoipBlasterDevice::SetReadFrameSize(unsigned, PINDEX size
)
701 readFrameSize
= size
;
706 PINDEX
OpalVoipBlasterDevice::GetWriteFrameSize(unsigned)
708 return writeFrameSize
;
712 BOOL
OpalVoipBlasterDevice::SetWriteFrameSize(unsigned, PINDEX size
)
714 writeFrameSize
= size
;
719 BOOL
OpalVoipBlasterDevice::ReadFrame(unsigned, void * buffer
, PINDEX
& wasRead
)
721 PWaitAndSignal
mutex(readMutex
);
731 if ((stat
= vBlaster
.ReadData(buffer
, 20)) != 20) {
732 PTRACE(1, "Error reading frame - " << stat
);
742 BOOL
OpalVoipBlasterDevice::WriteFrame(unsigned, const void * buffer
, PINDEX
/*count*/, PINDEX
& written
)
744 PWaitAndSignal
m(writeMutex
);
748 writeDelay
.Delay(30);
750 const BYTE
* p
= (const BYTE
*)buffer
;
756 vBlaster
.WriteData(buffer
, 24);
760 vBlaster
.WriteData(buffer
, 20);
766 // Check for frame erasure command
767 if (memcmp(buffer
, "\xff\xff\xff\xff", 4) == 0)
780 unsigned OpalVoipBlasterDevice::GetAverageSignalLevel(unsigned, BOOL
/*playback*/)
786 BOOL
OpalVoipBlasterDevice::EnableAudio(unsigned /*line*/, BOOL
/*enable*/)
792 BOOL
OpalVoipBlasterDevice::SetRecordVolume(unsigned /*line*/, unsigned /*volume*/)
798 BOOL
OpalVoipBlasterDevice::SetPlayVolume(unsigned /*line*/, unsigned /*volume*/)
804 BOOL
OpalVoipBlasterDevice::GetRecordVolume(unsigned, unsigned & /*volume*/)
810 BOOL
OpalVoipBlasterDevice::GetPlayVolume(unsigned, unsigned & volume
)
817 OpalLineInterfaceDevice::AECLevels
OpalVoipBlasterDevice::GetAEC(unsigned)
823 BOOL
OpalVoipBlasterDevice::SetAEC(unsigned, AECLevels
/*level*/)
829 BOOL
OpalVoipBlasterDevice::GetVAD(unsigned)
835 BOOL
OpalVoipBlasterDevice::SetVAD(unsigned, BOOL
/*enable*/)
841 BOOL
OpalVoipBlasterDevice::GetCallerID(unsigned, PString
& /*idString*/, BOOL
/*full*/)
847 BOOL
OpalVoipBlasterDevice::SetCallerID(unsigned, const PString
& /*idString*/)
853 BOOL
OpalVoipBlasterDevice::SendCallerIDOnCallWaiting(unsigned, const PString
& /*idString*/)
859 BOOL
OpalVoipBlasterDevice::SendVisualMessageWaitingIndicator(unsigned /*line*/, BOOL
/*isOn*/)
865 BOOL
OpalVoipBlasterDevice::PlayDTMF(unsigned /*line*/,
867 DWORD
/*onTime*/, DWORD
/*offTime*/)
870 for (i
= 0; digits
[i
] != '\0'; i
++) {
871 PWaitAndSignal
m(vbMutex
);
872 vBlaster
.WriteCommand((VoipBlasterInterface::Command
)digits
[i
]);
879 char OpalVoipBlasterDevice::ReadDTMF(unsigned)
881 int v
= dtmfQueue
.Dequeue();
882 return (v
< 0) ? (char)0 : (char)v
;
886 BOOL
OpalVoipBlasterDevice::GetRemoveDTMF(unsigned)
892 BOOL
OpalVoipBlasterDevice::SetRemoveDTMF(unsigned, BOOL
/*state*/)
898 unsigned OpalVoipBlasterDevice::IsToneDetected(unsigned /*line*/)
904 BOOL
OpalVoipBlasterDevice::PlayTone(unsigned /*line*/, CallProgressTones
/*tone*/)
909 BOOL
OpalVoipBlasterDevice::IsTonePlaying(unsigned)
915 BOOL
OpalVoipBlasterDevice::StopTone(unsigned)
921 DWORD
OpalVoipBlasterDevice::GetSerialNumber()
927 PStringArray
OpalVoipBlasterDevice::GetDeviceNames()
931 VoipBlasterInterface blaster
;
933 while ((i
< 16) && blaster
.IsDevicePresent(i
)) {
941 BOOL
OpalVoipBlasterDevice::SetCountryCode(T35CountryCodes
/*country*/)
947 /////////////////////////////////////////////////////////////////////////////
949 OpalVoipBlasterDevice::ByteQueue::ByteQueue(PINDEX _qMax
)
956 int OpalVoipBlasterDevice::ByteQueue::Dequeue()
958 PWaitAndSignal
m(mutex
);
965 qOut
= (qOut
% qMax
);
971 BOOL
OpalVoipBlasterDevice::ByteQueue::Enqueue(BYTE v
)
973 PWaitAndSignal
m(mutex
);
978 queue
[(qOut
+ qLen
) % qMax
] = v
;
985 /////////////////////////////////////////////////////////////////////////////
989 #include <initguid.h>
990 #include <setupapi.h>
993 #pragma comment(lib, "setupapi.lib")
996 // {00873FDF-61A8-11d1-AA5E-00C04FB1728B} for VB_USB.SYS
998 DEFINE_GUID(GUID_CLASS_VOIP_BLASTER
,
999 0x873fdf, 0x61a8, 0x11d1, 0xaa, 0x5e, 0x0, 0xc0, 0x4f, 0xb1, 0x72, 0x8b);
1001 static BOOL
GetDeviceInterfacePath(HDEVINFO hDevInfo
,
1002 PSP_DEVICE_INTERFACE_DATA pDeviceInterfaceData
,
1003 char *dest
, DWORD dwMaxLen
)
1005 PSP_INTERFACE_DEVICE_DETAIL_DATA pDetailData
= NULL
;
1006 ULONG requiredLength
= 0;
1008 // allocate a function class device data structure to receive the
1009 // goods about this particular device.
1010 SetupDiGetInterfaceDeviceDetail(hDevInfo
,
1011 pDeviceInterfaceData
, //
1012 NULL
, // probing so no output buffer yet
1013 0, // probing so output buffer length of zero
1015 NULL
); // not interested in the specific dev-node
1017 pDetailData
= (PSP_DEVICE_INTERFACE_DETAIL_DATA
)LocalAlloc(LPTR
, requiredLength
);
1018 ZeroMemory(pDetailData
, requiredLength
);
1019 pDetailData
->cbSize
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA
);
1021 // Retrieve the information from Plug and Play.
1022 if (!SetupDiGetDeviceInterfaceDetail(hDevInfo
,
1023 pDeviceInterfaceData
,
1028 PTRACE(1, "VB\tError " << GetLastError() << " in GetDeviceInterfacePath");
1029 LocalFree(pDetailData
);
1033 strncpy(dest
,pDetailData
->DevicePath
, dwMaxLen
);
1034 LocalFree(pDetailData
);
1038 static BOOL
GetDevicePath( LPGUID pGuid
, DWORD dwIndex
, char *dest
, DWORD dwMaxLen
)
1041 SP_DEVINFO_DATA DeviceInfoData
;
1042 SP_DEVICE_INTERFACE_DATA DeviceInterfaceData
;
1045 // Create a HDEVINFO with all present devices.
1046 HDEVINFO hDevInfoList
= SetupDiGetClassDevs(
1047 pGuid
, // this guid only
1050 DIGCF_PRESENT
| DIGCF_INTERFACEDEVICE
);
1052 if (hDevInfoList
== INVALID_HANDLE_VALUE
) {
1053 PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiGetClassDevs");
1057 // Get the Info for the specific device instance (dwIndex)
1058 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
1059 if (!SetupDiEnumDeviceInfo(hDevInfoList
, dwIndex
, &DeviceInfoData
)) {
1060 PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiEnumDeviceInfo");
1061 SetupDiDestroyDeviceInfoList(hDevInfoList
); // Cleanup
1065 // for the desired interface, get the path
1066 ZeroMemory(&DeviceInterfaceData
, sizeof(DeviceInterfaceData
));
1067 DeviceInterfaceData
.cbSize
= sizeof(DeviceInterfaceData
);
1068 if(!SetupDiEnumDeviceInterfaces(hDevInfoList
, &DeviceInfoData
, pGuid
, 0, &DeviceInterfaceData
)) {
1069 PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiEnumDeviceInterfaces");
1070 SetupDiDestroyDeviceInfoList(hDevInfoList
); // Cleanup
1074 result
= GetDeviceInterfacePath( hDevInfoList
, &DeviceInterfaceData
, dest
, dwMaxLen
);
1076 SetupDiDestroyDeviceInfoList(hDevInfoList
); // Cleanup
1081 static HANDLE
OpenPipe(const PString
& filename
, DWORD dwIndex
)
1083 char completeDeviceName
[256] = "";
1085 if (!GetDevicePath((LPGUID
)&GUID_CLASS_VOIP_BLASTER
, dwIndex
,
1086 completeDeviceName
, sizeof(completeDeviceName
))) {
1087 return INVALID_HANDLE_VALUE
;
1090 strcat(completeDeviceName
, "\\" );
1091 strcat(completeDeviceName
, filename
);
1093 HANDLE h
= CreateFile(completeDeviceName
,
1094 GENERIC_WRITE
| GENERIC_READ
,
1095 FILE_SHARE_WRITE
| FILE_SHARE_READ
,
1098 FILE_FLAG_OVERLAPPED
,
1101 if (h
== INVALID_HANDLE_VALUE
) {
1102 PTRACE(1, "Failed to open " << completeDeviceName
<< " : " << GetLastError());
1109 /////////////////////////////////////////////////////////////////////////////
1111 VoipBlasterInterface::VoipBlasterInterface()
1113 deviceIndex
= P_MAX_INDEX
;
1115 for (i
= 0; i
< NumPipes
; i
++)
1116 pipes
[i
] = INVALID_HANDLE_VALUE
;
1120 BOOL
VoipBlasterInterface::IsDevicePresent(PINDEX deviceIndex
)
1122 char completeDeviceName
[256] = "";
1124 return GetDevicePath((LPGUID
)&GUID_CLASS_VOIP_BLASTER
, deviceIndex
,
1125 completeDeviceName
, sizeof(completeDeviceName
));
1129 BOOL
VoipBlasterInterface::OpenCommand(PINDEX _deviceIndex
)
1133 deviceIndex
= _deviceIndex
;
1135 // open the command and status pipes to the driver
1136 if (!OpenVOIPPipe(CommandPipe
)) {
1137 PTRACE(1, "VB\tOpen of command pipe failed");
1142 if (!OpenVOIPPipe(StatusPipe
)) {
1143 PTRACE(1, "VB\tOpen of status pipe failed");
1151 BOOL
VoipBlasterInterface::CloseCommand()
1155 if (deviceIndex
== P_MAX_INDEX
)
1158 CloseHandle(pipes
[CommandPipe
]);
1159 pipes
[CommandPipe
] = INVALID_HANDLE_VALUE
;
1161 CloseHandle(pipes
[StatusPipe
]);
1162 pipes
[StatusPipe
] = INVALID_HANDLE_VALUE
;
1164 deviceIndex
= P_MAX_INDEX
;
1170 BOOL
VoipBlasterInterface::OpenData()
1172 if (deviceIndex
== P_MAX_INDEX
)
1175 // open the data pipes to the driver
1176 if (!OpenVOIPPipe(VoiceOutPipe
)) {
1177 PTRACE(1, "VB\tOpen of command pipe failed");
1182 if (!OpenVOIPPipe(VoiceInPipe
)) {
1183 PTRACE(1, "VB\tOpen of status pipe failed");
1192 BOOL
VoipBlasterInterface::CloseData()
1194 if (deviceIndex
== P_MAX_INDEX
)
1197 CloseHandle(pipes
[VoiceOutPipe
]);
1198 pipes
[VoiceOutPipe
] = INVALID_HANDLE_VALUE
;
1200 CloseHandle(pipes
[VoiceInPipe
]);
1201 pipes
[VoiceInPipe
] = INVALID_HANDLE_VALUE
;
1206 BOOL
VoipBlasterInterface::OpenVOIPPipe(VoipBlasterInterface::Pipe pipeIndex
)
1208 if (deviceIndex
== P_MAX_INDEX
)
1211 return (pipes
[pipeIndex
] = OpenPipe(psprintf("PIPE%02i", pipeIndex
), deviceIndex
)) != INVALID_HANDLE_VALUE
;
1215 BOOL
VoipBlasterInterface::WriteCommand(Command cmd
)
1218 return WritePipe(pipes
[CommandPipe
], &b
, 1) == 1;
1221 VoipBlasterInterface::Status
VoipBlasterInterface::ReadStatus(const PTimeInterval duration
)
1224 if (ReadPipe(pipes
[StatusPipe
], &b
, 1, duration
) == 1)
1227 return Status_Empty
;
1230 BOOL
VoipBlasterInterface::WriteData(const void * data
, PINDEX len
)
1232 return WritePipe(pipes
[VoiceOutPipe
], data
, len
) == len
;
1235 int VoipBlasterInterface::ReadData(void * data
, PINDEX len
, const PTimeInterval duration
)
1237 return ReadPipe(pipes
[VoiceInPipe
], data
, len
, duration
);
1240 void VoipBlasterInterface::Flush(const PTimeInterval wait
)
1243 while (ReadData(buffer
, 1000, wait
) > 0)
1246 while (ReadPipe(pipes
[StatusPipe
], buffer
, 1000, wait
) > 0)
1250 BOOL closeOnEnd
= (pipes
[VoiceInPipe
] == INVALID_HANDLE_VALUE
);
1251 if (closeOnEnd
&& !OpenVOIPPipe(VoiceInPipe
)) {
1252 PTRACE(2, "VB\tCould not open voice in pipe for flush");
1257 closeTimer
.SetNotifier(PCREATE_NOTIFIER(CloseTimeout
));
1262 if (ReadPipe(pipes
[VoiceInPipe
], &b
, 1) != 1)
1270 PError
<< "Flushed " << count
<< " bytes" << endl
;
1274 void VoipBlasterInterface::CloseTimeout(PTimer
&, INT
)
1276 if (pipes
[VoiceInPipe
] != INVALID_HANDLE_VALUE
) {
1277 CloseHandle(pipes
[VoiceInPipe
]);
1278 pipes
[VoiceInPipe
] = INVALID_HANDLE_VALUE
;
1282 int VoipBlasterInterface::WritePipe(HANDLE handle
, const void *bp
, DWORD len
)
1285 PWin32Overlapped overlap
;
1287 ResetEvent(overlap
.hEvent
);
1289 if (::WriteFile(handle
, bp
, len
, &writeCount
, &overlap
))
1292 if (GetLastError() != ERROR_IO_PENDING
) {
1293 PTRACE(1, "VB\tWriteFile to pipe failed");
1297 if (!GetOverlappedResult(handle
, &overlap
, &writeCount
, TRUE
)) {
1298 PTRACE(1, "VB\tGetOverlappedResult on pipe failed");
1306 int VoipBlasterInterface::ReadPipe(HANDLE handle
, void *bp
, DWORD len
, const PTimeInterval duration
)
1309 PWin32Overlapped overlap
;
1311 ResetEvent(overlap
.hEvent
);
1313 if (::ReadFile(handle
, bp
, len
, &readCount
, &overlap
))
1316 if (GetLastError() != ERROR_IO_PENDING
) {
1317 PTRACE(1, "VB\tReadFile from pipe failed");
1321 DWORD msecs
= (duration
== PMaxTimeInterval
) ? INFINITE
: duration
.GetInterval();
1325 switch (WaitForSingleObject(overlap
.hEvent
, msecs
)) {
1327 // do nothing ; the device is ready
1331 // cancel the I/O operation;
1336 PTRACE(1, "VB\tWaitForSingleObject on pipe failed");
1340 if (!GetOverlappedResult(handle
, &overlap
, &readCount
, TRUE
)) {
1341 PTRACE(1, "VB\tGetOverlappedResult on pipe failed");
1352 /////////////////////////////////////////////////////////////////////////////