OSDN Git Service

Add MS7619SE
[uclinux-h8/uClinux-dist.git] / lib / libopenh323 / src / ixjwin32.cxx
1 /*
2  * ixjwin32.cxx
3  *
4  * QuickNet Internet Phone/Line JACK codec interface
5  *
6  * Open H323 Library
7  *
8  * Copyright (c) 1999-2000 Equivalence Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Open H323 Library.
21  *
22  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23  *
24  * Portions of this code were written with the assisance of funding from 
25  * Quicknet Technologies, Inc. http://www.quicknet.net.
26  * 
27  * Contributor(s): ______________________________________.
28  *
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
34  *
35  * Revision 1.116  2005/06/07 07:59:11  csoutheren
36  * Applied patch 1176459 for PocketPC. Thanks to Matthias Weber
37  *
38  * Revision 1.115  2004/04/09 12:56:52  rjongbloed
39  * Fixed automatic loading of winmm.lib if this module included.
40  *
41  * Revision 1.114  2003/04/29 08:32:59  robertj
42  * Added new wink functions for Windows IxJ lid.
43  *
44  * Revision 1.113  2002/11/05 04:33:21  robertj
45  * Changed IsLineDisconnected() to work with POTSLine
46  *
47  * Revision 1.112  2002/11/05 04:27:58  robertj
48  * Imported RingLine() by array from OPAL.
49  *
50  * Revision 1.111  2002/10/30 05:54:17  craigs
51  * Fixed compatibilty problems with G.723.1 6k3 and 5k3
52  *
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.
57  *
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.
62  *
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.
65  *
66  * Revision 1.107  2002/02/08 14:41:49  craigs
67  * Changed codec table to use mediatream #defines. Thanks to Roger Hardiman
68  *
69  * Revision 1.106  2001/12/11 04:27:28  craigs
70  * Added support for 5.3kbps G723.1
71  *
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.
74  *
75  * Revision 1.104  2001/10/11 00:48:08  robertj
76  * Changed so if stopping read/write also stops fax and vice versa.
77  *
78  * Revision 1.103  2001/09/25 01:12:06  robertj
79  * Changed check to v 5.5.141
80  *
81  * Revision 1.102  2001/09/24 12:31:35  robertj
82  * Added backward compatibility with old drivers.
83  *
84  * Revision 1.101  2001/09/10 08:22:16  robertj
85  * Fixed minor problems with error codes.
86  *
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.
90  *
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.
94  *
95  * Revision 1.98  2001/07/06 03:44:35  robertj
96  * Oops, the WAVE output should ALWAYS be unmuted!
97  *
98  * Revision 1.97  2001/07/06 00:45:15  robertj
99  * Fixed accidentlaly unmuting microphone on audio output for LineJACKs.
100  *
101  * Revision 1.96  2001/06/12 00:12:23  craigs
102  * No longer looks for dialtone for end of call
103  *
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.
108  *
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
112  *
113  * Revision 1.93  2001/05/21 06:37:06  craigs
114  * Changed to allow optional wink detection for line disconnect
115  *
116  * Revision 1.92  2001/05/11 04:43:43  robertj
117  * Added variable names for standard PCM-16 media format name.
118  *
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.
122  *
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.
126  *
127  * Revision 1.89  2001/04/18 02:31:58  craigs
128  * Fixed problem with illegal date in caller ID causing assert
129  *
130  * Revision 1.88  2001/04/09 08:46:16  robertj
131  * Added implementations to set mode for removing DTMF from media.
132  *
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.
135  *
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.
139  *
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.
143  *
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.
146  *
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.
150  *
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.
154  *
155  * Revision 1.81  2001/02/21 08:09:15  robertj
156  * Added more tones for characters 'e' through 'o'.
157  *
158  * Revision 1.80  2001/02/16 08:18:17  robertj
159  * Fixed IXJ interface to compensate for driver bug.
160  *
161  * Revision 1.79  2001/02/15 05:15:34  robertj
162  * Compensated for driver failing to return serial number on Win98.
163  *
164  * Revision 1.78  2001/02/07 05:02:58  robertj
165  * Temporary removal of code due to broken driver.
166  *
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.
170  *
171  * Revision 1.76  2001/01/24 05:34:49  robertj
172  * Altered volume control range to be percentage, ie 100 is max volume.
173  *
174  * Revision 1.75  2001/01/11 05:39:44  robertj
175  * Fixed usage of G.723.1 CNG 1 byte frames.
176  *
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.
180  *
181  * Revision 1.73  2000/12/17 23:08:01  robertj
182  * Changed driver close so goes into POTS/PSTN pass through mode.
183  *
184  * Revision 1.72  2000/12/12 07:51:36  robertj
185  * Changed name to include word Internet as in Linux driver.
186  *
187  * Revision 1.71  2000/12/11 01:47:14  robertj
188  * Changed to use built PWLib class for overlapped I/O.
189  *
190  * Revision 1.70  2000/11/30 08:48:36  robertj
191  * Added functions to enable/disable Voice Activity Detection in LID's
192  *
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.
198  *
199  * Revision 1.68  2000/11/28 01:59:53  robertj
200  * Removed usage of deprecated volume setting calls.
201  *
202  * Revision 1.67  2000/11/27 00:12:18  robertj
203  * Added WIN32 version of hook flash detection function.
204  *
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
212  *    simultaneous tones
213  *
214  * Revision 1.65  2000/11/06 06:33:26  robertj
215  * Changed hook state debounce so does not block for 200ms.
216  *
217  * Revision 1.64  2000/11/03 06:25:37  robertj
218  * Added flag to IsLinePresent() to force slow test, guarenteeing correct value.
219  *
220  * Revision 1.63  2000/10/26 12:24:56  robertj
221  * Added configurable G.729 codec usage, based on separate license.
222  *
223  * Revision 1.62  2000/10/19 04:04:04  robertj
224  * Added functions to get xJACK card type and serial number.
225  *
226  * Revision 1.61  2000/10/13 02:21:28  robertj
227  * Changed volume control code to set more mixer values on LineJACK.
228  *
229  * Revision 1.60  2000/09/26 02:17:35  robertj
230  * Fixed MSVC warning
231  *
232  * Revision 1.59  2000/09/26 01:48:36  robertj
233  * Removed now redundent AEC resetting when starting read/write codec.
234  *
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.
237  *
238  * Revision 1.57  2000/09/22 01:35:51  robertj
239  * Added support for handling LID's that only do symmetric codecs.
240  *
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.
243  *
244  * Revision 1.55  2000/09/05 22:07:50  robertj
245  * Removed deprecated IOCTL_Idle_Idle.
246  *
247  * Revision 1.54  2000/09/04 05:45:03  robertj
248  * Added VMWI support and country codes to IXJ driver interface.
249  *
250  * Revision 1.53  2000/09/01 01:25:05  robertj
251  * Fixed incorrect class names and began country code setting of driver.
252  *
253  * Revision 1.52  2000/08/31 13:14:40  craigs
254  * Added functions to LID
255  * More bulletproofing to Linux driver
256  *
257  * Revision 1.51  2000/08/30 22:57:46  robertj
258  * Removed call to CancelIO, does not exist in Win95!
259  *
260  * Revision 1.50  2000/08/21 02:49:14  robertj
261  * Added timeout for driver read/write, should never block for long.
262  *
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.
265  *
266  * Revision 1.48  2000/07/28 06:29:20  robertj
267  * Fixed AEC under Win32 so can be changed from other processes.
268  *
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.
271  *
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.
275  *
276  * Revision 1.45  2000/07/12 10:25:07  robertj
277  * Added PhoneCARD support on Win9x systems.
278  *
279  * Revision 1.44  2000/06/20 12:51:23  robertj
280  * Changed IXJ driver open so does not change selected line to PSTN.
281  *
282  * Revision 1.43  2000/06/20 02:22:41  robertj
283  * Fixed NT version so can still use serial number to open device.
284  *
285  * Revision 1.42  2000/06/19 00:31:30  robertj
286  * Changed NT device name to be a bit more user friendly.
287  *
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.
291  *
292  * Revision 1.40  2000/05/25 02:23:25  robertj
293  * Added calls to get volume settings
294  *
295  * Revision 1.39  2000/05/15 08:38:59  robertj
296  * Changed LineJACK PSTN check so is initiated only if state unknown.
297  *
298  * Revision 1.38  2000/05/02 04:32:27  robertj
299  * Fixed copyright notice comment.
300  *
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.
303  *
304  * Revision 1.36  2000/04/28 07:00:26  robertj
305  * Fixed race condition causing RTP send thread to randomly shut down.
306  *
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.
310  *
311  * Revision 1.34  2000/04/12 23:56:37  robertj
312  * Fixed detection of PCI PhoneJACK on NT.
313  *
314  * Revision 1.33  2000/04/06 20:36:25  robertj
315  * Fixed some LineJACK compatbility problems (eg DTMF detect stopping).
316  *
317  * Revision 1.32  2000/04/05 20:55:41  robertj
318  * Added caller ID send, and fixed receive for multiple fields.
319  *
320  * Revision 1.31  2000/04/05 18:04:12  robertj
321  * Changed caller ID code for better portability.
322  *
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
326  *
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.
332  *
333  * Revision 1.28  2000/03/23 02:48:49  robertj
334  * Added calling tone detection code.
335  *
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.
339  *
340  * Revision 1.26  2000/02/24 00:35:22  robertj
341  * Fixed problem with unresolved SetRemoveDTMF function when not using linux telephony.
342  *
343  * Revision 1.25  2000/02/16 04:04:37  robertj
344  * Fixed bug where IXJ POTS handset never returned off hook.
345  *
346  * Revision 1.24  2000/01/07 08:28:09  robertj
347  * Additions and changes to line interface device base class.
348  *
349  * Revision 1.23  1999/12/30 09:16:41  robertj
350  * Fixed initialisation of driver handle, prevent crash in Close().
351  *
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.
354  *
355  * Revision 1.21  1999/11/29 04:50:11  robertj
356  * Added adaptive threshold calculation to silence detection.
357  *
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.
360  *
361  * Revision 1.19  1999/11/19 09:17:15  robertj
362  * Fixed problems with aycnhronous shut down of logical channels.
363  *
364  * Revision 1.18  1999/11/16 12:43:02  robertj
365  * Dixed missing initialise of AEC variable.
366  *
367  * Revision 1.17  1999/11/16 11:32:06  robertj
368  * Added some calling tones.
369  *
370  * Revision 1.16  1999/11/12 02:25:01  robertj
371  * More NT support.
372  *
373  * Revision 1.15  1999/11/11 23:15:31  robertj
374  * Fixed bug where closed driver was not flagged as closed.
375  *
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.
378  *
379  * Revision 1.13  1999/11/06 05:36:19  robertj
380  * Fixed problem with read/write locking up when stopping codec.
381  *
382  * Revision 1.12  1999/11/06 03:32:27  robertj
383  * Added volume control functions for record/playback.
384  *
385  * Revision 1.11  1999/11/05 12:53:40  robertj
386  * Fixed warnings on notrace version.
387  *
388  * Revision 1.10  1999/11/05 10:51:18  robertj
389  * Fixed problem with new ixj channel doing incorrect double initialise
390  *
391  * Revision 1.9  1999/11/05 08:54:41  robertj
392  * Rewrite of ixj interface code to fix support for arbitrary codecs.
393  *
394  * Revision 1.8  1999/11/02 00:24:29  robertj
395  * Added GetCallerID() function and implemented some LineJACK code.
396  *
397  * Revision 1.7  1999/11/01 01:20:26  robertj
398  * Added flunction to enabled/disable DTM detection
399  *
400  * Revision 1.6  1999/11/01 00:47:14  robertj
401  * Fixed problems with stopping codecs
402  *
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.
405  *
406  * Revision 1.4  1999/10/28 12:21:34  robertj
407  * Added AEC support and speakerphone switching button.
408  *
409  * Revision 1.3  1999/10/27 06:30:31  robertj
410  * Added CancelIO command when closing channel.
411  *
412  * Revision 1.2  1999/10/24 14:51:41  robertj
413  * Removed EnableDetectDTMF() as unix ioctl does not exist.
414  *
415  * Revision 1.1  1999/10/24 12:59:41  robertj
416  * Added platform independent support for Quicknet xJACK cards.
417  *
418  */
419
420 #include <ptlib.h>
421 #include "ixjlid.h"
422
423 #include <QTIoctl.h>
424 #include <ixjDefs.h>
425
426 #ifdef HAS_IXJ
427
428 #ifndef _WIN32_WINCE
429 #pragma comment(lib, "winmm.lib")
430 #endif
431
432 #define NEW_DRIVER_VERSION ((5<<24)|(5<<16)|141)
433
434 #define new PNEW
435
436
437 static enum {
438   IsWindows9x,
439   IsWindowsNT,
440   IsWindows2k
441 } GetOperatingSystem()
442 {
443   static OSVERSIONINFO version;
444   if (version.dwOSVersionInfoSize == 0) {
445     version.dwOSVersionInfoSize = sizeof(version);
446     GetVersionEx(&version);
447   }
448   if (version.dwPlatformId != VER_PLATFORM_WIN32_NT)
449     return IsWindows9x;
450   if (version.dwMajorVersion < 5)
451     return IsWindowsNT;
452   return IsWindows2k;
453 }
454
455 #define IsLineJACK() (dwCardType == 3)
456
457
458 /////////////////////////////////////////////////////////////////////////////
459
460 OpalIxJDevice::OpalIxJDevice()
461 {
462   hDriver = INVALID_HANDLE_VALUE;
463   driverVersion = 0;
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;
468   inRawMode = FALSE;
469   enabledAudioLine = UINT_MAX;
470   exclusiveAudioMode = TRUE;
471   lastDTMFDigit = 0;
472   hReadEvent = hWriteEvent = NULL;
473 }
474
475
476 BOOL OpalIxJDevice::Open(const PString & device)
477 {
478   Close();
479
480   PTRACE(3, "xJack\tOpening IxJ device \"" << device << '"');
481
482   DWORD dwDeviceId = device.AsUnsigned(16);
483
484   PString devicePath;
485   const char * DevicePathPrefix = "\\\\.\\QTJACKDevice";
486
487   switch (GetOperatingSystem()) {
488     case IsWindows2k :
489       DevicePathPrefix = "\\\\.\\QTIWDMDevice";
490       // Flow into NT case
491
492     case IsWindowsNT :
493       if (dwDeviceId < 100) {
494         devicePath = device.Left(device.Find(' '));
495         if (strspn(devicePath, "0123456789") == strlen(devicePath))
496           devicePath = DevicePathPrefix + devicePath;
497       }
498       else {
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(' '));
504             break;
505           }
506         }
507       }
508       timeBeginPeriod(2);
509       break;
510
511     case IsWindows9x :
512       devicePath = "\\\\.\\Qtipj.vxd";
513   }
514
515   hDriver = CreateFile(devicePath,
516                        GENERIC_READ | GENERIC_WRITE,
517                        FILE_SHARE_WRITE,
518                        NULL,
519                        OPEN_EXISTING,
520                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
521                        NULL);
522   if (hDriver == INVALID_HANDLE_VALUE) {
523     osError = ::GetLastError()|PWIN32ErrorFlag;
524     return FALSE;
525   }
526
527   if (GetOperatingSystem() == IsWindows9x) {
528     DWORD dwResult = 0;
529     if (!IoControl(IOCTL_Device_Open, dwDeviceId, &dwResult) || dwResult == 0) {
530       CloseHandle(hDriver);
531       hDriver = INVALID_HANDLE_VALUE;
532       osError = ENOENT;
533       return FALSE;
534     }
535     deviceName = psprintf("%08X", dwDeviceId);
536   }
537   else {
538     dwDeviceId = GetSerialNumber();
539     if (dwDeviceId == 0) {
540       CloseHandle(hDriver);
541       hDriver = INVALID_HANDLE_VALUE;
542       osError = ENOENT;
543       return FALSE;
544     }
545
546     PINDEX prefixLen = strlen(DevicePathPrefix);
547     if (strnicmp(devicePath, DevicePathPrefix, prefixLen) == 0)
548       deviceName = devicePath.Mid(prefixLen);
549     else
550       deviceName = devicePath;
551   }
552
553   dwCardType = dwDeviceId >> 28;
554
555   IoControl(IOCTL_Codec_SetKHz, 8000);
556   IoControl(IOCTL_Idle_SetMasterGain, 15);
557   IoControl(IOCTL_Filter_EnableDTMFDetect);
558   IoControl(IOCTL_Speakerphone_AECOn);
559
560   DWORD ver = 0;
561   IoControl(IOCTL_VxD_GetVersion, 0, &ver);
562   driverVersion = ((ver&0xff)<<24)|((ver&0xff00)<<8)|((ver>>16)&0xffff);
563
564   PTRACE(2, "xJack\tOpened IxJ device \"" << GetName() << "\" version "
565          << ((driverVersion>>24)&0xff  ) << '.'
566          << ((driverVersion>>16)&0xff  ) << '.'
567          << ( driverVersion     &0xffff));
568
569   os_handle = 1;
570
571   return TRUE;
572 }
573
574
575 BOOL OpalIxJDevice::Close()
576 {
577   if (!IsOpen())
578     return FALSE;
579
580   RingLine(0, 0);
581   StopRawCodec(0);
582   SetLineToLineDirect(0, 1, TRUE); // Back to pass through mode
583
584   if (GetOperatingSystem() == IsWindows9x)
585     IoControl(IOCTL_Device_Close);
586   else
587     timeEndPeriod(2);
588
589   deviceName = PString();
590
591   if (hReadEvent != NULL) {
592     CloseHandle(hReadEvent);
593     hReadEvent = NULL;
594   }
595   if (hWriteEvent != NULL) {
596     CloseHandle(hWriteEvent);
597     hWriteEvent = NULL;
598   }
599
600   BOOL ok = CloseHandle(hDriver);
601   hDriver = INVALID_HANDLE_VALUE;
602   os_handle = -1;
603   return ok;
604 }
605
606
607 PString OpalIxJDevice::GetName() const
608 {
609   PStringStream name;
610
611   name << "Internet ";
612
613   switch (dwCardType) {
614     case 0 :
615     case 1 :
616       name << "PhoneJACK";
617       break;
618     case 3 :
619       name << "LineJACK";
620       break;
621     case 4 :
622       name << "PhoneJACK-Lite";
623       break;
624     case 5 :
625       name << "PhoneJACK-PCI";
626       break;
627     case 6 :
628       name << "PhoneCARD";
629       break;
630     default :
631       name << "xJACK";
632   }
633
634   name << " (" << deviceName << ')';
635
636   return name;
637 }
638
639
640 unsigned OpalIxJDevice::GetLineCount()
641 {
642   return IsLineJACK() ? NumLines : 1;
643 }
644
645
646 BOOL OpalIxJDevice::IsLinePresent(unsigned line, BOOL force)
647 {
648   if (line >= GetLineCount())
649     return FALSE;
650
651   if (line != PSTNLine)
652     return FALSE;
653
654   int oldSlicState = -1;
655
656   DWORD dwResult = 0;
657   do {
658     if (!IoControl(IOCTL_DevCtrl_GetLineTestResult, 0, &dwResult))
659       return FALSE;
660     if (dwResult == 0xffffffff || force) {
661       if (dwResult == LINE_TEST_OK) {
662         IoControl(IOCTL_DevCtrl_GetPotsToSlic, 0, &dwResult);
663         oldSlicState = dwResult;
664       }
665       IoControl(IOCTL_DevCtrl_LineTest);
666       dwResult = LINE_TEST_TESTING;
667       force = FALSE;
668     }
669   } while (dwResult == LINE_TEST_TESTING);
670
671   if (oldSlicState >= 0)
672     IoControl(IOCTL_DevCtrl_SetPotsToSlic, oldSlicState);
673
674   return dwResult == LINE_TEST_OK;
675 }
676
677
678 BOOL OpalIxJDevice::IsLineOffHook(unsigned line)
679 {
680   if (line >= GetLineCount())
681     return FALSE;
682
683   DWORD dwResult = 0;
684
685   if (line == PSTNLine) {
686     if (!IoControl(IOCTL_DevCtrl_GetLineOnHook, 0, &dwResult))
687       return FALSE;
688     return dwResult == 0;
689   }
690
691   if (!IoControl(IsLineJACK() && IsLinePresent(PSTNLine)
692                               ? IOCTL_DevCtrl_GetLinePhoneOnHook
693                               : IOCTL_DevCtrl_GetOnHook, 0, &dwResult))
694     return FALSE;
695
696   BOOL newHookState = dwResult == 0;
697   if (lastHookState != newHookState) {
698     lastHookState = newHookState;
699     hookTimeout = 250;
700   }
701   else {
702     if (!hookTimeout.IsRunning())
703       currentHookState = lastHookState;
704   }
705
706   return currentHookState;
707 }
708
709
710 BOOL OpalIxJDevice::SetLineOffHook(unsigned line, BOOL newState)
711 {
712   if (line >= GetLineCount())
713     return FALSE;
714
715   if (line != PSTNLine)
716     return FALSE;
717
718   return IoControl(IOCTL_DevCtrl_LineSetOnHook, !newState);
719 }
720
721
722 BOOL OpalIxJDevice::HasHookFlash(unsigned line)
723 {
724   if (line != POTSLine)
725     return FALSE;
726
727   DWORD dwResult;
728   if (!IoControl(IOCTL_DevCtrl_GetFlashState, 0, &dwResult))
729     return FALSE;
730
731   if (lastFlashState == dwResult)
732     return FALSE;
733
734   lastFlashState = dwResult;
735   return dwResult != 0;
736 }
737
738
739 BOOL OpalIxJDevice::IsLineRinging(unsigned line, DWORD * /*cadence*/)
740 {
741   if (line >= GetLineCount())
742     return FALSE;
743
744   if (line != PSTNLine)
745     return FALSE;
746
747   if (ringTimeout.IsRunning())
748     return TRUE;
749
750   DWORD dwResult = 0;
751   if (!IoControl(IOCTL_DevCtrl_LineGetRinging, 0, &dwResult) || dwResult == 0)
752     return FALSE;
753
754   ringTimeout = 2500;
755   return TRUE;
756 }
757
758
759 BOOL OpalIxJDevice::RingLine(unsigned line, DWORD cadence)
760 {
761   if (line >= GetLineCount())
762     return FALSE;
763
764   if (line != POTSLine)
765     return FALSE;
766
767   if (cadence == TRUE) {
768     switch (countryCode) {
769       case Australia :
770         static unsigned AusRing[] = { 200, 400, 200, 2000 };
771         return RingLine(line, PARRAYSIZE(AusRing), AusRing);
772     }
773
774     // United States ring pattern
775     cadence = 0x00f;
776   }
777
778   IoControl(IOCTL_DevCtrl_SetPotsToSlic, 1);
779   return IoControl(IOCTL_DevCtrl_SetRingPattern, cadence);
780 }
781
782
783 BOOL OpalIxJDevice::RingLine(unsigned line, PINDEX nCadence, unsigned * pattern)
784 {
785   if (line >= GetLineCount())
786     return FALSE;
787
788   if (line != POTSLine)
789     return FALSE;
790
791   IoControl(IOCTL_DevCtrl_SetPotsToSlic, 1);
792
793
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
798   PINDEX i;
799   for (i = 2; i < nCadence; i++)
800     cadenceArray[i] = pattern[i-2];
801   for (; i < PARRAYSIZE(cadenceArray); i++)
802     cadenceArray[i] = 0;
803
804   return IoControl(IOCTL_DevCtrl_SetRingCadence,
805                    cadenceArray, sizeof(cadenceArray),
806                    &dwReturn, sizeof(dwReturn), &dwSize);
807 }
808
809
810 BOOL OpalIxJDevice::IsLineDisconnected(unsigned line, BOOL checkForWink)
811 {
812   if (line >= GetLineCount())
813     return FALSE;
814
815   if (line != PSTNLine)
816     return !IsLineOffHook(line);
817
818   if (checkForWink) {
819     DWORD dwResult = 0;
820     if (IoControl(IOCTL_DevCtrl_GetLineCallerOnHook, 0, &dwResult) && dwResult != 0) {
821       PTRACE(3, "xJack\tDetected wink, line disconnected.");
822       return TRUE;
823     }
824   }
825
826 /*  if ((IsToneDetected(line) & (DialTone|BusyTone)) != 0) { */
827   if ((IsToneDetected(line) & BusyTone) != 0) {
828     PTRACE(3, "xJack\tDetected dial or busy tone, line disconnected.");
829     return TRUE;
830   }
831
832   return FALSE;
833 }
834
835
836 BOOL OpalIxJDevice::SetLineToLineDirect(unsigned line1, unsigned line2, BOOL connect)
837 {
838   if (line1 >= GetLineCount() || line2 >= GetLineCount())
839     return FALSE;
840
841   if (line1 == line2)
842     return FALSE;
843
844   DWORD dwResult = 0;
845   return IoControl(IOCTL_DevCtrl_SetPotsToSlic, connect ? 0 : 1, &dwResult) && dwResult != 0;
846 }
847
848
849 BOOL OpalIxJDevice::IsLineToLineDirect(unsigned line1, unsigned line2)
850 {
851   if (line1 >= GetLineCount() || line2 >= GetLineCount())
852     return FALSE;
853
854   if (line1 == line2)
855     return FALSE;
856
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))
860     return FALSE;
861
862   DWORD dwResult = 1;
863   if (!IoControl(IOCTL_DevCtrl_GetPotsToSlic, 0, &dwResult))
864     return FALSE;
865
866   return dwResult == 0;
867 }
868
869
870
871 static const struct {
872   const char * mediaFormat;
873   unsigned dspBitMask:3; // bit0=8020,bit1=8021,bit2=8022
874   unsigned isG729:1;
875   unsigned isG7231:1;
876   unsigned vad:1;
877   PINDEX frameSize;
878   DWORD recordMode;
879   DWORD recordRate;
880   DWORD playbackMode;
881   DWORD playbackRate;
882 } CodecInfo[] = {
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    },
889
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 },
893
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 },
896
897 };
898
899
900 OpalMediaFormat::List OpalIxJDevice::GetMediaFormats() const
901 {
902   OpalMediaFormat::List codecs;
903
904   OpalIxJDevice * unconstThis = (OpalIxJDevice *)this;
905
906   DWORD dwIdCode = 0;
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) {
913         DWORD hasG729 = 1;
914         add = unconstThis->IoControl(IOCTL_Device_GetG729Enable, 0, &hasG729) && hasG729;
915       }
916       if (add)
917         codecs.Append(new OpalMediaFormat(CodecInfo[codec].mediaFormat));
918     }
919   }
920
921   return codecs;
922 }
923
924
925 static PINDEX FindCodec(const OpalMediaFormat & mediaFormat)
926 {
927   for (PINDEX codecType = 0; codecType < PARRAYSIZE(CodecInfo); codecType++) {
928     if (mediaFormat == CodecInfo[codecType].mediaFormat)
929       return codecType;
930   }
931
932   return P_MAX_INDEX;
933 }
934
935
936 BOOL OpalIxJDevice::SetReadFormat(unsigned line, const OpalMediaFormat & mediaFormat)
937 {
938   StopReadCodec(line);
939
940   PWaitAndSignal mutex(readMutex);
941
942   IoControl(IOCTL_Record_Stop);
943
944   readCodecType = FindCodec(mediaFormat);
945   if (readCodecType == P_MAX_INDEX) {
946     PTRACE(1, "xJack\tUnsupported read codec requested: " << mediaFormat);
947     return FALSE;
948   }
949
950   if (!writeStopped && readCodecType != writeCodecType) {
951     PTRACE(1, "xJack\tAsymmetric codecs requested: "
952               "read=" << CodecInfo[readCodecType].mediaFormat
953            << " write=" << CodecInfo[writeCodecType].mediaFormat);
954     return FALSE;
955   }
956
957   PTRACE(3, "xJack\tSetReadFormat(" << CodecInfo[readCodecType].mediaFormat << ')');
958
959   if (!IoControl(IOCTL_Codec_SetKHz, 8000))
960     return FALSE;
961
962   if (!IoControl(IOCTL_Record_SetBufferChannelLimit, 1))
963     return FALSE;
964
965   DWORD mode;
966   do {
967     if (!IoControl(IOCTL_Record_SetRECMODE, CodecInfo[readCodecType].recordMode))
968       return FALSE;
969     if (!IoControl(IOCTL_Record_GetRECMODE, 0, &mode))
970       return FALSE;
971     PTRACE_IF(3, mode != CodecInfo[readCodecType].recordMode,
972               "xJack\tSetRECMODE failed (" << mode << " -> " <<
973               CodecInfo[readCodecType].recordMode << "), retrying");
974   } while (mode != CodecInfo[readCodecType].recordMode);
975
976   DWORD rate;
977   do {
978     if (!IoControl(IOCTL_Record_SetRate, CodecInfo[readCodecType].recordRate))
979       return FALSE;
980     if (!IoControl(IOCTL_Record_GetRate, 0, &rate))
981       return FALSE;
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);
986
987   readFrameSize = CodecInfo[readCodecType].frameSize;
988   if (readFrameSize == 0) {
989     DWORD frameWords;
990     if (IoControl(IOCTL_Record_GetFrameSize, 0, &frameWords))
991       readFrameSize = frameWords*2;
992     else {
993       PTRACE(1, "xJack\tCould not get record frame size.");
994       return FALSE;
995     }
996   }
997
998   SetVAD(line, CodecInfo[readCodecType].vad);
999
1000   if (!IoControl(driverVersion >= NEW_DRIVER_VERSION ? IOCTL_Record_Start
1001                                                      : IOCTL_Record_Start_Old))
1002     return FALSE;
1003
1004   readStopped = FALSE;
1005
1006   return TRUE;
1007 }
1008
1009
1010 BOOL OpalIxJDevice::SetWriteFormat(unsigned line, const OpalMediaFormat & mediaFormat)
1011 {
1012   StopWriteCodec(line);
1013
1014   PWaitAndSignal mutex(writeMutex);
1015
1016   IoControl(IOCTL_Playback_Stop);
1017
1018   writeCodecType = FindCodec(mediaFormat);
1019   if (writeCodecType == P_MAX_INDEX) {
1020     PTRACE(1, "xJack\tUnsupported write codec requested: " << mediaFormat);
1021     return FALSE;
1022   }
1023
1024   if (!readStopped && writeCodecType != readCodecType) {
1025     PTRACE(1, "xJack\tAsymmetric codecs requested: "
1026               "read=" << CodecInfo[readCodecType].mediaFormat
1027            << " write=" << CodecInfo[writeCodecType].mediaFormat);
1028     return FALSE;
1029   }
1030
1031   PTRACE(3, "xJack\tSetWriteFormat(" << CodecInfo[writeCodecType].mediaFormat << ')');
1032
1033   if (!IoControl(IOCTL_Codec_SetKHz, 8000))
1034     return FALSE;
1035
1036   if (!IoControl(IOCTL_Playback_SetBufferChannelLimit, 1))
1037     return FALSE;
1038
1039   DWORD mode;
1040   do {
1041     if (!IoControl(IOCTL_Playback_SetPLAYMODE, CodecInfo[writeCodecType].playbackMode))
1042       return FALSE;
1043     if (!IoControl(IOCTL_Playback_GetPLAYMODE, 0, &mode))
1044       return FALSE;
1045     PTRACE_IF(2, mode != CodecInfo[writeCodecType].playbackMode,
1046               "xJack\tSetPLAYMODE failed (" << mode << " -> " <<
1047               CodecInfo[writeCodecType].playbackMode << "), retrying");
1048   } while (mode != CodecInfo[writeCodecType].playbackMode);
1049
1050   DWORD rate;
1051   do {
1052     if (!IoControl(IOCTL_Playback_SetRate, CodecInfo[writeCodecType].playbackRate))
1053       return FALSE;
1054     if (!IoControl(IOCTL_Playback_GetRate, 0, &rate))
1055       return FALSE;
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);
1060
1061   writeFrameSize = CodecInfo[writeCodecType].frameSize;
1062   if (writeFrameSize == 0) {
1063     DWORD frameWords;
1064     if (IoControl(IOCTL_Playback_GetFrameSize, 0, &frameWords))
1065       writeFrameSize = frameWords*2;
1066     else {
1067       PTRACE(1, "xJack\tCould not get playback frame size.");
1068       return FALSE;
1069     }
1070   }
1071
1072   SetVAD(line, CodecInfo[writeCodecType].vad);
1073
1074   if (!IoControl(driverVersion >= NEW_DRIVER_VERSION ? IOCTL_Playback_Start
1075                                                      : IOCTL_Playback_Start_Old))
1076     return FALSE;
1077
1078   writeStopped = FALSE;
1079   return TRUE;
1080 }
1081
1082
1083 OpalMediaFormat OpalIxJDevice::GetReadFormat(unsigned)
1084 {
1085   if (readCodecType == P_MAX_INDEX)
1086     return "";
1087   return CodecInfo[readCodecType].mediaFormat;
1088 }
1089
1090
1091 OpalMediaFormat OpalIxJDevice::GetWriteFormat(unsigned)
1092 {
1093   if (writeCodecType == P_MAX_INDEX)
1094     return "";
1095   return CodecInfo[writeCodecType].mediaFormat;
1096 }
1097
1098
1099 BOOL OpalIxJDevice::SetRawCodec(unsigned)
1100 {
1101   if (inRawMode)
1102     return FALSE;
1103
1104   PTRACE(3, "xJack\tSetRawCodec()");
1105
1106   // Default to 30ms frames of 16 bit PCM data
1107   readFrameSize = 480;
1108   writeFrameSize = 480;
1109
1110   if (hReadEvent == NULL)
1111     hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1112   if (hWriteEvent == NULL)
1113     hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1114
1115   HANDLE hEventArr[2];
1116
1117   if (GetOperatingSystem() == IsWindows9x) {
1118     CHAR K32Path[MAX_PATH];
1119     HINSTANCE hK32;
1120     HANDLE (WINAPI *OpenVxDHandle)(HANDLE);
1121
1122     GetSystemDirectory(K32Path, MAX_PATH);
1123     strcat(K32Path, "\\kernel32.dll");
1124     hK32 = LoadLibrary(K32Path);
1125
1126     OpenVxDHandle = (HANDLE(WINAPI *)(HANDLE))GetProcAddress(hK32, "OpenVxDHandle");
1127     hEventArr[0] = OpenVxDHandle(hReadEvent);
1128     hEventArr[1] = OpenVxDHandle(hWriteEvent);
1129     FreeLibrary(hK32);
1130   }
1131   else
1132   {
1133     hEventArr[0] = hReadEvent;
1134     hEventArr[1] = hWriteEvent;
1135   }
1136
1137   readMutex.Wait();
1138   writeMutex.Wait();
1139
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;
1146
1147   readMutex.Signal();
1148   writeMutex.Signal();
1149
1150   return inRawMode;
1151 }
1152
1153
1154 BOOL OpalIxJDevice::StopReadCodec(unsigned line)
1155 {
1156   if (inRawMode)
1157     return StopRawCodec(line);
1158
1159   PTRACE(3, "xJack\tStopping read codec");
1160
1161   readMutex.Wait();
1162   if (!readStopped) {
1163     readStopped = TRUE;
1164     IoControl(IOCTL_Record_Stop);
1165   }
1166   readMutex.Signal();
1167
1168   return OpalLineInterfaceDevice::StopReadCodec(line);
1169 }
1170
1171
1172 BOOL OpalIxJDevice::StopWriteCodec(unsigned line)
1173 {
1174   if (inRawMode)
1175     return StopRawCodec(line);
1176
1177   PTRACE(3, "xJack\tStopping write codec");
1178
1179   writeMutex.Wait();
1180   if (!writeStopped) {
1181     writeStopped = TRUE;
1182     IoControl(IOCTL_Playback_Stop);
1183   }
1184   writeMutex.Signal();
1185
1186   return OpalLineInterfaceDevice::StopWriteCodec(line);
1187 }
1188
1189
1190 BOOL OpalIxJDevice::StopRawCodec(unsigned line)
1191 {
1192   if (!inRawMode) {
1193     BOOL ok = StopReadCodec(line);
1194     return StopWriteCodec(line) && ok;
1195   }
1196
1197   PTRACE(3, "xJack\tStopping raw codec");
1198
1199   readMutex.Wait();
1200   writeMutex.Wait();
1201   readStopped = TRUE;
1202   writeStopped = TRUE;
1203   BOOL ok = IoControl(IOCTL_Fax_Stop);
1204   readMutex.Signal();
1205   writeMutex.Signal();
1206
1207   inRawMode = FALSE;
1208
1209   OpalLineInterfaceDevice::StopReadCodec(line);
1210   OpalLineInterfaceDevice::StopWriteCodec(line);
1211   return ok;
1212 }
1213
1214
1215 PINDEX OpalIxJDevice::GetReadFrameSize(unsigned)
1216 {
1217   return readFrameSize;
1218 }
1219
1220
1221 BOOL OpalIxJDevice::SetReadFrameSize(unsigned, PINDEX size)
1222 {
1223   if (!inRawMode)
1224     return FALSE;
1225
1226   readFrameSize = size;
1227   return TRUE;
1228 }
1229
1230
1231 PINDEX OpalIxJDevice::GetWriteFrameSize(unsigned)
1232 {
1233   return writeFrameSize;
1234 }
1235
1236
1237 BOOL OpalIxJDevice::SetWriteFrameSize(unsigned, PINDEX size)
1238 {
1239   if (!inRawMode)
1240     return FALSE;
1241
1242   writeFrameSize = size;
1243   return TRUE;
1244 }
1245
1246
1247 BOOL OpalIxJDevice::ReadFrame(unsigned, void * buffer, PINDEX & count)
1248 {
1249   count = 0;
1250
1251   PWaitAndSignal mutex(readMutex);
1252   if (readStopped)
1253     return FALSE;
1254
1255   DWORD dwBytesReturned = 0;
1256   if (inRawMode) {
1257     if (WaitForSingleObjectEx(hReadEvent, 1000, TRUE) != WAIT_OBJECT_0) {
1258       osError = EAGAIN;
1259       PTRACE(1, "xJack\tRead Timeout!");
1260       return FALSE;
1261     }
1262     IoControl(IOCTL_Fax_Read, NULL, 0, buffer, readFrameSize, &dwBytesReturned);
1263     count = (PINDEX)dwBytesReturned;
1264     return TRUE;
1265   }
1266
1267   BOOL reblockG729 = CodecInfo[readCodecType].isG729;
1268   WORD temp_frame_buffer[6];
1269
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))
1275     return FALSE;
1276
1277   if (reblockG729) {
1278     switch (temp_frame_buffer[0]) {
1279       case 1 :
1280         memcpy(buffer, &temp_frame_buffer[1], 10);
1281         count = 10;
1282         break;
1283       case 2 :
1284         if (CodecInfo[readCodecType].vad) {
1285           *(WORD *)buffer = temp_frame_buffer[1];
1286           count = 2;
1287         }
1288         else {
1289           memset(buffer, 0, 10);
1290           count = 10;
1291         }
1292         break;
1293       default : // Must be old driver
1294         memcpy(buffer, temp_frame_buffer, 10);
1295         count = 10;
1296     }
1297   }
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];
1302   }
1303   else
1304     count = (PINDEX)dwBytesReturned;
1305
1306   return TRUE;
1307 }
1308
1309
1310 BOOL OpalIxJDevice::WriteFrame(unsigned, const void * buffer, PINDEX count, PINDEX & written)
1311 {
1312   PWaitAndSignal mutex(writeMutex);
1313   if (writeStopped)
1314     return FALSE;
1315
1316   DWORD dwResult = 0;
1317   DWORD dwBytesReturned = 0;
1318
1319   if (inRawMode) {
1320     for (written = 0; written < count; written += dwResult) {
1321       if (WaitForSingleObjectEx(hWriteEvent, 1000, TRUE) != WAIT_OBJECT_0) {
1322         osError = EAGAIN;
1323         PTRACE(1, "xJack\tWrite Timeout!");
1324         return FALSE;
1325       }
1326       IoControl(IOCTL_Fax_Write, ((BYTE *)buffer)+written, count-written,
1327                 &dwResult, sizeof(dwResult), &dwBytesReturned);
1328     }
1329     return TRUE;
1330   }
1331
1332   WORD temp_frame_buffer[12];
1333   PINDEX bytesToWrite;
1334
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
1339         written = 24;
1340         break;
1341       case 1 : // 5.3kbps rate frame
1342         written = 20;
1343         break;
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;
1348         written = 4;
1349         break;
1350       case 3 : // repeat last CNG frame
1351         // Check for frame erasure command
1352         if (memcmp(buffer, "\xff\xff\xff\xff", 4) == 0)
1353           written = 24;
1354         else {
1355           memset(temp_frame_buffer, 0, sizeof(temp_frame_buffer));
1356           temp_frame_buffer[0] = 3;
1357           buffer = temp_frame_buffer;
1358           written = 1;
1359         }
1360         break;
1361     }
1362     bytesToWrite = 24;
1363   }
1364   else if (CodecInfo[writeCodecType].isG729) {
1365     if (count == 2) {
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);
1371       written = 2;
1372     }
1373     else {
1374       if (memcmp(buffer, "\0\0\0\0\0\0\0\0\0", 10) == 0) {
1375 #if 0
1376         memset(temp_frame_buffer, 0, 12);
1377 #else
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);
1382 #endif
1383       }
1384       else {
1385         temp_frame_buffer[0] = 1;
1386         memcpy(&temp_frame_buffer[1], buffer, 10);
1387       }
1388       written = 10;
1389     }
1390     buffer = temp_frame_buffer;
1391     bytesToWrite = 12;
1392   }
1393   else {
1394     bytesToWrite = writeFrameSize;
1395     written = bytesToWrite;
1396   }
1397
1398   if (count < written) {
1399     osError = EINVAL;
1400     PTRACE(1, "xJack\tWrite of too small a buffer");
1401     return FALSE;
1402   }
1403
1404   PWin32Overlapped overlap;
1405   return IoControl(IOCTL_Device_Write, (void *)buffer, bytesToWrite,
1406                    &dwResult, sizeof(dwResult), &dwBytesReturned, &overlap);
1407 }
1408
1409
1410 unsigned OpalIxJDevice::GetAverageSignalLevel(unsigned, BOOL playback)
1411 {
1412   DWORD dwLevel;
1413   if (!IoControl(playback ? IOCTL_Playback_GetAvgPlaybackLevel
1414                           : IOCTL_Record_GetAvgRecordLevel,
1415                  0, &dwLevel))
1416     return UINT_MAX;
1417
1418   return dwLevel;
1419 }
1420
1421
1422 BOOL OpalIxJDevice::EnableAudio(unsigned line, BOOL enable)
1423 {
1424   if (line >= GetLineCount())
1425     return FALSE;
1426
1427   DWORD dwSource = ANALOG_SOURCE_SPEAKERPHONE;
1428
1429   if (enable) {
1430     if (enabledAudioLine != line) {
1431       if (enabledAudioLine != UINT_MAX && exclusiveAudioMode) {
1432         PTRACE(3, "xJack\tEnableAudio on port when already enabled other port.");
1433         return FALSE;
1434       }
1435       enabledAudioLine = line;
1436     }
1437     dwSource = line == POTSLine ? ANALOG_SOURCE_POTSPHONE : ANALOG_SOURCE_PSTNLINE;
1438   }
1439   else
1440     enabledAudioLine = UINT_MAX;
1441
1442   if (!IsLineJACK())
1443     return IoControl(IOCTL_DevCtrl_SetAnalogSource, dwSource);
1444
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);
1450
1451   if (dwSource != ANALOG_SOURCE_PSTNLINE) {
1452     if (!IoControl(IOCTL_DevCtrl_SetAnalogSource, dwSource))
1453       return FALSE;
1454   }
1455
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);
1462
1463   return TRUE;
1464 }
1465
1466
1467 BOOL OpalIxJDevice::IsAudioEnabled(unsigned line)
1468 {
1469   return enabledAudioLine == line;
1470 }
1471
1472
1473 BOOL OpalIxJDevice::InternalSetVolume(BOOL record, unsigned id, int volume, int mute)
1474 {
1475   MIXER_LINE mixer;
1476   mixer.dwLineID = id;
1477
1478   DWORD dwSize = 0;
1479   if (!IoControl(record ? IOCTL_Mixer_GetRecordLineControls
1480                         : IOCTL_Mixer_GetPlaybackLineControls,
1481                  &mixer, sizeof(mixer), &mixer, sizeof(mixer), &dwSize))
1482     return FALSE;
1483
1484   if (volume >= 0) {
1485     if (volume >= 100)
1486       mixer.dwRightVolume = 65535;
1487     else
1488       mixer.dwRightVolume = volume*65536/100;
1489     mixer.dwLeftVolume = mixer.dwRightVolume;
1490   }
1491   if (mute >= 0)
1492     mixer.dwMute = mute != 0;
1493
1494   DWORD dwReturn;
1495   return IoControl(record ? IOCTL_Mixer_SetRecordLineControls
1496                           : IOCTL_Mixer_SetPlaybackLineControls,
1497                    &mixer, sizeof(mixer), &dwReturn, sizeof(dwReturn), &dwSize);
1498 }
1499
1500
1501 BOOL OpalIxJDevice::SetRecordVolume(unsigned line, unsigned volume)
1502 {
1503   if (IsLineJACK()) {
1504     if (!InternalSetVolume(TRUE,
1505                            line == POTSLine ? RecordPhoneIn : RecordPhoneLineIn,
1506                            volume,
1507                            -1))
1508       return FALSE;
1509   }
1510
1511   return InternalSetVolume(TRUE, RecordMaster, volume, -1);
1512 }
1513
1514
1515 BOOL OpalIxJDevice::SetPlayVolume(unsigned line, unsigned volume)
1516 {
1517   if (IsLineJACK()) {
1518     if (!InternalSetVolume(FALSE,
1519                            line == POTSLine ? PlaybackPhoneOut : PlaybackPhoneLineOut,
1520                            volume,
1521                            -1))
1522       return FALSE;
1523   }
1524
1525   return InternalSetVolume(FALSE, PlaybackMaster, volume, -1);
1526 }
1527
1528
1529 BOOL OpalIxJDevice::GetRecordVolume(unsigned, unsigned & volume)
1530 {
1531   MIXER_LINE mixer;
1532   mixer.dwLineID = 0;
1533
1534   DWORD dwSize = 0;
1535   if (!IoControl(IOCTL_Mixer_GetRecordLineControls,
1536                  &mixer, sizeof(mixer), &mixer, sizeof(mixer), &dwSize))
1537     return FALSE;
1538
1539   if (mixer.dwLeftVolume > 65208) // 99.5%
1540     volume = 100;
1541   else
1542     volume = mixer.dwLeftVolume*100/65536;
1543   return TRUE;
1544 }
1545
1546
1547 BOOL OpalIxJDevice::GetPlayVolume(unsigned, unsigned & volume)
1548 {
1549   MIXER_LINE mixer;
1550   mixer.dwLineID = 0;
1551
1552   DWORD dwSize = 0;
1553   if (!IoControl(IOCTL_Mixer_GetPlaybackLineControls,
1554                  &mixer, sizeof(mixer), &mixer, sizeof(mixer), &dwSize))
1555     return FALSE;
1556
1557   if (mixer.dwLeftVolume > 65208) // 99.5%
1558     volume = 100;
1559   else
1560     volume = mixer.dwLeftVolume*100/65536;
1561   return TRUE;
1562 }
1563
1564
1565 OpalLineInterfaceDevice::AECLevels OpalIxJDevice::GetAEC(unsigned)
1566 {
1567   DWORD level = AECOff;
1568   IoControl(IOCTL_Speakerphone_GetAEC, 0, &level);
1569   return (AECLevels)level;
1570 }
1571
1572
1573 BOOL OpalIxJDevice::SetAEC(unsigned, AECLevels level)
1574 {
1575   return IoControl(IOCTL_Speakerphone_SetAEC, level);
1576 }
1577
1578
1579 unsigned OpalIxJDevice::GetWinkDuration(unsigned)
1580 {
1581   DWORD level = 0;
1582   IoControl(IOCTL_DevCtrl_GetLineWinkDetTime, 0, &level);
1583   return (unsigned)level;
1584 }
1585
1586
1587 BOOL OpalIxJDevice::SetWinkDuration(unsigned, unsigned winkDuration)
1588 {
1589   return IoControl(IOCTL_DevCtrl_SetLineWinkDetTime, winkDuration);
1590 }
1591
1592
1593 BOOL OpalIxJDevice::GetVAD(unsigned)
1594 {
1595   return vadEnabled;
1596 }
1597
1598
1599 BOOL OpalIxJDevice::SetVAD(unsigned, BOOL enable)
1600 {
1601   PTRACE(3, "xJack\tSet VAD " << (enable ? "on" : "off"));
1602   vadEnabled = enable;
1603   return IoControl(enable ? IOCTL_Record_EnableVAD : IOCTL_Record_DisableVAD);
1604 }
1605
1606
1607 BOOL OpalIxJDevice::GetCallerID(unsigned, PString & idString, BOOL full)
1608 {
1609   idString = PString();
1610
1611   BYTE buffer[512];
1612   buffer[0] = 0;
1613   DWORD dwBytesReturned;
1614   if (!IoControl(IOCTL_DevCtrl_LineGetCallerID,
1615                  buffer, sizeof(buffer),
1616                  buffer, sizeof(buffer),
1617                  &dwBytesReturned))
1618     return FALSE;
1619
1620   PTRACE_IF(3, buffer[0] != 0, "xJack\tCaller ID:\n"
1621             << hex << setprecision(2)
1622             << PBYTEArray(buffer, dwBytesReturned)
1623             << dec);
1624
1625   PString name, timeStr;
1626
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);
1631       break;
1632
1633     case 128 : {
1634       PINDEX totalLength = buffer[1];
1635       PINDEX pos = 2;
1636       while (pos < totalLength) {
1637         switch (buffer[pos]) {
1638           case 1 :
1639             timeStr = PString((char *)&buffer[pos+2], buffer[pos+1]);
1640             break;
1641           case 2 :
1642             idString = PString((char *)&buffer[pos+2], buffer[pos+1]);
1643             break;
1644           case 7 :
1645             name = PString((char *)&buffer[pos+2], buffer[pos+1]);
1646             break;
1647         }
1648         pos += buffer[pos+1]+2;
1649       }
1650       break;
1651     }
1652
1653     default :
1654       return FALSE;
1655   }
1656
1657   if (full && !timeStr.IsEmpty()) {
1658     PTime now;
1659     int minute = timeStr(6,7).AsUnsigned() % 60;
1660     int hour   = timeStr(4,5).AsUnsigned() % 24;
1661     int day    = timeStr(2,3).AsUnsigned();
1662     if (day < 1)
1663       day = 1;
1664     else if (day > 31)
1665       day = 31;
1666     int month  = timeStr(0,1).AsUnsigned();
1667     if (month < 1)
1668       month = 1;
1669     else if (month > 12)
1670       month = 12;
1671
1672     PTime theTime(0, minute, hour, day, month, now.GetYear());
1673     idString += '\t' + theTime.AsString(PTime::ShortDateTime) + '\t' + name;
1674   }
1675
1676   return TRUE;
1677 }
1678
1679
1680 BOOL OpalIxJDevice::SetCallerID(unsigned line, const PString & idString)
1681 {
1682   if (line != POTSLine)
1683     return FALSE;
1684
1685   if (idString.IsEmpty())
1686     return TRUE;
1687
1688   PString name, number;
1689   PTime theTime;
1690
1691   PStringArray fields = idString.Tokenise('\t', TRUE);
1692   switch (fields.GetSize()) {
1693     case 3 :
1694       name = fields[2];
1695     case 2 :
1696       theTime = PTime(fields[1]);
1697     case 1 :
1698      number = fields[0];
1699      break;
1700     default :
1701       return FALSE;
1702   }
1703
1704   PINDEX numberLength = number.GetLength();
1705   PINDEX nameLength = name.GetLength();
1706
1707   char buffer[256];
1708   buffer[0] = 1;
1709   buffer[1] = '\x80';
1710   buffer[2] = (char)(14+numberLength+nameLength);
1711   buffer[3] = 1;
1712   buffer[4] = 8;
1713   sprintf(&buffer[5],
1714           "%02u%02u%02u%02u",
1715           theTime.GetMonth(),
1716           theTime.GetDay(),
1717           theTime.GetHour(),
1718           theTime.GetMinute());
1719   buffer[13] = 2;
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);
1725
1726   DWORD dwReturn = 0;
1727   DWORD dwBytesReturned;
1728   return IoControl(IOCTL_FSK_SetMsgData,
1729                    buffer, 17+numberLength+nameLength,
1730                    &dwReturn, sizeof(dwReturn), &dwBytesReturned);
1731 }
1732
1733
1734 BOOL OpalIxJDevice::SendCallerIDOnCallWaiting(unsigned, const PString & /*idString*/)
1735 {
1736   return FALSE;
1737 }
1738
1739
1740 BOOL OpalIxJDevice::SendVisualMessageWaitingIndicator(unsigned line, BOOL isOn)
1741 {
1742   if (IsLineOffHook(line))
1743     return FALSE;
1744
1745   BYTE buffer[] = { 0, 130, 3, 11, 1, 0 };
1746   if (isOn)
1747     buffer[5] = 255;
1748   DWORD dwReturn = 0;
1749   DWORD dwBytesReturned;
1750   return IoControl(IOCTL_FSK_SetMsgData,
1751                    buffer, sizeof(buffer),
1752                    &dwReturn, sizeof(dwReturn), &dwBytesReturned);
1753 }
1754
1755
1756 BOOL OpalIxJDevice::PlayDTMF(unsigned line,
1757                              const char * digits,
1758                              DWORD onTime, DWORD offTime)
1759 {
1760   while (*digits != '\0') {
1761     DWORD dwToneIndex;
1762     int digit = toupper(*digits++);
1763     switch (digit) {
1764       case '0' :
1765         dwToneIndex = IDLE_TONE_0;
1766         break;
1767       case '1' :
1768         dwToneIndex = IDLE_TONE_1;
1769         break;
1770       case '2' :
1771         dwToneIndex = IDLE_TONE_2;
1772         break;
1773       case '3' :
1774         dwToneIndex = IDLE_TONE_3;
1775         break;
1776       case '4' :
1777         dwToneIndex = IDLE_TONE_4;
1778         break;
1779       case '5' :
1780         dwToneIndex = IDLE_TONE_5;
1781         break;
1782       case '6' :
1783         dwToneIndex = IDLE_TONE_6;
1784         break;
1785       case '7' :
1786         dwToneIndex = IDLE_TONE_7;
1787         break;
1788       case '8' :
1789         dwToneIndex = IDLE_TONE_8;
1790         break;
1791       case '9' :
1792         dwToneIndex = IDLE_TONE_9;
1793         break;
1794       case '*' :
1795         dwToneIndex = IDLE_TONE_STAR;
1796         break;
1797       case '#' :
1798         dwToneIndex = IDLE_TONE_POUND;
1799         break;
1800       case 'A' :
1801         dwToneIndex = IDLE_TONE_A;
1802         break;
1803       case 'B' :
1804         dwToneIndex = IDLE_TONE_B;
1805         break;
1806       case 'C' :
1807         dwToneIndex = IDLE_TONE_C;
1808         break;
1809       case 'D' :
1810         dwToneIndex = IDLE_TONE_D;
1811         break;
1812       case ',' :
1813         dwToneIndex = IDLE_TONE_NOTONE;
1814         Sleep(2000);
1815         break;
1816       default :
1817         if ('E' <= digit && digit <= ('E' + 11))
1818           dwToneIndex = (digit - 'E') + 13;
1819         else {
1820           dwToneIndex = IDLE_TONE_NOTONE;
1821           Sleep(onTime+offTime);
1822         }
1823         break;
1824     }
1825
1826     if (dwToneIndex != IDLE_TONE_NOTONE)
1827       if (!InternalPlayTone(line, dwToneIndex, onTime, offTime, TRUE))
1828         return FALSE;
1829   }
1830
1831   return TRUE;
1832 }
1833
1834
1835 char OpalIxJDevice::ReadDTMF(unsigned)
1836 {
1837   DWORD dwNewDigit;
1838   if (!IoControl(IOCTL_Filter_GetDTMFDigit, lastDTMFDigit, &dwNewDigit))
1839     return '\0';
1840
1841   if (dwNewDigit == 0 || dwNewDigit == lastDTMFDigit)
1842     return '\0';
1843
1844   lastDTMFDigit = dwNewDigit;
1845
1846   static char const dtmf[16] = {
1847     'D','1','2','3','4','5','6','7','8','9','*','0','#','A','B','C'
1848   };
1849   PTRACE(3, "xJack\tDetected DTMF tone: " << dtmf[dwNewDigit&0xf]);
1850
1851   return dtmf[dwNewDigit&0xf];
1852 }
1853
1854
1855 BOOL OpalIxJDevice::GetRemoveDTMF(unsigned)
1856 {
1857   DWORD result = FALSE;
1858   if (!IoControl(IOCTL_Record_GetDisableOnDTMFDetect, 0, &result))
1859     return FALSE;
1860
1861   return result != 0;
1862 }
1863
1864
1865 BOOL OpalIxJDevice::SetRemoveDTMF(unsigned, BOOL state)
1866 {
1867   return IoControl(IOCTL_Record_SetDisableOnDTMFDetect, state);
1868 }
1869
1870
1871 unsigned OpalIxJDevice::IsToneDetected(unsigned line)
1872 {
1873   if (line >= GetLineCount())
1874     return NoTone;
1875
1876   if (!EnableAudio(line, TRUE))
1877     return NoTone;
1878
1879   int tones = NoTone;
1880
1881   DWORD dwReturn = 0;
1882   if (IoControl(IOCTL_Filter_IsToneCadenceValid, 0, &dwReturn) && dwReturn != 0) 
1883     tones |= DialTone;
1884
1885   dwReturn = 0;
1886   if (IoControl(IOCTL_Filter_IsToneCadenceValid, 1, &dwReturn) && dwReturn != 0) 
1887     tones |= RingTone;
1888
1889   dwReturn = 0;
1890   if (IoControl(IOCTL_Filter_IsToneCadenceValid, 2, &dwReturn) && dwReturn != 0) 
1891     tones |= BusyTone;
1892
1893   dwReturn = 0;
1894   if (IoControl(IOCTL_Filter_IsToneCadenceValid, 3, &dwReturn) && dwReturn != 0) 
1895     tones |= CNGTone;
1896
1897   return tones;
1898 }
1899
1900
1901 BOOL OpalIxJDevice::SetToneFilterParameters(unsigned /*line*/,
1902                                             CallProgressTones tone,
1903                                             unsigned   lowFrequency,
1904                                             unsigned   highFrequency,
1905                                             PINDEX     numCadences,
1906                                             const unsigned * onTimes,
1907                                             const unsigned * offTimes)
1908 {
1909   DWORD toneIndex;
1910   switch (tone) {
1911     case DialTone :
1912       toneIndex = 0;
1913       break;
1914     case RingTone :
1915       toneIndex = 1;
1916       break;
1917     case BusyTone :
1918       toneIndex = 2;
1919       break;
1920     case CNGTone :
1921       toneIndex = 3;
1922       break;
1923     default :
1924       PTRACE(1, "xJack\tCannot set filter for tone: " << tone);
1925       return FALSE;
1926   }
1927
1928   if (numCadences > 0) {
1929     qthDetectToneCadence dtc;
1930
1931     if (numCadences > PARRAYSIZE(dtc.element)) {
1932       PTRACE(1, "xJack\tToo many cadence elements: " << numCadences);
1933       return FALSE;
1934     }
1935
1936
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];
1947     }
1948
1949     PTRACE(2, "xJack\tSetting cadence for tone index " << toneIndex
1950            << ", num=" << numCadences
1951            << ' ' << dtc.element[0].ulOnTime
1952            << '-' << dtc.element[0].ulOffTime);
1953
1954     DWORD dwReturn = 0;
1955     DWORD dwBytesReturned;
1956     IoControl(IOCTL_Filter_DetectToneCadence, &dtc, sizeof(dtc),
1957               &dwReturn, sizeof(dwReturn), &dwBytesReturned, FALSE);
1958   }
1959
1960   static struct FilterTableEntry {
1961     unsigned lowFrequency;
1962     unsigned highFrequency;
1963     unsigned predefinedFilterSet;  // 0 = custom
1964     short    coefficients[19];
1965   } const FilterTable[] = {
1966     {  300, 640, 4 },
1967     {  300, 500, 5 },
1968     { 1100,1100, 6 },
1969     {  350, 350, 7 },
1970     {  400, 400, 8 },
1971     {  480, 480, 9 },
1972     {  440, 440, 10},
1973     {  620, 620, 11},
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 },
1977   };
1978
1979   FilterTableEntry match = { 0, 0, UINT_MAX };
1980
1981   PINDEX i;
1982
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];
1988       break;
1989     }
1990   }
1991
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;
1997     }
1998
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];
2007         break;
2008       }
2009     }
2010   }
2011
2012   if (match.predefinedFilterSet == UINT_MAX) {
2013     PTRACE(1, "xJack\tInvalid frequency for fixed filter sets: "
2014             << lowFrequency << '-' << highFrequency);
2015     return FALSE;
2016   }
2017
2018   struct {
2019     DWORD dwFilterNum;
2020     union {
2021       DWORD predefinedFilterSet;
2022       short coefficients[19];
2023     };
2024   } filterSet;
2025   PINDEX sizeOfFilterSet;
2026
2027   if (match.predefinedFilterSet != 0) {
2028     filterSet.predefinedFilterSet = match.predefinedFilterSet;
2029     sizeOfFilterSet = sizeof(filterSet.dwFilterNum) + sizeof(filterSet.predefinedFilterSet);
2030   }
2031   else {
2032     memcpy(filterSet.coefficients, match.coefficients, sizeof(filterSet.coefficients));
2033     sizeOfFilterSet = sizeof(filterSet);
2034   }
2035
2036   filterSet.dwFilterNum = toneIndex;
2037
2038   PTRACE(2, "xJack\tSetting filter for tone index " << toneIndex
2039          << " freq: " << match.lowFrequency << '-' << match.highFrequency);
2040
2041   DWORD dwReturn = 0;
2042   DWORD dwBytesReturned;
2043   return IoControl(IOCTL_Filter_ProgramFilter, &filterSet, sizeOfFilterSet,
2044                    &dwReturn, sizeof(dwReturn), &dwBytesReturned, FALSE);
2045 }
2046
2047
2048 BOOL OpalIxJDevice::PlayTone(unsigned line, CallProgressTones tone)
2049 {
2050   switch (tone) {
2051     case DialTone :
2052       return InternalPlayTone(line, IDLE_TONE_DIAL, 0, 0, FALSE);
2053     case RingTone :
2054       return InternalPlayTone(line, IDLE_TONE_RING, 0, 0, FALSE);
2055     case BusyTone :
2056       return InternalPlayTone(line, IDLE_TONE_BUSY, 0, 0, FALSE);
2057     case ClearTone :
2058       return InternalPlayTone(line, IDLE_TONE_BUSY, 0, 0, FALSE);
2059     default :
2060       return InternalPlayTone(line, IDLE_TONE_NOTONE, 0, 0, FALSE);
2061   }
2062 }
2063
2064
2065 BOOL OpalIxJDevice::IsTonePlaying(unsigned)
2066 {
2067   return PTimer::Tick() < toneSendCompletionTime;
2068 }
2069
2070
2071 BOOL OpalIxJDevice::StopTone(unsigned)
2072 {
2073   PTRACE(3, "xJack\tStopping tones");
2074
2075   return IoControl(IOCTL_Idle_StopTone);
2076 }
2077
2078
2079 BOOL OpalIxJDevice::InternalPlayTone(unsigned line,
2080                                      DWORD toneIndex,
2081                                      DWORD onTime, DWORD offTime,
2082                                      BOOL synchronous)
2083 {
2084   StopTone(line);
2085
2086   PTRACE(3, "xJack\tPlaying tone: "
2087          << toneIndex << ' ' << onTime << ' ' << offTime << ' ' << synchronous);
2088
2089   IDLE_TONE tone;
2090
2091   tone.dwToneIndex = toneIndex;
2092   tone.dwToneOnPeriod = onTime;
2093   tone.dwToneOffPeriod = offTime;
2094   tone.dwDuration = tone.dwToneOnPeriod+tone.dwToneOffPeriod;
2095   tone.dwMasterGain = 15;
2096
2097   DWORD dwReturn = 0;
2098   DWORD dwBytesReturned;
2099   if (!IoControl(IOCTL_Idle_PlayTone,
2100                  &tone, sizeof(tone),
2101                  &dwReturn, sizeof(dwReturn), &dwBytesReturned) ||
2102       dwBytesReturned != sizeof(dwReturn) ||
2103       dwReturn == 0)
2104     return FALSE;
2105
2106   toneSendCompletionTime = PTimer::Tick() + (int)tone.dwDuration - 1;
2107   if (synchronous)
2108     Sleep(tone.dwDuration);
2109
2110   return TRUE;
2111 }
2112
2113
2114 BOOL OpalIxJDevice::SetCountryCode(T35CountryCodes country)
2115 {
2116   OpalLineInterfaceDevice::SetCountryCode(country);
2117
2118   // if a LineJack, the set the DAA coeffiecients
2119   if (!IsLineJACK())
2120     return FALSE;
2121
2122   if (country == UnknownCountry)
2123     return TRUE;
2124
2125   static struct {
2126     T35CountryCodes t35Code;
2127     DWORD           ixjCode;
2128   } ixjCountry[] = {
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 }
2144   };
2145
2146   PINDEX i;
2147   for (i = PARRAYSIZE(ixjCountry)-1; i > 0; i--) {
2148     if (ixjCountry[i].t35Code == countryCode)
2149       break;
2150   }
2151
2152   PTRACE(2, "xJack\tSetting coefficient group for " << GetCountryCodeName(ixjCountry[i].t35Code));
2153   return IoControl(IOCTL_DevCtrl_SetCoefficientGroup, ixjCountry[i].ixjCode);
2154 }
2155
2156
2157 DWORD OpalIxJDevice::GetSerialNumber()
2158 {
2159   if (GetOperatingSystem() == IsWindows9x)
2160     return deviceName.AsUnsigned(16);
2161
2162   DWORD devId;
2163   if (IoControl(IOCTL_Device_GetSerialNumber, 0, &devId))
2164     return devId;
2165
2166   return 0;
2167 }
2168
2169
2170 PStringArray OpalIxJDevice::GetDeviceNames()
2171 {
2172   PStringArray array;
2173
2174   PINDEX i;
2175
2176   const char * DevicePath = "\\\\.\\QTJACKDevice%u";
2177
2178   switch (GetOperatingSystem()) {
2179     case IsWindows2k :
2180       DevicePath = "\\\\.\\QTIWDMDevice%u";
2181       // Fall into NT case
2182
2183     case IsWindowsNT :
2184       for (i = 0; i < 100; i++) {
2185         PString devpath;
2186         devpath.sprintf(DevicePath, i);
2187         HANDLE hDriver = CreateFile(devpath,
2188                                     GENERIC_READ,
2189                                     FILE_SHARE_WRITE,
2190                                     NULL,
2191                                     OPEN_EXISTING,
2192                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
2193                                     NULL);
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));
2201           }
2202           CloseHandle(hDriver);
2203         }
2204       }
2205       break;
2206
2207     case IsWindows9x :
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)));
2216         }
2217       }
2218   }
2219
2220   return array;
2221 }
2222
2223
2224 BOOL OpalIxJDevice::IoControl(DWORD dwIoControlCode,
2225                               DWORD inParam,
2226                               DWORD * outParam)
2227 {
2228   DWORD dwDummy;
2229   if (outParam == NULL)
2230     outParam = &dwDummy;
2231
2232   DWORD dwBytesReturned = 0;
2233   return IoControl(dwIoControlCode, &inParam, sizeof(DWORD),
2234                    outParam, sizeof(DWORD), &dwBytesReturned) &&
2235          dwBytesReturned == sizeof(DWORD);
2236 }
2237
2238
2239 BOOL OpalIxJDevice::IoControl(DWORD dwIoControlCode,
2240                                LPVOID lpInBuffer,
2241                                DWORD nInBufferSize,
2242                                LPVOID lpOutBuffer,
2243                                DWORD nOutBufferSize,
2244                                LPDWORD lpdwBytesReturned,
2245                                PWin32Overlapped * overlap)
2246 {
2247   if (hDriver == INVALID_HANDLE_VALUE)
2248     return FALSE;
2249
2250   DWORD newError = ERROR_SUCCESS;
2251   if (!DeviceIoControl(hDriver,
2252                       dwIoControlCode,
2253                       lpInBuffer,
2254                       nInBufferSize,
2255                       lpOutBuffer,
2256                       nOutBufferSize,
2257                       lpdwBytesReturned,
2258                       overlap)) {
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!");
2264       }
2265       else if (GetOverlappedResult(hDriver, overlap, lpdwBytesReturned, FALSE))
2266         newError = ERROR_SUCCESS;
2267       else
2268         newError = ::GetLastError();
2269     }
2270   }
2271
2272   PTRACE_IF(1, newError != ERROR_SUCCESS,
2273             "xJack\tError in DeviceIoControl, device=\"" << deviceName << "\", code=" << newError);
2274
2275   osError = newError|PWIN32ErrorFlag;
2276   return newError == ERROR_SUCCESS;
2277 }
2278 #endif // HAS_IXJ
2279
2280
2281 /////////////////////////////////////////////////////////////////////////////