4 * QuickNet Internet Phone/Line JACK codec interface
8 * Copyright (c) 1999-2000 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 * Portions of this code were written with the assisance of funding from
25 * Quicknet Technologies, Inc. http://www.quicknet.net.
27 * Contributor(s): ______________________________________.
29 * $Log: ixjwin32.cxx,v $
30 * Revision 1.117 2005/08/04 19:38:51 csoutheren
31 * Applied patch #1240871
32 * Fixed problem with disabling IXJ code
33 * Thanks to Borko Jandras
35 * Revision 1.116 2005/06/07 07:59:11 csoutheren
36 * Applied patch 1176459 for PocketPC. Thanks to Matthias Weber
38 * Revision 1.115 2004/04/09 12:56:52 rjongbloed
39 * Fixed automatic loading of winmm.lib if this module included.
41 * Revision 1.114 2003/04/29 08:32:59 robertj
42 * Added new wink functions for Windows IxJ lid.
44 * Revision 1.113 2002/11/05 04:33:21 robertj
45 * Changed IsLineDisconnected() to work with POTSLine
47 * Revision 1.112 2002/11/05 04:27:58 robertj
48 * Imported RingLine() by array from OPAL.
50 * Revision 1.111 2002/10/30 05:54:17 craigs
51 * Fixed compatibilty problems with G.723.1 6k3 and 5k3
53 * Revision 1.110 2002/06/25 08:30:12 robertj
54 * Changes to differentiate between stright G.723.1 and G.723.1 Annex A using
55 * the OLC dataType silenceSuppression field so does not send SID frames
56 * to receiver codecs that do not understand them.
58 * Revision 1.109 2002/05/09 06:26:34 robertj
59 * Added fuction to get the current audio enable state for line in device.
60 * Changed IxJ EnableAudio() semantics so is exclusive, no direct switching
61 * from PSTN to POTS and vice versa without disabling the old one first.
63 * Revision 1.108 2002/03/21 02:37:33 robertj
64 * Fixed G.723.1 5.3k mode so receiver (playback) still accepts 6.3k data.
66 * Revision 1.107 2002/02/08 14:41:49 craigs
67 * Changed codec table to use mediatream #defines. Thanks to Roger Hardiman
69 * Revision 1.106 2001/12/11 04:27:28 craigs
70 * Added support for 5.3kbps G723.1
72 * Revision 1.105 2001/12/03 00:40:43 robertj
73 * Fixed problem with false off hook detect with LineJACK and no PSTN active.
75 * Revision 1.104 2001/10/11 00:48:08 robertj
76 * Changed so if stopping read/write also stops fax and vice versa.
78 * Revision 1.103 2001/09/25 01:12:06 robertj
79 * Changed check to v 5.5.141
81 * Revision 1.102 2001/09/24 12:31:35 robertj
82 * Added backward compatibility with old drivers.
84 * Revision 1.101 2001/09/10 08:22:16 robertj
85 * Fixed minor problems with error codes.
87 * Revision 1.100 2001/07/24 02:29:56 robertj
88 * Added setting of xJack filter coefficients for some frequencies,
89 * values taken from open source Linux driver.
91 * Revision 1.99 2001/07/19 05:54:30 robertj
92 * Updated interface to xJACK drivers to utilise cadence and filter functions
93 * for dial tone, busy tone and ringback tone detection.
95 * Revision 1.98 2001/07/06 03:44:35 robertj
96 * Oops, the WAVE output should ALWAYS be unmuted!
98 * Revision 1.97 2001/07/06 00:45:15 robertj
99 * Fixed accidentlaly unmuting microphone on audio output for LineJACKs.
101 * Revision 1.96 2001/06/12 00:12:23 craigs
102 * No longer looks for dialtone for end of call
104 * Revision 1.95 2001/05/30 04:06:26 robertj
105 * Fixed setting of LineJACK mixer mutes to AFTER setting of analague source
106 * as each source has different mixer settings in driver.
107 * Change start of codec (SetXXXFormat()) to explicitly stop codec.
109 * Revision 1.94 2001/05/25 02:19:53 robertj
110 * Fixed problem with codec data reblocking code not being reset when
111 * code is stopped and restarted, thanks Artis Kugevics
113 * Revision 1.93 2001/05/21 06:37:06 craigs
114 * Changed to allow optional wink detection for line disconnect
116 * Revision 1.92 2001/05/11 04:43:43 robertj
117 * Added variable names for standard PCM-16 media format name.
119 * Revision 1.91 2001/05/10 02:07:11 robertj
120 * Fixed possibility of output on POTS/PSTN being muted accidentally and
121 * thus not having any audio. Explicitly unmutes it on selection.
123 * Revision 1.90 2001/04/20 02:27:29 robertj
124 * Added extra mutex in SetReadFormat() for if it ever gets called without
125 * a StopCodec() beforehand.
127 * Revision 1.89 2001/04/18 02:31:58 craigs
128 * Fixed problem with illegal date in caller ID causing assert
130 * Revision 1.88 2001/04/09 08:46:16 robertj
131 * Added implementations to set mode for removing DTMF from media.
133 * Revision 1.87 2001/03/31 02:55:55 robertj
134 * Removed some interlocks on functions that are no longer required in current drivers.
136 * Revision 1.86 2001/03/30 05:46:09 robertj
137 * Added trace output of driver version number.
138 * Added check for PSTN line disconnect to include looking for tones.
140 * Revision 1.85 2001/03/29 23:40:46 robertj
141 * Added ability to get average signal level for both receive and transmit.
142 * Changed G.729A silence frames to be CNG frames to stop clicking sound.
144 * Revision 1.84 2001/03/24 00:52:34 robertj
145 * Fixed incorrect conditional on error trace for G.729B packet in G.729A mode.
147 * Revision 1.83 2001/03/23 05:38:09 robertj
148 * Added PTRACE_IF to output trace if a conditional is TRUE.
149 * Indicate if get sent a G.729B packet when in G.729A mode.
151 * Revision 1.82 2001/03/22 06:18:49 robertj
152 * Fixed very subtle problem with Quicknet cards under NT/2K, caused occassional
153 * blip in continuous audio due to the resolution of internal timers.
155 * Revision 1.81 2001/02/21 08:09:15 robertj
156 * Added more tones for characters 'e' through 'o'.
158 * Revision 1.80 2001/02/16 08:18:17 robertj
159 * Fixed IXJ interface to compensate for driver bug.
161 * Revision 1.79 2001/02/15 05:15:34 robertj
162 * Compensated for driver failing to return serial number on Win98.
164 * Revision 1.78 2001/02/07 05:02:58 robertj
165 * Temporary removal of code due to broken driver.
167 * Revision 1.77 2001/01/25 07:27:16 robertj
168 * Major changes to add more flexible OpalMediaFormat class to normalise
169 * all information about media types, especially codecs.
171 * Revision 1.76 2001/01/24 05:34:49 robertj
172 * Altered volume control range to be percentage, ie 100 is max volume.
174 * Revision 1.75 2001/01/11 05:39:44 robertj
175 * Fixed usage of G.723.1 CNG 1 byte frames.
177 * Revision 1.74 2000/12/18 21:56:13 robertj
178 * Fixed saving of POTS/PSTN link state when doing PSTN line test.
179 * Changed caller ID code to allow for single record protocol.
181 * Revision 1.73 2000/12/17 23:08:01 robertj
182 * Changed driver close so goes into POTS/PSTN pass through mode.
184 * Revision 1.72 2000/12/12 07:51:36 robertj
185 * Changed name to include word Internet as in Linux driver.
187 * Revision 1.71 2000/12/11 01:47:14 robertj
188 * Changed to use built PWLib class for overlapped I/O.
190 * Revision 1.70 2000/11/30 08:48:36 robertj
191 * Added functions to enable/disable Voice Activity Detection in LID's
193 * Revision 1.69 2000/11/30 05:59:57 robertj
194 * Changed raw mode transfer block size to 30ms blocks.
195 * Removed test of raw mode read/write as driver returns incorrect value.
196 * Added PTRACE of error code from driver when error occurs.
197 * Fixed bug in raw mode write, count of bytes too large if second write in loop.
199 * Revision 1.68 2000/11/28 01:59:53 robertj
200 * Removed usage of deprecated volume setting calls.
202 * Revision 1.67 2000/11/27 00:12:18 robertj
203 * Added WIN32 version of hook flash detection function.
205 * Revision 1.66 2000/11/24 10:58:47 robertj
206 * Added a raw PCM dta mode for generating/detecting standard tones.
207 * Modified the ReadFrame/WriteFrame functions to allow for variable length codecs.
208 * Fixed hook state debouncing.
209 * Added codec to explicitly set LineJACK mixer settings to avoid funny modes
210 * the driver/hardware gets into sometimes.
211 * Changed tone detection API slightly to allow detection of multiple
214 * Revision 1.65 2000/11/06 06:33:26 robertj
215 * Changed hook state debounce so does not block for 200ms.
217 * Revision 1.64 2000/11/03 06:25:37 robertj
218 * Added flag to IsLinePresent() to force slow test, guarenteeing correct value.
220 * Revision 1.63 2000/10/26 12:24:56 robertj
221 * Added configurable G.729 codec usage, based on separate license.
223 * Revision 1.62 2000/10/19 04:04:04 robertj
224 * Added functions to get xJACK card type and serial number.
226 * Revision 1.61 2000/10/13 02:21:28 robertj
227 * Changed volume control code to set more mixer values on LineJACK.
229 * Revision 1.60 2000/09/26 02:17:35 robertj
232 * Revision 1.59 2000/09/26 01:48:36 robertj
233 * Removed now redundent AEC resetting when starting read/write codec.
235 * Revision 1.58 2000/09/23 06:55:24 robertj
236 * Put code back so gets driver default frame size instead of trying to set it. Caused lockups.
238 * Revision 1.57 2000/09/22 01:35:51 robertj
239 * Added support for handling LID's that only do symmetric codecs.
241 * Revision 1.56 2000/09/15 23:01:50 robertj
242 * Fixed choppy audio in som cases with PCM, explicitly set frame size now.
244 * Revision 1.55 2000/09/05 22:07:50 robertj
245 * Removed deprecated IOCTL_Idle_Idle.
247 * Revision 1.54 2000/09/04 05:45:03 robertj
248 * Added VMWI support and country codes to IXJ driver interface.
250 * Revision 1.53 2000/09/01 01:25:05 robertj
251 * Fixed incorrect class names and began country code setting of driver.
253 * Revision 1.52 2000/08/31 13:14:40 craigs
254 * Added functions to LID
255 * More bulletproofing to Linux driver
257 * Revision 1.51 2000/08/30 22:57:46 robertj
258 * Removed call to CancelIO, does not exist in Win95!
260 * Revision 1.50 2000/08/21 02:49:14 robertj
261 * Added timeout for driver read/write, should never block for long.
263 * Revision 1.49 2000/08/01 03:24:49 robertj
264 * Changed enumeration of Quicknet devices to use new technique for future Win2k drives.
266 * Revision 1.48 2000/07/28 06:29:20 robertj
267 * Fixed AEC under Win32 so can be changed from other processes.
269 * Revision 1.47 2000/07/25 02:07:34 robertj
270 * Reduced max range of device numbers as can get some low serial numbers.
272 * Revision 1.46 2000/07/14 14:12:06 robertj
273 * Added turning off of VAD which results in 1 byte G.723.1 frames that not
274 * everyone supports yet.
276 * Revision 1.45 2000/07/12 10:25:07 robertj
277 * Added PhoneCARD support on Win9x systems.
279 * Revision 1.44 2000/06/20 12:51:23 robertj
280 * Changed IXJ driver open so does not change selected line to PSTN.
282 * Revision 1.43 2000/06/20 02:22:41 robertj
283 * Fixed NT version so can still use serial number to open device.
285 * Revision 1.42 2000/06/19 00:31:30 robertj
286 * Changed NT device name to be a bit more user friendly.
288 * Revision 1.41 2000/06/08 02:33:25 robertj
289 * Fixed detection of correct xJack card type under NT.
290 * Added ability to use just "0" or "1" instead of "\\.\QTJackDevice0" as device name.
292 * Revision 1.40 2000/05/25 02:23:25 robertj
293 * Added calls to get volume settings
295 * Revision 1.39 2000/05/15 08:38:59 robertj
296 * Changed LineJACK PSTN check so is initiated only if state unknown.
298 * Revision 1.38 2000/05/02 04:32:27 robertj
299 * Fixed copyright notice comment.
301 * Revision 1.37 2000/04/30 04:00:33 robertj
302 * Changed determination of frame size to use driver ioctl for PCM, uLAw and ALaw.
304 * Revision 1.36 2000/04/28 07:00:26 robertj
305 * Fixed race condition causing RTP send thread to randomly shut down.
307 * Revision 1.35 2000/04/19 01:57:39 robertj
308 * Added mixer code to get volume control support on LineJACK;s.
309 * Attempt to prevent getting very occassional ReadFrame() failure causing tx channel stop.
311 * Revision 1.34 2000/04/12 23:56:37 robertj
312 * Fixed detection of PCI PhoneJACK on NT.
314 * Revision 1.33 2000/04/06 20:36:25 robertj
315 * Fixed some LineJACK compatbility problems (eg DTMF detect stopping).
317 * Revision 1.32 2000/04/05 20:55:41 robertj
318 * Added caller ID send, and fixed receive for multiple fields.
320 * Revision 1.31 2000/04/05 18:04:12 robertj
321 * Changed caller ID code for better portability.
323 * Revision 1.30 2000/03/30 01:55:13 robertj
324 * Added function so silence detection can work on xJack internetl codecs.
325 * Fixed length of G.728 codec frames
327 * Revision 1.29 2000/03/29 20:59:52 robertj
328 * Added function on LID to get available codecs.
329 * Improved consistency in "device name".
330 * Fixed codec table for G.729 codec
331 * Fixed lockup bug with tone/codec interaction.
333 * Revision 1.28 2000/03/23 02:48:49 robertj
334 * Added calling tone detection code.
336 * Revision 1.27 2000/03/17 20:59:42 robertj
337 * Fixed line count to be xJACK card dependent.
338 * Added support for more xJACK card types.
340 * Revision 1.26 2000/02/24 00:35:22 robertj
341 * Fixed problem with unresolved SetRemoveDTMF function when not using linux telephony.
343 * Revision 1.25 2000/02/16 04:04:37 robertj
344 * Fixed bug where IXJ POTS handset never returned off hook.
346 * Revision 1.24 2000/01/07 08:28:09 robertj
347 * Additions and changes to line interface device base class.
349 * Revision 1.23 1999/12/30 09:16:41 robertj
350 * Fixed initialisation of driver handle, prevent crash in Close().
352 * Revision 1.22 1999/12/23 23:02:36 robertj
353 * File reorganision for separating RTP from H.323 and creation of LID for VPB support.
355 * Revision 1.21 1999/11/29 04:50:11 robertj
356 * Added adaptive threshold calculation to silence detection.
358 * Revision 1.20 1999/11/20 04:38:03 robertj
359 * Removed potential driver lockups by doing overlapped I/O only on read/write ioctls.
361 * Revision 1.19 1999/11/19 09:17:15 robertj
362 * Fixed problems with aycnhronous shut down of logical channels.
364 * Revision 1.18 1999/11/16 12:43:02 robertj
365 * Dixed missing initialise of AEC variable.
367 * Revision 1.17 1999/11/16 11:32:06 robertj
368 * Added some calling tones.
370 * Revision 1.16 1999/11/12 02:25:01 robertj
373 * Revision 1.15 1999/11/11 23:15:31 robertj
374 * Fixed bug where closed driver was not flagged as closed.
376 * Revision 1.14 1999/11/11 01:16:57 robertj
377 * Added NT support, debounce of phone hook state and wait for line test completion.
379 * Revision 1.13 1999/11/06 05:36:19 robertj
380 * Fixed problem with read/write locking up when stopping codec.
382 * Revision 1.12 1999/11/06 03:32:27 robertj
383 * Added volume control functions for record/playback.
385 * Revision 1.11 1999/11/05 12:53:40 robertj
386 * Fixed warnings on notrace version.
388 * Revision 1.10 1999/11/05 10:51:18 robertj
389 * Fixed problem with new ixj channel doing incorrect double initialise
391 * Revision 1.9 1999/11/05 08:54:41 robertj
392 * Rewrite of ixj interface code to fix support for arbitrary codecs.
394 * Revision 1.8 1999/11/02 00:24:29 robertj
395 * Added GetCallerID() function and implemented some LineJACK code.
397 * Revision 1.7 1999/11/01 01:20:26 robertj
398 * Added flunction to enabled/disable DTM detection
400 * Revision 1.6 1999/11/01 00:47:14 robertj
401 * Fixed problems with stopping codecs
403 * Revision 1.5 1999/10/30 12:43:25 robertj
404 * Fixed "lock up" problem, DTMF problem and added function to get line status.
406 * Revision 1.4 1999/10/28 12:21:34 robertj
407 * Added AEC support and speakerphone switching button.
409 * Revision 1.3 1999/10/27 06:30:31 robertj
410 * Added CancelIO command when closing channel.
412 * Revision 1.2 1999/10/24 14:51:41 robertj
413 * Removed EnableDetectDTMF() as unix ioctl does not exist.
415 * Revision 1.1 1999/10/24 12:59:41 robertj
416 * Added platform independent support for Quicknet xJACK cards.
429 #pragma comment(lib, "winmm.lib")
432 #define NEW_DRIVER_VERSION ((5<<24)|(5<<16)|141)
441 } GetOperatingSystem()
443 static OSVERSIONINFO version;
444 if (version.dwOSVersionInfoSize == 0) {
445 version.dwOSVersionInfoSize = sizeof(version);
446 GetVersionEx(&version);
448 if (version.dwPlatformId != VER_PLATFORM_WIN32_NT)
450 if (version.dwMajorVersion < 5)
455 #define IsLineJACK() (dwCardType == 3)
458 /////////////////////////////////////////////////////////////////////////////
460 OpalIxJDevice::OpalIxJDevice()
462 hDriver = INVALID_HANDLE_VALUE;
464 readStopped = writeStopped = TRUE;
465 readFrameSize = writeFrameSize = 480; // 30 milliseconds of 16 bit PCM data
466 readCodecType = writeCodecType = P_MAX_INDEX;
467 currentHookState = lastHookState = FALSE;
469 enabledAudioLine = UINT_MAX;
470 exclusiveAudioMode = TRUE;
472 hReadEvent = hWriteEvent = NULL;
476 BOOL OpalIxJDevice::Open(const PString & device)
480 PTRACE(3, "xJack\tOpening IxJ device \"" << device << '"');
482 DWORD dwDeviceId = device.AsUnsigned(16);
485 const char * DevicePathPrefix = "\\\\.\\QTJACKDevice";
487 switch (GetOperatingSystem()) {
489 DevicePathPrefix = "\\\\.\\QTIWDMDevice";
493 if (dwDeviceId < 100) {
494 devicePath = device.Left(device.Find(' '));
495 if (strspn(devicePath, "0123456789") == strlen(devicePath))
496 devicePath = DevicePathPrefix + devicePath;
499 PStringArray allDevices = GetDeviceNames();
500 for (PINDEX dev = 0; dev < allDevices.GetSize(); dev++) {
501 PString thisDevice = allDevices[dev];
502 if (thisDevice.Find(device) != P_MAX_INDEX) {
503 devicePath = thisDevice.Left(thisDevice.Find(' '));
512 devicePath = "\\\\.\\Qtipj.vxd";
515 hDriver = CreateFile(devicePath,
516 GENERIC_READ | GENERIC_WRITE,
520 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
522 if (hDriver == INVALID_HANDLE_VALUE) {
523 osError = ::GetLastError()|PWIN32ErrorFlag;
527 if (GetOperatingSystem() == IsWindows9x) {
529 if (!IoControl(IOCTL_Device_Open, dwDeviceId, &dwResult) || dwResult == 0) {
530 CloseHandle(hDriver);
531 hDriver = INVALID_HANDLE_VALUE;
535 deviceName = psprintf("%08X", dwDeviceId);
538 dwDeviceId = GetSerialNumber();
539 if (dwDeviceId == 0) {
540 CloseHandle(hDriver);
541 hDriver = INVALID_HANDLE_VALUE;
546 PINDEX prefixLen = strlen(DevicePathPrefix);
547 if (strnicmp(devicePath, DevicePathPrefix, prefixLen) == 0)
548 deviceName = devicePath.Mid(prefixLen);
550 deviceName = devicePath;
553 dwCardType = dwDeviceId >> 28;
555 IoControl(IOCTL_Codec_SetKHz, 8000);
556 IoControl(IOCTL_Idle_SetMasterGain, 15);
557 IoControl(IOCTL_Filter_EnableDTMFDetect);
558 IoControl(IOCTL_Speakerphone_AECOn);
561 IoControl(IOCTL_VxD_GetVersion, 0, &ver);
562 driverVersion = ((ver&0xff)<<24)|((ver&0xff00)<<8)|((ver>>16)&0xffff);
564 PTRACE(2, "xJack\tOpened IxJ device \"" << GetName() << "\" version "
565 << ((driverVersion>>24)&0xff ) << '.'
566 << ((driverVersion>>16)&0xff ) << '.'
567 << ( driverVersion &0xffff));
575 BOOL OpalIxJDevice::Close()
582 SetLineToLineDirect(0, 1, TRUE); // Back to pass through mode
584 if (GetOperatingSystem() == IsWindows9x)
585 IoControl(IOCTL_Device_Close);
589 deviceName = PString();
591 if (hReadEvent != NULL) {
592 CloseHandle(hReadEvent);
595 if (hWriteEvent != NULL) {
596 CloseHandle(hWriteEvent);
600 BOOL ok = CloseHandle(hDriver);
601 hDriver = INVALID_HANDLE_VALUE;
607 PString OpalIxJDevice::GetName() const
613 switch (dwCardType) {
622 name << "PhoneJACK-Lite";
625 name << "PhoneJACK-PCI";
634 name << " (" << deviceName << ')';
640 unsigned OpalIxJDevice::GetLineCount()
642 return IsLineJACK() ? NumLines : 1;
646 BOOL OpalIxJDevice::IsLinePresent(unsigned line, BOOL force)
648 if (line >= GetLineCount())
651 if (line != PSTNLine)
654 int oldSlicState = -1;
658 if (!IoControl(IOCTL_DevCtrl_GetLineTestResult, 0, &dwResult))
660 if (dwResult == 0xffffffff || force) {
661 if (dwResult == LINE_TEST_OK) {
662 IoControl(IOCTL_DevCtrl_GetPotsToSlic, 0, &dwResult);
663 oldSlicState = dwResult;
665 IoControl(IOCTL_DevCtrl_LineTest);
666 dwResult = LINE_TEST_TESTING;
669 } while (dwResult == LINE_TEST_TESTING);
671 if (oldSlicState >= 0)
672 IoControl(IOCTL_DevCtrl_SetPotsToSlic, oldSlicState);
674 return dwResult == LINE_TEST_OK;
678 BOOL OpalIxJDevice::IsLineOffHook(unsigned line)
680 if (line >= GetLineCount())
685 if (line == PSTNLine) {
686 if (!IoControl(IOCTL_DevCtrl_GetLineOnHook, 0, &dwResult))
688 return dwResult == 0;
691 if (!IoControl(IsLineJACK() && IsLinePresent(PSTNLine)
692 ? IOCTL_DevCtrl_GetLinePhoneOnHook
693 : IOCTL_DevCtrl_GetOnHook, 0, &dwResult))
696 BOOL newHookState = dwResult == 0;
697 if (lastHookState != newHookState) {
698 lastHookState = newHookState;
702 if (!hookTimeout.IsRunning())
703 currentHookState = lastHookState;
706 return currentHookState;
710 BOOL OpalIxJDevice::SetLineOffHook(unsigned line, BOOL newState)
712 if (line >= GetLineCount())
715 if (line != PSTNLine)
718 return IoControl(IOCTL_DevCtrl_LineSetOnHook, !newState);
722 BOOL OpalIxJDevice::HasHookFlash(unsigned line)
724 if (line != POTSLine)
728 if (!IoControl(IOCTL_DevCtrl_GetFlashState, 0, &dwResult))
731 if (lastFlashState == dwResult)
734 lastFlashState = dwResult;
735 return dwResult != 0;
739 BOOL OpalIxJDevice::IsLineRinging(unsigned line, DWORD * /*cadence*/)
741 if (line >= GetLineCount())
744 if (line != PSTNLine)
747 if (ringTimeout.IsRunning())
751 if (!IoControl(IOCTL_DevCtrl_LineGetRinging, 0, &dwResult) || dwResult == 0)
759 BOOL OpalIxJDevice::RingLine(unsigned line, DWORD cadence)
761 if (line >= GetLineCount())
764 if (line != POTSLine)
767 if (cadence == TRUE) {
768 switch (countryCode) {
770 static unsigned AusRing[] = { 200, 400, 200, 2000 };
771 return RingLine(line, PARRAYSIZE(AusRing), AusRing);
774 // United States ring pattern
778 IoControl(IOCTL_DevCtrl_SetPotsToSlic, 1);
779 return IoControl(IOCTL_DevCtrl_SetRingPattern, cadence);
783 BOOL OpalIxJDevice::RingLine(unsigned line, PINDEX nCadence, unsigned * pattern)
785 if (line >= GetLineCount())
788 if (line != POTSLine)
791 IoControl(IOCTL_DevCtrl_SetPotsToSlic, 1);
794 DWORD dwReturn, dwSize;
795 DWORD cadenceArray[10];
796 cadenceArray[0] = (nCadence+1)/2; // Number of pairs
797 cadenceArray[1] = (nCadence&1) == 0; // If odd then repeat last entry
799 for (i = 2; i < nCadence; i++)
800 cadenceArray[i] = pattern[i-2];
801 for (; i < PARRAYSIZE(cadenceArray); i++)
804 return IoControl(IOCTL_DevCtrl_SetRingCadence,
805 cadenceArray, sizeof(cadenceArray),
806 &dwReturn, sizeof(dwReturn), &dwSize);
810 BOOL OpalIxJDevice::IsLineDisconnected(unsigned line, BOOL checkForWink)
812 if (line >= GetLineCount())
815 if (line != PSTNLine)
816 return !IsLineOffHook(line);
820 if (IoControl(IOCTL_DevCtrl_GetLineCallerOnHook, 0, &dwResult) && dwResult != 0) {
821 PTRACE(3, "xJack\tDetected wink, line disconnected.");
826 /* if ((IsToneDetected(line) & (DialTone|BusyTone)) != 0) { */
827 if ((IsToneDetected(line) & BusyTone) != 0) {
828 PTRACE(3, "xJack\tDetected dial or busy tone, line disconnected.");
836 BOOL OpalIxJDevice::SetLineToLineDirect(unsigned line1, unsigned line2, BOOL connect)
838 if (line1 >= GetLineCount() || line2 >= GetLineCount())
845 return IoControl(IOCTL_DevCtrl_SetPotsToSlic, connect ? 0 : 1, &dwResult) && dwResult != 0;
849 BOOL OpalIxJDevice::IsLineToLineDirect(unsigned line1, unsigned line2)
851 if (line1 >= GetLineCount() || line2 >= GetLineCount())
857 // The IOCTL_DevCtrl_GetPotsToSlic is broken unless the line test has been
858 // performed and there is a PSTN line present.
859 if (!IsLinePresent(PSTNLine))
863 if (!IoControl(IOCTL_DevCtrl_GetPotsToSlic, 0, &dwResult))
866 return dwResult == 0;
871 static const struct {
872 const char * mediaFormat;
873 unsigned dspBitMask:3; // bit0=8020,bit1=8021,bit2=8022
883 { OPAL_PCM16, 7, 0, 0, 0, 0, RECORD_MODE_16LINEAR, 0, PLAYBACK_MODE_16LINEAR, 0 },
884 { OPAL_G711_ULAW_64K, 7, 0, 0, 0, 0, RECORD_MODE_ULAW, 0, PLAYBACK_MODE_ULAW, 0 },
885 { OPAL_G711_ALAW_64K, 7, 0, 0, 0, 0, RECORD_MODE_ALAW, 0, PLAYBACK_MODE_ALAW, 0 },
886 { OPAL_G728, 2, 0, 0, 0, 20, RECORD_MODE_TRUESPEECH, RECORD_RATE_G728, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G728 },
887 { OPAL_G729, 6, 1, 0, 0, 10, RECORD_MODE_TRUESPEECH, RECORD_RATE_G729, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G729 },
888 { OPAL_G729AB, 6, 1, 0, 1, 10, RECORD_MODE_TRUESPEECH, RECORD_RATE_G729, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G729 },
890 // these two lines should be for the 5k3 codec, but this does not work properly in the driver so we lie
891 { OPAL_G7231_5k3, 7, 0, 1, 0, 24, RECORD_MODE_TRUESPEECH, RECORD_RATE_G723_63, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G723_63 },
892 { OPAL_G7231A_5k3, 7, 0, 1, 1, 24, RECORD_MODE_TRUESPEECH, RECORD_RATE_G723_63, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G723_63 },
894 { OPAL_G7231_6k3, 7, 0, 1, 0, 24, RECORD_MODE_TRUESPEECH, RECORD_RATE_G723_63, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G723_63 },
895 { OPAL_G7231A_6k3, 7, 0, 1, 1, 24, RECORD_MODE_TRUESPEECH, RECORD_RATE_G723_63, PLAYBACK_MODE_TRUESPEECH, PLAYBACK_RATE_G723_63 },
900 OpalMediaFormat::List OpalIxJDevice::GetMediaFormats() const
902 OpalMediaFormat::List codecs;
904 OpalIxJDevice * unconstThis = (OpalIxJDevice *)this;
907 if (unconstThis->IoControl(IOCTL_DevCtrl_GetIdCode, 0, &dwIdCode)) {
908 unsigned dspBit = 1 << (dwIdCode&3);
909 PINDEX codec = PARRAYSIZE(CodecInfo);
910 while (codec-- > 0) {
911 BOOL add = (CodecInfo[codec].dspBitMask&dspBit) != 0;
912 if (add && CodecInfo[codec].isG729) {
914 add = unconstThis->IoControl(IOCTL_Device_GetG729Enable, 0, &hasG729) && hasG729;
917 codecs.Append(new OpalMediaFormat(CodecInfo[codec].mediaFormat));
925 static PINDEX FindCodec(const OpalMediaFormat & mediaFormat)
927 for (PINDEX codecType = 0; codecType < PARRAYSIZE(CodecInfo); codecType++) {
928 if (mediaFormat == CodecInfo[codecType].mediaFormat)
936 BOOL OpalIxJDevice::SetReadFormat(unsigned line, const OpalMediaFormat & mediaFormat)
940 PWaitAndSignal mutex(readMutex);
942 IoControl(IOCTL_Record_Stop);
944 readCodecType = FindCodec(mediaFormat);
945 if (readCodecType == P_MAX_INDEX) {
946 PTRACE(1, "xJack\tUnsupported read codec requested: " << mediaFormat);
950 if (!writeStopped && readCodecType != writeCodecType) {
951 PTRACE(1, "xJack\tAsymmetric codecs requested: "
952 "read=" << CodecInfo[readCodecType].mediaFormat
953 << " write=" << CodecInfo[writeCodecType].mediaFormat);
957 PTRACE(3, "xJack\tSetReadFormat(" << CodecInfo[readCodecType].mediaFormat << ')');
959 if (!IoControl(IOCTL_Codec_SetKHz, 8000))
962 if (!IoControl(IOCTL_Record_SetBufferChannelLimit, 1))
967 if (!IoControl(IOCTL_Record_SetRECMODE, CodecInfo[readCodecType].recordMode))
969 if (!IoControl(IOCTL_Record_GetRECMODE, 0, &mode))
971 PTRACE_IF(3, mode != CodecInfo[readCodecType].recordMode,
972 "xJack\tSetRECMODE failed (" << mode << " -> " <<
973 CodecInfo[readCodecType].recordMode << "), retrying");
974 } while (mode != CodecInfo[readCodecType].recordMode);
978 if (!IoControl(IOCTL_Record_SetRate, CodecInfo[readCodecType].recordRate))
980 if (!IoControl(IOCTL_Record_GetRate, 0, &rate))
982 PTRACE_IF(3, rate != CodecInfo[readCodecType].recordRate,
983 "xJack\tRecord_SetRate failed (" << rate << " -> " <<
984 CodecInfo[readCodecType].recordRate << "), retrying");
985 } while (rate != CodecInfo[readCodecType].recordRate);
987 readFrameSize = CodecInfo[readCodecType].frameSize;
988 if (readFrameSize == 0) {
990 if (IoControl(IOCTL_Record_GetFrameSize, 0, &frameWords))
991 readFrameSize = frameWords*2;
993 PTRACE(1, "xJack\tCould not get record frame size.");
998 SetVAD(line, CodecInfo[readCodecType].vad);
1000 if (!IoControl(driverVersion >= NEW_DRIVER_VERSION ? IOCTL_Record_Start
1001 : IOCTL_Record_Start_Old))
1004 readStopped = FALSE;
1010 BOOL OpalIxJDevice::SetWriteFormat(unsigned line, const OpalMediaFormat & mediaFormat)
1012 StopWriteCodec(line);
1014 PWaitAndSignal mutex(writeMutex);
1016 IoControl(IOCTL_Playback_Stop);
1018 writeCodecType = FindCodec(mediaFormat);
1019 if (writeCodecType == P_MAX_INDEX) {
1020 PTRACE(1, "xJack\tUnsupported write codec requested: " << mediaFormat);
1024 if (!readStopped && writeCodecType != readCodecType) {
1025 PTRACE(1, "xJack\tAsymmetric codecs requested: "
1026 "read=" << CodecInfo[readCodecType].mediaFormat
1027 << " write=" << CodecInfo[writeCodecType].mediaFormat);
1031 PTRACE(3, "xJack\tSetWriteFormat(" << CodecInfo[writeCodecType].mediaFormat << ')');
1033 if (!IoControl(IOCTL_Codec_SetKHz, 8000))
1036 if (!IoControl(IOCTL_Playback_SetBufferChannelLimit, 1))
1041 if (!IoControl(IOCTL_Playback_SetPLAYMODE, CodecInfo[writeCodecType].playbackMode))
1043 if (!IoControl(IOCTL_Playback_GetPLAYMODE, 0, &mode))
1045 PTRACE_IF(2, mode != CodecInfo[writeCodecType].playbackMode,
1046 "xJack\tSetPLAYMODE failed (" << mode << " -> " <<
1047 CodecInfo[writeCodecType].playbackMode << "), retrying");
1048 } while (mode != CodecInfo[writeCodecType].playbackMode);
1052 if (!IoControl(IOCTL_Playback_SetRate, CodecInfo[writeCodecType].playbackRate))
1054 if (!IoControl(IOCTL_Playback_GetRate, 0, &rate))
1056 PTRACE_IF(2, rate != CodecInfo[writeCodecType].playbackRate,
1057 "xJack\tPlayback_SetRate failed (" << rate << " -> " <<
1058 CodecInfo[writeCodecType].playbackRate << "), retrying");
1059 } while (rate != CodecInfo[writeCodecType].playbackRate);
1061 writeFrameSize = CodecInfo[writeCodecType].frameSize;
1062 if (writeFrameSize == 0) {
1064 if (IoControl(IOCTL_Playback_GetFrameSize, 0, &frameWords))
1065 writeFrameSize = frameWords*2;
1067 PTRACE(1, "xJack\tCould not get playback frame size.");
1072 SetVAD(line, CodecInfo[writeCodecType].vad);
1074 if (!IoControl(driverVersion >= NEW_DRIVER_VERSION ? IOCTL_Playback_Start
1075 : IOCTL_Playback_Start_Old))
1078 writeStopped = FALSE;
1083 OpalMediaFormat OpalIxJDevice::GetReadFormat(unsigned)
1085 if (readCodecType == P_MAX_INDEX)
1087 return CodecInfo[readCodecType].mediaFormat;
1091 OpalMediaFormat OpalIxJDevice::GetWriteFormat(unsigned)
1093 if (writeCodecType == P_MAX_INDEX)
1095 return CodecInfo[writeCodecType].mediaFormat;
1099 BOOL OpalIxJDevice::SetRawCodec(unsigned)
1104 PTRACE(3, "xJack\tSetRawCodec()");
1106 // Default to 30ms frames of 16 bit PCM data
1107 readFrameSize = 480;
1108 writeFrameSize = 480;
1110 if (hReadEvent == NULL)
1111 hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1112 if (hWriteEvent == NULL)
1113 hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1115 HANDLE hEventArr[2];
1117 if (GetOperatingSystem() == IsWindows9x) {
1118 CHAR K32Path[MAX_PATH];
1120 HANDLE (WINAPI *OpenVxDHandle)(HANDLE);
1122 GetSystemDirectory(K32Path, MAX_PATH);
1123 strcat(K32Path, "\\kernel32.dll");
1124 hK32 = LoadLibrary(K32Path);
1126 OpenVxDHandle = (HANDLE(WINAPI *)(HANDLE))GetProcAddress(hK32, "OpenVxDHandle");
1127 hEventArr[0] = OpenVxDHandle(hReadEvent);
1128 hEventArr[1] = OpenVxDHandle(hWriteEvent);
1133 hEventArr[0] = hReadEvent;
1134 hEventArr[1] = hWriteEvent;
1140 DWORD dwReturn, dwBytesReturned;
1141 inRawMode = IoControl(IOCTL_Fax_Start,
1142 hEventArr, sizeof(hEventArr),
1143 &dwReturn, sizeof(dwReturn), &dwBytesReturned);
1144 readCodecType = writeCodecType = 0;
1145 readStopped = writeStopped = !inRawMode;
1148 writeMutex.Signal();
1154 BOOL OpalIxJDevice::StopReadCodec(unsigned line)
1157 return StopRawCodec(line);
1159 PTRACE(3, "xJack\tStopping read codec");
1164 IoControl(IOCTL_Record_Stop);
1168 return OpalLineInterfaceDevice::StopReadCodec(line);
1172 BOOL OpalIxJDevice::StopWriteCodec(unsigned line)
1175 return StopRawCodec(line);
1177 PTRACE(3, "xJack\tStopping write codec");
1180 if (!writeStopped) {
1181 writeStopped = TRUE;
1182 IoControl(IOCTL_Playback_Stop);
1184 writeMutex.Signal();
1186 return OpalLineInterfaceDevice::StopWriteCodec(line);
1190 BOOL OpalIxJDevice::StopRawCodec(unsigned line)
1193 BOOL ok = StopReadCodec(line);
1194 return StopWriteCodec(line) && ok;
1197 PTRACE(3, "xJack\tStopping raw codec");
1202 writeStopped = TRUE;
1203 BOOL ok = IoControl(IOCTL_Fax_Stop);
1205 writeMutex.Signal();
1209 OpalLineInterfaceDevice::StopReadCodec(line);
1210 OpalLineInterfaceDevice::StopWriteCodec(line);
1215 PINDEX OpalIxJDevice::GetReadFrameSize(unsigned)
1217 return readFrameSize;
1221 BOOL OpalIxJDevice::SetReadFrameSize(unsigned, PINDEX size)
1226 readFrameSize = size;
1231 PINDEX OpalIxJDevice::GetWriteFrameSize(unsigned)
1233 return writeFrameSize;
1237 BOOL OpalIxJDevice::SetWriteFrameSize(unsigned, PINDEX size)
1242 writeFrameSize = size;
1247 BOOL OpalIxJDevice::ReadFrame(unsigned, void * buffer, PINDEX & count)
1251 PWaitAndSignal mutex(readMutex);
1255 DWORD dwBytesReturned = 0;
1257 if (WaitForSingleObjectEx(hReadEvent, 1000, TRUE) != WAIT_OBJECT_0) {
1259 PTRACE(1, "xJack\tRead Timeout!");
1262 IoControl(IOCTL_Fax_Read, NULL, 0, buffer, readFrameSize, &dwBytesReturned);
1263 count = (PINDEX)dwBytesReturned;
1267 BOOL reblockG729 = CodecInfo[readCodecType].isG729;
1268 WORD temp_frame_buffer[6];
1270 PWin32Overlapped overlap;
1271 if (!IoControl(IOCTL_Device_Read, NULL, 0,
1272 reblockG729 ? temp_frame_buffer : buffer,
1273 reblockG729 ? sizeof(temp_frame_buffer) : readFrameSize,
1274 &dwBytesReturned, &overlap))
1278 switch (temp_frame_buffer[0]) {
1280 memcpy(buffer, &temp_frame_buffer[1], 10);
1284 if (CodecInfo[readCodecType].vad) {
1285 *(WORD *)buffer = temp_frame_buffer[1];
1289 memset(buffer, 0, 10);
1293 default : // Must be old driver
1294 memcpy(buffer, temp_frame_buffer, 10);
1298 else if (CodecInfo[readCodecType].isG7231) {
1299 // Pick out special cases for G.723.1 based codecs (variable length frames)
1300 static const PINDEX g723count[4] = { 24, 20, 4, 1 };
1301 count = g723count[(*(BYTE *)buffer)&3];
1304 count = (PINDEX)dwBytesReturned;
1310 BOOL OpalIxJDevice::WriteFrame(unsigned, const void * buffer, PINDEX count, PINDEX & written)
1312 PWaitAndSignal mutex(writeMutex);
1317 DWORD dwBytesReturned = 0;
1320 for (written = 0; written < count; written += dwResult) {
1321 if (WaitForSingleObjectEx(hWriteEvent, 1000, TRUE) != WAIT_OBJECT_0) {
1323 PTRACE(1, "xJack\tWrite Timeout!");
1326 IoControl(IOCTL_Fax_Write, ((BYTE *)buffer)+written, count-written,
1327 &dwResult, sizeof(dwResult), &dwBytesReturned);
1332 WORD temp_frame_buffer[12];
1333 PINDEX bytesToWrite;
1335 if (CodecInfo[writeCodecType].isG7231) {
1336 // Pick out special cases for G.723.1 based codecs (variable length frames)
1337 switch ((*(BYTE *)buffer)&3) {
1338 case 0 : // 6.3kbps rate frame
1341 case 1 : // 5.3kbps rate frame
1344 case 2 : // a Silence Insertion Descriptor
1345 memset(temp_frame_buffer, 0, sizeof(temp_frame_buffer));
1346 *(DWORD *)(temp_frame_buffer) = *(const DWORD *)buffer;
1347 buffer = temp_frame_buffer;
1350 case 3 : // repeat last CNG frame
1351 // Check for frame erasure command
1352 if (memcmp(buffer, "\xff\xff\xff\xff", 4) == 0)
1355 memset(temp_frame_buffer, 0, sizeof(temp_frame_buffer));
1356 temp_frame_buffer[0] = 3;
1357 buffer = temp_frame_buffer;
1364 else if (CodecInfo[writeCodecType].isG729) {
1366 PTRACE_IF(2, !CodecInfo[readCodecType].vad,
1367 "xJack\tG.729B frame received, but not selected.");
1368 temp_frame_buffer[0] = 2;
1369 temp_frame_buffer[1] = *(const WORD *)buffer;
1370 memset(&temp_frame_buffer[2], 0, 8);
1374 if (memcmp(buffer, "\0\0\0\0\0\0\0\0\0", 10) == 0) {
1376 memset(temp_frame_buffer, 0, 12);
1378 // We really should be sending a full frame of zeros here, but the codec
1379 // makes a clicking sound if you do so send annex B CNG frames instead.
1380 temp_frame_buffer[0] = 2;
1381 memset(&temp_frame_buffer[1], 0, 10);
1385 temp_frame_buffer[0] = 1;
1386 memcpy(&temp_frame_buffer[1], buffer, 10);
1390 buffer = temp_frame_buffer;
1394 bytesToWrite = writeFrameSize;
1395 written = bytesToWrite;
1398 if (count < written) {
1400 PTRACE(1, "xJack\tWrite of too small a buffer");
1404 PWin32Overlapped overlap;
1405 return IoControl(IOCTL_Device_Write, (void *)buffer, bytesToWrite,
1406 &dwResult, sizeof(dwResult), &dwBytesReturned, &overlap);
1410 unsigned OpalIxJDevice::GetAverageSignalLevel(unsigned, BOOL playback)
1413 if (!IoControl(playback ? IOCTL_Playback_GetAvgPlaybackLevel
1414 : IOCTL_Record_GetAvgRecordLevel,
1422 BOOL OpalIxJDevice::EnableAudio(unsigned line, BOOL enable)
1424 if (line >= GetLineCount())
1427 DWORD dwSource = ANALOG_SOURCE_SPEAKERPHONE;
1430 if (enabledAudioLine != line) {
1431 if (enabledAudioLine != UINT_MAX && exclusiveAudioMode) {
1432 PTRACE(3, "xJack\tEnableAudio on port when already enabled other port.");
1435 enabledAudioLine = line;
1437 dwSource = line == POTSLine ? ANALOG_SOURCE_POTSPHONE : ANALOG_SOURCE_PSTNLINE;
1440 enabledAudioLine = UINT_MAX;
1443 return IoControl(IOCTL_DevCtrl_SetAnalogSource, dwSource);
1445 BOOL connected = IsLineToLineDirect(POTSLine, PSTNLine);
1446 IoControl(IOCTL_DevCtrl_SetLineJackMode,
1447 dwSource == ANALOG_SOURCE_PSTNLINE ? LINEJACK_MODE_LINEJACK
1448 : LINEJACK_MODE_PHONEJACK);
1449 SetLineToLineDirect(POTSLine, PSTNLine, connected);
1451 if (dwSource != ANALOG_SOURCE_PSTNLINE) {
1452 if (!IoControl(IOCTL_DevCtrl_SetAnalogSource, dwSource))
1456 InternalSetVolume(TRUE, RecordMicrophone, -1, dwSource != ANALOG_SOURCE_SPEAKERPHONE);
1457 InternalSetVolume(TRUE, RecordPhoneIn, -1, dwSource != ANALOG_SOURCE_POTSPHONE);
1458 InternalSetVolume(FALSE,PlaybackPhoneOut, -1, dwSource != ANALOG_SOURCE_POTSPHONE);
1459 InternalSetVolume(TRUE, RecordPhoneLineIn, -1, dwSource != ANALOG_SOURCE_PSTNLINE);
1460 InternalSetVolume(FALSE,PlaybackPhoneLineOut,-1, dwSource != ANALOG_SOURCE_PSTNLINE);
1461 InternalSetVolume(FALSE,PlaybackWave, -1, FALSE);
1467 BOOL OpalIxJDevice::IsAudioEnabled(unsigned line)
1469 return enabledAudioLine == line;
1473 BOOL OpalIxJDevice::InternalSetVolume(BOOL record, unsigned id, int volume, int mute)
1476 mixer.dwLineID = id;
1479 if (!IoControl(record ? IOCTL_Mixer_GetRecordLineControls
1480 : IOCTL_Mixer_GetPlaybackLineControls,
1481 &mixer, sizeof(mixer), &mixer, sizeof(mixer), &dwSize))
1486 mixer.dwRightVolume = 65535;
1488 mixer.dwRightVolume = volume*65536/100;
1489 mixer.dwLeftVolume = mixer.dwRightVolume;
1492 mixer.dwMute = mute != 0;
1495 return IoControl(record ? IOCTL_Mixer_SetRecordLineControls
1496 : IOCTL_Mixer_SetPlaybackLineControls,
1497 &mixer, sizeof(mixer), &dwReturn, sizeof(dwReturn), &dwSize);
1501 BOOL OpalIxJDevice::SetRecordVolume(unsigned line, unsigned volume)
1504 if (!InternalSetVolume(TRUE,
1505 line == POTSLine ? RecordPhoneIn : RecordPhoneLineIn,
1511 return InternalSetVolume(TRUE, RecordMaster, volume, -1);
1515 BOOL OpalIxJDevice::SetPlayVolume(unsigned line, unsigned volume)
1518 if (!InternalSetVolume(FALSE,
1519 line == POTSLine ? PlaybackPhoneOut : PlaybackPhoneLineOut,
1525 return InternalSetVolume(FALSE, PlaybackMaster, volume, -1);
1529 BOOL OpalIxJDevice::GetRecordVolume(unsigned, unsigned & volume)
1535 if (!IoControl(IOCTL_Mixer_GetRecordLineControls,
1536 &mixer, sizeof(mixer), &mixer, sizeof(mixer), &dwSize))
1539 if (mixer.dwLeftVolume > 65208) // 99.5%
1542 volume = mixer.dwLeftVolume*100/65536;
1547 BOOL OpalIxJDevice::GetPlayVolume(unsigned, unsigned & volume)
1553 if (!IoControl(IOCTL_Mixer_GetPlaybackLineControls,
1554 &mixer, sizeof(mixer), &mixer, sizeof(mixer), &dwSize))
1557 if (mixer.dwLeftVolume > 65208) // 99.5%
1560 volume = mixer.dwLeftVolume*100/65536;
1565 OpalLineInterfaceDevice::AECLevels OpalIxJDevice::GetAEC(unsigned)
1567 DWORD level = AECOff;
1568 IoControl(IOCTL_Speakerphone_GetAEC, 0, &level);
1569 return (AECLevels)level;
1573 BOOL OpalIxJDevice::SetAEC(unsigned, AECLevels level)
1575 return IoControl(IOCTL_Speakerphone_SetAEC, level);
1579 unsigned OpalIxJDevice::GetWinkDuration(unsigned)
1582 IoControl(IOCTL_DevCtrl_GetLineWinkDetTime, 0, &level);
1583 return (unsigned)level;
1587 BOOL OpalIxJDevice::SetWinkDuration(unsigned, unsigned winkDuration)
1589 return IoControl(IOCTL_DevCtrl_SetLineWinkDetTime, winkDuration);
1593 BOOL OpalIxJDevice::GetVAD(unsigned)
1599 BOOL OpalIxJDevice::SetVAD(unsigned, BOOL enable)
1601 PTRACE(3, "xJack\tSet VAD " << (enable ? "on" : "off"));
1602 vadEnabled = enable;
1603 return IoControl(enable ? IOCTL_Record_EnableVAD : IOCTL_Record_DisableVAD);
1607 BOOL OpalIxJDevice::GetCallerID(unsigned, PString & idString, BOOL full)
1609 idString = PString();
1613 DWORD dwBytesReturned;
1614 if (!IoControl(IOCTL_DevCtrl_LineGetCallerID,
1615 buffer, sizeof(buffer),
1616 buffer, sizeof(buffer),
1620 PTRACE_IF(3, buffer[0] != 0, "xJack\tCaller ID:\n"
1621 << hex << setprecision(2)
1622 << PBYTEArray(buffer, dwBytesReturned)
1625 PString name, timeStr;
1627 switch (buffer[0]) {
1628 case 4 : // Single message
1629 timeStr = PString((char *)&buffer[2], 8);
1630 idString = PString((char *)&buffer[10], buffer[1]-8);
1634 PINDEX totalLength = buffer[1];
1636 while (pos < totalLength) {
1637 switch (buffer[pos]) {
1639 timeStr = PString((char *)&buffer[pos+2], buffer[pos+1]);
1642 idString = PString((char *)&buffer[pos+2], buffer[pos+1]);
1645 name = PString((char *)&buffer[pos+2], buffer[pos+1]);
1648 pos += buffer[pos+1]+2;
1657 if (full && !timeStr.IsEmpty()) {
1659 int minute = timeStr(6,7).AsUnsigned() % 60;
1660 int hour = timeStr(4,5).AsUnsigned() % 24;
1661 int day = timeStr(2,3).AsUnsigned();
1666 int month = timeStr(0,1).AsUnsigned();
1669 else if (month > 12)
1672 PTime theTime(0, minute, hour, day, month, now.GetYear());
1673 idString += '\t' + theTime.AsString(PTime::ShortDateTime) + '\t' + name;
1680 BOOL OpalIxJDevice::SetCallerID(unsigned line, const PString & idString)
1682 if (line != POTSLine)
1685 if (idString.IsEmpty())
1688 PString name, number;
1691 PStringArray fields = idString.Tokenise('\t', TRUE);
1692 switch (fields.GetSize()) {
1696 theTime = PTime(fields[1]);
1704 PINDEX numberLength = number.GetLength();
1705 PINDEX nameLength = name.GetLength();
1710 buffer[2] = (char)(14+numberLength+nameLength);
1718 theTime.GetMinute());
1720 buffer[14] = (char)numberLength;
1721 strcpy(&buffer[15], number);
1722 buffer[15+numberLength] = 7;
1723 buffer[16+numberLength] = (char)nameLength;
1724 strcpy(&buffer[17+numberLength], name);
1727 DWORD dwBytesReturned;
1728 return IoControl(IOCTL_FSK_SetMsgData,
1729 buffer, 17+numberLength+nameLength,
1730 &dwReturn, sizeof(dwReturn), &dwBytesReturned);
1734 BOOL OpalIxJDevice::SendCallerIDOnCallWaiting(unsigned, const PString & /*idString*/)
1740 BOOL OpalIxJDevice::SendVisualMessageWaitingIndicator(unsigned line, BOOL isOn)
1742 if (IsLineOffHook(line))
1745 BYTE buffer[] = { 0, 130, 3, 11, 1, 0 };
1749 DWORD dwBytesReturned;
1750 return IoControl(IOCTL_FSK_SetMsgData,
1751 buffer, sizeof(buffer),
1752 &dwReturn, sizeof(dwReturn), &dwBytesReturned);
1756 BOOL OpalIxJDevice::PlayDTMF(unsigned line,
1757 const char * digits,
1758 DWORD onTime, DWORD offTime)
1760 while (*digits != '\0') {
1762 int digit = toupper(*digits++);
1765 dwToneIndex = IDLE_TONE_0;
1768 dwToneIndex = IDLE_TONE_1;
1771 dwToneIndex = IDLE_TONE_2;
1774 dwToneIndex = IDLE_TONE_3;
1777 dwToneIndex = IDLE_TONE_4;
1780 dwToneIndex = IDLE_TONE_5;
1783 dwToneIndex = IDLE_TONE_6;
1786 dwToneIndex = IDLE_TONE_7;
1789 dwToneIndex = IDLE_TONE_8;
1792 dwToneIndex = IDLE_TONE_9;
1795 dwToneIndex = IDLE_TONE_STAR;
1798 dwToneIndex = IDLE_TONE_POUND;
1801 dwToneIndex = IDLE_TONE_A;
1804 dwToneIndex = IDLE_TONE_B;
1807 dwToneIndex = IDLE_TONE_C;
1810 dwToneIndex = IDLE_TONE_D;
1813 dwToneIndex = IDLE_TONE_NOTONE;
1817 if ('E' <= digit && digit <= ('E' + 11))
1818 dwToneIndex = (digit - 'E') + 13;
1820 dwToneIndex = IDLE_TONE_NOTONE;
1821 Sleep(onTime+offTime);
1826 if (dwToneIndex != IDLE_TONE_NOTONE)
1827 if (!InternalPlayTone(line, dwToneIndex, onTime, offTime, TRUE))
1835 char OpalIxJDevice::ReadDTMF(unsigned)
1838 if (!IoControl(IOCTL_Filter_GetDTMFDigit, lastDTMFDigit, &dwNewDigit))
1841 if (dwNewDigit == 0 || dwNewDigit == lastDTMFDigit)
1844 lastDTMFDigit = dwNewDigit;
1846 static char const dtmf[16] = {
1847 'D','1','2','3','4','5','6','7','8','9','*','0','#','A','B','C'
1849 PTRACE(3, "xJack\tDetected DTMF tone: " << dtmf[dwNewDigit&0xf]);
1851 return dtmf[dwNewDigit&0xf];
1855 BOOL OpalIxJDevice::GetRemoveDTMF(unsigned)
1857 DWORD result = FALSE;
1858 if (!IoControl(IOCTL_Record_GetDisableOnDTMFDetect, 0, &result))
1865 BOOL OpalIxJDevice::SetRemoveDTMF(unsigned, BOOL state)
1867 return IoControl(IOCTL_Record_SetDisableOnDTMFDetect, state);
1871 unsigned OpalIxJDevice::IsToneDetected(unsigned line)
1873 if (line >= GetLineCount())
1876 if (!EnableAudio(line, TRUE))
1882 if (IoControl(IOCTL_Filter_IsToneCadenceValid, 0, &dwReturn) && dwReturn != 0)
1886 if (IoControl(IOCTL_Filter_IsToneCadenceValid, 1, &dwReturn) && dwReturn != 0)
1890 if (IoControl(IOCTL_Filter_IsToneCadenceValid, 2, &dwReturn) && dwReturn != 0)
1894 if (IoControl(IOCTL_Filter_IsToneCadenceValid, 3, &dwReturn) && dwReturn != 0)
1901 BOOL OpalIxJDevice::SetToneFilterParameters(unsigned /*line*/,
1902 CallProgressTones tone,
1903 unsigned lowFrequency,
1904 unsigned highFrequency,
1906 const unsigned * onTimes,
1907 const unsigned * offTimes)
1924 PTRACE(1, "xJack\tCannot set filter for tone: " << tone);
1928 if (numCadences > 0) {
1929 qthDetectToneCadence dtc;
1931 if (numCadences > PARRAYSIZE(dtc.element)) {
1932 PTRACE(1, "xJack\tToo many cadence elements: " << numCadences);
1937 dtc.ulFilter = toneIndex;
1938 dtc.ulNumElements = numCadences;
1939 dtc.type = QTH_DETECT_TONE_TYPE_ADD;
1940 dtc.term = QTH_DETECT_TONE_REPEAT_ALL;
1941 dtc.ulTolerance = 10; // in %
1942 dtc.ulMinDetectLoops = 1;
1943 memset(dtc.element, 0, sizeof(dtc.element));
1944 for (PINDEX i = 0; i < numCadences; i++) {
1945 dtc.element[i].ulOnTime = onTimes[i];
1946 dtc.element[i].ulOffTime = offTimes[i];
1949 PTRACE(2, "xJack\tSetting cadence for tone index " << toneIndex
1950 << ", num=" << numCadences
1951 << ' ' << dtc.element[0].ulOnTime
1952 << '-' << dtc.element[0].ulOffTime);
1955 DWORD dwBytesReturned;
1956 IoControl(IOCTL_Filter_DetectToneCadence, &dtc, sizeof(dtc),
1957 &dwReturn, sizeof(dwReturn), &dwBytesReturned, FALSE);
1960 static struct FilterTableEntry {
1961 unsigned lowFrequency;
1962 unsigned highFrequency;
1963 unsigned predefinedFilterSet; // 0 = custom
1964 short coefficients[19];
1965 } const FilterTable[] = {
1974 { 425, 425, 0, 30850,-32534,-504,0,504,30831,-32669,24303,-22080,24303,30994,-32673, 1905, -1811, 1905,5,129,17,0xff5 },
1975 { 350, 440, 0, 30634,-31533,-680,0,680,30571,-32277,12894,-11945,12894,31367,-32379,23820,-23104,23820,7,159,21,0x0FF5 },
1976 { 400, 450, 0, 30613,-32031,-618,0,618,30577,-32491, 9612, -8935, 9612,31071,-32524,21596,-20667,21596,7,159,21,0x0FF5 },
1979 FilterTableEntry match = { 0, 0, UINT_MAX };
1983 // Look for exact match
1984 for (i = 0; i < PARRAYSIZE(FilterTable); i++) {
1985 if (lowFrequency == FilterTable[i].lowFrequency &&
1986 highFrequency == FilterTable[i].highFrequency) {
1987 match = FilterTable[i];
1992 if (match.predefinedFilterSet == UINT_MAX) {
1993 // If single frequency, make a band out of it, +/- 5%
1994 if (lowFrequency == highFrequency) {
1995 lowFrequency -= lowFrequency/20;
1996 highFrequency += highFrequency/20;
1999 // Try again looking for a band that is just a bit larger than required, no
2000 // more than twice the size required.
2001 for (i = 0; i < PARRAYSIZE(FilterTable); i++) {
2002 if (lowFrequency > FilterTable[i].lowFrequency &&
2003 highFrequency < FilterTable[i].highFrequency &&
2004 2*(highFrequency - lowFrequency) >
2005 (FilterTable[i].highFrequency - FilterTable[i].lowFrequency)) {
2006 match = FilterTable[i];
2012 if (match.predefinedFilterSet == UINT_MAX) {
2013 PTRACE(1, "xJack\tInvalid frequency for fixed filter sets: "
2014 << lowFrequency << '-' << highFrequency);
2021 DWORD predefinedFilterSet;
2022 short coefficients[19];
2025 PINDEX sizeOfFilterSet;
2027 if (match.predefinedFilterSet != 0) {
2028 filterSet.predefinedFilterSet = match.predefinedFilterSet;
2029 sizeOfFilterSet = sizeof(filterSet.dwFilterNum) + sizeof(filterSet.predefinedFilterSet);
2032 memcpy(filterSet.coefficients, match.coefficients, sizeof(filterSet.coefficients));
2033 sizeOfFilterSet = sizeof(filterSet);
2036 filterSet.dwFilterNum = toneIndex;
2038 PTRACE(2, "xJack\tSetting filter for tone index " << toneIndex
2039 << " freq: " << match.lowFrequency << '-' << match.highFrequency);
2042 DWORD dwBytesReturned;
2043 return IoControl(IOCTL_Filter_ProgramFilter, &filterSet, sizeOfFilterSet,
2044 &dwReturn, sizeof(dwReturn), &dwBytesReturned, FALSE);
2048 BOOL OpalIxJDevice::PlayTone(unsigned line, CallProgressTones tone)
2052 return InternalPlayTone(line, IDLE_TONE_DIAL, 0, 0, FALSE);
2054 return InternalPlayTone(line, IDLE_TONE_RING, 0, 0, FALSE);
2056 return InternalPlayTone(line, IDLE_TONE_BUSY, 0, 0, FALSE);
2058 return InternalPlayTone(line, IDLE_TONE_BUSY, 0, 0, FALSE);
2060 return InternalPlayTone(line, IDLE_TONE_NOTONE, 0, 0, FALSE);
2065 BOOL OpalIxJDevice::IsTonePlaying(unsigned)
2067 return PTimer::Tick() < toneSendCompletionTime;
2071 BOOL OpalIxJDevice::StopTone(unsigned)
2073 PTRACE(3, "xJack\tStopping tones");
2075 return IoControl(IOCTL_Idle_StopTone);
2079 BOOL OpalIxJDevice::InternalPlayTone(unsigned line,
2081 DWORD onTime, DWORD offTime,
2086 PTRACE(3, "xJack\tPlaying tone: "
2087 << toneIndex << ' ' << onTime << ' ' << offTime << ' ' << synchronous);
2091 tone.dwToneIndex = toneIndex;
2092 tone.dwToneOnPeriod = onTime;
2093 tone.dwToneOffPeriod = offTime;
2094 tone.dwDuration = tone.dwToneOnPeriod+tone.dwToneOffPeriod;
2095 tone.dwMasterGain = 15;
2098 DWORD dwBytesReturned;
2099 if (!IoControl(IOCTL_Idle_PlayTone,
2100 &tone, sizeof(tone),
2101 &dwReturn, sizeof(dwReturn), &dwBytesReturned) ||
2102 dwBytesReturned != sizeof(dwReturn) ||
2106 toneSendCompletionTime = PTimer::Tick() + (int)tone.dwDuration - 1;
2108 Sleep(tone.dwDuration);
2114 BOOL OpalIxJDevice::SetCountryCode(T35CountryCodes country)
2116 OpalLineInterfaceDevice::SetCountryCode(country);
2118 // if a LineJack, the set the DAA coeffiecients
2122 if (country == UnknownCountry)
2126 T35CountryCodes t35Code;
2129 { UnitedStates, COEFF_US },
2130 { Australia, COEFF_AUSTRALIA },
2131 { Czechoslovakia,COEFF_CZECH },
2132 { France, COEFF_FRANCE },
2133 { Germany, COEFF_GERMANY },
2134 { Italy, COEFF_ITALY },
2135 { Japan, COEFF_JAPAN },
2136 { KoreaRepublic, COEFF_SOUTH_KOREA },
2137 { NewZealand, COEFF_NEW_ZEALAND },
2138 { Norway, COEFF_NORWAY },
2139 { Philippines, COEFF_PHILIPPINES },
2140 { Poland, COEFF_POLAND },
2141 { SouthAfrica, COEFF_SOUTH_AFRICA },
2142 { Sweden, COEFF_SWEDEN },
2143 { UnitedKingdom, COEFF_UK }
2147 for (i = PARRAYSIZE(ixjCountry)-1; i > 0; i--) {
2148 if (ixjCountry[i].t35Code == countryCode)
2152 PTRACE(2, "xJack\tSetting coefficient group for " << GetCountryCodeName(ixjCountry[i].t35Code));
2153 return IoControl(IOCTL_DevCtrl_SetCoefficientGroup, ixjCountry[i].ixjCode);
2157 DWORD OpalIxJDevice::GetSerialNumber()
2159 if (GetOperatingSystem() == IsWindows9x)
2160 return deviceName.AsUnsigned(16);
2163 if (IoControl(IOCTL_Device_GetSerialNumber, 0, &devId))
2170 PStringArray OpalIxJDevice::GetDeviceNames()
2176 const char * DevicePath = "\\\\.\\QTJACKDevice%u";
2178 switch (GetOperatingSystem()) {
2180 DevicePath = "\\\\.\\QTIWDMDevice%u";
2181 // Fall into NT case
2184 for (i = 0; i < 100; i++) {
2186 devpath.sprintf(DevicePath, i);
2187 HANDLE hDriver = CreateFile(devpath,
2192 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
2194 if (hDriver != INVALID_HANDLE_VALUE) {
2195 DWORD devId, bytesReturned;
2196 if (DeviceIoControl(hDriver, IOCTL_Device_GetSerialNumber,
2197 NULL, 0, &devId, sizeof(devId), &bytesReturned, NULL) &&
2198 bytesReturned == sizeof(devId) && devId != 0) {
2199 devpath.sprintf(" (%08X)", devId);
2200 array.SetAt(array.GetSize(), new PCaselessString(devpath));
2202 CloseHandle(hDriver);
2208 PStringArray devices = PSoundChannel::GetDeviceNames(PSoundChannel::Player);
2209 for (i = 0; i < devices.GetSize(); i++) {
2210 PString dev = devices[i];
2211 if (dev.Find("Internet") != P_MAX_INDEX &&
2212 (dev.Find("JACK") != P_MAX_INDEX || dev.Find("PhoneCARD") != P_MAX_INDEX)) {
2213 PINDEX lparen = dev.Find('(');
2214 PINDEX rparen = dev.Find(')', lparen);
2215 array.SetAt(array.GetSize(), new PCaselessString(dev(lparen+1, rparen-1)));
2224 BOOL OpalIxJDevice::IoControl(DWORD dwIoControlCode,
2229 if (outParam == NULL)
2230 outParam = &dwDummy;
2232 DWORD dwBytesReturned = 0;
2233 return IoControl(dwIoControlCode, &inParam, sizeof(DWORD),
2234 outParam, sizeof(DWORD), &dwBytesReturned) &&
2235 dwBytesReturned == sizeof(DWORD);
2239 BOOL OpalIxJDevice::IoControl(DWORD dwIoControlCode,
2241 DWORD nInBufferSize,
2243 DWORD nOutBufferSize,
2244 LPDWORD lpdwBytesReturned,
2245 PWin32Overlapped * overlap)
2247 if (hDriver == INVALID_HANDLE_VALUE)
2250 DWORD newError = ERROR_SUCCESS;
2251 if (!DeviceIoControl(hDriver,
2259 newError = ::GetLastError();
2260 while (newError == ERROR_IO_PENDING) {
2261 if (WaitForSingleObject(overlap->hEvent, 1000) != WAIT_OBJECT_0) {
2262 newError = ERROR_TIMEOUT;
2263 PTRACE(1, "xJack\tRead/Write Timeout!");
2265 else if (GetOverlappedResult(hDriver, overlap, lpdwBytesReturned, FALSE))
2266 newError = ERROR_SUCCESS;
2268 newError = ::GetLastError();
2272 PTRACE_IF(1, newError != ERROR_SUCCESS,
2273 "xJack\tError in DeviceIoControl, device=\"" << deviceName << "\", code=" << newError);
2275 osError = newError|PWIN32ErrorFlag;
2276 return newError == ERROR_SUCCESS;
2281 /////////////////////////////////////////////////////////////////////////////