Utilise new MergeSym feature to no longer overwrite the source .DEF file when buildin...
[openh323.git] / src / vblasterlid.cxx
blobb2f1e08fbf1ac1a316904db2c048c279ae93c678
1 /*
2 * vblasterlid.cxx
4 * Creative Labs VOIP Blaster codec interface
6 * Open H323 Library
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
18 * under the License.
20 * The Original Code is Open H323 Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
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
61 * Initial version
65 #include <ptlib.h>
67 #ifdef __GNUC__
68 #pragma implementation "vblasterlid.h"
69 #endif
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
80 general use.
82 Introduction
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
87 receiving audio data.
89 Initialisation:
90 TBD
92 Commands:
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)
122 Status:
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
136 Voice:
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,
156 0x0a,0x00,0x0f
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)
222 statusThread = NULL;
225 OpalVoipBlasterDevice::~OpalVoipBlasterDevice()
227 Close();
230 BOOL OpalVoipBlasterDevice::Open(const PString & device)
232 Close();
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 << '"');
241 return FALSE;
243 if (!vBlaster.OpenData()) {
244 PTRACE(3, "vBlaster\tCould not open VoipBlaster data for device \"" << device << '"');
245 return FALSE;
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);
254 // flush all input
255 vBlaster.Flush(STARTUP_TIMEOUT);
257 vBlaster.WriteCommand(VoipBlasterInterface::Command_SETUP_MODE);
259 // setup for incoming status
260 hookState = FALSE;
261 headset = FALSE;
262 ringOn = TRUE; // so we can detect ring off
263 firstTime = FALSE;
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;
278 int i;
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);
285 PTimer timer(2000);
287 do {
288 status = vBlaster.ReadStatus(STARTUP_TIMEOUT);
289 if (status != VoipBlasterInterface::Status_Empty)
290 HandleStatus(status);
291 } while (ringOn && timer.IsRunning());
294 if (i == 0) {
295 PTRACE(3, "vBlaster\tCould not initialise");
296 return FALSE;
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,
304 "VbStatus:%x");
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
313 deviceName = device;
314 PTRACE(3, "vBlaster\tVoipBlaster device \"" << device << "\" opened");
315 os_handle = deviceIndex;
317 return TRUE;
321 BOOL OpalVoipBlasterDevice::Close()
323 PWaitAndSignal m(vbMutex);
325 if (!IsOpen())
326 return FALSE;
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();
343 delete statusThread;
344 statusThread = NULL;
346 deviceName = PString();
348 os_handle = -1;
349 return TRUE;
353 PString OpalVoipBlasterDevice::GetName() const
355 return deviceName;
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)
369 switch (status) {
370 //case VoipBlasterInterface::Status_NONE: // No status
371 // break;
373 case VoipBlasterInterface::Status_HOOK_OFF: // Offhook
374 PTRACE(1, "VB\tHook off");
375 hookState = TRUE;
376 headset = FALSE;
377 break;
379 case VoipBlasterInterface::Status_HOOK_ON: // Onhook
380 PTRACE(1, "VB\tHook on");
381 hookState = FALSE;
382 headset = FALSE;
383 break;
385 case VoipBlasterInterface::Status_RINGING_ON: // Ring started
386 PTRACE(1, "VB\tRing start");
387 ringOn = TRUE;
388 break;
390 case VoipBlasterInterface::Status_RINGING_OFF: // Ring stopped
391 PTRACE(1, "VB\tRing end");
392 ringOn = FALSE;
393 break;
395 case VoipBlasterInterface::Status_HEADSET_IN: // Headset plugged in
396 PTRACE(1, "VB\tHeadset in");
397 headset = TRUE;
398 break;
400 case VoipBlasterInterface::Status_HEADSET_OUT: // Headset unplugged
401 PTRACE(1, "VB\tHeadset out");
402 headset = FALSE;
403 break;
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);
413 PString str;
414 for (int i = 0; i < sizeof(serialNumber); i++)
415 str.sprintf("%02x ", serialNumber[i]);
416 PTRACE(3, "vBlaster\tFirst time serial number " << str);
418 firstTime = TRUE;
419 break;
421 case VoipBlasterInterface::Status_VOUT_DONE: // Voice output done
422 PTRACE(1, "VB\tVOUT done");
423 break;
425 case '0':
426 case '1':
427 case '2':
428 case '3':
429 case '4':
430 case '5':
431 case '6':
432 case '7':
433 case '8':
434 case '9':
435 case 'A':
436 case 'B':
437 case 'C':
438 case 'D':
439 case '*':
440 case '#':
441 PTRACE(1, "VB\tDTMF digit " << (char )status);
442 dtmfQueue.Enqueue((BYTE)status);
443 break;
445 default:
446 PTRACE(1, "VB\tUnknown status value " << status);
451 BOOL OpalVoipBlasterDevice::IsLineOffHook(unsigned line)
453 PWaitAndSignal m(vbMutex);
455 if (!IsOpen())
456 return FALSE;
458 if (line > 0)
459 return FALSE;
461 return hookState;
465 BOOL OpalVoipBlasterDevice::SetLineOffHook(unsigned /*line*/, BOOL /* newState*/)
467 return FALSE;
471 BOOL OpalVoipBlasterDevice::HasHookFlash(unsigned /* line */)
473 return FALSE;
477 BOOL OpalVoipBlasterDevice::IsLineRinging(unsigned /*line*/, DWORD * /*cadence*/)
479 return FALSE;
483 BOOL OpalVoipBlasterDevice::RingLine(unsigned line, DWORD cadence)
485 PWaitAndSignal m(vbMutex);
487 if (!IsOpen())
488 return FALSE;
490 if (line > 0)
491 return FALSE;
493 // %%%%%% this really needs to use a timer to implement cadences.
494 if (cadence == 0)
495 vBlaster.WriteCommand(VoipBlasterInterface::Command_RING_OFF);
496 else
497 vBlaster.WriteCommand(VoipBlasterInterface::Command_RING_ON);
499 return TRUE;
503 BOOL OpalVoipBlasterDevice::IsLineDisconnected(unsigned /*line*/, BOOL /*checkForWink*/)
505 return FALSE;
509 BOOL OpalVoipBlasterDevice::SetLineToLineDirect(unsigned /*line1 */, unsigned /*line2*/, BOOL /*connect*/)
511 return FALSE;
515 BOOL OpalVoipBlasterDevice::IsLineToLineDirect(unsigned /*line1*/, unsigned /*line2*/)
517 return FALSE;
521 static const struct {
522 const char * mediaFormat;
523 PINDEX frameSize;
524 } CodecInfo[] = {
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));
536 return codecs;
540 static PINDEX FindCodec(const OpalMediaFormat & mediaFormat)
542 for (PINDEX codecType = 0; codecType < PARRAYSIZE(CodecInfo); codecType++) {
543 if (mediaFormat == CodecInfo[codecType].mediaFormat)
544 return codecType;
547 return P_MAX_INDEX;
551 BOOL OpalVoipBlasterDevice::SetReadFormat(unsigned line, const OpalMediaFormat & mediaFormat)
553 StopReadCodec(line);
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);
561 return FALSE;
564 if (!writeStopped && readCodecType != writeCodecType) {
565 PTRACE(1, "vBlaster\tAsymmetric codecs requested: "
566 "read=" << CodecInfo[readCodecType].mediaFormat
567 << " write=" << CodecInfo[writeCodecType].mediaFormat);
568 return FALSE;
571 PTRACE(3, "vBlaster\tSetReadFormat(" << CodecInfo[readCodecType].mediaFormat << ')');
573 readFrameSize = CodecInfo[readCodecType].frameSize;
575 if (writeStopped)
576 vBlaster.OpenData();
578 vBlaster.WriteCommand(VoipBlasterInterface::Command_VINP_START);
580 readDelay.Restart();
582 readStopped = FALSE;
584 return TRUE;
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);
598 return FALSE;
601 if (!readStopped && writeCodecType != readCodecType) {
602 PTRACE(1, "vBlaster\tAsymmetric codecs requested: "
603 "read=" << CodecInfo[readCodecType].mediaFormat
604 << " write=" << CodecInfo[writeCodecType].mediaFormat);
605 return FALSE;
608 PTRACE(3, "vBlaster\tSetWriteFormat(" << CodecInfo[writeCodecType].mediaFormat << ')');
610 writeFrameSize = CodecInfo[writeCodecType].frameSize;
612 if (readStopped)
613 vBlaster.OpenData();
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;
622 return TRUE;
626 OpalMediaFormat OpalVoipBlasterDevice::GetReadFormat(unsigned)
628 if (readCodecType == P_MAX_INDEX)
629 return "";
630 return CodecInfo[readCodecType].mediaFormat;
634 OpalMediaFormat OpalVoipBlasterDevice::GetWriteFormat(unsigned)
636 if (writeCodecType == P_MAX_INDEX)
637 return "";
638 return CodecInfo[writeCodecType].mediaFormat;
642 BOOL OpalVoipBlasterDevice::SetRawCodec(unsigned)
644 return FALSE;
647 BOOL OpalVoipBlasterDevice::StopReadCodec(unsigned line)
649 PTRACE(3, "vBlaster\tStopping read codec");
651 PWaitAndSignal m(vbMutex);
652 readMutex.Wait();
653 if (!readStopped) {
654 readStopped = TRUE;
655 vBlaster.WriteCommand(VoipBlasterInterface::Command_VINP_STOP);
658 if (writeStopped)
659 vBlaster.CloseData();
661 readMutex.Signal();
663 return OpalLineInterfaceDevice::StopReadCodec(line);
667 BOOL OpalVoipBlasterDevice::StopWriteCodec(unsigned line)
669 PTRACE(3, "vBlaster\tStopping write codec");
671 PWaitAndSignal m(vbMutex);
672 writeMutex.Wait();
673 if (!writeStopped) {
674 writeStopped = TRUE;
675 vBlaster.WriteCommand(VoipBlasterInterface::Command_VOUT_STOP);
678 if (readStopped)
679 vBlaster.CloseData();
681 writeMutex.Signal();
683 return OpalLineInterfaceDevice::StopWriteCodec(line);
687 BOOL OpalVoipBlasterDevice::StopRawCodec(unsigned /*line*/)
689 return FALSE;
693 PINDEX OpalVoipBlasterDevice::GetReadFrameSize(unsigned)
695 return readFrameSize;
699 BOOL OpalVoipBlasterDevice::SetReadFrameSize(unsigned, PINDEX size)
701 readFrameSize = size;
702 return TRUE;
706 PINDEX OpalVoipBlasterDevice::GetWriteFrameSize(unsigned)
708 return writeFrameSize;
712 BOOL OpalVoipBlasterDevice::SetWriteFrameSize(unsigned, PINDEX size)
714 writeFrameSize = size;
715 return TRUE;
719 BOOL OpalVoipBlasterDevice::ReadFrame(unsigned, void * buffer, PINDEX & wasRead)
721 PWaitAndSignal mutex(readMutex);
722 if (readStopped)
723 return FALSE;
725 readDelay.Delay(30);
727 if (!hookState)
728 return FALSE;
730 int stat;
731 if ((stat = vBlaster.ReadData(buffer, 20)) != 20) {
732 PTRACE(1, "Error reading frame - " << stat);
733 return FALSE;
736 wasRead = 20;
738 return TRUE;
742 BOOL OpalVoipBlasterDevice::WriteFrame(unsigned, const void * buffer, PINDEX /*count*/, PINDEX & written)
744 PWaitAndSignal m(writeMutex);
745 if (writeStopped)
746 return FALSE;
748 writeDelay.Delay(30);
750 const BYTE * p = (const BYTE *)buffer;
752 PINDEX toWrite = 0;
753 switch (*p & 3) {
754 case 0:
755 toWrite = 24;
756 vBlaster.WriteData(buffer, 24);
757 break;
758 case 1:
759 toWrite = 20;
760 vBlaster.WriteData(buffer, 20);
761 break;
762 case 2:
763 toWrite = 4;
764 break;
765 default:
766 // Check for frame erasure command
767 if (memcmp(buffer, "\xff\xff\xff\xff", 4) == 0)
768 toWrite = 24;
769 else
770 toWrite = 1;
771 break;
774 written = toWrite;
776 return TRUE;
780 unsigned OpalVoipBlasterDevice::GetAverageSignalLevel(unsigned, BOOL /*playback*/)
782 return UINT_MAX;
786 BOOL OpalVoipBlasterDevice::EnableAudio(unsigned /*line*/, BOOL /*enable*/)
788 return TRUE;
792 BOOL OpalVoipBlasterDevice::SetRecordVolume(unsigned /*line*/, unsigned /*volume*/)
794 return TRUE;
798 BOOL OpalVoipBlasterDevice::SetPlayVolume(unsigned /*line*/, unsigned /*volume*/)
800 return TRUE;
804 BOOL OpalVoipBlasterDevice::GetRecordVolume(unsigned, unsigned & /*volume*/)
806 return TRUE;
810 BOOL OpalVoipBlasterDevice::GetPlayVolume(unsigned, unsigned & volume)
812 volume = 50;
813 return TRUE;
817 OpalLineInterfaceDevice::AECLevels OpalVoipBlasterDevice::GetAEC(unsigned)
819 return AECOff;
823 BOOL OpalVoipBlasterDevice::SetAEC(unsigned, AECLevels /*level*/)
825 return FALSE;
829 BOOL OpalVoipBlasterDevice::GetVAD(unsigned)
831 return FALSE;
835 BOOL OpalVoipBlasterDevice::SetVAD(unsigned, BOOL /*enable*/)
837 return FALSE;
841 BOOL OpalVoipBlasterDevice::GetCallerID(unsigned, PString & /*idString*/, BOOL /*full*/)
843 return FALSE;
847 BOOL OpalVoipBlasterDevice::SetCallerID(unsigned, const PString & /*idString*/)
849 return FALSE;
853 BOOL OpalVoipBlasterDevice::SendCallerIDOnCallWaiting(unsigned, const PString & /*idString*/)
855 return FALSE;
859 BOOL OpalVoipBlasterDevice::SendVisualMessageWaitingIndicator(unsigned /*line*/, BOOL /*isOn*/)
861 return FALSE;
865 BOOL OpalVoipBlasterDevice::PlayDTMF(unsigned /*line*/,
866 const char * digits,
867 DWORD /*onTime*/, DWORD /*offTime*/)
869 PINDEX i;
870 for (i = 0; digits[i] != '\0'; i++) {
871 PWaitAndSignal m(vbMutex);
872 vBlaster.WriteCommand((VoipBlasterInterface::Command)digits[i]);
875 return TRUE;
879 char OpalVoipBlasterDevice::ReadDTMF(unsigned)
881 int v = dtmfQueue.Dequeue();
882 return (v < 0) ? (char)0 : (char)v;
886 BOOL OpalVoipBlasterDevice::GetRemoveDTMF(unsigned)
888 return TRUE;
892 BOOL OpalVoipBlasterDevice::SetRemoveDTMF(unsigned, BOOL /*state*/)
894 return TRUE;
898 unsigned OpalVoipBlasterDevice::IsToneDetected(unsigned /*line*/)
900 return FALSE;
904 BOOL OpalVoipBlasterDevice::PlayTone(unsigned /*line*/, CallProgressTones /*tone*/)
906 return FALSE;
909 BOOL OpalVoipBlasterDevice::IsTonePlaying(unsigned)
911 return FALSE;
915 BOOL OpalVoipBlasterDevice::StopTone(unsigned)
917 return FALSE;
921 DWORD OpalVoipBlasterDevice::GetSerialNumber()
923 return 0;
927 PStringArray OpalVoipBlasterDevice::GetDeviceNames()
929 PStringArray array;
931 VoipBlasterInterface blaster;
932 PINDEX i = 0;
933 while ((i < 16) && blaster.IsDevicePresent(i)) {
934 array[i] = i;
935 i++;
938 return array;
941 BOOL OpalVoipBlasterDevice::SetCountryCode(T35CountryCodes /*country*/)
943 return TRUE;
947 /////////////////////////////////////////////////////////////////////////////
949 OpalVoipBlasterDevice::ByteQueue::ByteQueue(PINDEX _qMax)
950 : qMax(_qMax)
952 qOut = qLen = 0;
953 queue.SetSize(qMax);
956 int OpalVoipBlasterDevice::ByteQueue::Dequeue()
958 PWaitAndSignal m(mutex);
959 if (qLen == 0)
960 return -1;
962 int v = queue[qOut];
964 ++qOut;
965 qOut = (qOut % qMax);
966 qLen--;
968 return v;
971 BOOL OpalVoipBlasterDevice::ByteQueue::Enqueue(BYTE v)
973 PWaitAndSignal m(mutex);
975 if (qLen == qMax)
976 return FALSE;
978 queue[(qOut + qLen) % qMax] = v;
979 qLen++;
981 return TRUE;
985 /////////////////////////////////////////////////////////////////////////////
987 #ifdef _WIN32
989 #include <initguid.h>
990 #include <setupapi.h>
992 #ifndef _WIN32_WINCE
993 #pragma comment(lib, "setupapi.lib")
994 #endif
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
1014 &requiredLength, //
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,
1024 pDetailData,
1025 requiredLength,
1026 NULL,
1027 NULL)) {
1028 PTRACE(1, "VB\tError " << GetLastError() << " in GetDeviceInterfacePath");
1029 LocalFree(pDetailData);
1030 return FALSE;
1033 strncpy(dest,pDetailData->DevicePath, dwMaxLen);
1034 LocalFree(pDetailData);
1035 return TRUE;
1038 static BOOL GetDevicePath( LPGUID pGuid, DWORD dwIndex, char *dest, DWORD dwMaxLen )
1041 SP_DEVINFO_DATA DeviceInfoData;
1042 SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
1043 BOOL result;
1045 // Create a HDEVINFO with all present devices.
1046 HDEVINFO hDevInfoList = SetupDiGetClassDevs(
1047 pGuid, // this guid only
1048 0, // Enumerator
1050 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE );
1052 if (hDevInfoList == INVALID_HANDLE_VALUE) {
1053 PTRACE(1, "VB\tError " << GetLastError() << " in GetDevicePath:SetupDiGetClassDevs");
1054 return FALSE;
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
1062 return FALSE;
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
1071 return FALSE;
1074 result = GetDeviceInterfacePath( hDevInfoList, &DeviceInterfaceData, dest, dwMaxLen );
1076 SetupDiDestroyDeviceInfoList(hDevInfoList); // Cleanup
1078 return result;
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,
1096 NULL,
1097 OPEN_EXISTING,
1098 FILE_FLAG_OVERLAPPED,
1099 NULL);
1101 if (h == INVALID_HANDLE_VALUE) {
1102 PTRACE(1, "Failed to open " << completeDeviceName << " : " << GetLastError());
1105 return h;
1109 /////////////////////////////////////////////////////////////////////////////
1111 VoipBlasterInterface::VoipBlasterInterface()
1113 deviceIndex = P_MAX_INDEX;
1114 PINDEX i;
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)
1131 CloseCommand();
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");
1138 CloseCommand();
1139 return FALSE;
1142 if (!OpenVOIPPipe(StatusPipe)) {
1143 PTRACE(1, "VB\tOpen of status pipe failed");
1144 CloseCommand();
1145 return FALSE;
1148 return TRUE;
1151 BOOL VoipBlasterInterface::CloseCommand()
1153 CloseData();
1155 if (deviceIndex == P_MAX_INDEX)
1156 return FALSE;
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;
1166 return TRUE;
1170 BOOL VoipBlasterInterface::OpenData()
1172 if (deviceIndex == P_MAX_INDEX)
1173 return FALSE;
1175 // open the data pipes to the driver
1176 if (!OpenVOIPPipe(VoiceOutPipe)) {
1177 PTRACE(1, "VB\tOpen of command pipe failed");
1178 CloseCommand();
1179 return FALSE;
1182 if (!OpenVOIPPipe(VoiceInPipe)) {
1183 PTRACE(1, "VB\tOpen of status pipe failed");
1184 CloseCommand();
1185 return FALSE;
1188 return TRUE;
1192 BOOL VoipBlasterInterface::CloseData()
1194 if (deviceIndex == P_MAX_INDEX)
1195 return FALSE;
1197 CloseHandle(pipes[VoiceOutPipe]);
1198 pipes[VoiceOutPipe] = INVALID_HANDLE_VALUE;
1200 CloseHandle(pipes[VoiceInPipe]);
1201 pipes[VoiceInPipe] = INVALID_HANDLE_VALUE;
1203 return TRUE;
1206 BOOL VoipBlasterInterface::OpenVOIPPipe(VoipBlasterInterface::Pipe pipeIndex)
1208 if (deviceIndex == P_MAX_INDEX)
1209 return FALSE;
1211 return (pipes[pipeIndex] = OpenPipe(psprintf("PIPE%02i", pipeIndex), deviceIndex)) != INVALID_HANDLE_VALUE;
1215 BOOL VoipBlasterInterface::WriteCommand(Command cmd)
1217 BYTE b = (BYTE)cmd;
1218 return WritePipe(pipes[CommandPipe], &b, 1) == 1;
1221 VoipBlasterInterface::Status VoipBlasterInterface::ReadStatus(const PTimeInterval duration)
1223 BYTE b;
1224 if (ReadPipe(pipes[StatusPipe], &b, 1, duration) == 1)
1225 return (Status)b;
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)
1242 BYTE buffer[1000];
1243 while (ReadData(buffer, 1000, wait) > 0)
1246 while (ReadPipe(pipes[StatusPipe], buffer, 1000, wait) > 0)
1249 #if 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");
1253 return;
1256 PTimer closeTimer;
1257 closeTimer.SetNotifier(PCREATE_NOTIFIER(CloseTimeout));
1258 closeTimer = wait;
1259 PINDEX count = 0;
1260 for (;;) {
1261 BYTE b;
1262 if (ReadPipe(pipes[VoiceInPipe], &b, 1) != 1)
1263 break;
1264 count++;
1265 closeTimer.Reset();
1268 closeTimer.Stop();
1270 PError << "Flushed " << count << " bytes" << endl;
1271 #endif
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)
1284 DWORD writeCount;
1285 PWin32Overlapped overlap;
1287 ResetEvent(overlap.hEvent);
1289 if (::WriteFile(handle, bp, len, &writeCount, &overlap))
1290 return writeCount;
1292 if (GetLastError() != ERROR_IO_PENDING) {
1293 PTRACE(1, "VB\tWriteFile to pipe failed");
1294 return -1;
1297 if (!GetOverlappedResult(handle, &overlap, &writeCount, TRUE)) {
1298 PTRACE(1, "VB\tGetOverlappedResult on pipe failed");
1299 return -1;
1302 return writeCount;
1306 int VoipBlasterInterface::ReadPipe(HANDLE handle, void *bp, DWORD len, const PTimeInterval duration)
1308 DWORD readCount;
1309 PWin32Overlapped overlap;
1311 ResetEvent(overlap.hEvent);
1313 if (::ReadFile(handle, bp, len, &readCount, &overlap))
1314 return readCount;
1316 if (GetLastError() != ERROR_IO_PENDING) {
1317 PTRACE(1, "VB\tReadFile from pipe failed");
1318 return -1;
1321 DWORD msecs = (duration == PMaxTimeInterval) ? INFINITE : duration.GetInterval();
1322 if (msecs < 500)
1323 msecs = 500;
1325 switch (WaitForSingleObject(overlap.hEvent, msecs)) {
1326 case WAIT_OBJECT_0:
1327 // do nothing ; the device is ready
1328 break;
1330 case WAIT_TIMEOUT:
1331 // cancel the I/O operation;
1332 CancelIo(handle);
1333 break;
1335 default:
1336 PTRACE(1, "VB\tWaitForSingleObject on pipe failed");
1337 return -1;
1340 if (!GetOverlappedResult(handle, &overlap, &readCount, TRUE)) {
1341 PTRACE(1, "VB\tGetOverlappedResult on pipe failed");
1342 return -1;
1345 return readCount;
1349 #endif // _WIN32
1352 /////////////////////////////////////////////////////////////////////////////