📄 btmodem.cxx
字号:
CloseHandle (hModemComPort);
gfServiceState = SERVICE_STATE_OFF;
return 0;
}
if (! SetCommState(hModemComPort, &dcb)) {
RETAILMSG(1, (L"Bluetooth modem gateway: Initialization failed: could not configure state of modem port (error %d)" CRLF, GetLastError ()));
CloseHandle (hModemComPort);
gfServiceState = SERVICE_STATE_OFF;
return 0;
}
// Create and open Bluetooth device
HANDLE hBthDevice = CreatePort (port, channel, mtu, fAuthenticate, fEncrypt);
if (! hBthDevice) {
CloseHandle (hModemComPort);
gfServiceState = SERVICE_STATE_OFF;
return 0;
}
HANDLE hBthComPort = CreateFile (szBthPortName,
GENERIC_READ | GENERIC_WRITE,
0, // comm devices must be opened w/exclusive-access
NULL, // no security attrs
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_ATTRIBUTE_NORMAL, // overlapped I/O
NULL // hTemplate must be NULL for comm devices
);
if (hBthComPort == INVALID_HANDLE_VALUE) {
RETAILMSG(1, (L"Bluetooth modem gateway: Initialization failed: could not open Bluetooth device port (error %d)" CRLF, GetLastError ()));
DeactivateDevice (hBthDevice);
CloseHandle (hModemComPort);
gfServiceState = SERVICE_STATE_OFF;
return 0;
}
commTimeouts.ReadTotalTimeoutMultiplier = 1;
commTimeouts.ReadIntervalTimeout = 50;
commTimeouts.ReadTotalTimeoutConstant = 50;
commTimeouts.WriteTotalTimeoutMultiplier = 5;
commTimeouts.WriteTotalTimeoutConstant = 500;
if (! SetCommTimeouts (hBthComPort, &commTimeouts)) {
RETAILMSG(1, (L"Bluetooth modem gateway: Initialization failed: could not configure timeouts on bluetooth port (error %d)" CRLF, GetLastError ()));
CloseHandle (hBthComPort);
DeactivateDevice (hBthDevice);
CloseHandle (hModemComPort);
gfServiceState = SERVICE_STATE_OFF;
return 0;
}
unsigned long ulSdpRecord = RegisterSDP (hBthComPort);
if (! ulSdpRecord) {
RETAILMSG(1, (L"Bluetooth modem gateway: Initialization failed: could not register SDP" CRLF));
CloseHandle (hBthComPort);
DeactivateDevice (hBthDevice);
CloseHandle (hModemComPort);
gfServiceState = SERVICE_STATE_OFF;
return 0;
}
// Go on listening...
void *ahA[3];
void *ahB[3];
ahA[0] = hBthComPort;
ahA[1] = hModemComPort;
ahA[2] = BTH_BLUETOOTH_READER;
ahB[0] = hModemComPort;
ahB[1] = hBthComPort;
ahB[2] = BTH_MODEM_READER;
HANDLE hThreadA = CreateThread (NULL, 0, ComCopyThread, ahA, 0, NULL);
HANDLE hThreadB = CreateThread (NULL, 0, ComCopyThread, ahB, 0, NULL);
if (hThreadA && hThreadB) {
gfServiceState = SERVICE_STATE_ON;
BT_ADDR bt;
if (BthReadLocalAddr (&bt) == ERROR_SUCCESS)
RETAILMSG(1, (L"Bluetooth modem gateway: Initialization complete. Local address %04x%08x" CRLF, GET_NAP(bt), GET_SAP(bt)));
else
RETAILMSG(1, (L"Bluetooth modem gateway: Initialization complete. No bluetooth adapter detected." CRLF));
WaitForSingleObject (ghServiceExitEvent, INFINITE);
} else
RETAILMSG(1, (L"Bluetooth modem gateway: could not create worker threads." CRLF));
gfServiceState = SERVICE_STATE_SHUTTING_DOWN;
RETAILMSG(1, (L"Bluetooth modem gateway: closing down." CRLF));
SetEvent (ghServiceExitEvent);
CloseHandle (hBthComPort);
HangUpModem (hModemComPort);
CloseHandle (hModemComPort);
DeRegisterSDP (ulSdpRecord);
DeactivateDevice (hBthDevice);
if (hThreadA) {
if (WAIT_TIMEOUT != WaitForSingleObject (hThreadA, BTH_MODEM_TIMEOUT))
TerminateThread (hThreadA, 0);
CloseHandle (hThreadA);
}
if (hThreadB) {
if (WAIT_TIMEOUT != WaitForSingleObject (hThreadB, BTH_MODEM_TIMEOUT))
TerminateThread (hThreadB, 0);
CloseHandle (hThreadB);
}
gfServiceState = SERVICE_STATE_OFF;
RETAILMSG(1, (L"Bluetooth modem gateway: down." CRLF));
return 0;
}
// @func PVOID | HLO_Init | Service initialization routine
// @parm DWORD | dwData | Info passed to RegisterDevice
// @rdesc Returns a DWORD which will be passed to Open & Deinit or NULL if
// unable to initialize the device.
//
// This is called only once. Do the startup initialization in a thread
// spawned by this function, but DO NOT BLOCK HERE!
//
extern "C" DWORD BTM_Init (DWORD dwData)
{
ghServiceExitEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
if (! ghServiceExitEvent)
return 0;
ghServiceThread = CreateThread (NULL, 0, ComServiceThread, NULL, 0, NULL);
if (! ghServiceThread) {
CloseHandle (ghServiceExitEvent);
return 0;
}
return 1;
}
// @func PVOID | HLO_Deinit | Device deinitialization routine
// @parm DWORD | dwData | value returned from HLO_Init call
// @rdesc Returns TRUE for success, FALSE for failure.
//
// The library WILL BE UNLOADED after this. Block here
// until the state is completely clear and all the
// threads are gone.
//
extern "C" BOOL BTM_Deinit(DWORD dwData)
{
if (ghServiceExitEvent) {
SetEvent (ghServiceExitEvent);
CloseHandle (ghServiceExitEvent);
ghServiceExitEvent = NULL;
}
if (ghServiceThread) {
if (WAIT_OBJECT_0 != WaitForSingleObject (ghServiceThread, BTH_MODEM_TIMEOUT))
TerminateThread (ghServiceThread, 0);
CloseHandle (ghServiceThread);
ghServiceThread = NULL;
}
return TRUE;
}
// @func PVOID | HLO_Open | Device open routine
// @parm DWORD | dwData | value returned from HLO_Init call
// @parm DWORD | dwAccess | requested access (combination of GENERIC_READ
// and GENERIC_WRITE)
// @parm DWORD | dwShareMode | requested share mode (combination of
// FILE_SHARE_READ and FILE_SHARE_WRITE)
// @rdesc Returns a DWORD which will be passed to Read, Write, etc or NULL if
// unable to open device.
//
// We don't do anything here, but in a real service this is a place to create
// client process state. HCO_Close will be called with this handle when the process
// exits or terminates, so the clean-up is easy.
//
extern "C" DWORD BTM_Open (DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
return TRUE;
}
// @func BOOL | HLO_Close | Device close routine
// @parm DWORD | dwOpenData | value returned from HLO_Open call
// @rdesc Returns TRUE for success, FALSE for failure
//
// Clean-up the client process state here.
//
extern "C" BOOL BTM_Close (DWORD dwData)
{
return TRUE;
}
// @func DWORD | HLO_Write | Device write routine
// @parm DWORD | dwOpenData | value returned from HLO_Open call
// @parm LPCVOID | pBuf | buffer containing data
// @parm DWORD | len | maximum length to write [IN BYTES, NOT WORDS!!!]
// @rdesc Returns -1 for error, otherwise the number of bytes written. The
// length returned is guaranteed to be the length requested unless an
// error condition occurs.
//
// This is a vestige of streaming driver interface. We don't use it for services.
//
extern "C" DWORD BTM_Write (DWORD dwData, LPCVOID pInBuf, DWORD dwInLen)
{
return -1;
}
// @func DWORD | HLO_Read | Device read routine
// @parm DWORD | dwOpenData | value returned from HLO_Open call
// @parm LPVOID | pBuf | buffer to receive data
// @parm DWORD | len | maximum length to read [IN BYTES, not WORDS!!]
// @rdesc Returns 0 for end of file, -1 for error, otherwise the number of
// bytes read. The length returned is guaranteed to be the length
// requested unless end of file or an error condition occurs.
//
// This is a vestige of streaming driver interface. We don't use it for services.
//
extern "C" DWORD BTM_Read (DWORD dwData, LPVOID pBuf, DWORD dwLen)
{
return -1;
}
// @func DWORD | HLO_Seek | Device seek routine
// @parm DWORD | dwOpenData | value returned from HLO_Open call
// @parm long | pos | position to seek to (relative to type)
// @parm DWORD | type | FILE_BEGIN, FILE_CURRENT, or FILE_END
// @rdesc Returns current position relative to start of file, or -1 on error
//
// This is a vestige of streaming driver interface. We don't use it for services.
//
extern "C" DWORD BTM_Seek (DWORD dwData, long pos, DWORD type)
{
return (DWORD)-1;
}
// @func void | HLO_PowerUp | Device powerup routine
// @comm Called to restore device from suspend mode.
// You cannot call any routines aside from those in your dll in this call.
//
// This is a vestige of streaming driver interface. We don't use it for services.
//
extern "C" void BTM_PowerUp(void)
{
return;
}
// @func void | HLO_PowerDown | Device powerdown routine
// @comm Called to suspend device. You cannot call any routines aside from
// those in your dll in this call.
//
// This is a vestige of streaming driver interface. We don't use it for services.
//
extern "C" void BTM_PowerDown(void)
{
return;
}
// @func BOOL | HLO_IOControl | Device IO control routine
// @parm DWORD | dwOpenData | value returned from HLO_Open call
// @parm DWORD | dwCode | io control code to be performed
// @parm PBYTE | pBufIn | input data to the device
// @parm DWORD | dwLenIn | number of bytes being passed in
// @parm PBYTE | pBufOut | output data from the device
// @parm DWORD | dwLenOut |maximum number of bytes to receive from device
// @parm PDWORD | pdwActualOut | actual number of bytes received from device
// @rdesc Returns TRUE for success, FALSE for failure
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
//
// This is THE way to expose both manageability of the service and the feature API set.
// Consumer of the API set will marshal input arguments into input buffer (pBufIn).
// This function unmarshals it and executes the API, and then marshals output parameters
// into output buffer (pBufOut).
//
extern "C" BOOL BTM_IOControl(DWORD dwData, DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut)
{
switch (dwCode) {
// Control code sent to start a service (not the same as IOCTL_SERVICE_STARTED).
case IOCTL_SERVICE_START: // start a service that is currently in the stopped stage.
if (gfServiceState == SERVICE_STATE_OFF) {
if (ghServiceThread) {
if (WAIT_TIMEOUT == WaitForSingleObject (ghServiceThread, BTH_MODEM_TIMEOUT))
return FALSE;
CloseHandle (ghServiceThread);
ghServiceThread = NULL;
}
ResetEvent (ghServiceExitEvent);
ghServiceThread = CreateThread (NULL, 0, ComServiceThread, NULL, 0, NULL);
return ghServiceThread != NULL;
}
SetLastError (ERROR_SERVICE_ALREADY_RUNNING);
return FALSE;
// stop a service and for refresh bring it up again.
// REFRESH is required for more complicated servers
// that only read in their registry configuration
// during initilization.
case IOCTL_SERVICE_REFRESH:
case IOCTL_SERVICE_STOP:
if (gfServiceState == SERVICE_STATE_ON) {
if (ghServiceThread) {
SetEvent (ghServiceExitEvent);
if (WAIT_TIMEOUT == WaitForSingleObject (ghServiceThread, BTH_MODEM_TIMEOUT))
return FALSE;
CloseHandle (ghServiceThread);
ghServiceThread = NULL;
}
if (dwCode == IOCTL_SERVICE_REFRESH) {
ResetEvent (ghServiceExitEvent);
ghServiceThread = CreateThread (NULL, 0, ComServiceThread, NULL, 0, NULL);
return ghServiceThread != NULL;
}
return TRUE;
}
SetLastError (ERROR_SERVICE_NOT_ACTIVE);
return FALSE;
case IOCTL_SERVICE_STATUS:
if (pBufOut && dwLenOut == sizeof(DWORD)) {
*(DWORD *)pBufOut = gfServiceState;
if (pdwActualOut)
*pdwActualOut = sizeof(DWORD);
return TRUE;
}
break;
// IOCTL_SERVICE_CONTROL contains service specific information passed to it by
// a calling application.
case IOCTL_SERVICE_CONTROL:
return TRUE;
// Called once all initialization has completed and the session is ready
// to start running. By convention all services will (or at least should) be
// passed their service handle (what RegisterService() returns). This value
// can be saved in case a service wishes to call DeregisterService on itself.
//
// This is not available in device.exe!
//
case IOCTL_SERVICE_STARTED:
return TRUE;
case IOCTL_SERVICE_REGISTER_SOCKADDR:
// The super services thread or an application will call this IOCTL will
// pBufIn = NULL as a means of querying whether or not the particular service
// will support having a socket bound to it or not. Service will return FALSE
// if it was not designed to take IOCTL_SERVICE_CONNECTION commands, or
// if it is not ready.
//
// This is not available in device.exe!
//
return FALSE;
case IOCTL_SERVICE_DEREGISTER_SOCKADDR:
// When our sockets are being disconnected this message is sent to us.
//
// This is not available in device.exe!
//
return TRUE;
case IOCTL_SERVICE_CONNECTION:
// This IOControl is called when a socket connection for a socked associated
// with the service comes off the wire. The socket accept() returns is the
// value passed. Note that it is the service's responsibilty to close
// the socket, even if the service is not in the active state.
//
// This is not available in device.exe!
//
return FALSE;
// This can be used to programmatically turn on/off debug zones.
case IOCTL_SERVICE_DEBUG:
return TRUE;
// Unsupported SERVICES IOCTLs
// case IOCTL_SERVICE_INSTALL:
// case IOCTL_SERVICE_UNINSTALL:
}
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -