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

📄 wavepdd.cxx

📁 Windows CE操作系统中适用的蓝牙驱动程序
💻 CXX
📖 第 1 页 / 共 4 页
字号:
//
// 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.
//
// -----------------------------------------------------------------------------
//
//      THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//      ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//      THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//      PARTICULAR PURPOSE.
//  
//  Module Name:  
//  
//      wavepdd.cxx
//  
//  Abstract:  
//      The PDD (Platform Dependent Driver) is responsible for 
//      communicating with the audio circuit to start and stop playback
//      and/or recording and initialize and deinitialize the circuits.
//  
//  Functions:
//      PDD_AudioMessage
//      PDD_AudioInitialize
//      PDD_AudioDeinitialize
//      PDD_AudioPowerHandler
//      PDD_WaveProc
//  
//  Notes:
//  
// -----------------------------------------------------------------------------
#include <windows.h>
#include <types.h>
#include <memory.h>
#include <excpt.h>
#include <nkintr.h>
#include <ceddk.h>
#include <cardserv.h>
#include <devload.h>
#include <ddkreg.h>
#include <giisr.h>
#include <wavedbg.h>
#include <waveddsi.h>

#include "wavemdd.h"

#include "bthsco.h"


typedef struct _STREAM_INFO_PDD
{
    BOOL            bOpened;
    BOOL            bStarted;
    WAVEFORMATEX    wfx;                    // does not have the trailing info after cbSize
} STREAM_INFO_PDD;


// the events that the audio thread responds to
#define AUDIO_EVENT_EXIT_THREAD         0
#define AUDIO_EVENT_RX_DATA             1
#define AUDIO_EVENT_TX_DATA             2
#define AUDIO_EVENT_STACK_DOWN          3
#define AUDIO_EVENT_STACK_UP            4
#define AUDIO_EVENT_SCO_LINK_DISCONNECT	5
#define NUM_AUDIO_EVENTS                6

#define ABORT_IN                        0x1
#define ABORT_OUT                       0x2
#define SILENCE                         0
#define RX_BUFFER_SIZE                  4096
#define MAX_BASEBAND_CONNECTIONS        20
#define DEFAULT_SCO_CONNECT_IN_TIMEOUT  10000
#define DEFAULT_SCO_PERSIST_TIMEOUT		60000

#define TX_BUFFER_SIZE					256


//
// file globals
//

// critical section and variables controlled by it
static CRITICAL_SECTION g_csStreamInfo[2];                  // critical section for each g_StreamInfo
static STREAM_INFO_PDD  g_StreamInfo[2];                    // one for WAPI_IN and one for WAPI_OUT

// critical section and variables controlled by it
static CRITICAL_SECTION g_csRxBuffer;                       // critical section for g_pRxBuffer,g_dwRxBufferLen,g_dwRxBufferStart
static BYTE             g_pRxBuffer[RX_BUFFER_SIZE];        // circular buffer for receiving audio data
static DWORD            g_dwRxBufferLen;                    // length of data in g_pRxBuffer
static DWORD            g_dwRxBufferStart;                  // start of data in g_pRxBuffer

// critical section and variables controlled by it
static CRITICAL_SECTION g_csTxBuffer;                       // critical section for g_pTxBuffer,g_dwTxPacketsToSend
static UCHAR			g_TxBuffer[TX_BUFFER_SIZE];			// static buffer for transmit data
static UCHAR*           g_pTxBuffer;   						// pointer to buffer for transmit data
static DWORD            g_dwTxPacketsToSend;                // how many more packets should be DataPacketDown'ed

static HANDLE           g_hAudioEvents[NUM_AUDIO_EVENTS];   // handle to events used by audio thread
static HANDLE			g_hReadyEvent;						// handle to event to signal driver is ready

static CRITICAL_SECTION g_csPddWaveProcLock;                // lock acquired everytime in PDD_WaveProc
static SCO_INTERFACE    g_ScoIntf;                          // interface to SCO functions
static ULONG            g_dwVolume;                         // current volume setting
static DWORD            g_dwScoPacketSize;                  // how big each packet should be
static DWORD			g_dwConcurrentPackets;				// max concurrent SCO packets in HCI
static USHORT           g_usScoConnHandle;                  // handle to SCO connection

// registry settings
static BD_ADDR          g_bdAddressIn;                      // what address to accept a connection from (zero ==> all)
static BD_ADDR          g_bdAddressOut;                     // what address to connect to
static BOOL             g_bConnectOut;                      // outbound or inbound connection establishment
static DWORD            g_dwConnectInTimeout;               // timeout for inbound connection establishment
static DWORD            g_dwAirCoding;                      // CVSD, ALAW, ULAW (for writing device's voice setting)

static BOOL				g_fSigned;							// Converts data to 8-bit signed samples
static BOOL 			g_f16Bit;							// Converts data from 16-bit to 8-bit

// macros for locking/unlocking g_StreamInfo[apidir]
#define   LOCK_STREAM_INFO(apidir)  EnterCriticalSection(&(g_csStreamInfo[apidir]))
#define UNLOCK_STREAM_INFO(apidir)  LeaveCriticalSection(&(g_csStreamInfo[apidir])) 

// macros for locking/unlocking g_pRxBuffer,g_dwRxBufferLen,g_dwRxBufferStart
#define   LOCK_RXBUFFER()           EnterCriticalSection(&g_csRxBuffer)
#define UNLOCK_RXBUFFER()           LeaveCriticalSection(&g_csRxBuffer) 

// macros for locking/unlocking g_pTxBuffer,g_dwTxPacketsToSend
#define   LOCK_TXBUFFER()           EnterCriticalSection(&g_csTxBuffer)
#define UNLOCK_TXBUFFER()           LeaveCriticalSection(&g_csTxBuffer) 

// message handlers
static MMRESULT PddMsg_Close           (WAPI_INOUT apidir);
static MMRESULT PddMsg_GetDevCaps      (WAPI_INOUT apidir, PVOID pCaps, UINT wSize);
static MMRESULT PddMsg_Open            (WAPI_INOUT apidir, LPWAVEFORMATEX lpFormat, BOOL bQueryFormatOnly);
static MMRESULT PddMsg_Start           (WAPI_INOUT apidir, PWAVEHDR pwh);
static MMRESULT PddMsg_Stop            (WAPI_INOUT apidir);
static MMRESULT PddMsg_OutGetVolume    (PULONG pulVolume);
static MMRESULT PddMsg_OutSetVolume    (ULONG ulVolume);

DWORD AudioThreadProc(LPVOID lpParameter);

// SCO callbacks
static int PddSco_DataPacketUp     (void *pUserData, USHORT hConnection, PUCHAR pBuffer, DWORD dwBufferLen);
static int PddSco_DataPacketDown   (void *pUserData, void *pPacketUserContext, USHORT hConnection, DWORD dwError);
static int PddSco_StackEvent       (void *pUserData, int iEvent);


// -----------------------------------------------------------------------------
//                          PddSco_DataPacketUp
// -----------------------------------------------------------------------------
static int 
PddSco_DataPacketUp (
    void    *pUserData, 
    USHORT  hConnection, 
    PUCHAR  pBuffer, 
    DWORD   dwBufferLen
    )
{
    DWORD dwEndIndex;
    DWORD dwSpaceLeft;
    DWORD dwCopyPass1;
    DWORD dwCopyPass2;

    //BSS_INTMSG2("PddSco_DataPacketUp : h=%#x len=%d", hConnection, dwBufferLen);

    //
    // discard this data if it is not being captured
    //
    LOCK_STREAM_INFO(WAPI_IN);
    BOOL bRunning = (g_StreamInfo[WAPI_IN].bOpened && g_StreamInfo[WAPI_IN].bStarted);
    UNLOCK_STREAM_INFO(WAPI_IN);
    if (!bRunning) {
        return ERROR_SUCCESS;
    }


    // general guideline: whenever we are going to lose data
    // scrap the oldest data...i.e. keep the most recent data

    ASSERT(dwBufferLen <= RX_BUFFER_SIZE);
    if (dwBufferLen > RX_BUFFER_SIZE) {
        // RX_BUFFER_SIZE should be large enough for any packet size
        // otherwise, we will lose data on every packet!
        BSS_INTMSG("Amount of data in packet exceeds RxBuffer max capacity!");
        // keep the most recent RX_BUFFER_SIZE worth
        pBuffer += (dwBufferLen - RX_BUFFER_SIZE);
        dwBufferLen = RX_BUFFER_SIZE;
    }

    // we will copy all the data into g_pRxBuffer overwriting old data (if req'd)

    LOCK_RXBUFFER();

    dwEndIndex  = (g_dwRxBufferStart + g_dwRxBufferLen) % RX_BUFFER_SIZE;    // idx to write new data to
    dwSpaceLeft = RX_BUFFER_SIZE - dwEndIndex;      // # bytes until end of buffer
    dwCopyPass1 = min(dwBufferLen, dwSpaceLeft);    // # bytes to write from end of data to end of buffer
    dwCopyPass2 = dwBufferLen - dwCopyPass1;        // # bytes to write from begginging of buffer

    memcpy(g_pRxBuffer + dwEndIndex, pBuffer, dwCopyPass1);

    // wrap around
    if (dwCopyPass2 > 0) {
        memcpy(g_pRxBuffer, pBuffer + dwCopyPass1, dwCopyPass2);
    }

    if ( (g_dwRxBufferLen + dwBufferLen) > RX_BUFFER_SIZE ) {
        // old data has been scrapped
        DWORD dwOverwrite = (g_dwRxBufferLen + dwBufferLen) - RX_BUFFER_SIZE;
        g_dwRxBufferLen = RX_BUFFER_SIZE;
        g_dwRxBufferStart = (g_dwRxBufferStart + dwOverwrite) % RX_BUFFER_SIZE;
    } else {
        g_dwRxBufferLen += dwBufferLen;
    }

    UNLOCK_RXBUFFER();

    SetEvent(g_hAudioEvents[AUDIO_EVENT_RX_DATA]);
    return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------
//                          PddSco_DataPacketDown
// -----------------------------------------------------------------------------
static int 
PddSco_DataPacketDown (
    void    *pUserData, 
    void    *pPacketUserContext, 
    USHORT  hConnection, 
    DWORD   dwError
    )
{
    if (dwError) {
        BSS_ERRMSG2("PddSco_DataPacketDown : h=%#x err=%d", hConnection, dwError);
    }

    LOCK_TXBUFFER();
    g_dwTxPacketsToSend++;
    UNLOCK_TXBUFFER();

    //DEBUGMSG(1, (L"BTSCOSND: PddSco_DataPacketDown %d. GetTickCount=%u\n", g_dwTxPacketsToSend, GetTickCount()));

    SetEvent(g_hAudioEvents[AUDIO_EVENT_TX_DATA]);
    return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------
//                          PddSco_StackEvent
// -----------------------------------------------------------------------------
static int 
PddSco_StackEvent (
    void    *pUserData, 
    int     iEvent
    )
{
    switch (iEvent)
    {
        case BTH_STACK_DISCONNECT:
        case BTH_STACK_DOWN:
            SetEvent(g_hAudioEvents[AUDIO_EVENT_STACK_DOWN]);
            break;
        case BTH_STACK_UP:
            SetEvent(g_hAudioEvents[AUDIO_EVENT_STACK_UP]);
            break;
        case SCO_LINK_DISCONNECT:
        	SetEvent(g_hAudioEvents[AUDIO_EVENT_SCO_LINK_DISCONNECT]);
        	break;
    }
    return ERROR_SUCCESS;
}

void ConvertM08toM16(UINT8 *pSrc, UINT16 * pDst, INT nSamples) 
{
	while (nSamples-- > 0) {
		UINT8 sample8 = *pSrc++;
		UINT16 m16;
		if (g_fSigned) {
		    m16 = (UINT16)(sample8 << 8);
		}
		else {
		    m16 = (UINT16)((sample8 ^ 0x80) << 8);
		}
		*pDst++ = m16;
	}
}


// -----------------------------------------------------------------------------
//                      RxBufferToWaveBuffers
// -----------------------------------------------------------------------------
//  Copies data from g_pRxBuffer to wave buffer
// -----------------------------------------------------------------------------
static void
RxBufferToWaveBuffers(
    PWAVEHDR    pwh
    )
{   
    LOCK_STREAM_INFO(WAPI_IN);
    BOOL fConvertTo16Bit = (g_StreamInfo[WAPI_IN].wfx.wBitsPerSample == 16);
    UNLOCK_STREAM_INFO(WAPI_IN);
    
    while ( (g_dwRxBufferLen > 0) && (pwh != NULL) ) 
    {
        if (pwh->dwBytesRecorded >= pwh->dwBufferLength) {
            // this WAVEHDR is full
            pwh = pwh->lpNext;
            continue;
        }

        // transfer min (rx_bytes_to_end_of_buffer, rx_data_len, wavebuf_space)
        DWORD dwRxBytesToEnd = RX_BUFFER_SIZE - g_dwRxBufferStart;
        DWORD dwBytesWaveHdr = pwh->dwBufferLength - pwh->dwBytesRecorded;
        if (fConvertTo16Bit) {
            dwBytesWaveHdr /= 2; // Converting to 16bit needs double space
        }            
        DWORD dwNumBytesToCopy = g_dwRxBufferLen;
        if (dwNumBytesToCopy > dwBytesWaveHdr) {
            dwNumBytesToCopy = dwBytesWaveHdr;
        }
        if (dwNumBytesToCopy > dwRxBytesToEnd) {
            dwNumBytesToCopy = dwRxBytesToEnd;
        }
	
		if (fConvertTo16Bit) {
		    ConvertM08toM16(g_pRxBuffer + g_dwRxBufferStart, (UINT16*)(pwh->lpData + pwh->dwBytesRecorded), dwNumBytesToCopy);
		    pwh->dwBytesRecorded += (dwNumBytesToCopy * 2);
		}
		else {
            if (g_fSigned) {
        		UCHAR* p = g_pRxBuffer + g_dwRxBufferStart;
        		for (DWORD i = 0; i < dwNumBytesToCopy; i++) {
        			*p ^= 0x80;
        			p++;
        		}
        	}
            
            memcpy (pwh->lpData + pwh->dwBytesRecorded, 
                g_pRxBuffer + g_dwRxBufferStart, 
                dwNumBytesToCopy);

            pwh->dwBytesRecorded += dwNumBytesToCopy;
		}     
        
        g_dwRxBufferLen      -= dwNumBytesToCopy;
        g_dwRxBufferStart     = (g_dwRxBufferStart + dwNumBytesToCopy) % RX_BUFFER_SIZE;
    }

    if (g_dwRxBufferLen == 0) {
        g_dwRxBufferStart = 0; // if no data then normalize the buffer (for cleanliness)
    }
}


// -----------------------------------------------------------------------------
//                      ConvertS16toM08
// -----------------------------------------------------------------------------
//  Convert from 16-bit stereo to 8-bit mono
// -----------------------------------------------------------------------------
void ConvertS16toM08(UINT32 *pSrc, UINT8 * pDst, INT nSamples) 
{
	while (nSamples-- > 0) {
		UINT32 sample32 = *pSrc++;
		INT16 L = (INT16) (sample32 & 0xffff);
		INT16 R = (INT16) (sample32 >> 16);
		INT16 M = (L+R) >> 1; // merge left/right w/ simple average

        UINT m8;
		if (g_fSigned) {
		    m8 = (UINT8) (M >> 8);
		}
		else {

⌨️ 快捷键说明

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