⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 device.cpp

📁 The following file describes how to use the Bluetooth Headset Audio Gateway.
💻 CPP
字号:
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//

#include <windows.h>
#include "device.h"
#include "btagw_dbg.h"

// Global data
HANDLE				g_hDevice;
HANDLE				g_hThread;
HANDLE				g_hParserStopEvent;
CRITICAL_SECTION	g_csDevice;
BOOL				g_fRinging;


DWORD WINAPI ATParserThread(LPVOID pv);



// Initialize data in device module
DWORD InitializeDevice(void)
{
	InitializeCriticalSection(&g_csDevice);
	g_hDevice = INVALID_HANDLE_VALUE;
	
	return ERROR_SUCCESS;
}



// DeInitialize data in the device module
DWORD DeInitializeDevice(void)
{
	DeleteCriticalSection(&g_csDevice);
	return ERROR_SUCCESS;
}



// Establishes ACL connection and starts parser thread
DWORD StartDevice(void)
{
	DWORD 	dwRetVal = ERROR_SUCCESS;
	
	EnterCriticalSection(&g_csDevice);

	g_hParserStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (! g_hParserStopEvent) {
		dwRetVal = GetLastError();
		DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error creating stop event for ACL connection: %d"), dwRetVal));
		goto exit;
	}

	g_hDevice = CreateFile(L"BSP1:", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (g_hDevice == INVALID_HANDLE_VALUE) {
		dwRetVal = GetLastError();
		DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error creating ACL Connection with the headset: %d"), dwRetVal));
		goto exit;
	}

	DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: *** Established ACL connection with device ***")));

	// Start thread to listen for headset messages
	g_hThread = CreateThread(NULL, 0, ATParserThread, NULL, 0, NULL);
	if (! g_hThread) {
		dwRetVal = GetLastError();
		DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Error creating AT parser thread: %d"), dwRetVal));
	}

exit:
	// Clean up on failure
	if (dwRetVal != ERROR_SUCCESS) {
		if (g_hParserStopEvent) {
			CloseHandle(g_hParserStopEvent);
			g_hParserStopEvent = NULL;
		}
		if (g_hThread) {
			CloseHandle(g_hThread);
			g_hThread = NULL;
		}
		if (g_hDevice != INVALID_HANDLE_VALUE) {
			CloseHandle(g_hDevice);
			g_hDevice = INVALID_HANDLE_VALUE;
		}	
	}
	
	LeaveCriticalSection(&g_csDevice);
	return dwRetVal;
}



// Closes ACL connection and signals parser thread to stop
DWORD StopDevice(void)
{
	DWORD dwRetVal = ERROR_SUCCESS;

	SetEvent(g_hParserStopEvent);

	EnterCriticalSection(&g_csDevice);
	if (g_hDevice != INVALID_HANDLE_VALUE) {
		CloseHandle(g_hDevice);
		g_hDevice = INVALID_HANDLE_VALUE;
	}
	LeaveCriticalSection(&g_csDevice);

	// If we are calling from any thread other than the ATParserThread
	// then we have to wait for the thread to exit.
	if ((DWORD)g_hThread != GetCurrentThreadId()) {
		if (WaitForSingleObject(g_hThread, INFINITE) == WAIT_OBJECT_0) {
			DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: ACL connection to the headset has been successfully closed")));
		}
		else {
			DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error waiting for parser thread to exit: %d")));
		}
	}

	if (g_hThread) {
		CloseHandle(g_hThread);
		g_hThread = NULL;
	}
	if (g_hParserStopEvent) {
		CloseHandle(g_hParserStopEvent);
		g_hParserStopEvent = NULL;
	}

	return dwRetVal;
}



// Informs the device module that it is currently in a ringing state.  In
// other words it is currently calling the headset.
void SetRingingState(BOOL fRinging)
{
	EnterCriticalSection(&g_csDevice);
	g_fRinging = fRinging;
	LeaveCriticalSection(&g_csDevice);
}



// Send AT command to device
DWORD SendATCommand(char* szCommand, DWORD cdwBuf)
{
	DWORD 	dwRetVal = ERROR_SUCCESS;
	BOOL	fWrite;
	DWORD	cdwBytesWritten = 0;

	EnterCriticalSection(&g_csDevice);

	// Send the AT command
	fWrite = WriteFile(g_hDevice, (LPVOID)szCommand, cdwBuf, &cdwBytesWritten, NULL);
	if (! fWrite) {
		dwRetVal = GetLastError();
		DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error writing AT command to the device: %d"), dwRetVal));
	}

#ifdef DEBUG
	if (cdwBuf != cdwBytesWritten) {
		DEBUGMSG(ZONE_WARN, (_T("BTAUDIOGW: Warning --> Send only wrote %d of %d total bytes"), cdwBytesWritten, cdwBuf));
	}
#endif

	LeaveCriticalSection(&g_csDevice);
	return dwRetVal;
}



// Extract the volume level from the AT command string sent from the headset
DWORD GetVolumeFromATCmd(CHAR* szBuf, USHORT* pusVol)
{
	DWORD dwRetVal = ERROR_SUCCESS;
	CHAR* pStart;
	CHAR* pEnd;
	CHAR* pTmp = NULL;
	
	// pStart will point to start of string and pEnd will advance
	// to the null terminating string
	pStart = pEnd = &szBuf[7];
	while (*pEnd != '\r' && *pEnd != '\0') {
		pEnd++;
	}
	
	if (*pEnd == '\r') {
		*pEnd = '\0';
	}

	if (pusVol) {
		*pusVol = (USHORT) strtol(pStart, &pTmp, 10);
		if (pTmp != pEnd) {
			dwRetVal = ERROR_INVALID_DATA;
			SetLastError(dwRetVal);
			*pusVol = 0;
		}
	}

	return dwRetVal;
}



// Thread which reads commands from the headset and processes them 
DWORD WINAPI ATParserThread(LPVOID pv)
{
	CHAR	szBuf[MAX_RECV_BUF];
	DWORD	cdwBytesRead = 0;
	BOOL	fRead;
	DWORD	dwRetVal;

	DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Entering ATParserThread")));

	while (1) {
		fRead = ReadFile(g_hDevice, szBuf, MAX_RECV_BUF, &cdwBytesRead, NULL);
		if (fRead && cdwBytesRead) {
			szBuf[cdwBytesRead] = '\0';
			if (cdwBytesRead == 12 && (! strncmp(szBuf, "AT+CKPD=200\r", 12))) {
				DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Received 'AT+CKPD=200' from headset"))); 
				if (SendATCommand("\r\nOK\r\n", 6) != ERROR_SUCCESS) {
					DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error sending the OK command: %d"), GetLastError()));
				}

				EnterCriticalSection(&g_csDevice);

				// If we are currently ringing the headset then this is an answer.  
				// Otherwise, the headset has hung up.
				if (g_fRinging) {
					SetEvent(g_hRingAnswerEvent);
					LeaveCriticalSection(&g_csDevice);
				}
				else {
					if (g_fPcmMode) {
						DeinitSco();
					}
					StopDevice();
					DEBUGMSG(ZONE_EVENTS, (_T("BTAUDIOGW: Event Signalled --> g_hCloseEvent")));
					
					EnterCriticalSection(&g_csData);
					g_dwAGState = AG_DISCONNECTED;
					if (g_hCloseEvent)
						SetEvent(g_hCloseEvent);
					LeaveCriticalSection(&g_csData);
					
					LeaveCriticalSection(&g_csDevice);
					break;
				}
			}
			else if (cdwBytesRead > 8 && (! strncmp(szBuf, "AT+VGM=", 7))) {
				USHORT usVol = 0;
				
				DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Received 'AT+VGM=x' from headset")));
				if (SendATCommand(AT_OK, 6) != ERROR_SUCCESS) {
					DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error sending the OK command: %d"), GetLastError()));
				}

				if (GetVolumeFromATCmd(szBuf, &usVol) == ERROR_SUCCESS) {
					DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Mic volume changed to %d"), usVol));
					g_usMicVol = usVol;
					DEBUGMSG(ZONE_EVENTS, (_T("BTAUDIOGW: Event Signalled --> g_hMicVolEvent")));
					
					EnterCriticalSection(&g_csData);
					if (g_hMicVolEvent)
						SetEvent(g_hMicVolEvent);
					LeaveCriticalSection(&g_csData);
				}
				else {
					DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error getting mic volume from AT command: %d"), GetLastError()));
				}
			}
			else if (cdwBytesRead > 8 && (! strncmp(szBuf, "AT+VGS=", 7))) {
				USHORT usVol;
				
				DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Received 'AT+VGS=x' from headset")));
				if (SendATCommand(AT_OK, 6) != ERROR_SUCCESS) {
					DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error sending the OK command: %d"), GetLastError()));
				}
				
				if (GetVolumeFromATCmd(szBuf, &usVol) == ERROR_SUCCESS) {
					DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Speaker volume changed to %d"), usVol));
					g_usSpkVol = usVol;
					DEBUGMSG(ZONE_EVENTS, (_T("BTAUDIOGW: Event Signalled --> g_hSpkVolEvent")));

					EnterCriticalSection(&g_csData);
					if (g_hSpkVolEvent)
						SetEvent(g_hSpkVolEvent);
					LeaveCriticalSection(&g_csData);
				}
				else {
					DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error getting speaker volume from AT command: %d"), GetLastError()));
				}
			}
			else {
				if (g_pfnATCmdCallback) {
					DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Received unknown command from headset - calling external handler")));

					// Call external handler to process command
					__try {
						dwRetVal = g_pfnATCmdCallback(szBuf, cdwBytesRead);
					}
					__except (1) {
						DEBUGMSG(ZONE_ERROR, (_T("ZONE_ERROR: Caught an exception while calling external AT Command handler")));
					}

					// If external handler failed to process this AT Command then let's send error back ourselves
					if (dwRetVal != ERROR_SUCCESS) {
						DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Handler did not process AT Command - sending back ERROR")));
						if (SendATCommand(AT_ERROR, 9) != ERROR_SUCCESS) {
							DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error sending the ERROR command: %d"), GetLastError()));
						}	
					}
				}
				else {
					// No external handler exists so just send an error
					DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Received unknown command from headset - sending back ERROR")));
					if (SendATCommand(AT_ERROR, 9) != ERROR_SUCCESS) {
						DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: Error sending the ERROR command: %d"), GetLastError()));
					}	
				}
			}
		}
		else if (! fRead) {
			if (WaitForSingleObject(g_hParserStopEvent, 0) == WAIT_OBJECT_0) {
				DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: ACL Connection is closing, exiting parser thread")));
				break;
			}
			DEBUGMSG(ZONE_ERROR, (_T("BTAUDIOGW: ReadFile failed in ATParserThread: %d"), GetLastError()));
		}
	}

	DEBUGMSG(ZONE_OUTPUT, (_T("BTAUDIOGW: Exiting ATParserThread")));
	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -