source: trunk/src/kernel32/hmdevio.cpp@ 7549

Last change on this file since 7549 was 7549, checked in by sandervl, 24 years ago

preliminary changes for new overlapped io framework

File size: 20.6 KB
Line 
1/* $Id: hmdevio.cpp,v 1.19 2001-12-05 14:16:00 sandervl Exp $ */
2
3/*
4 * Win32 Device IOCTL API functions for OS/2
5 *
6 * Copyright 1998 Sander van Leeuwen
7 *
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 */
12#define INCL_DOSPROFILE
13#define INCL_DOSDEVICES
14#define INCL_DOSDEVIOCTL
15#define INCL_GPI
16#define INCL_DOSFILEMGR /* File Manager values */
17#define INCL_DOSERRORS /* DOS Error values */
18#define INCL_DOSPROCESS /* DOS Process values */
19#define INCL_DOSMISC /* DOS Miscellanous values */
20#include <os2wrap.h> //Odin32 OS/2 api wrappers
21#include <string.h>
22#include <stdio.h>
23
24#include <win32type.h>
25#include <win32api.h>
26#include <misc.h>
27#include <win\winioctl.h>
28#include "hmdevio.h"
29#include "cio.h"
30#include "map.h"
31#include "exceptutil.h"
32#include "oslibdos.h"
33
34#define DBG_LOCALLOG DBG_hmdevio
35#include "dbglocal.h"
36
37static BOOL fX86Init = FALSE;
38//SvL: Used in iccio.asm (how can you put these in the .asm data segment without messing things up?)
39ULONG ioentry = 0;
40USHORT gdt = 0;
41char devname[] = "/dev/fastio$";
42
43static BOOL GpdDevIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
44static BOOL MAPMEMIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
45static BOOL FXMEMMAPIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
46static BOOL VPCIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
47
48static WIN32DRV knownDriver[] =
49 {{"\\\\.\\GpdDev", "", TRUE, 666, GpdDevIOCtl},
50 { "\\\\.\\MAPMEM", "PMAP$", FALSE, 0, MAPMEMIOCtl},
51 { "FXMEMMAP.VXD", "PMAP$", FALSE, 0, FXMEMMAPIOCtl},
52#if 1
53 { "\\\\.\\VPCAppSv", "", TRUE, 667, VPCIOCtl}};
54#else
55 };
56#endif
57
58static int nrKnownDrivers = sizeof(knownDriver)/sizeof(WIN32DRV);
59BOOL fVirtualPC = FALSE;
60
61//******************************************************************************
62//******************************************************************************
63void RegisterDevices()
64{
65 HMDeviceDriver *driver;
66 DWORD rc;
67
68 for(int i=0;i<nrKnownDrivers;i++)
69 {
70 driver = new HMDeviceDriver(knownDriver[i].szWin32Name,
71 knownDriver[i].szOS2Name,
72 knownDriver[i].fCreateFile,
73 knownDriver[i].devIOCtl);
74
75 rc = HMDeviceRegister(knownDriver[i].szWin32Name, driver);
76 if (rc != NO_ERROR) /* check for errors */
77 dprintf(("KERNEL32:RegisterDevices: registering %s failed with %u.\n",
78 knownDriver[i].szWin32Name, rc));
79 }
80
81 //check registry for Odin driver plugin dlls
82 HKEY hkDrivers, hkDrvDll;
83
84 rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
85 "System\\CurrentControlSet\\Services",
86 0, KEY_READ, &hkDrivers);
87
88 if(rc == 0) {
89 char szDllName[CCHMAXPATH];
90 char szKeyName[CCHMAXPATH];
91 char szDrvName[CCHMAXPATH];
92 DWORD dwType, dwSize;
93 int iSubKey = 0;
94
95 while(rc == 0) {
96 rc = RegEnumKeyA(hkDrivers, iSubKey++, szKeyName, sizeof(szKeyName));
97 if(rc) break;
98
99 rc = RegOpenKeyExA(hkDrivers, szKeyName,
100 0, KEY_READ, &hkDrvDll);
101 if(rc == 0) {
102 dwSize = sizeof(szDllName);
103 rc = RegQueryValueExA(hkDrvDll,
104 "DllName",
105 NULL,
106 &dwType,
107 (LPBYTE)szDllName,
108 &dwSize);
109
110 RegCloseKey(hkDrvDll);
111 if(rc == 0 && dwType == REG_SZ)
112 {
113 HINSTANCE hDrvDll = LoadLibraryA(szDllName);
114 if(hDrvDll)
115 {
116 sprintf(szDrvName, "\\\\.\\%s", szKeyName);
117 driver = new HMCustomDriver(hDrvDll, szDrvName);
118
119 rc = HMDeviceRegister(szDrvName, driver);
120 if (rc != NO_ERROR) /* check for errors */
121 dprintf(("KERNEL32:RegisterDevices: registering %s failed with %u.\n", szDrvName, rc));
122
123 // @@@PH
124 // there should be an symbolic link:
125 // "\\.\drvname$" -> "drvname$"
126 }
127 }
128 rc = 0;
129 }
130 }
131 RegCloseKey(hkDrivers);
132 }
133
134 return;
135}
136//******************************************************************************
137//******************************************************************************
138BOOL WIN32API RegisterCustomDriver(PFNDRVOPEN pfnDriverOpen, PFNDRVCLOSE pfnDriverClose,
139 PFNDRVIOCTL pfnDriverIOCtl, LPCSTR lpDeviceName)
140{
141 HMDeviceDriver *driver;
142 DWORD rc;
143
144 driver = new HMCustomDriver(pfnDriverOpen, pfnDriverClose, pfnDriverIOCtl, lpDeviceName);
145 if(driver == NULL) {
146 DebugInt3();
147 return FALSE;
148 }
149 rc = HMDeviceRegister((LPSTR)lpDeviceName, driver);
150 if (rc != NO_ERROR) { /* check for errors */
151 dprintf(("KERNEL32:RegisterDevices: registering %s failed with %u.\n", lpDeviceName, rc));
152 return FALSE;
153 }
154 return TRUE;
155}
156//******************************************************************************
157//******************************************************************************
158HMDeviceDriver::HMDeviceDriver(LPCSTR lpDeviceName, LPSTR lpOS2DevName, BOOL fCreate,
159 WINIOCTL pDevIOCtl)
160 : HMDeviceKernelObjectClass(lpDeviceName)
161{
162 this->fCreateFile = fCreateFile;
163 this->szOS2Name = lpOS2DevName;
164 this->devIOCtl = pDevIOCtl;
165}
166//******************************************************************************
167//******************************************************************************
168HMDeviceDriver::HMDeviceDriver(LPCSTR lpDeviceName)
169 : HMDeviceKernelObjectClass(lpDeviceName)
170{
171}
172//******************************************************************************
173//******************************************************************************
174DWORD HMDeviceDriver::CreateFile (LPCSTR lpFileName,
175 PHMHANDLEDATA pHMHandleData,
176 PVOID lpSecurityAttributes,
177 PHMHANDLEDATA pHMHandleDataTemplate)
178{
179 APIRET rc;
180 HFILE hfFileHandle = 0L; /* Handle for file being manipulated */
181 ULONG ulAction = 0; /* Action taken by DosOpen */
182 ULONG sharetype = 0;
183
184 if(pHMHandleData->dwAccess & (GENERIC_READ | GENERIC_WRITE))
185 sharetype |= OPEN_ACCESS_READWRITE;
186 else
187 if(pHMHandleData->dwAccess & GENERIC_WRITE)
188 sharetype |= OPEN_ACCESS_WRITEONLY;
189
190 if(pHMHandleData->dwShare == 0)
191 sharetype |= OPEN_SHARE_DENYREADWRITE;
192 else
193 if(pHMHandleData->dwShare & (FILE_SHARE_READ | FILE_SHARE_WRITE))
194 sharetype |= OPEN_SHARE_DENYNONE;
195 else
196 if(pHMHandleData->dwShare & FILE_SHARE_WRITE)
197 sharetype |= OPEN_SHARE_DENYREAD;
198 else
199 if(pHMHandleData->dwShare & FILE_SHARE_READ)
200 sharetype |= OPEN_SHARE_DENYWRITE;
201
202 if(szOS2Name[0] == 0) {
203 pHMHandleData->hHMHandle = 0;
204 return (NO_ERROR);
205 }
206
207tryopen:
208 rc = DosOpen( szOS2Name, /* File path name */
209 &hfFileHandle, /* File handle */
210 &ulAction, /* Action taken */
211 0,
212 FILE_NORMAL,
213 FILE_OPEN,
214 sharetype,
215 0L); /* No extended attribute */
216
217 if(rc == ERROR_TOO_MANY_OPEN_FILES) {
218 ULONG CurMaxFH;
219 LONG ReqCount = 32;
220
221 rc = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
222 if(rc) {
223 dprintf(("DosSetRelMaxFH returned %d", rc));
224 return error2WinError(rc);
225 }
226 dprintf(("DosOpen failed -> increased nr open files to %d", CurMaxFH));
227 goto tryopen;
228 }
229
230 dprintf(("DosOpen %s returned %d\n", szOS2Name, rc));
231
232 if(rc == NO_ERROR)
233 {
234 pHMHandleData->hHMHandle = hfFileHandle;
235 return (NO_ERROR);
236 }
237 else
238 return(error2WinError(rc));
239}
240//******************************************************************************
241//******************************************************************************
242BOOL HMDeviceDriver::CloseHandle(PHMHANDLEDATA pHMHandleData)
243{
244 DWORD rc = 0;
245
246 if(pHMHandleData->hHMHandle) {
247 rc = DosClose(pHMHandleData->hHMHandle);
248 }
249 pHMHandleData->hHMHandle = 0;
250 return rc;
251}
252//******************************************************************************
253//******************************************************************************
254BOOL HMDeviceDriver::DeviceIoControl(PHMHANDLEDATA pHMHandleData, DWORD dwIoControlCode,
255 LPVOID lpInBuffer, DWORD nInBufferSize,
256 LPVOID lpOutBuffer, DWORD nOutBufferSize,
257 LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
258{
259 return devIOCtl(pHMHandleData->hHMHandle, dwIoControlCode, lpInBuffer, nInBufferSize,
260 lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped);
261}
262//******************************************************************************
263//******************************************************************************
264static BOOL GpdDevIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
265{
266 ULONG port, val = 0;
267
268 if(fX86Init == FALSE) {
269 if(io_init() == 0)
270 fX86Init = TRUE;
271 else return(FALSE);
272 }
273
274 *lpBytesReturned = 0;
275
276 port = ((GENPORT_WRITE_INPUT *)lpInBuffer)->PortNumber;
277
278 switch((dwIoControlCode >> 2) & 0xFFF) {
279 case IOCTL_GPD_READ_PORT_UCHAR:
280 if(nOutBufferSize < sizeof(char))
281 return(FALSE);
282
283 val = c_inb(port);
284 *(char *)lpOutBuffer = val;
285 *lpBytesReturned = sizeof(char);
286 break;
287 case IOCTL_GPD_READ_PORT_USHORT:
288 if(nOutBufferSize < sizeof(USHORT))
289 return(FALSE);
290
291 val = c_inw(port);
292 *(USHORT *)lpOutBuffer = val;
293 *lpBytesReturned = sizeof(USHORT);
294 break;
295 case IOCTL_GPD_READ_PORT_ULONG:
296 if(nOutBufferSize < sizeof(ULONG))
297 return(FALSE);
298
299 val = c_inl(port);
300 *(ULONG *)lpOutBuffer = val;
301 *lpBytesReturned = sizeof(ULONG);
302 break;
303 case IOCTL_GPD_WRITE_PORT_UCHAR:
304 val = ((GENPORT_WRITE_INPUT *)lpInBuffer)->CharData;
305 c_outb(port, val);
306 break;
307 case IOCTL_GPD_WRITE_PORT_USHORT:
308 val = ((GENPORT_WRITE_INPUT *)lpInBuffer)->ShortData;
309 c_outw(port, val);
310 break;
311 case IOCTL_GPD_WRITE_PORT_ULONG:
312 val = ((GENPORT_WRITE_INPUT *)lpInBuffer)->LongData;
313 c_outl(port, val);
314 break;
315 default:
316 dprintf(("GpdDevIOCtl unknown func %X\n", (dwIoControlCode >> 2) & 0xFFF));
317 return(FALSE);
318 }
319
320 return(TRUE);
321}
322//******************************************************************************
323//******************************************************************************
324static BOOL MAPMEMIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
325{
326 PHYSICAL_MEMORY_INFO *meminfo = (PHYSICAL_MEMORY_INFO *)lpInBuffer;
327 struct map_ioctl memmap;
328
329 *lpBytesReturned = 0;
330
331 switch((dwIoControlCode >> 2) & 0xFFF) {
332 case IOCTL_MAPMEM_MAP_USER_PHYSICAL_MEMORY:
333 if(nInBufferSize != sizeof(PHYSICAL_MEMORY_INFO))
334 return(FALSE);
335
336 memmap.a.phys = meminfo->BusAddress.u.LowPart;
337 memmap.size = meminfo->Length;
338 dprintf(("DeviceIoControl map phys address %X length %X\n", memmap.a.phys, memmap.size));
339 if(mpioctl((HFILE)hDevice, IOCTL_MAP, &memmap) == -1) {
340 dprintf(("mpioctl failed!\n"));
341 return(FALSE);
342 }
343
344 dprintf(("DeviceIoControl map virt address = %X\n", memmap.a.user));
345 *(ULONG *)lpOutBuffer = (ULONG)memmap.a.user;
346 break;
347 case IOCTL_MAPMEM_UNMAP_USER_PHYSICAL_MEMORY:
348 dprintf(("Unmap mapping %X\n", *(ULONG *)lpInBuffer));
349 memmap.a.phys = *(ULONG *)lpInBuffer;
350 memmap.size = 0;
351 if(mpioctl((HFILE)hDevice, IOCTL_MAP, &memmap) == -1)
352 return(FALSE);
353 break;
354 default:
355 dprintf(("MAPMEMIOCtl unknown func %X\n", (dwIoControlCode >> 2) & 0xFFF));
356 return(FALSE);
357 }
358
359 return(TRUE);
360}
361//******************************************************************************
362//******************************************************************************
363static BOOL FXMEMMAPIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
364{
365 struct map_ioctl memmap;
366 MAPDEVREQUEST *vxdmem = (MAPDEVREQUEST *)lpInBuffer;
367
368 switch(dwIoControlCode) {
369 case 1:
370 break;
371 case 2:
372 memmap.a.phys = (DWORD)vxdmem->mdr_PhysicalAddress;
373 memmap.size = vxdmem->mdr_SizeInBytes;
374 dprintf(("DeviceIoControl map phys address %X length %X\n", memmap.a.phys, memmap.size));
375 if(mpioctl((HFILE)hDevice, IOCTL_MAP, &memmap) == -1) {
376 dprintf(("mpioctl failed!\n"));
377 return(FALSE);
378 }
379
380 dprintf(("DeviceIoControl map virt address = %X\n", memmap.a.user));
381 vxdmem->mdr_LinearAddress = (PVOID)memmap.a.user;
382 break;
383 default:
384 dprintf(("FXMEMMAPIOCtl unknown func %X\n", (dwIoControlCode >> 2) & 0xFFF));
385 return(FALSE);
386 }
387
388 return(TRUE);
389}
390//******************************************************************************
391//******************************************************************************
392static BOOL VPCIOCtl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
393{
394 APIRET rc;
395
396 dprintf(("VPCIOCtl func %x: %x %d %x %d %x %x", dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped));
397 switch(dwIoControlCode) {
398 case 0x9C402880: //0x00
399 if(nOutBufferSize < 4) {
400 SetLastError(ERROR_BAD_LENGTH);
401 return FALSE;
402 }
403 *(DWORD *)lpOutBuffer = 0x60001;
404 *lpBytesReturned = 4;
405 return TRUE;
406
407 case 0x9C402894: //0x14 (get IDT table)
408 {
409 DWORD *lpBuffer = (DWORD *)lpOutBuffer;
410 if(nOutBufferSize < 0x800) {
411 SetLastError(ERROR_BAD_LENGTH);
412 return FALSE;
413 }
414 memset(lpOutBuffer, 0, nOutBufferSize);
415 for(int i=0;i<32;i++) {
416 lpBuffer[i*2] = 0x0178c4c8;
417 lpBuffer[i*2+1] = 0xfff18F00;
418 }
419 for(i=0x50;i<0x57;i++) {
420 lpBuffer[i*2] = 0x0178c4c8;
421 lpBuffer[i*2+1] = 0xfff18E00;
422 }
423 for(i=0x70;i<0x77;i++) {
424 lpBuffer[i*2] = 0x0178c4c8;
425 lpBuffer[i*2+1] = 0xfff18E00;
426 }
427 lpBuffer[0x4F*2] = 0x0178c4c8;
428 lpBuffer[0x4F*2+1] = 0xfff18E00;
429 lpBuffer[0xEF*2] = 0x0178c4c8;
430 lpBuffer[0xEF*2+1] = 0xfff18E00;
431 *lpBytesReturned = 0xFF*8;
432 return TRUE;
433 }
434 case 0x9C40288C: //0x0C change IDT
435 if(nInBufferSize < 0x22) {
436 SetLastError(ERROR_BAD_LENGTH);
437 return FALSE;
438 }
439 fVirtualPC = TRUE;
440 return TRUE;
441
442 case 0x9C402884: //0x04 ExAllocatePoolWithTag
443 {
444 DWORD *lpBuffer = (DWORD *)lpInBuffer;
445 if(nInBufferSize < 0x08) {
446 SetLastError(ERROR_BAD_LENGTH);
447 return FALSE;
448 }
449 dprintf(("In: %x %x", lpBuffer[0], lpBuffer[1]));
450 return TRUE;
451 }
452
453 case 0x9C402898: //0x18 Remove IDT patch
454 if(nInBufferSize < 0x01) {
455 SetLastError(ERROR_BAD_LENGTH);
456 return FALSE;
457 }
458 fVirtualPC = FALSE;
459 return TRUE;
460 default:
461 dprintf(("VPCIOCtl unknown func %X\n", dwIoControlCode));
462 return FALSE;
463 }
464}
465//******************************************************************************
466//******************************************************************************
467HMCustomDriver::HMCustomDriver(HINSTANCE hInstance, LPCSTR lpDeviceName)
468 : HMDeviceDriver(lpDeviceName), hDrvDll(0)
469{
470 hDrvDll = hInstance ;
471 *(ULONG *)&driverOpen = (ULONG)GetProcAddress(hDrvDll, "DrvOpen");
472 *(ULONG *)&driverClose = (ULONG)GetProcAddress(hDrvDll, "DrvClose");
473 *(ULONG *)&driverIOCtl = (ULONG)GetProcAddress(hDrvDll, "DrvIOCtl");
474}
475//******************************************************************************
476//******************************************************************************
477HMCustomDriver::HMCustomDriver(PFNDRVOPEN pfnDriverOpen, PFNDRVCLOSE pfnDriverClose,
478 PFNDRVIOCTL pfnDriverIOCtl, LPCSTR lpDeviceName)
479 : HMDeviceDriver(lpDeviceName), hDrvDll(0)
480{
481 driverOpen = pfnDriverOpen;
482 driverClose = pfnDriverClose;
483 driverIOCtl = pfnDriverIOCtl;
484}
485//******************************************************************************
486//******************************************************************************
487HMCustomDriver::~HMCustomDriver()
488{
489 if(hDrvDll) FreeLibrary(hDrvDll);
490}
491//******************************************************************************
492//******************************************************************************
493DWORD HMCustomDriver::CreateFile (LPCSTR lpFileName,
494 PHMHANDLEDATA pHMHandleData,
495 PVOID lpSecurityAttributes,
496 PHMHANDLEDATA pHMHandleDataTemplate)
497{
498 pHMHandleData->hHMHandle = driverOpen(pHMHandleData->dwAccess, pHMHandleData->dwShare);
499 if(pHMHandleData->hHMHandle == 0) {
500 return 2;
501 }
502 return 0;
503}
504//******************************************************************************
505//******************************************************************************
506BOOL HMCustomDriver::CloseHandle(PHMHANDLEDATA pHMHandleData)
507{
508 if(pHMHandleData->hHMHandle) {
509 driverClose(pHMHandleData->hHMHandle);
510 }
511 pHMHandleData->hHMHandle = 0;
512 return TRUE;
513}
514//******************************************************************************
515//******************************************************************************
516BOOL HMCustomDriver::DeviceIoControl(PHMHANDLEDATA pHMHandleData, DWORD dwIoControlCode,
517 LPVOID lpInBuffer, DWORD nInBufferSize,
518 LPVOID lpOutBuffer, DWORD nOutBufferSize,
519 LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
520{
521 BOOL ret;
522
523 ret = driverIOCtl(pHMHandleData->hHMHandle, dwIoControlCode, lpInBuffer, nInBufferSize,
524 lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped);
525 dprintf(("DeviceIoControl %x returned %d", dwIoControlCode, ret));
526 return ret;
527}
528//******************************************************************************
529//******************************************************************************
530BOOL WIN32API QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount)
531{
532 QWORD time;
533 APIRET rc;
534
535 rc = DosTmrQueryTime(&time);
536 if(rc) {
537 dprintf(("DosTmrQueryTime returned %d\n", rc));
538 return(FALSE);
539 }
540 lpPerformanceCount->u.LowPart = time.ulLo;
541 lpPerformanceCount->u.HighPart = time.ulHi;
542 return(TRUE);
543}
544//******************************************************************************
545//******************************************************************************
546BOOL WIN32API QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency)
547{
548 APIRET rc;
549 ULONG freq;
550
551 rc = DosTmrQueryFreq(&freq);
552 if(rc) {
553 dprintf(("DosTmrQueryFreq returned %d\n", rc));
554 return(FALSE);
555 }
556 lpFrequency->u.LowPart = freq;
557 lpFrequency->u.HighPart = 0;
558 dprintf2(("QueryPerformanceFrequency returned 0x%X%X\n", lpFrequency->u.HighPart, lpFrequency->u.LowPart));
559 return(TRUE);
560}
561//******************************************************************************
562//******************************************************************************
Note: See TracBrowser for help on using the repository browser.