📄 portemu.cxx
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
//------------------------------------------------------------------------------
//
// Bluetooth Virtual Comm Port
//
//
// Module Name:
//
// portemu.cxx
//
// Abstract:
//
// This file implements vCOMM port layer
//
//
//------------------------------------------------------------------------------
#include <windows.h>
#include <svsutil.hxx>
#include <bt_buffer.h>
#include <bt_ddi.h>
#include <bt_api.h>
#include <bt_debug.h>
#include <bt_sdp.h>
#include <bthapi.h>
#include <ceport.h>
#include <sdplib.h>
#include <bt_os.h>
#include <windev.h>
#include <pegdser.h>
#define EA_BIT 0x01
#define BIT(c) (1 << (c))
#define MSC_EA_BIT EA_BIT
#define MSC_FC_BIT BIT(1) // Flow control, clear if we can receive
#define MSC_RTC_BIT BIT(2) // Ready to communicate, set when ready
#define MSC_RTR_BIT BIT(3) // Ready to receive, set when ready
#define MSC_IC_BIT BIT(6) // Incoming call
#define MSC_DV_BIT BIT(7) // Data valid
#define PORTEMU_OPEN_MASK 0x3
#define PORTEMU_OPEN_MAX 4 // Must be power of 2; x - 1 used for mask
#define PORTEMU_OPEN_ALLUSED ((1 << PORTEMU_OPEN_MAX) - 1)
#define PORTEMU_MAX_RFCOMM_CHANNEL 31
#define PORTEMU_OPEN_ATTEMPTS 5
#define PORTEMU_OPEN_TIMEOUT 1000
enum COMM_OP {
COMM_NO_OP = 0,
COMM_OPEN = 1,
COMM_WAIT_EVENT = 2,
COMM_READ = 3,
COMM_WRITE = 4,
COMM_RPN = 5,
COMM_DISC = 6
};
struct PendingIO : public SVSAllocClass {
PendingIO *pNext; // Next in list
HANDLE hEvent; // Event to signal on completion
COMM_OP op; // COMM_OP (open, wait event, read, write)
unsigned char *buffer; // Buffer
int cbuffer; // Size of buffer
DWORD dwPerms; // Permissions
int cbuffer_used; // Bytes used in the buffer so far
int iIoResult; // IO result on completion
DWORD dwEvent; // COMM events
DWORD dwLineError; // COMM line errors
int iPortNum; // Port number for this IO
PendingIO (COMM_OP a_op, int a_iPortNum);
~PendingIO (void);
};
enum RFCOMM_OP {
NO_OP = 0,
RFCOMM_CONNECT = 1,
RFCOMM_SDP = 2,
RFCOMM_PN = 3,
RFCOMM_WRITE = 4,
RFCOMM_RPN = 5,
RFCOMM_DISC = 6
};
struct PORTEMU_CONTEXT;
struct ECall {
ECall *pNext; // Next in list
RFCOMM_OP fWhat; // RFCOMM_OP (connect or write)
int cBytes; // Number of bytes on write
PORTEMU_CONTEXT *pContext; // Owner context
ECall (RFCOMM_OP a_fWhat, PORTEMU_CONTEXT *a_pContext) {
fWhat = a_fWhat;
pContext = a_pContext;
pNext = NULL;
cBytes = 0;
}
void *operator new (size_t size);
void operator delete (void *ptr);
};
struct BD_BUFFER_LIST {
BD_BUFFER_LIST *pNext; // Next in list
BD_BUFFER *pb; // Buffer
void *operator new (size_t size);
void operator delete (void *ptr);
};
// This structure is what describes the device. Several important notes.
// Lifecycle starts with registering device and ends with unregistering it.
// The port is connected when it is opened, and disconnected when it's closed.
// Ownership only commences with OPENING the device. Ownership is surrendered
// by CLOSING device. When device is unowned, the com port can be configured
// using the following call:
// RFCOMMSetServerChannel (int index, int channel, int flocal, BD_ADDR *pdevice, int iminmtu, int imaxmtu);
// {if (flocal) ASSERT (! pdevice);}
//
struct PORTEMU_CONTEXT : public SVSAllocClass {
PORTEMU_CONTEXT *pNext; // Next in list
HANDLE hRFCOMM; // RFCOMM handle
RFCOMM_INTERFACE rfcomm_if; // RFCOMM interface table
int iDeviceHead; // Pre-alloc in front of the buffer
int iDeviceTrail; // Pre-alloc at the end of the buffer
unsigned int channel : 5; // Server channel id
unsigned int fLocal : 1; // Local channel
unsigned int fLocalOpen : 1; // Port really connected
unsigned int fSdpQueryOn : 1; // SDP query is in progress
unsigned int fSentXon : 1; // Xon sent
unsigned int fc : 1; // flow, 1 = don't send
unsigned int fc_aggregate : 1; // aggregate flow, 1 = don't send
unsigned int credit_fc : 1; // use credit-based flow
unsigned int local_dcb : 1; // use local DCB, do not send RPN commands
unsigned int fkeep_dcd : 1; // keep DCD up for the duration of connection
unsigned int freq_auth : 1; // require authentication
unsigned int freq_encrypt : 1; // require encryption
unsigned int uiOpenRef : 4; // Open ref field
unsigned int uiReadRef : 4; // Open ref field
unsigned int uiWriteRef : 4; // Open ref field
unsigned int fSending : 1; // in SendData
unsigned int fClosing : 1; // Closing port
unsigned int fConnected : 1; // Connection is up
int iHaveCredits; // Have credits
int iGaveCredits; // Credits outstanding
BD_ADDR b; // BD_ADDR of the other side
HANDLE hConnection; // Connection handle (NULL = not connected)
int iMTU; // Negotiated MTU
int iMinMTU; // MIN MTU for negotiation
int iMaxMTU; // MAX MTU for negotiation
int iSendQuota; // Send quota
int iSendQuotaUsed; // Used quota for send
int iRecvQuota; // Recv quota
int iRecvQuotaUsed; // Used recv quota
DWORD adwEnabledEvents[PORTEMU_OPEN_MAX]; // Enabled COMM events (SetCommEvents)
DWORD adwOccuredEvents[PORTEMU_OPEN_MAX]; // Occured COMM events (WaitCommEvents)
DWORD adwErr[PORTEMU_OPEN_MAX]; // COMM line error
HANDLE hOwnerProc[PORTEMU_OPEN_MAX]; // Owner proc
unsigned char ucv24out; // COMM modem status on outgoing lines
DWORD dwModemStatusIn; // COMM modem status on incoming lines
PendingIO *pops; // list of pending io
BD_BUFFER_LIST *pbl; // Arrived data list
ECall *pCalls; // pending calls
DCB dcb; // DCB
COMMTIMEOUTS ct; // COMMTIMEOUTS
GUID uuidService; //UUID of the service
unsigned short sdpCid; //channel id for sdp search
PORTEMU_CONTEXT (void) {
memset (this, 0, sizeof(*this));
fLocal = TRUE;
local_dcb = TRUE;
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = CBR_115200;
dcb.fBinary = TRUE;
dcb.fParity = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fTXContinueOnXoff = TRUE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = TRUE;
dcb.XonLim = PORTEMU_XONLIM;
dcb.XoffLim = PORTEMU_XOFFLIM;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
ct.ReadIntervalTimeout = -1;
ct.ReadTotalTimeoutConstant = PORTEMU_RTO;
ct.ReadTotalTimeoutMultiplier = 0;
ct.WriteTotalTimeoutConstant = PORTEMU_WTO;
ct.WriteTotalTimeoutMultiplier = 0;
}
~PORTEMU_CONTEXT (void);
};
class PORTEMU : public SVSAllocClass, public SVSSynch, public SVSRefObj {
public:
PORTEMU_CONTEXT *pPorts; // List of ports
FixedMemDescr *pfmdPBL; // FMD for buffer list
FixedMemDescr *pfmdCalls; // FMD for calls
int iPIOBlocksUsed; // Ref count on PIO blocks
int fInitialized; // Initialized?
int iDefaultMTUMin; // Default MTU Min
int iDefaultMTUMax; // Default MTU Max
int iDefaultSendQuota; // Default Send Quota
int iDefaultRecvQuota; // Default Recv Quota
HANDLE hSDP; // SDP handle
SDP_INTERFACE sdp_if; // sdp interface table
PORTEMU (void) {
pPorts = NULL;
pfmdPBL = NULL;
pfmdCalls = NULL;
iPIOBlocksUsed = 0;
fInitialized = FALSE;
iDefaultMTUMin = iDefaultMTUMax = 0;
iDefaultSendQuota = iDefaultRecvQuota = 0;
hSDP = NULL;
memset(&sdp_if, 0, sizeof(sdp_if));
}
};
extern "C" BOOL COM_Close (DWORD dwData);
//
// Static data
//
static PORTEMU *gpPORTEMU;
PendingIO::PendingIO (COMM_OP a_op, int a_iPortNum) {
++gpPORTEMU->iPIOBlocksUsed;
memset (this, 0, sizeof(*this));
op = a_op;
iPortNum = a_iPortNum;
hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
}
PendingIO::~PendingIO (void) {
--gpPORTEMU->iPIOBlocksUsed;
CloseHandle (hEvent);
#if defined (DEBUG) || defined (_DEBUG)
PORTEMU_CONTEXT *pCtx = gpPORTEMU->pPorts;
while (pCtx) {
PendingIO *pio = pCtx->pops;
while (pio && (pio != this))
pio = pio->pNext;
SVSUTIL_ASSERT (! pio);
pCtx = pCtx->pNext;
}
#endif
}
static int IsSerialIOCTL (DWORD ioctl_code) {
if (((ioctl_code >> 16) & 0xffff) == FILE_DEVICE_SERIAL_PORT)
return TRUE;
return FALSE;
}
#define BAUD_ARRAY_SIZE 9
static int byte_to_baud[BAUD_ARRAY_SIZE] = {CBR_2400, CBR_4800, 7200, CBR_9600, CBR_19200, CBR_38400, CBR_57600, CBR_115200, 230400};
static int BaudToByte (int baud) {
for (int i = 0 ; i < BAUD_ARRAY_SIZE ; ++i) {
if (byte_to_baud[i] == baud)
return i;
}
IFDBG(DebugOut (DEBUG_WARN, L"[PORTEMU] ByteToBaud : baud rate translation failed, baud = %d!\n", baud));
return -1;
}
static int VerifyDCB (DCB *pdcb) {
if (pdcb->DCBlength != sizeof(*pdcb))
return FALSE;
if (-1 == BaudToByte (pdcb->BaudRate))
return FALSE;
if ((pdcb->ByteSize < 5) || (pdcb->ByteSize > 8))
return FALSE;
if ((pdcb->StopBits != ONESTOPBIT) && (pdcb->StopBits != ONE5STOPBITS))
return FALSE;
if (pdcb->fParity && (pdcb->Parity > 4))
return FALSE;
if (! pdcb->fBinary)
return FALSE;
if (pdcb->fOutxCtsFlow || pdcb->fOutxDsrFlow || pdcb->fDsrSensitivity ||
(pdcb->fDtrControl != DTR_CONTROL_DISABLE) || (pdcb->fRtsControl != RTS_CONTROL_DISABLE))
return FALSE;
if (pdcb->fOutX || pdcb->fInX || pdcb->fErrorChar || pdcb->fNull)
return FALSE;
if (pdcb->XonLim < pdcb->XoffLim)
return FALSE;
pdcb->EofChar = pdcb->ErrorChar = pdcb->EvtChar = pdcb->XoffChar = pdcb->XonChar = 0;
pdcb->fAbortOnError = TRUE;
pdcb->fTXContinueOnXoff = TRUE;
return TRUE;
}
static ECall *NewCall (RFCOMM_OP fWhat, PORTEMU_CONTEXT *pContext) { return new ECall (fWhat, pContext); }
static PORTEMU_CONTEXT *NewPORTEMU_CONTEXT (void) { return new PORTEMU_CONTEXT; }
static PendingIO *NewPendingIO (COMM_OP op, int iPortNum) { return new PendingIO (op, iPortNum); }
static PORTEMU_CONTEXT *GetContext (DWORD dwData) {
PORTEMU_CONTEXT *pContext = gpPORTEMU->pPorts;
while (pContext && (pContext != (PORTEMU_CONTEXT *)dwData))
pContext = pContext->pNext;
return pContext;
}
PORTEMU_CONTEXT::~PORTEMU_CONTEXT (void) {
IFDBG(DebugOut (DEBUG_RFCOMM_TRACE, L"[PORTEMU] deleting context 0x%08x\n", this));
while (pops) {
PendingIO *pNext = pops->pNext;
pops->pNext = NULL;
pops->iIoResult = ERROR_OPERATION_ABORTED;
SetEvent (pops->hEvent);
pops = pNext;
}
while (pCalls) {
ECall *pNext = pCalls->pNext;
delete pCalls;
pCalls = pNext;
}
while (pbl) {
BD_BUFFER_LIST *pbl_next = pbl->pNext;
if (pbl->pb->pFree)
pbl->pb->pFree (pbl->pb);
delete pbl;
pbl = pbl_next;
}
if (hRFCOMM) {
gpPORTEMU->Unlock ();
RFCOMM_CloseDeviceContext (hRFCOMM);
gpPORTEMU->Lock ();
}
}
static void ForgetPort (PORTEMU_CONTEXT *pContext, int iPort) {
unsigned int mask = ~(1 << iPort);
pContext->uiOpenRef &= mask;
pContext->uiReadRef &= mask;
pContext->uiWriteRef &= mask;
pContext->hOwnerProc[iPort] = NULL;
}
static ECall *FindCall (void *pCallContext) {
if (! pCallContext)
return NULL;
PORTEMU_CONTEXT *pContext = NULL;
__try {
pContext = ((ECall *)pCallContext)->pContext;
} __except (1) {
IFDBG(DebugOut (DEBUG_ERROR, L"FindCall excepted!\n"));
}
if (! pContext)
return NULL;
PORTEMU_CONTEXT *pC = gpPORTEMU->pPorts;
while (pC && (pC != pContext))
pC = pC->pNext;
if (! pC)
return NULL;
ECall *pCall = pC->pCalls;
while (pCall && (pCall != pCallContext))
pCall = pCall->pNext;
return pCall;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -