5103 lines
155 KiB
C
5103 lines
155 KiB
C
/*****************************************************************************
|
|
*
|
|
* Copyright (c) 1995 Microsoft Corporation
|
|
*
|
|
* @doc
|
|
* @module irlmp.c | Provides IrLMP API
|
|
*
|
|
* Author: mbert
|
|
*
|
|
* Date: 4/15/95
|
|
*
|
|
* @comm
|
|
*
|
|
* This module exports the following API's:
|
|
*
|
|
* IrlmpOpenLink()
|
|
* IrlmpCloseLink()
|
|
* IrlmpDown()
|
|
* IrlmpUp()
|
|
*
|
|
*
|
|
* |---------|
|
|
* | Tdi |
|
|
* |---------|
|
|
* /|\ |
|
|
* | |
|
|
* TdiUp() | | IrlmpDown()
|
|
* | |
|
|
* | \|/
|
|
* |---------| IrdaTimerStart() |-------|
|
|
* | |-------------------------->| |
|
|
* | IRLMP | | TIMER |
|
|
* | |<--------------------------| |
|
|
* |---------| ExpFunc() |-------|
|
|
* /|\ |
|
|
* | |
|
|
* IrlmpUp() | | IrlapDown()
|
|
* | |
|
|
* | \|/
|
|
* |---------|
|
|
* | IRLAP |
|
|
* |---------|
|
|
*
|
|
* See irda.h for complete message definitions
|
|
*
|
|
* Connection context for IRLMP and Tdi are exchanged
|
|
* during connection establishment:
|
|
*
|
|
* Active connection:
|
|
* +------------+ IRLMP_CONNECT_REQ(TdiContext) +-------+
|
|
* | |---------------------------------------->| |
|
|
* | Tdi | IRLMP_CONNECT_CONF(IrlmpContext) | IRMLP |
|
|
* | |<----------------------------------------| |
|
|
* +------------+ +-------+
|
|
*
|
|
* Passive connection:
|
|
* +------------+ IRLMP_CONNECT_IND(IrlmpContext) +-------+
|
|
* | |<----------------------------------------| |
|
|
* | Tdi | IRLMP_CONNECT_RESP(TdiContext) | IRMLP |
|
|
* | |---------------------------------------->| |
|
|
* +------------+ +-------+
|
|
*
|
|
*
|
|
* Tdi calling IrlmpDown(void *pIrlmpContext, IRDA_MSG *pMsg)
|
|
* pIrlmpContext = NULL for the following:
|
|
* pMsg->Prim = IRLMP_DISCOVERY_REQ,
|
|
* IRLMP_CONNECT_REQ,
|
|
* IRLMP_FLOWON_REQ,
|
|
* IRLMP_GETVALUEBYCLASS_REQ.
|
|
* In all other cases, the pIRLMPContext must be a valid context.
|
|
*
|
|
* IRLMP calling TdiUp(void *TdiContext, IRDA_MSG *pMsg)
|
|
* TdiContext = NULL for the following:
|
|
* pMsg->Prim = IRLAP_STATUS_IND,
|
|
* IRLMP_DISCOVERY_CONF,
|
|
* IRLMP_CONNECT_IND,
|
|
* IRLMP_GETVALUEBYCLASS_CONF.
|
|
* In all other cases, the TdiContext will have a valid context.
|
|
*/
|
|
#include <irda.h>
|
|
#include <irioctl.h>
|
|
#include <irlap.h>
|
|
#include <irlmp.h>
|
|
#include <irlmpp.h>
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGE")
|
|
#endif
|
|
|
|
// IAS
|
|
UCHAR IAS_IrLMPSupport[] = {IAS_IRLMP_VERSION, IAS_SUPPORT_BIT_FIELD,
|
|
IAS_LMMUX_SUPPORT_BIT_FIELD};
|
|
|
|
CHAR IasClassName_Device[] = "Device";
|
|
CHAR IasAttribName_DeviceName[] = "DeviceName";
|
|
CHAR IasAttribName_IrLMPSupport[] = "IrLMPSupport";
|
|
CHAR IasAttribName_TTPLsapSel[] = "IrDA:TinyTP:LsapSel";
|
|
CHAR IasAttribName_IrLMPLsapSel[] = "IrDA:IrLMP:LsapSel";
|
|
CHAR IasAttribName_IrLMPLsapSel2[] = "IrDA:IrLMP:LSAPSel"; // jeez
|
|
|
|
UCHAR IasClassNameLen_Device = sizeof(IasClassName_Device)-1;
|
|
UCHAR IasAttribNameLen_DeviceName = sizeof(IasAttribName_DeviceName)-1;
|
|
UCHAR IasAttribNameLen_IrLMPSupport = sizeof(IasAttribName_IrLMPSupport)-1;
|
|
UCHAR IasAttribNameLen_TTPLsapSel = sizeof(IasAttribName_TTPLsapSel)-1;
|
|
UCHAR IasAttribNameLen_IrLMPLsapSel = sizeof(IasAttribName_IrLMPLsapSel)-1;
|
|
|
|
UINT NextObjectId;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
|
|
// Globals
|
|
LIST_ENTRY RegisteredLsaps;
|
|
LIST_ENTRY IasObjects;
|
|
LIST_ENTRY gDeviceList;
|
|
IRDA_EVENT EvDiscoveryReq;
|
|
IRDA_EVENT EvConnectReq;
|
|
IRDA_EVENT EvConnectResp;
|
|
IRDA_EVENT EvLmConnectReq;
|
|
IRDA_EVENT EvIrlmpCloseLink;
|
|
IRDA_EVENT EvRetryIasQuery;
|
|
BOOLEAN DscvReqScheduled;
|
|
LIST_ENTRY IrdaLinkCbList;
|
|
KSPIN_LOCK gSpinLock;
|
|
|
|
// Prototypes
|
|
STATIC UINT CreateLsap(PIRLMP_LINK_CB, IRLMP_LSAP_CB **);
|
|
STATIC VOID FreeLsap(IRLMP_LSAP_CB *);
|
|
STATIC VOID DeleteLsap(IRLMP_LSAP_CB *pLsapCb);
|
|
STATIC VOID TearDownConnections(PIRLMP_LINK_CB, IRLMP_DISC_REASON);
|
|
STATIC VOID IrlmpMoreCreditReq(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID IrlmpDiscoveryReq(IRDA_MSG *pMsg);
|
|
STATIC UINT IrlmpConnectReq(IRDA_MSG *);
|
|
STATIC UINT IrlmpConnectResp(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC UINT IrlmpDisconnectReq(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID IrlmpCloseLsapReq(IRLMP_LSAP_CB *);
|
|
STATIC UINT IrlmpDataReqExclusive(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC UINT IrlmpDataReqMultiplexed(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID FormatAndSendDataReq(IRLMP_LSAP_CB *, IRDA_MSG *, BOOLEAN, BOOLEAN);
|
|
STATIC UINT IrlmpAccessModeReq(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC void SetupTtp(IRLMP_LSAP_CB *);
|
|
STATIC VOID SendCntlPdu(IRLMP_LSAP_CB *, int, int, int, int);
|
|
STATIC VOID LsapResponseTimerExp(PVOID);
|
|
STATIC VOID IrlapDiscoveryConf(PIRLMP_LINK_CB, IRDA_MSG *);
|
|
STATIC void UpdateDeviceList(PIRLMP_LINK_CB, LIST_ENTRY *);
|
|
STATIC VOID IrlapConnectInd(PIRLMP_LINK_CB, IRDA_MSG *pMsg);
|
|
STATIC VOID IrlapConnectConf(PIRLMP_LINK_CB, IRDA_MSG *pMsg);
|
|
STATIC VOID IrlapDisconnectInd(PIRLMP_LINK_CB, IRDA_MSG *pMsg);
|
|
STATIC IRLMP_LSAP_CB *GetLsapInState(PIRLMP_LINK_CB, int, int, BOOLEAN);
|
|
STATIC IRLMP_LINK_CB *GetIrlmpCb(PUCHAR);
|
|
STATIC VOID DiscDelayTimerFunc(PVOID);
|
|
STATIC VOID IrlapDataConf(IRDA_MSG *pMsg);
|
|
STATIC VOID IrlapDataInd(PIRLMP_LINK_CB, IRDA_MSG *pMsg);
|
|
STATIC VOID LmPduConnectReq(PIRLMP_LINK_CB, IRDA_MSG *, int, int, UCHAR *);
|
|
STATIC VOID LmPduConnectConf(PIRLMP_LINK_CB, IRDA_MSG *, int, int, UCHAR *);
|
|
STATIC VOID LmPduDisconnectReq(PIRLMP_LINK_CB, IRDA_MSG *, int, int, UCHAR *);
|
|
STATIC VOID SendCreditPdu(IRLMP_LSAP_CB *);
|
|
STATIC VOID LmPduData(PIRLMP_LINK_CB, IRDA_MSG *, int, int);
|
|
STATIC VOID SetupTtpAndStoreConnData(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID LmPduAccessModeReq(PIRLMP_LINK_CB, int, int, UCHAR *, UCHAR *);
|
|
STATIC VOID LmPduAccessModeConf(PIRLMP_LINK_CB, int, int, UCHAR *, UCHAR *);
|
|
STATIC IRLMP_LSAP_CB *GetLsap(PIRLMP_LINK_CB, int, int);
|
|
STATIC VOID UnroutableSendLMDisc(PIRLMP_LINK_CB, int, int);
|
|
STATIC VOID ScheduleConnectReq(PIRLMP_LINK_CB);
|
|
STATIC void InitiateCloseLink(PVOID Context);
|
|
STATIC void InitiateConnectReq(PVOID Context);
|
|
STATIC void InitiateDiscoveryReq(PVOID Context);
|
|
STATIC void InitiateConnectResp(PVOID Context);
|
|
STATIC void InitiateLMConnectReq(PVOID Context);
|
|
STATIC void InitiateRetryIasQuery(PVOID Context);
|
|
STATIC UINT IrlmpGetValueByClassReq(IRDA_MSG *);
|
|
STATIC IAS_OBJECT *IasGetObject(CHAR *pClassName);
|
|
STATIC IasGetValueByClass(CONST CHAR *, int, CONST CHAR *, int, void **,
|
|
int *, UCHAR *);
|
|
STATIC VOID IasConnectReq(PIRLMP_LINK_CB, int);
|
|
STATIC VOID IasServerDisconnectReq(IRLMP_LSAP_CB *pLsapCb);
|
|
STATIC VOID IasClientDisconnectReq(IRLMP_LSAP_CB *pLsapCb, IRLMP_DISC_REASON);
|
|
STATIC VOID IasSendQueryResp(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID IasProcessQueryResp(PIRLMP_LINK_CB, IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID SendGetValueByClassReq(IRLMP_LSAP_CB *);
|
|
STATIC VOID SendGetValueByClassResp(IRLMP_LSAP_CB *, IRDA_MSG *);
|
|
STATIC VOID RegisterLsapProtocol(int Lsap, BOOLEAN UseTTP);
|
|
STATIC UINT IasAddAttribute(IAS_SET *pIASSet, PVOID *pAttribHandle);
|
|
STATIC VOID IasDelAttribute(PVOID AttribHandle);
|
|
STATIC VOID FlushDiscoveryCache();
|
|
|
|
#if DBG
|
|
TCHAR *LSAPStateStr[] =
|
|
{
|
|
TEXT("LSAP_CREATED"),
|
|
TEXT("LSAP_DISCONNECTED"),
|
|
TEXT("LSAP_IRLAP_CONN_PEND"),
|
|
TEXT("LSAP_LMCONN_CONF_PEND"),
|
|
TEXT("LSAP_CONN_RESP_PEND"),
|
|
TEXT("LSAP_CONN_REQ_PEND"),
|
|
TEXT("LSAP_EXCLUSIVEMODE_PEND"),
|
|
TEXT("LSAP_MULTIPLEXEDMODE_PEND"),
|
|
TEXT("LSAP_READY"),
|
|
TEXT("LSAP_NO_TX_CREDIT")
|
|
};
|
|
|
|
TCHAR *LinkStateStr[] =
|
|
{
|
|
TEXT("LINK_DOWN"),
|
|
TEXT("LINK_DISCONNECTED"),
|
|
TEXT("LINK_DISCONNECTING"),
|
|
TEXT("LINK_IN_DISCOVERY"),
|
|
TEXT("LINK_CONNECTING"),
|
|
TEXT("LINK_READY")
|
|
};
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(INIT, IrlmpInitialize)
|
|
|
|
#pragma alloc_text(PAGEIRDA, DeleteLsap)
|
|
#pragma alloc_text(PAGEIRDA, TearDownConnections)
|
|
#pragma alloc_text(PAGEIRDA, IrlmpAccessModeReq)
|
|
#pragma alloc_text(PAGEIRDA, SetupTtp)
|
|
#pragma alloc_text(PAGEIRDA, LsapResponseTimerExp)
|
|
#pragma alloc_text(PAGEIRDA, IrlapConnectInd)
|
|
#pragma alloc_text(PAGEIRDA, IrlapConnectConf)
|
|
#pragma alloc_text(PAGEIRDA, IrlapDisconnectInd)
|
|
#pragma alloc_text(PAGEIRDA, GetLsapInState)
|
|
#pragma alloc_text(PAGEIRDA, DiscDelayTimerFunc)
|
|
#pragma alloc_text(PAGEIRDA, LmPduConnectReq)
|
|
#pragma alloc_text(PAGEIRDA, LmPduConnectConf)
|
|
#pragma alloc_text(PAGEIRDA, SetupTtpAndStoreConnData)
|
|
#pragma alloc_text(PAGEIRDA, LmPduAccessModeReq)
|
|
#pragma alloc_text(PAGEIRDA, LmPduAccessModeConf)
|
|
#pragma alloc_text(PAGEIRDA, UnroutableSendLMDisc)
|
|
#pragma alloc_text(PAGEIRDA, ScheduleConnectReq)
|
|
#pragma alloc_text(PAGEIRDA, InitiateCloseLink)
|
|
#pragma alloc_text(PAGEIRDA, InitiateConnectReq)
|
|
#pragma alloc_text(PAGEIRDA, InitiateConnectResp)
|
|
#pragma alloc_text(PAGEIRDA, InitiateLMConnectReq)
|
|
#pragma alloc_text(PAGEIRDA, IrlmpGetValueByClassReq)
|
|
#pragma alloc_text(PAGEIRDA, IasGetValueByClass)
|
|
#pragma alloc_text(PAGEIRDA, IasConnectReq)
|
|
#pragma alloc_text(PAGEIRDA, IasServerDisconnectReq)
|
|
#pragma alloc_text(PAGEIRDA, IasClientDisconnectReq)
|
|
#pragma alloc_text(PAGEIRDA, IasSendQueryResp)
|
|
#pragma alloc_text(PAGEIRDA, IasProcessQueryResp)
|
|
#pragma alloc_text(PAGEIRDA, SendGetValueByClassReq)
|
|
#pragma alloc_text(PAGEIRDA, SendGetValueByClassResp)
|
|
#endif
|
|
/*****************************************************************************
|
|
*
|
|
*/
|
|
VOID
|
|
IrlmpInitialize()
|
|
{
|
|
PAGED_CODE();
|
|
|
|
InitializeListHead(&RegisteredLsaps);
|
|
InitializeListHead(&IasObjects);
|
|
InitializeListHead(&IrdaLinkCbList);
|
|
InitializeListHead(&gDeviceList);
|
|
|
|
KeInitializeSpinLock(&gSpinLock);
|
|
|
|
DscvReqScheduled = FALSE;
|
|
IrdaEventInitialize(&EvDiscoveryReq, InitiateDiscoveryReq);
|
|
IrdaEventInitialize(&EvConnectReq, InitiateConnectReq);
|
|
IrdaEventInitialize(&EvConnectResp,InitiateConnectResp);
|
|
IrdaEventInitialize(&EvLmConnectReq, InitiateLMConnectReq);
|
|
IrdaEventInitialize(&EvIrlmpCloseLink, InitiateCloseLink);
|
|
IrdaEventInitialize(&EvRetryIasQuery, InitiateRetryIasQuery);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
*/
|
|
VOID
|
|
IrdaShutdown()
|
|
{
|
|
PIRDA_LINK_CB pIrdaLinkCb, pIrdaLinkCbNext;
|
|
KIRQL OldIrql;
|
|
LARGE_INTEGER SleepMs;
|
|
NTSTATUS Status;
|
|
UINT Seconds;
|
|
|
|
SleepMs.QuadPart = -(10*1000*1000); // 1 second
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = pIrdaLinkCbNext)
|
|
{
|
|
pIrdaLinkCbNext = (PIRDA_LINK_CB) pIrdaLinkCb->Linkage.Flink;
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
IrlmpCloseLink(pIrdaLinkCb);
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
}
|
|
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
Seconds = 0;
|
|
while (Seconds < 30)
|
|
{
|
|
if (IsListEmpty(&IrdaLinkCbList))
|
|
break;
|
|
|
|
KeDelayExecutionThread(KernelMode, FALSE, &SleepMs);
|
|
|
|
Seconds++;
|
|
}
|
|
|
|
#if DBG
|
|
if (Seconds >= 30)
|
|
{
|
|
DbgPrint("Link left open at shutdown!\n");
|
|
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = pIrdaLinkCbNext)
|
|
{
|
|
DbgPrint("pIrdaLinkCb: %X\n", pIrdaLinkCb);
|
|
DbgPrint(" pIrlmpCb: %X\n", pIrdaLinkCb->IrlmpContext);
|
|
DbgPrint(" pIrlapCb: %X\n", pIrdaLinkCb->IrlapContext);
|
|
}
|
|
ASSERT(0);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("Irda shutdown complete\n");
|
|
}
|
|
#endif
|
|
|
|
KeDelayExecutionThread(KernelMode, FALSE, &SleepMs);
|
|
|
|
NdisDeregisterProtocol(&Status, NdisIrdaHandle);
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
*/
|
|
VOID
|
|
IrlmpOpenLink(OUT PNTSTATUS Status,
|
|
IN PIRDA_LINK_CB pIrdaLinkCb,
|
|
IN UCHAR *pDeviceName,
|
|
IN int DeviceNameLen,
|
|
IN UCHAR CharSet)
|
|
{
|
|
PIRLMP_LINK_CB pIrlmpCb;
|
|
ULONG IASBuf[(sizeof(IAS_SET) + 128)/sizeof(ULONG)];
|
|
IAS_SET *pIASSet;
|
|
|
|
*Status = STATUS_SUCCESS;
|
|
|
|
if (IRDA_ALLOC_MEM(pIrlmpCb, sizeof(IRLMP_LINK_CB), MT_IRLMPCB) == NULL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("Alloc failed\n")));
|
|
*Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
}
|
|
|
|
pIrdaLinkCb->IrlmpContext = pIrlmpCb;
|
|
|
|
#if DBG
|
|
pIrlmpCb->DiscDelayTimer.pName = "DiscDelay";
|
|
#endif
|
|
IrdaTimerInitialize(&pIrlmpCb->DiscDelayTimer,
|
|
DiscDelayTimerFunc,
|
|
IRLMP_DISCONNECT_DELAY_TIMEOUT,
|
|
pIrlmpCb,
|
|
pIrdaLinkCb);
|
|
|
|
InitializeListHead(&pIrlmpCb->LsapCbList);
|
|
InitializeListHead(&pIrlmpCb->DeviceList);
|
|
|
|
pIrlmpCb->pIrdaLinkCb = pIrdaLinkCb;
|
|
pIrlmpCb->ConnReqScheduled = FALSE;
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTED;
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
pIrlmpCb->pIasQuery = NULL;
|
|
|
|
// ConnDevAddrSet is set to true if LINK_IN_DISCOVERY or
|
|
// LINK_DISCONNECTING and an LSAP requests a connection. Subsequent
|
|
// LSAP connection requests check to see if this flag is set. If so
|
|
// the requested device address must match that contained in the
|
|
// IRLMP control block (set by the first connect request)
|
|
pIrlmpCb->ConnDevAddrSet = FALSE;
|
|
|
|
// Add device info to IAS
|
|
pIASSet = (IAS_SET *) IASBuf;
|
|
RtlCopyMemory(pIASSet->irdaClassName, IasClassName_Device,
|
|
IasClassNameLen_Device+1);
|
|
RtlCopyMemory(pIASSet->irdaAttribName, IasAttribName_DeviceName,
|
|
IasAttribNameLen_DeviceName+1);
|
|
pIASSet->irdaAttribType = IAS_ATTRIB_VAL_STRING;
|
|
RtlCopyMemory(pIASSet->irdaAttribute.irdaAttribUsrStr.UsrStr,
|
|
pDeviceName,
|
|
DeviceNameLen + 1);
|
|
pIASSet->irdaAttribute.irdaAttribUsrStr.CharSet = CharSet;
|
|
pIASSet->irdaAttribute.irdaAttribUsrStr.Len = (u_char) DeviceNameLen;
|
|
IasAddAttribute(pIASSet, &pIrlmpCb->hAttribDeviceName);
|
|
|
|
RtlCopyMemory(pIASSet->irdaClassName, IasClassName_Device,
|
|
IasClassNameLen_Device+1);
|
|
RtlCopyMemory(pIASSet->irdaAttribName, IasAttribName_IrLMPSupport,
|
|
IasAttribNameLen_IrLMPSupport+1);
|
|
pIASSet->irdaAttribType = IAS_ATTRIB_VAL_BINARY;
|
|
RtlCopyMemory(pIASSet->irdaAttribute.irdaAttribOctetSeq.OctetSeq,
|
|
IAS_IrLMPSupport, sizeof(IAS_IrLMPSupport));
|
|
pIASSet->irdaAttribute.irdaAttribOctetSeq.Len =
|
|
sizeof(IAS_IrLMPSupport);
|
|
|
|
IasAddAttribute(pIASSet, &pIrlmpCb->hAttribIrlmpSupport);
|
|
|
|
if (*Status != STATUS_SUCCESS)
|
|
{
|
|
IRDA_FREE_MEM(pIrlmpCb);
|
|
}
|
|
else
|
|
{
|
|
ExInterlockedInsertTailList(&IrdaLinkCbList,
|
|
&pIrdaLinkCb->Linkage,
|
|
&gSpinLock);
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP initialized, status %x\n"), *Status));
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
VOID
|
|
IrlmpDeleteInstance(PVOID Context)
|
|
{
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) Context;
|
|
PIRLMP_LINK_CB pIrlmpCb2;
|
|
PIRDA_LINK_CB pIrdaLinkCb;
|
|
KIRQL OldIrql;
|
|
BOOLEAN RescheduleDiscovery = FALSE;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Delete instance %X\n"), Context));
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
FlushDiscoveryCache();
|
|
|
|
// We may have been in the middle of discovery when
|
|
// this link went down. Reschedule the discovery inorder
|
|
// to complete the discovery request.
|
|
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = (PIRDA_LINK_CB) pIrdaLinkCb->Linkage.Flink)
|
|
{
|
|
pIrlmpCb2 = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
if (pIrlmpCb2->DiscoveryFlags)
|
|
{
|
|
RescheduleDiscovery = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// remove IrdaLinkCb from List
|
|
|
|
RemoveEntryList(&pIrlmpCb->pIrdaLinkCb->Linkage);
|
|
|
|
if (IsListEmpty(&IrdaLinkCbList))
|
|
{
|
|
RescheduleDiscovery = TRUE;
|
|
}
|
|
|
|
ASSERT(IsListEmpty(&pIrlmpCb->LsapCbList));
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
IasDelAttribute(pIrlmpCb->hAttribDeviceName);
|
|
IasDelAttribute(pIrlmpCb->hAttribIrlmpSupport);
|
|
|
|
IRDA_FREE_MEM(pIrlmpCb);
|
|
|
|
if (RescheduleDiscovery)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Reschedule discovery, link gone\n")));
|
|
IrdaEventSchedule(&EvDiscoveryReq, NULL);
|
|
}
|
|
|
|
/*
|
|
// Cleanup registered LSAP
|
|
while (!IsListEmpty(&RegisteredLsaps))
|
|
{
|
|
pPtr = RemoveHeadList(&RegisteredLsaps);
|
|
IRDA_FREE_MEM(pPtr);
|
|
}
|
|
// And the device list
|
|
while (!IsListEmpty(&DeviceList))
|
|
{
|
|
pPtr = RemoveHeadList(&DeviceList);
|
|
IRDA_FREE_MEM(pPtr);
|
|
}
|
|
|
|
// And IAS entries
|
|
for (pObject = (IAS_OBJECT *) IasObjects.Flink;
|
|
(LIST_ENTRY *) pObject != &IasObjects;
|
|
pObject = pNextObject)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Deleting object %s\n"),
|
|
pObject->pClassName));
|
|
|
|
// Get the next cuz this ones being deleted
|
|
pNextObject = (IAS_OBJECT *) pObject->Linkage.Flink;
|
|
|
|
IasDeleteObject(pObject->pClassName);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpCloseLink | Shuts down IRDA stack
|
|
*
|
|
* @rdesc SUCCESS or error
|
|
*
|
|
*/
|
|
VOID
|
|
IrlmpCloseLink(PIRDA_LINK_CB pIrdaLinkCb)
|
|
{
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
DEBUGMSG(1, (TEXT("IRLMP: CloseLink request\n")));
|
|
|
|
if (pIrlmpCb->LinkState == LINK_DOWN)
|
|
{
|
|
DEBUGMSG(1, (TEXT("IRLMP: Link already down, ignoring\n")));
|
|
return;
|
|
}
|
|
|
|
if (pIrlmpCb->LinkState == LINK_IN_DISCOVERY)
|
|
{
|
|
// Discovery was interrupted so schedule the next link
|
|
IrdaEventSchedule(&EvDiscoveryReq, NULL);
|
|
}
|
|
|
|
pIrlmpCb->LinkState = LINK_DOWN;
|
|
|
|
IrdaEventSchedule(&EvIrlmpCloseLink, pIrdaLinkCb);
|
|
|
|
return;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpRegisterLSAPProtocol | Bag to let IRLMP know if
|
|
* a connect ind is using TTP
|
|
* @rdesc SUCCESS or error
|
|
*
|
|
* @parm int | LSAP | LSAP being registered
|
|
* @parm BOOLEAN | UseTtp |
|
|
*/
|
|
VOID
|
|
RegisterLsapProtocol(int Lsap, BOOLEAN UseTtp)
|
|
{
|
|
PIRLMP_REGISTERED_LSAP pRegLsap;
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
for (pRegLsap = (PIRLMP_REGISTERED_LSAP) RegisteredLsaps.Flink;
|
|
(LIST_ENTRY *) pRegLsap != &RegisteredLsaps;
|
|
pRegLsap = (PIRLMP_REGISTERED_LSAP) pRegLsap->Linkage.Flink)
|
|
{
|
|
if (pRegLsap->Lsap == Lsap)
|
|
{
|
|
|
|
if (UseTtp) {
|
|
|
|
pRegLsap->Flags |= LCBF_USE_TTP;
|
|
|
|
} else {
|
|
|
|
pRegLsap->Flags &= ~LCBF_USE_TTP;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (IRDA_ALLOC_MEM(pRegLsap, sizeof(IRLMP_REGISTERED_LSAP),
|
|
MT_IRLMP_REGLSAP) == NULL)
|
|
{
|
|
ASSERT(0);
|
|
goto done;
|
|
}
|
|
pRegLsap->Lsap = Lsap;
|
|
pRegLsap->Flags = UseTtp ? LCBF_USE_TTP : 0;
|
|
InsertTailList(&RegisteredLsaps, &pRegLsap->Linkage);
|
|
|
|
done:
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: LSAP %x registered, %s\n"), Lsap,
|
|
UseTtp ? TEXT("use TTP") : TEXT("no TTP")));
|
|
}
|
|
|
|
VOID
|
|
DeregisterLsapProtocol(int Lsap)
|
|
{
|
|
PIRLMP_REGISTERED_LSAP pRegLsap;
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
for (pRegLsap = (PIRLMP_REGISTERED_LSAP) RegisteredLsaps.Flink;
|
|
(LIST_ENTRY *) pRegLsap != &RegisteredLsaps;
|
|
pRegLsap = (PIRLMP_REGISTERED_LSAP) pRegLsap->Linkage.Flink)
|
|
{
|
|
if (pRegLsap->Lsap == Lsap)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: LSAP %x deregistered\n"),
|
|
Lsap));
|
|
|
|
RemoveEntryList(&pRegLsap->Linkage);
|
|
|
|
IRDA_FREE_MEM(pRegLsap);
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | FreeLsap | Delete an Lsap control context and
|
|
*
|
|
* @rdesc pointer to Lsap context or 0 on error
|
|
*
|
|
* @parm void | pLsapCb | pointer to an Lsap control block
|
|
*/
|
|
void
|
|
FreeLsap(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
ASSERT(pLsapCb->State == LSAP_DISCONNECTED);
|
|
|
|
ASSERT(IsListEmpty(&pLsapCb->SegTxMsgList));
|
|
|
|
ASSERT(IsListEmpty(&pLsapCb->TxMsgList));
|
|
|
|
LOCK_LINK(pLsapCb->pIrlmpCb->pIrdaLinkCb);
|
|
|
|
#ifdef DBG
|
|
pLsapCb->Sig = 0xdaeddead;
|
|
#endif
|
|
|
|
RemoveEntryList(&pLsapCb->Linkage);
|
|
|
|
UNLOCK_LINK(pLsapCb->pIrlmpCb->pIrdaLinkCb);
|
|
|
|
IrdaTimerStop(&pLsapCb->ResponseTimer);
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Deleting LsapCb:%X (%d,%d)\n"),
|
|
pLsapCb, pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel));
|
|
|
|
REFDEL(&pLsapCb->pIrlmpCb->pIrdaLinkCb->RefCnt, 'PASL');
|
|
|
|
IRDA_FREE_MEM(pLsapCb);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | CreateLsap | Create an LSAP control context and
|
|
*/
|
|
UINT
|
|
CreateLsap(PIRLMP_LINK_CB pIrlmpCb, IRLMP_LSAP_CB **ppLsapCb)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
*ppLsapCb = NULL;
|
|
|
|
IRDA_ALLOC_MEM(*ppLsapCb, sizeof(IRLMP_LSAP_CB), MT_IRLMP_LSAP_CB);
|
|
|
|
if (*ppLsapCb == NULL)
|
|
{
|
|
return IRLMP_ALLOC_FAILED;
|
|
}
|
|
|
|
CTEMemSet(*ppLsapCb, 0, sizeof(IRLMP_LSAP_CB));
|
|
|
|
(*ppLsapCb)->pIrlmpCb = pIrlmpCb;
|
|
(*ppLsapCb)->State = LSAP_CREATED;
|
|
(*ppLsapCb)->UserDataLen = 0;
|
|
(*ppLsapCb)->DiscReason = IRLMP_NO_RESPONSE_LSAP;
|
|
|
|
InitializeListHead(&(*ppLsapCb)->TxMsgList);
|
|
InitializeListHead(&(*ppLsapCb)->SegTxMsgList);
|
|
|
|
ReferenceInit(&(*ppLsapCb)->RefCnt, *ppLsapCb, FreeLsap);
|
|
REFADD(&(*ppLsapCb)->RefCnt, ' TS1');
|
|
|
|
#if DBG
|
|
(*ppLsapCb)->ResponseTimer.pName = "ResponseTimer";
|
|
|
|
(*ppLsapCb)->Sig = LSAPSIG;
|
|
|
|
#endif
|
|
IrdaTimerInitialize(&(*ppLsapCb)->ResponseTimer,
|
|
LsapResponseTimerExp,
|
|
LSAP_RESPONSE_TIMEOUT,
|
|
*ppLsapCb,
|
|
pIrlmpCb->pIrdaLinkCb);
|
|
|
|
// Insert into list in the link control block
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
InsertTailList(&pIrlmpCb->LsapCbList, &((*ppLsapCb)->Linkage));
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
REFADD(&pIrlmpCb->pIrdaLinkCb->RefCnt, 'PASL');
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: New LsapCb:%X\n"),
|
|
*ppLsapCb));
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
void
|
|
DeleteLsap(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
IRDA_MSG IMsg, *pMsg, *pNextMsg, *pSegParentMsg;
|
|
|
|
PAGED_CODE();
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
pLsapCb->State = LSAP_CREATED;
|
|
return;
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: DeleteLsap:%X\n"), pLsapCb));
|
|
|
|
if (pLsapCb->State == LSAP_DISCONNECTED)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb == pLsapCb->pIrlmpCb->pExclLsapCb)
|
|
{
|
|
pLsapCb->pIrlmpCb->pExclLsapCb = NULL;
|
|
}
|
|
|
|
pLsapCb->State = LSAP_DISCONNECTED;
|
|
|
|
// Clean up the segmented tx msg list
|
|
while (!IsListEmpty(&pLsapCb->SegTxMsgList))
|
|
{
|
|
pMsg = (IRDA_MSG *) RemoveHeadList(&pLsapCb->SegTxMsgList);
|
|
|
|
// Decrement the segment counter contained in the parent data request
|
|
pSegParentMsg = pMsg->DataContext;
|
|
pSegParentMsg->IRDA_MSG_SegCount -= 1;
|
|
// IRLMP owns these
|
|
FreeIrdaBuf(IrdaMsgPool, pMsg);
|
|
}
|
|
|
|
// return any outstanding data requests (unless there are outstanding segments)
|
|
for (pMsg = (IRDA_MSG *) pLsapCb->TxMsgList.Flink;
|
|
(LIST_ENTRY *) pMsg != &(pLsapCb->TxMsgList);
|
|
pMsg = pNextMsg)
|
|
{
|
|
pNextMsg = (IRDA_MSG *) pMsg->Linkage.Flink;
|
|
|
|
if (pMsg->IRDA_MSG_SegCount == 0)
|
|
{
|
|
RemoveEntryList(&pMsg->Linkage);
|
|
|
|
if (pLsapCb->TdiContext)
|
|
{
|
|
pMsg->Prim = IRLMP_DATA_CONF;
|
|
pMsg->IRDA_MSG_DataStatus = IRLMP_DATA_REQUEST_FAILED;
|
|
TdiUp(pLsapCb->TdiContext, pMsg);
|
|
}
|
|
else
|
|
{
|
|
CTEAssert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pLsapCb->TdiContext && (pLsapCb->Flags & LCBF_TDI_OPEN))
|
|
{
|
|
IMsg.Prim = IRLMP_DISCONNECT_IND;
|
|
IMsg.IRDA_MSG_DiscReason = pLsapCb->DiscReason;
|
|
IMsg.IRDA_MSG_pDiscData = NULL;
|
|
IMsg.IRDA_MSG_DiscDataLen = 0;
|
|
|
|
TdiUp(pLsapCb->TdiContext, &IMsg);
|
|
}
|
|
|
|
pLsapCb->LocalLsapSel = -1;
|
|
pLsapCb->RemoteLsapSel = -1;
|
|
|
|
REFDEL(&pLsapCb->RefCnt, ' TS1');
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func void | TearDownConnections | Tears down and cleans up connections
|
|
*
|
|
* @parm IRLMP_DISC_REASONE| DiscReason | The reason connections are being
|
|
* torn down. Passed to IRLMP clients
|
|
* in IRLMP_DISCONNECT_IND
|
|
*/
|
|
void
|
|
TearDownConnections(PIRLMP_LINK_CB pIrlmpCb, IRLMP_DISC_REASON DiscReason)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb, *pLsapCbNext;
|
|
|
|
PAGED_CODE();
|
|
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Tearing down connections\r\n")));
|
|
|
|
// Clean up each LSAP
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = pLsapCbNext)
|
|
{
|
|
pLsapCbNext = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Teardown LsapCb:%X\n"), pLsapCb));
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->LocalLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasServerDisconnectReq(pLsapCb);
|
|
continue;
|
|
}
|
|
|
|
if (pLsapCb->LocalLsapSel == IAS_LOCAL_LSAP_SEL &&
|
|
pLsapCb->RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasClientDisconnectReq(pLsapCb, DiscReason);
|
|
}
|
|
else
|
|
{
|
|
IrdaTimerStop(&pLsapCb->ResponseTimer);
|
|
|
|
if (pLsapCb->State != LSAP_DISCONNECTED)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP: Sending IRLMP Disconnect Ind\r\n")));
|
|
|
|
pLsapCb->DiscReason = DiscReason;
|
|
|
|
DeleteLsap(pLsapCb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpDown | Message from Upper layer to LMP
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm void * | void_pLsapCb | void pointer to an LSAP_CB. Can be NULL
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpDown(void *void_pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
UINT rc = SUCCESS;
|
|
PIRDA_LINK_CB pIrdaLinkCb = NULL;
|
|
|
|
IRLMP_LSAP_CB *pLsapCb =
|
|
(IRLMP_LSAP_CB *) void_pLsapCb;
|
|
|
|
if (pLsapCb)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
pIrdaLinkCb = pLsapCb->pIrlmpCb->pIrdaLinkCb;
|
|
|
|
// This could be the last lsap closing so
|
|
// add a reference to the IrdaLinkCb so
|
|
// it won't go away before we have a chance
|
|
// to call UNLOCK_LINK
|
|
|
|
REFADD(&pIrdaLinkCb->RefCnt, 'NWDI');
|
|
|
|
LOCK_LINK(pIrdaLinkCb);
|
|
}
|
|
|
|
switch (pMsg->Prim)
|
|
{
|
|
case IRLMP_DISCOVERY_REQ:
|
|
IrlmpDiscoveryReq(pMsg);
|
|
break;
|
|
|
|
case IRLMP_CONNECT_REQ:
|
|
rc = IrlmpConnectReq(pMsg);
|
|
break;
|
|
|
|
case IRLMP_CONNECT_RESP:
|
|
if (!pLsapCb)
|
|
{
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
rc = IrlmpConnectResp(pLsapCb, pMsg);
|
|
break;
|
|
|
|
case IRLMP_DISCONNECT_REQ:
|
|
if (!pLsapCb)
|
|
{
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
rc = IrlmpDisconnectReq(pLsapCb, pMsg);
|
|
break;
|
|
|
|
case IRLMP_CLOSELSAP_REQ:
|
|
if (!pLsapCb)
|
|
{
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
IrlmpCloseLsapReq(pLsapCb);
|
|
break;
|
|
|
|
case IRLMP_DATA_REQ:
|
|
if (!pLsapCb)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: error IRLMP_DATA_REQ on null LsapCb\n")));
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
if (pLsapCb->pIrlmpCb->pExclLsapCb != NULL)
|
|
{
|
|
rc = IrlmpDataReqExclusive(pLsapCb, pMsg);
|
|
}
|
|
else
|
|
{
|
|
rc = IrlmpDataReqMultiplexed(pLsapCb, pMsg);
|
|
}
|
|
break;
|
|
|
|
case IRLMP_ACCESSMODE_REQ:
|
|
if (!pLsapCb)
|
|
{
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
rc = IrlmpAccessModeReq(pLsapCb, pMsg);
|
|
break;
|
|
|
|
case IRLMP_MORECREDIT_REQ:
|
|
if (!pLsapCb)
|
|
{
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
IrlmpMoreCreditReq(pLsapCb, pMsg);
|
|
break;
|
|
|
|
case IRLMP_GETVALUEBYCLASS_REQ:
|
|
rc = IrlmpGetValueByClassReq(pMsg);
|
|
break;
|
|
|
|
case IRLMP_REGISTERLSAP_REQ:
|
|
RegisterLsapProtocol(pMsg->IRDA_MSG_LocalLsapSel,
|
|
pMsg->IRDA_MSG_UseTtp);
|
|
break;
|
|
|
|
case IRLMP_DEREGISTERLSAP_REQ:
|
|
DeregisterLsapProtocol(pMsg->IRDA_MSG_LocalLsapSel);
|
|
break;
|
|
|
|
case IRLMP_ADDATTRIBUTE_REQ:
|
|
rc = IasAddAttribute(pMsg->IRDA_MSG_pIasSet, pMsg->IRDA_MSG_pAttribHandle);
|
|
break;
|
|
|
|
case IRLMP_DELATTRIBUTE_REQ:
|
|
IasDelAttribute(pMsg->IRDA_MSG_AttribHandle);
|
|
break;
|
|
|
|
case IRLMP_FLUSHDSCV_REQ:
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
FlushDiscoveryCache();
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
break;
|
|
}
|
|
|
|
case IRLAP_STATUS_REQ:
|
|
if (!pLsapCb)
|
|
{
|
|
rc = IRLMP_INVALID_LSAP_CB;
|
|
break;
|
|
}
|
|
IrlapDown(pLsapCb->pIrlmpCb->pIrdaLinkCb->IrlapContext, pMsg);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
if (pIrdaLinkCb)
|
|
{
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
|
|
REFDEL(&pIrdaLinkCb->RefCnt, 'NWDI');
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
VOID
|
|
IrlmpMoreCreditReq(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
int CurrentAvail = pLsapCb->AvailableCredit;
|
|
|
|
pLsapCb->AvailableCredit += pMsg->IRDA_MSG_TtpCredits;
|
|
|
|
if (pLsapCb->Flags & LCBF_USE_TTP)
|
|
{
|
|
if (CurrentAvail == 0)
|
|
{
|
|
// remote peer completely out of credit, send'm some
|
|
SendCreditPdu(pLsapCb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pLsapCb == pLsapCb->pIrlmpCb->pExclLsapCb)
|
|
{
|
|
pLsapCb->RemoteTxCredit += pMsg->IRDA_MSG_TtpCredits;
|
|
pMsg->Prim = IRLAP_FLOWON_REQ;
|
|
IrlapDown(pLsapCb->pIrlmpCb->pIrdaLinkCb->IrlapContext, pMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpDiscoveryReq | initiates a discovery request
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
VOID
|
|
IrlmpDiscoveryReq(IRDA_MSG *pMsg)
|
|
{
|
|
PIRDA_LINK_CB pIrdaLinkCb;
|
|
PIRLMP_LINK_CB pIrlmpCb;
|
|
KIRQL OldIrql;
|
|
|
|
DEBUGMSG(DBG_DISCOVERY, (TEXT("IRLMP: IRLMP_DISCOVERY_REQ\n")));
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
if (DscvReqScheduled)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Discovery already schedule\n")));
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
}
|
|
else
|
|
{
|
|
// Flag each link for discovery
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = (PIRDA_LINK_CB) pIrdaLinkCb->Linkage.Flink)
|
|
{
|
|
pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
pIrlmpCb->DiscoveryFlags = DF_NORMAL_DSCV;
|
|
|
|
if (pIrlmpCb->LinkState == LINK_DOWN &&
|
|
!IsListEmpty(&pIrlmpCb->DeviceList))
|
|
{
|
|
FlushDiscoveryCache();
|
|
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Flush discovery cache, link down\n")));
|
|
|
|
}
|
|
|
|
DscvReqScheduled = TRUE;
|
|
}
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
// Schedule the first link
|
|
|
|
IrdaEventSchedule(&EvDiscoveryReq, NULL);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpConnectReq | Process IRLMP connect request
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpConnectReq(IRDA_MSG *pMsg)
|
|
{
|
|
PIRLMP_LSAP_CB pLsapCb = NULL;
|
|
PIRLMP_LINK_CB pIrlmpCb = GetIrlmpCb(pMsg->IRDA_MSG_RemoteDevAddr);
|
|
UINT rc = SUCCESS;
|
|
|
|
if (pIrlmpCb == NULL)
|
|
return IRLMP_BAD_DEV_ADDR;
|
|
|
|
LOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
if (pIrlmpCb->pExclLsapCb != NULL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: IrlmpConnectReq failed, link in exclusive mode\n")));
|
|
rc = IRLMP_IN_EXCLUSIVE_MODE;
|
|
}
|
|
else if ((pLsapCb = GetLsap(pIrlmpCb, pMsg->IRDA_MSG_LocalLsapSel,
|
|
pMsg->IRDA_MSG_RemoteLsapSel)) != NULL &&
|
|
pLsapCb->RemoteLsapSel != IAS_LSAP_SEL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: IrlmpConnectReq failed, LsapSel in use\n")));
|
|
rc = IRLMP_LSAP_SEL_IN_USE;
|
|
}
|
|
else if ((UINT)pMsg->IRDA_MSG_ConnDataLen > IRLMP_MAX_USER_DATA_LEN)
|
|
{
|
|
rc = IRLMP_USER_DATA_LEN_EXCEEDED;
|
|
}
|
|
else if (pLsapCb == NULL && CreateLsap(pIrlmpCb, &pLsapCb) != SUCCESS)
|
|
{
|
|
rc = 1;
|
|
}
|
|
|
|
if (rc != SUCCESS)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// Initialize the LSAP endpoint
|
|
pLsapCb->LocalLsapSel = pMsg->IRDA_MSG_LocalLsapSel;
|
|
pLsapCb->RemoteLsapSel = pMsg->IRDA_MSG_RemoteLsapSel;
|
|
pLsapCb->TdiContext = pMsg->IRDA_MSG_pContext;
|
|
pLsapCb->RxMaxSDUSize = pMsg->IRDA_MSG_MaxSDUSize;
|
|
pLsapCb->AvailableCredit = pMsg->IRDA_MSG_TtpCredits;
|
|
pLsapCb->UserDataLen = pMsg->IRDA_MSG_ConnDataLen;
|
|
pLsapCb->Flags |= pMsg->IRDA_MSG_UseTtp ? LCBF_USE_TTP : 0;
|
|
|
|
RtlCopyMemory(pLsapCb->UserData, pMsg->IRDA_MSG_pConnData,
|
|
pMsg->IRDA_MSG_ConnDataLen);
|
|
|
|
// TDI can abort this connection before the confirm
|
|
// from peer is received. TDI will call into LMP to
|
|
// do this so we must return the Lsap context now.
|
|
// This is the only time we actually return something
|
|
// in an Irda Message.
|
|
pMsg->IRDA_MSG_pContext = pLsapCb;
|
|
|
|
DEBUGMSG((DBG_IRLMP | DBG_IRLMP_CONN),
|
|
(TEXT("IRLMP: IRLMP_CONNECT_REQ (l=%d,r=%d), Tdi:%X LinkState=%s\r\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel, pLsapCb->TdiContext,
|
|
pIrlmpCb->LinkState == LINK_DISCONNECTED ? TEXT("DISCONNECTED") :
|
|
pIrlmpCb->LinkState == LINK_IN_DISCOVERY ? TEXT("IN_DISCOVERY") :
|
|
pIrlmpCb->LinkState == LINK_DISCONNECTING? TEXT("DISCONNECTING"):
|
|
pIrlmpCb->LinkState == LINK_READY ? TEXT("READY") : TEXT("oh!")));
|
|
|
|
switch (pIrlmpCb->LinkState)
|
|
{
|
|
case LINK_DISCONNECTED:
|
|
RtlCopyMemory(pIrlmpCb->ConnDevAddr, pMsg->IRDA_MSG_RemoteDevAddr,
|
|
IRDA_DEV_ADDR_LEN);
|
|
|
|
pLsapCb->State = LSAP_IRLAP_CONN_PEND;
|
|
SetupTtp(pLsapCb);
|
|
|
|
pMsg->Prim = IRLAP_CONNECT_REQ;
|
|
rc = IrlapDown(pIrlmpCb->pIrdaLinkCb->IrlapContext, pMsg);
|
|
if (rc == SUCCESS)
|
|
{
|
|
pIrlmpCb->LinkState = LINK_CONNECTING;
|
|
}
|
|
|
|
break;
|
|
|
|
case LINK_IN_DISCOVERY:
|
|
case LINK_DISCONNECTING:
|
|
if (pIrlmpCb->ConnDevAddrSet == FALSE)
|
|
{
|
|
// Ensure that only the first device to request a connection
|
|
// sets the device address of the remote to be connected to.
|
|
RtlCopyMemory(pIrlmpCb->ConnDevAddr, pMsg->IRDA_MSG_RemoteDevAddr,
|
|
IRDA_DEV_ADDR_LEN);
|
|
pIrlmpCb->ConnDevAddrSet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Link in use!\r\n")));
|
|
|
|
if (CTEMemCmp(pMsg->IRDA_MSG_RemoteDevAddr,
|
|
pIrlmpCb->ConnDevAddr,
|
|
IRDA_DEV_ADDR_LEN) != 0)
|
|
{
|
|
// This LSAP is requesting a connection to another device
|
|
DeleteLsap(pLsapCb);
|
|
rc = IRLMP_LINK_IN_USE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pLsapCb->State = LSAP_CONN_REQ_PEND;
|
|
SetupTtp(pLsapCb);
|
|
|
|
// This request will complete when discovery/disconnect ends
|
|
break;
|
|
|
|
case LINK_CONNECTING:
|
|
if (CTEMemCmp(pMsg->IRDA_MSG_RemoteDevAddr,
|
|
pIrlmpCb->ConnDevAddr,
|
|
IRDA_DEV_ADDR_LEN) != 0)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Link in use!\r\n")));
|
|
// This LSAP is requesting a connection to another device,
|
|
// not the one IRLAP is currently connected to
|
|
DeleteLsap(pLsapCb);
|
|
rc = IRLMP_LINK_IN_USE;
|
|
break;
|
|
}
|
|
|
|
// The LSAP will be notified when the IRLAP connection that is
|
|
// underway has completed (see IRLAP_ConnectConf)
|
|
pLsapCb->State = LSAP_IRLAP_CONN_PEND;
|
|
|
|
SetupTtp(pLsapCb);
|
|
|
|
break;
|
|
|
|
case LINK_READY:
|
|
if (CTEMemCmp(pMsg->IRDA_MSG_RemoteDevAddr,
|
|
pIrlmpCb->ConnDevAddr,
|
|
IRDA_DEV_ADDR_LEN) != 0)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Link in use!\r\n")));
|
|
// This LSAP is requesting a connection to another device
|
|
DeleteLsap(pLsapCb);
|
|
rc = IRLMP_LINK_IN_USE;
|
|
break;
|
|
}
|
|
IrdaTimerRestart(&pLsapCb->ResponseTimer);
|
|
|
|
pLsapCb->State = LSAP_LMCONN_CONF_PEND;
|
|
SetupTtp(pLsapCb);
|
|
|
|
// Ask remote LSAP for a connection
|
|
SendCntlPdu(pLsapCb, IRLMP_CONNECT_PDU,
|
|
IRLMP_ABIT_REQUEST, IRLMP_RSVD_PARM, 0);
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
|
|
if (pLsapCb)
|
|
{
|
|
if (rc == SUCCESS)
|
|
{
|
|
if (pLsapCb->RemoteLsapSel != IAS_LSAP_SEL)
|
|
{
|
|
pLsapCb->Flags |= LCBF_TDI_OPEN;
|
|
REFADD(&pLsapCb->RefCnt, 'NEPO');
|
|
}
|
|
}
|
|
else if (pLsapCb->RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
DeleteLsap(pLsapCb);
|
|
}
|
|
}
|
|
|
|
UNLOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
return rc;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func void | SetupTtp | if using TTP, calculate initial credits
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
*/
|
|
void
|
|
SetupTtp(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->AvailableCredit > 127)
|
|
{
|
|
pLsapCb->RemoteTxCredit = 127;
|
|
pLsapCb->AvailableCredit -= 127;
|
|
}
|
|
else
|
|
{
|
|
pLsapCb->RemoteTxCredit = pLsapCb->AvailableCredit;
|
|
pLsapCb->AvailableCredit = 0;
|
|
}
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: RemoteTxCredit %d\n"),
|
|
pLsapCb->RemoteTxCredit));
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpConnectResp | Process IRLMP connect response
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpConnectResp(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: IRLMP_CONNECT_RESP l=%d r=%d\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel));
|
|
|
|
if (pLsapCb->pIrlmpCb->LinkState != LINK_READY)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Bad link state\n")));
|
|
ASSERT(0);
|
|
return IRLMP_LINK_BAD_STATE;
|
|
}
|
|
|
|
if (pLsapCb->State != LSAP_CONN_RESP_PEND)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Bad LSAP state\n")));
|
|
ASSERT(0);
|
|
return IRLMP_LSAP_BAD_STATE;
|
|
}
|
|
|
|
IrdaTimerStop(&pLsapCb->ResponseTimer);
|
|
|
|
if (pMsg->IRDA_MSG_ConnDataLen > IRLMP_MAX_USER_DATA_LEN)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: User data len exceeded\n")));
|
|
return IRLMP_USER_DATA_LEN_EXCEEDED;
|
|
}
|
|
|
|
pLsapCb->RxMaxSDUSize = pMsg->IRDA_MSG_MaxSDUSize;
|
|
pLsapCb->UserDataLen = pMsg->IRDA_MSG_ConnDataLen;
|
|
pLsapCb->AvailableCredit = pMsg->IRDA_MSG_TtpCredits;
|
|
RtlCopyMemory(pLsapCb->UserData, pMsg->IRDA_MSG_pConnData,
|
|
pMsg->IRDA_MSG_ConnDataLen);
|
|
|
|
pLsapCb->TdiContext = pMsg->IRDA_MSG_pContext;
|
|
|
|
CTEAssert(pLsapCb->TdiContext);
|
|
|
|
SetupTtp(pLsapCb);
|
|
|
|
pLsapCb->State = LSAP_READY;
|
|
|
|
pLsapCb->Flags |= LCBF_TDI_OPEN;
|
|
|
|
REFADD(&pLsapCb->RefCnt, 'NEPO');
|
|
|
|
SendCntlPdu(pLsapCb, IRLMP_CONNECT_PDU, IRLMP_ABIT_CONFIRM,
|
|
IRLMP_RSVD_PARM, 0);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
IrlmpCloseLsapReq(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
if (pLsapCb == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
DEBUGMSG((DBG_IRLMP | DBG_IRLMP_CONN),
|
|
(TEXT("IRLMP: IRLMP_CLOSELSAP_REQ (l=%d,r=%d) Flags:%d State:%s\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel,
|
|
pLsapCb->Flags, LSAPStateStr[pLsapCb->State]));
|
|
|
|
pLsapCb->Flags &= ~LCBF_TDI_OPEN;
|
|
|
|
REFDEL(&pLsapCb->RefCnt, 'NEPO');
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpDisconnectReq | Process IRLMP disconnect request
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpDisconnectReq(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
if (pLsapCb == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return 1;
|
|
}
|
|
|
|
DEBUGMSG((DBG_IRLMP | DBG_IRLMP_CONN),
|
|
(TEXT("IRLMP: IRLMP_DISCONNECT_REQ (l=%d,r=%d) Flags:%d State:%s\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel,
|
|
pLsapCb->Flags, LSAPStateStr[pLsapCb->State]));
|
|
|
|
|
|
if (pLsapCb->State == LSAP_DISCONNECTED)
|
|
{
|
|
return SUCCESS;
|
|
}
|
|
|
|
if (pLsapCb->State == LSAP_LMCONN_CONF_PEND ||
|
|
pLsapCb->State == LSAP_CONN_RESP_PEND)
|
|
{
|
|
IrdaTimerStop(&pLsapCb->ResponseTimer);
|
|
}
|
|
|
|
if (pLsapCb->State == LSAP_CONN_RESP_PEND || pLsapCb->State >= LSAP_READY)
|
|
{
|
|
// Either the LSAP is connected or the peer is waiting for a
|
|
// response from our client
|
|
|
|
if (pMsg->IRDA_MSG_DiscDataLen > IRLMP_MAX_USER_DATA_LEN)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: User data len exceeded\n")));
|
|
return IRLMP_USER_DATA_LEN_EXCEEDED;
|
|
}
|
|
|
|
pLsapCb->UserDataLen = pMsg->IRDA_MSG_DiscDataLen;
|
|
RtlCopyMemory(pLsapCb->UserData, pMsg->IRDA_MSG_pDiscData,
|
|
pMsg->IRDA_MSG_DiscDataLen);
|
|
|
|
// Notify peer of the disconnect request, reason: user request
|
|
// Send on different thread in case TranportAPI calls this on rx thread
|
|
SendCntlPdu(pLsapCb,IRLMP_DISCONNECT_PDU,IRLMP_ABIT_REQUEST,
|
|
pLsapCb->State == LSAP_CONN_RESP_PEND ? IRLMP_DISC_LSAP :
|
|
IRLMP_USER_REQUEST, 0);
|
|
}
|
|
|
|
IrdaTimerRestart(&pLsapCb->pIrlmpCb->DiscDelayTimer);
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
return SUCCESS;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpDataReqExclusive | Process IRLMP data request
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpDataReqExclusive(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
NDIS_BUFFER *pNBuf = (NDIS_BUFFER *) pMsg->DataContext;
|
|
NDIS_BUFFER *pNextNBuf;
|
|
UCHAR *pData;
|
|
int DataLen;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Exclusive mode data request\n")));
|
|
|
|
if (pLsapCb->pIrlmpCb->LinkState != LINK_READY)
|
|
{
|
|
return IRLMP_LINK_BAD_STATE;
|
|
}
|
|
|
|
if (pLsapCb != pLsapCb->pIrlmpCb->pExclLsapCb)
|
|
{
|
|
return IRLMP_INVALID_LSAP_CB;
|
|
}
|
|
|
|
NdisQueryBuffer(pNBuf, &pData, &DataLen);
|
|
|
|
NdisGetNextBuffer(pNBuf, &pNextNBuf);
|
|
|
|
ASSERT(pNextNBuf == NULL);
|
|
|
|
pMsg->IRDA_MSG_SegCount = 0; // see DATA_CONF on how I'm using this
|
|
pMsg->IRDA_MSG_SegFlags = SEG_FINAL;
|
|
|
|
pMsg->IRDA_MSG_pRead = pData;
|
|
pMsg->IRDA_MSG_pWrite = pData + DataLen;
|
|
|
|
FormatAndSendDataReq(pLsapCb, pMsg, FALSE, FALSE);
|
|
|
|
return SUCCESS;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpDataReqMultiplexed | Process IRLMP data request
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpDataReqMultiplexed(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
NDIS_BUFFER *pNBuf = (NDIS_BUFFER *) pMsg->DataContext;
|
|
NDIS_BUFFER *pNextNBuf;
|
|
UCHAR *pData;
|
|
int DataLen;
|
|
int SegLen;
|
|
IRDA_MSG *pSegMsg;
|
|
|
|
if (pLsapCb->State < LSAP_READY)
|
|
{
|
|
return IRLMP_LSAP_BAD_STATE;
|
|
}
|
|
|
|
// Place this message on the LSAP's TxMsgList. The message remains
|
|
// here until all segments for it are sent and confirmed
|
|
InsertTailList(&pLsapCb->TxMsgList, &pMsg->Linkage);
|
|
|
|
pMsg->IRDA_MSG_SegCount = 0;
|
|
// If it fails, this will be changed
|
|
pMsg->IRDA_MSG_DataStatus = IRLMP_DATA_REQUEST_COMPLETED;
|
|
|
|
// Segment the message into PDUs. The segment will either be:
|
|
// 1. Sent immediately to IRLAP if the link is not busy
|
|
// 2. If link is busy, placed on TxMsgList contained in IRLMP_LCB
|
|
// 3. If no credit, placed onto this LSAPS SegTxMsgList
|
|
|
|
while (pNBuf != NULL)
|
|
{
|
|
NdisQueryBufferSafe(pNBuf, &pData, &DataLen, NormalPagePriority);
|
|
|
|
if (pData == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Get the next one now so I know when to set SegFlag to final
|
|
NdisGetNextBuffer(pNBuf, &pNextNBuf);
|
|
|
|
while (DataLen != 0)
|
|
{
|
|
if ((pSegMsg = AllocIrdaBuf(IrdaMsgPool))
|
|
== NULL)
|
|
{
|
|
ASSERT(0);
|
|
return IRLMP_ALLOC_FAILED;
|
|
}
|
|
pSegMsg->IRDA_MSG_pOwner = pLsapCb; // MUX routing
|
|
pSegMsg->DataContext = pMsg; // Parent of segment
|
|
pSegMsg->IRDA_MSG_IrCOMM_9Wire = pMsg->IRDA_MSG_IrCOMM_9Wire;
|
|
|
|
// Increment segment count contained in original messages
|
|
pMsg->IRDA_MSG_SegCount++;
|
|
|
|
if (DataLen > pLsapCb->pIrlmpCb->MaxPDUSize)
|
|
{
|
|
SegLen = pLsapCb->pIrlmpCb->MaxPDUSize;
|
|
}
|
|
else
|
|
{
|
|
SegLen = DataLen;
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Sending SegLen %d\n"),
|
|
SegLen));
|
|
|
|
pSegMsg->IRDA_MSG_pRead = pData;
|
|
pSegMsg->IRDA_MSG_pWrite = pData + SegLen;
|
|
|
|
// Indicate this message is part of a segmented message
|
|
pSegMsg->IRDA_MSG_SegCount = pMsg->IRDA_MSG_SegCount;
|
|
|
|
pData += SegLen;
|
|
DataLen -= SegLen;
|
|
|
|
if (DataLen == 0 && pNextNBuf == NULL)
|
|
{
|
|
pSegMsg->IRDA_MSG_SegFlags = SEG_FINAL;
|
|
}
|
|
else
|
|
{
|
|
pSegMsg->IRDA_MSG_SegFlags = 0;
|
|
}
|
|
|
|
if (pLsapCb->State == LSAP_NO_TX_CREDIT)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP: Out of credit, placing on SegList\n")));
|
|
InsertTailList(&pLsapCb->SegTxMsgList, &pSegMsg->Linkage);
|
|
}
|
|
else
|
|
{
|
|
FormatAndSendDataReq(pLsapCb, pSegMsg, FALSE, FALSE);
|
|
}
|
|
}
|
|
pNBuf = pNextNBuf;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FormatAndSendDataReq(IRLMP_LSAP_CB *pLsapCb,
|
|
IRDA_MSG *pMsg,
|
|
BOOLEAN LocallyGenerated,
|
|
BOOLEAN Expedited)
|
|
{
|
|
IRLMP_HEADER *pLMHeader;
|
|
TTP_DATA_HEADER *pTTPHeader;
|
|
int AdditionalCredit;
|
|
UCHAR *pLastHdrByte;
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
// Initialize the header pointers to the end of the header block
|
|
pMsg->IRDA_MSG_pHdrRead =
|
|
pMsg->IRDA_MSG_pHdrWrite = pMsg->IRDA_MSG_Header + IRDA_HEADER_LEN;
|
|
|
|
// Back up the read pointer for the LMP header
|
|
pMsg->IRDA_MSG_pHdrRead -= sizeof(IRLMP_HEADER);
|
|
|
|
// Back up header read pointer for TTP
|
|
if (pLsapCb->Flags & LCBF_USE_TTP)
|
|
{
|
|
pMsg->IRDA_MSG_pHdrRead -= (sizeof(TTP_DATA_HEADER));
|
|
}
|
|
|
|
// WHACK FOR IRCOMM YUK YUK !!
|
|
if (pMsg->IRDA_MSG_IrCOMM_9Wire == TRUE)
|
|
{
|
|
pMsg->IRDA_MSG_pHdrRead -= 1;
|
|
}
|
|
|
|
ASSERT(pMsg->IRDA_MSG_pHdrRead >= pMsg->IRDA_MSG_Header);
|
|
|
|
// Build the LMP Header
|
|
pLMHeader = (IRLMP_HEADER *) pMsg->IRDA_MSG_pHdrRead;
|
|
pLMHeader->DstLsapSel = (UCHAR) pLsapCb->RemoteLsapSel;
|
|
pLMHeader->SrcLsapSel = (UCHAR) pLsapCb->LocalLsapSel;
|
|
pLMHeader->CntlBit = IRLMP_DATA_PDU;
|
|
pLMHeader->RsvrdBit = 0;
|
|
|
|
pLastHdrByte = (UCHAR *) (pLMHeader + 1);
|
|
|
|
// Build the TTP Header
|
|
if (pLsapCb->Flags & LCBF_USE_TTP)
|
|
{
|
|
pTTPHeader = (TTP_DATA_HEADER *) (pLMHeader + 1);
|
|
|
|
// Credit
|
|
if (pLsapCb->AvailableCredit > 127)
|
|
{
|
|
AdditionalCredit = 127;
|
|
pLsapCb->AvailableCredit -= 127;
|
|
}
|
|
else
|
|
{
|
|
AdditionalCredit = pLsapCb->AvailableCredit;
|
|
pLsapCb->AvailableCredit = 0;
|
|
}
|
|
|
|
pTTPHeader->AdditionalCredit = (UCHAR) AdditionalCredit;
|
|
pLsapCb->RemoteTxCredit += AdditionalCredit;
|
|
|
|
if (pMsg->IRDA_MSG_pRead != pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
// Only decrement my TxCredit if I'm sending data
|
|
// (may be sending dataless PDU to extend credit to peer)
|
|
pLsapCb->LocalTxCredit -= 1;
|
|
|
|
if (pLsapCb->LocalTxCredit == 0)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP: l%d,r%d No credit\n"), pLsapCb->LocalLsapSel,
|
|
pLsapCb->RemoteLsapSel));
|
|
pLsapCb->State = LSAP_NO_TX_CREDIT;
|
|
}
|
|
}
|
|
|
|
// SAR
|
|
if (pMsg->IRDA_MSG_SegFlags & SEG_FINAL)
|
|
{
|
|
pTTPHeader->MoreBit = TTP_MBIT_FINAL;
|
|
}
|
|
else
|
|
{
|
|
pTTPHeader->MoreBit = TTP_MBIT_NOT_FINAL;
|
|
}
|
|
|
|
pLastHdrByte = (UCHAR *) (pTTPHeader + 1);
|
|
}
|
|
|
|
// WHACK FOR IRCOMM YUK YUK !!
|
|
if (pMsg->IRDA_MSG_IrCOMM_9Wire == TRUE)
|
|
{
|
|
*pLastHdrByte = 0;
|
|
}
|
|
|
|
pMsg->Prim = IRLAP_DATA_REQ;
|
|
|
|
pMsg->IRDA_MSG_Expedited = Expedited;
|
|
|
|
if (LocallyGenerated)
|
|
{
|
|
pMsg->IRDA_MSG_SegFlags = SEG_LOCAL | SEG_FINAL;
|
|
pMsg->IRDA_MSG_pOwner = 0;
|
|
}
|
|
else
|
|
{
|
|
pMsg->IRDA_MSG_pOwner = pLsapCb;
|
|
REFADD(&pLsapCb->RefCnt, 'ATAD');
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Sending Data request pMsg:%X LsapCb:%X\n"),
|
|
pMsg, pMsg->IRDA_MSG_pOwner));
|
|
|
|
if (IrlapDown(pLsapCb->pIrlmpCb->pIrdaLinkCb->IrlapContext,
|
|
pMsg) != SUCCESS)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: IRLAP_DATA_REQ failed, faking CONF\n")));
|
|
|
|
pMsg->IRDA_MSG_DataStatus = IRLAP_DATA_REQUEST_FAILED_LINK_RESET;
|
|
IrlapDataConf(pMsg);
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP_CRED,
|
|
(TEXT("IRLMP(l%d,r%d): Tx LocTxCredit %d,RemoteTxCredit %d\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel,
|
|
pLsapCb->LocalTxCredit, pLsapCb->RemoteTxCredit));
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpAccessModeReq | Processes the IRLMP_ACCESSMODE_REQ
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
UINT
|
|
IrlmpAccessModeReq(IRLMP_LSAP_CB *pRequestingLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
PIRLMP_LINK_CB pIrlmpCb = pRequestingLsapCb->pIrlmpCb;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pIrlmpCb->LinkState != LINK_READY)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Link bad state %x\n"),
|
|
pIrlmpCb->LinkState));
|
|
return IRLMP_LINK_BAD_STATE;
|
|
}
|
|
if (pRequestingLsapCb->State != LSAP_READY)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: LSAP bad state %x\n"),
|
|
pRequestingLsapCb->State));
|
|
return IRLMP_LSAP_BAD_STATE;
|
|
}
|
|
switch (pMsg->IRDA_MSG_AccessMode)
|
|
{
|
|
case IRLMP_EXCLUSIVE:
|
|
if (pIrlmpCb->pExclLsapCb != NULL)
|
|
{
|
|
// Another LSAP has it already
|
|
return IRLMP_IN_EXCLUSIVE_MODE;
|
|
}
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->State != LSAP_DISCONNECTED &&
|
|
pLsapCb != pRequestingLsapCb)
|
|
{
|
|
return IRLMP_IN_MULTIPLEXED_MODE;
|
|
}
|
|
}
|
|
|
|
// OK to request exclusive mode from peer
|
|
pIrlmpCb->pExclLsapCb = pRequestingLsapCb;
|
|
|
|
if (pMsg->IRDA_MSG_IrLPTMode == TRUE)
|
|
{
|
|
pMsg->Prim = IRLMP_ACCESSMODE_CONF;
|
|
pMsg->IRDA_MSG_AccessMode = IRLMP_EXCLUSIVE;
|
|
pMsg->IRDA_MSG_ModeStatus = IRLMP_ACCESSMODE_SUCCESS;
|
|
|
|
TdiUp(pRequestingLsapCb->TdiContext, pMsg);
|
|
return SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
pRequestingLsapCb->State = LSAP_EXCLUSIVEMODE_PEND;
|
|
|
|
SendCntlPdu(pRequestingLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_REQUEST, IRLMP_RSVD_PARM,
|
|
IRLMP_EXCLUSIVE);
|
|
|
|
IrdaTimerRestart(&pRequestingLsapCb->ResponseTimer);
|
|
|
|
}
|
|
break;
|
|
|
|
case IRLMP_MULTIPLEXED:
|
|
if (pIrlmpCb->pExclLsapCb == NULL)
|
|
{
|
|
return IRLMP_IN_MULTIPLEXED_MODE;
|
|
}
|
|
if (pIrlmpCb->pExclLsapCb != pRequestingLsapCb)
|
|
{
|
|
return IRLMP_NOT_LSAP_IN_EXCLUSIVE_MODE;
|
|
}
|
|
|
|
if (pMsg->IRDA_MSG_IrLPTMode == TRUE)
|
|
{
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
pMsg->Prim = IRLMP_ACCESSMODE_CONF;
|
|
pMsg->IRDA_MSG_AccessMode = IRLMP_MULTIPLEXED;
|
|
pMsg->IRDA_MSG_ModeStatus = IRLMP_ACCESSMODE_SUCCESS;
|
|
return TdiUp(pRequestingLsapCb->TdiContext,
|
|
pMsg);
|
|
}
|
|
else
|
|
{
|
|
pRequestingLsapCb->State = LSAP_MULTIPLEXEDMODE_PEND;
|
|
|
|
SendCntlPdu(pRequestingLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_REQUEST, IRLMP_RSVD_PARM,
|
|
IRLMP_MULTIPLEXED);
|
|
|
|
IrdaTimerRestart(&pRequestingLsapCb->ResponseTimer);
|
|
}
|
|
break;
|
|
default:
|
|
return IRLMP_BAD_ACCESSMODE;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | SendCntlPdu | Sends connect request to IRLAP
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer LSAP control block
|
|
*/
|
|
VOID
|
|
SendCntlPdu(IRLMP_LSAP_CB *pLsapCb, int OpCode, int ABit,
|
|
int Parm1, int Parm2)
|
|
{
|
|
IRDA_MSG *pMsg = AllocIrdaBuf(IrdaMsgPool);
|
|
IRLMP_HEADER *pLMHeader;
|
|
IRLMP_CNTL_FORMAT *pCntlFormat;
|
|
TTP_CONN_HEADER *pTTPHeader;
|
|
TTP_CONN_PARM *pTTPParm;
|
|
UINT rc = SUCCESS;
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pMsg == NULL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Alloc failed\n")));
|
|
ASSERT(0);
|
|
return;// IRLMP_ALLOC_FAILED;
|
|
}
|
|
|
|
pMsg->IRDA_MSG_SegFlags = SEG_LOCAL | SEG_FINAL;
|
|
|
|
// Initialize the header pointers to the end of the header block
|
|
pMsg->IRDA_MSG_pHdrRead =
|
|
pMsg->IRDA_MSG_pHdrWrite = pMsg->IRDA_MSG_Header + IRDA_HEADER_LEN;
|
|
|
|
// Back up the read pointer for the LMP header
|
|
pMsg->IRDA_MSG_pHdrRead -= (sizeof(IRLMP_HEADER) + \
|
|
sizeof(IRLMP_CNTL_FORMAT));
|
|
|
|
// move it forward for non access mode control requests
|
|
// (connect and disconnect don't have a Parm2)
|
|
if (OpCode != IRLMP_ACCESSMODE_PDU)
|
|
{
|
|
pMsg->IRDA_MSG_pHdrRead++;
|
|
}
|
|
|
|
// If using Tiny TPP back up the read pointer for its header
|
|
// From LMPs point of view this is where the user data begins.
|
|
// We are sticking it in the header because TTP is now part of IRLMP.
|
|
|
|
// TTP connect PDU's are only used for connection establishment
|
|
if ((pLsapCb->Flags & LCBF_USE_TTP) && OpCode == IRLMP_CONNECT_PDU)
|
|
{
|
|
pMsg->IRDA_MSG_pHdrRead -= sizeof(TTP_CONN_HEADER);
|
|
|
|
if (pLsapCb->RxMaxSDUSize > 0)
|
|
{
|
|
pMsg->IRDA_MSG_pHdrRead -= sizeof(TTP_CONN_PARM);
|
|
}
|
|
}
|
|
|
|
// Build the IRLMP header
|
|
pLMHeader = (IRLMP_HEADER *) pMsg->IRDA_MSG_pHdrRead;
|
|
pLMHeader->DstLsapSel = (UCHAR) pLsapCb->RemoteLsapSel;
|
|
pLMHeader->SrcLsapSel = (UCHAR) pLsapCb->LocalLsapSel;
|
|
pLMHeader->CntlBit = IRLMP_CNTL_PDU;
|
|
pLMHeader->RsvrdBit = 0;
|
|
// Control portion of header
|
|
pCntlFormat = (IRLMP_CNTL_FORMAT *) (pLMHeader + 1);
|
|
pCntlFormat->OpCode = (UCHAR) OpCode;
|
|
pCntlFormat->ABit = (UCHAR) ABit;
|
|
pCntlFormat->Parm1 = (UCHAR) Parm1;
|
|
if (OpCode == IRLMP_ACCESSMODE_PDU)
|
|
{
|
|
pCntlFormat->Parm2 = (UCHAR) Parm2; // Access mode
|
|
}
|
|
|
|
// Build the TTP header if needed (we are using TTP and this is a
|
|
// connection request or confirmation not).
|
|
if ((pLsapCb->Flags & LCBF_USE_TTP) && OpCode == IRLMP_CONNECT_PDU)
|
|
{
|
|
// Always using the MaxSDUSize parameter. If the client wishes
|
|
// to disable, MaxSDUSize = 0
|
|
|
|
pTTPHeader = (TTP_CONN_HEADER *) (pCntlFormat + 1) - 1;
|
|
// -1, LM-Connect-PDU doesn't use parm2
|
|
|
|
/*
|
|
#define TTP_PFLAG_NO_PARMS 0
|
|
#define TTP_PFLAG_PARMS 1
|
|
*/
|
|
|
|
pTTPHeader->ParmFlag = (pLsapCb->RxMaxSDUSize > 0);
|
|
|
|
pTTPHeader->InitialCredit = (UCHAR) pLsapCb->RemoteTxCredit;
|
|
|
|
pTTPParm = (TTP_CONN_PARM *) (pTTPHeader + 1);
|
|
|
|
if (pLsapCb->RxMaxSDUSize > 0)
|
|
{
|
|
// HARDCODE-O-RAMA
|
|
pTTPParm->PLen = 6;
|
|
pTTPParm->PI = TTP_MAX_SDU_SIZE_PI;
|
|
pTTPParm->PL = TTP_MAX_SDU_SIZE_PL;
|
|
pTTPParm->PV[3] = (UCHAR) (pLsapCb->RxMaxSDUSize & 0xFF);
|
|
pTTPParm->PV[2] = (UCHAR) ((pLsapCb->RxMaxSDUSize & 0xFF00)
|
|
>> 8);
|
|
pTTPParm->PV[1] = (UCHAR) ((pLsapCb->RxMaxSDUSize & 0xFF0000)
|
|
>> 16);
|
|
pTTPParm->PV[0] = (UCHAR) ((pLsapCb->RxMaxSDUSize & 0xFF000000)
|
|
>> 24);
|
|
}
|
|
|
|
}
|
|
|
|
// Client connection data, Access mode does not include client data
|
|
if (pLsapCb->UserDataLen == 0 || OpCode == IRLMP_ACCESSMODE_PDU)
|
|
{
|
|
pMsg->IRDA_MSG_pBase =
|
|
pMsg->IRDA_MSG_pRead =
|
|
pMsg->IRDA_MSG_pWrite =
|
|
pMsg->IRDA_MSG_pLimit = NULL;
|
|
}
|
|
else
|
|
{
|
|
pMsg->IRDA_MSG_pBase = pLsapCb->UserData;
|
|
pMsg->IRDA_MSG_pRead = pLsapCb->UserData;
|
|
pMsg->IRDA_MSG_pWrite = pLsapCb->UserData + pLsapCb->UserDataLen;
|
|
pMsg->IRDA_MSG_pLimit = pMsg->IRDA_MSG_pWrite;
|
|
}
|
|
|
|
// Message built, send data request to IRLAP
|
|
pMsg->Prim = IRLAP_DATA_REQ;
|
|
|
|
pMsg->IRDA_MSG_Expedited = TRUE;
|
|
|
|
pMsg->IRDA_MSG_pOwner = 0;
|
|
|
|
DEBUGMSG(DBG_IRLMP,(TEXT("IRLMP: Sending LM_%s_%s for l=%d,r=%d pMsg:%X LsapCb:%X\n"),
|
|
(OpCode == IRLMP_CONNECT_PDU ? TEXT("CONNECT") :
|
|
OpCode == IRLMP_DISCONNECT_PDU ? TEXT("DISCONNECT") :
|
|
OpCode == IRLMP_ACCESSMODE_PDU ? TEXT("ACCESSMODE") :
|
|
TEXT("!!oops!!")),
|
|
(ABit==IRLMP_ABIT_REQUEST?TEXT("REQ"):TEXT("CONF")),
|
|
pLsapCb->LocalLsapSel,
|
|
pLsapCb->RemoteLsapSel,
|
|
pMsg, pMsg->IRDA_MSG_pOwner));
|
|
|
|
if (IrlapDown(pLsapCb->pIrlmpCb->pIrdaLinkCb->IrlapContext,
|
|
pMsg) != SUCCESS)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: IRLAP_DATA_REQUEST failed\n")));
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | LsapResponseTimerExp | Timer expiration callback
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
*/
|
|
VOID
|
|
LsapResponseTimerExp(PVOID Context)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb = (IRLMP_LSAP_CB *) Context;
|
|
IRDA_MSG IMsg;
|
|
UINT rc = SUCCESS;
|
|
PIRLMP_LINK_CB pIrlmpCb = pLsapCb->pIrlmpCb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: LSAP timer expired\n")));
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
switch (pLsapCb->State)
|
|
{
|
|
case LSAP_LMCONN_CONF_PEND:
|
|
if (pLsapCb->LocalLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasServerDisconnectReq(pLsapCb);
|
|
break;
|
|
}
|
|
|
|
if (pLsapCb->LocalLsapSel == IAS_LOCAL_LSAP_SEL &&
|
|
pLsapCb->RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasClientDisconnectReq(pLsapCb, IRLMP_NO_RESPONSE_LSAP);
|
|
}
|
|
else
|
|
{
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
}
|
|
break;
|
|
|
|
case LSAP_CONN_RESP_PEND:
|
|
pLsapCb->UserDataLen = 0; // This will ensure no client data sent in
|
|
// Disconnect PDU below
|
|
|
|
// Tell remote LSAP that its peer did not respond
|
|
SendCntlPdu(pLsapCb,IRLMP_DISCONNECT_PDU,IRLMP_ABIT_REQUEST,
|
|
IRLMP_NO_RESPONSE_LSAP, 0);
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
DeleteLsap(pLsapCb);
|
|
break;
|
|
|
|
case LSAP_MULTIPLEXEDMODE_PEND:
|
|
// Spec says peer can't refuse request to return to multiplex mode
|
|
// but if no answer, go into multiplexed anyway?
|
|
case LSAP_EXCLUSIVEMODE_PEND:
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
// Peer didn't respond, maybe we are not connected anymore ???
|
|
|
|
pLsapCb->State = LSAP_READY;
|
|
|
|
IMsg.Prim = IRLMP_ACCESSMODE_CONF;
|
|
IMsg.IRDA_MSG_AccessMode = IRLMP_MULTIPLEXED;
|
|
IMsg.IRDA_MSG_ModeStatus = IRLMP_ACCESSMODE_FAILURE;
|
|
TdiUp(pLsapCb->TdiContext, &IMsg);
|
|
break;
|
|
|
|
default:
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("Ignoring timer expiry in this state, %d\n"),pLsapCb->State));
|
|
; // ignore
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlmpUp | Bottom of IRLMP, called by IRLAP with
|
|
* IRLAP messages. This is the MUX
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
*/
|
|
UINT
|
|
IrlmpUp(PIRDA_LINK_CB pIrdaLinkCb, IRDA_MSG *pMsg)
|
|
{
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
switch (pMsg->Prim)
|
|
{
|
|
case IRLAP_DISCOVERY_IND:
|
|
UpdateDeviceList(pIrlmpCb, pMsg->IRDA_MSG_pDevList);
|
|
/*
|
|
TDI ignores this
|
|
pMsg->Prim = IRLMP_DISCOVERY_IND;
|
|
pMsg->IRDA_MSG_pDevList = &DeviceList;
|
|
TdiUp(NULL, pMsg);
|
|
*/
|
|
return SUCCESS;
|
|
|
|
case IRLAP_DISCOVERY_CONF:
|
|
IrlapDiscoveryConf(pIrlmpCb, pMsg);
|
|
return SUCCESS;
|
|
|
|
case IRLAP_CONNECT_IND:
|
|
IrlapConnectInd(pIrlmpCb, pMsg);
|
|
return SUCCESS;
|
|
|
|
case IRLAP_CONNECT_CONF:
|
|
IrlapConnectConf(pIrlmpCb, pMsg);
|
|
return SUCCESS;
|
|
|
|
case IRLAP_DISCONNECT_IND:
|
|
IrlapDisconnectInd(pIrlmpCb, pMsg);
|
|
return SUCCESS;
|
|
|
|
case IRLAP_DATA_CONF:
|
|
IrlapDataConf(pMsg);
|
|
return SUCCESS;
|
|
|
|
case IRLAP_DATA_IND:
|
|
IrlapDataInd(pIrlmpCb, pMsg);
|
|
if (pIrlmpCb->pExclLsapCb &&
|
|
pIrlmpCb->pExclLsapCb->RemoteTxCredit <=0)
|
|
return IRLMP_LOCAL_BUSY;
|
|
else
|
|
return SUCCESS;
|
|
|
|
case IRLAP_UDATA_IND:
|
|
ASSERT(0);
|
|
return SUCCESS;
|
|
|
|
case IRLAP_STATUS_IND:
|
|
TdiUp(NULL, pMsg);
|
|
return SUCCESS;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlapDiscoveryConf | Process the discovery confirm
|
|
*/
|
|
VOID
|
|
IrlapDiscoveryConf(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg)
|
|
{
|
|
DEBUGMSG(DBG_DISCOVERY, (TEXT("IRLMP: IRLAP_DISCOVERY_CONF\n")));
|
|
|
|
if (pIrlmpCb->LinkState != LINK_IN_DISCOVERY)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Link bad state\n")));
|
|
|
|
ASSERT(pIrlmpCb->LinkState == LINK_DOWN);
|
|
|
|
return;// IRLMP_LINK_BAD_STATE;
|
|
}
|
|
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTED;
|
|
|
|
if (pMsg->IRDA_MSG_DscvStatus == IRLAP_DISCOVERY_COMPLETED)
|
|
{
|
|
UpdateDeviceList(pIrlmpCb, pMsg->IRDA_MSG_pDevList);
|
|
}
|
|
|
|
// Initiate discovery on next link
|
|
IrdaEventSchedule(&EvDiscoveryReq, NULL);
|
|
|
|
// Initiate a connection if one was requested while in discovery
|
|
ScheduleConnectReq(pIrlmpCb);
|
|
}
|
|
|
|
void
|
|
AddDeviceToGlobalList(IRDA_DEVICE *pNewDevice)
|
|
{
|
|
IRDA_DEVICE *pDevice;
|
|
|
|
for (pDevice = (IRDA_DEVICE *) gDeviceList.Flink;
|
|
(LIST_ENTRY *) pDevice != &gDeviceList;
|
|
pDevice = (IRDA_DEVICE *) pDevice->Linkage.Flink)
|
|
{
|
|
if (pNewDevice->DscvInfoLen == pDevice->DscvInfoLen &&
|
|
CTEMemCmp(pNewDevice->DevAddr, pDevice->DevAddr,
|
|
IRDA_DEV_ADDR_LEN) == 0 &&
|
|
CTEMemCmp(pNewDevice->DscvInfo, pDevice->DscvInfo,
|
|
(ULONG) pNewDevice->DscvInfoLen) == 0)
|
|
{
|
|
// Device is already in the global list
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IRDA_ALLOC_MEM(pDevice, sizeof(IRDA_DEVICE), MT_IRLMP_DEVICE) != NULL)
|
|
{
|
|
RtlCopyMemory(pDevice, pNewDevice, sizeof(IRDA_DEVICE));
|
|
InsertHeadList(&gDeviceList, &pDevice->Linkage);
|
|
}
|
|
}
|
|
|
|
void
|
|
DeleteDeviceFromGlobalList(IRDA_DEVICE *pOldDevice)
|
|
{
|
|
IRDA_DEVICE *pDevice;
|
|
|
|
for (pDevice = (IRDA_DEVICE *) gDeviceList.Flink;
|
|
(LIST_ENTRY *) pDevice != &gDeviceList;
|
|
pDevice = (IRDA_DEVICE *) pDevice->Linkage.Flink)
|
|
{
|
|
if (pOldDevice->DscvInfoLen == pDevice->DscvInfoLen &&
|
|
CTEMemCmp(pOldDevice->DevAddr, pDevice->DevAddr,
|
|
IRDA_DEV_ADDR_LEN) == 0 &&
|
|
CTEMemCmp(pOldDevice->DscvInfo, pDevice->DscvInfo,
|
|
(ULONG) pOldDevice->DscvInfoLen) == 0)
|
|
{
|
|
RemoveEntryList(&pDevice->Linkage);
|
|
IRDA_FREE_MEM(pDevice);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func void | UpdateDeviceList | Determines if new devices need to be
|
|
* added or old ones removed from the device
|
|
* list maintained by IRLMP
|
|
*
|
|
* @parm LIST_ENTRY * | pDevList | pointer to a list of devices
|
|
*/
|
|
/*void
|
|
UpdateDeviceList(PIRLMP_LINK_CB pIrlmpCb, LIST_ENTRY *pNewDevList)
|
|
{
|
|
IRDA_DEVICE *pNewDevice;
|
|
IRDA_DEVICE *pOldDevice;
|
|
IRDA_DEVICE *pDevice;
|
|
BOOLEAN DeviceInList;
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
// Add new devices, set not seen count to zero if devices is
|
|
// seen again
|
|
for (pNewDevice = (IRDA_DEVICE *) pNewDevList->Flink;
|
|
(LIST_ENTRY *) pNewDevice != pNewDevList;
|
|
pNewDevice = (IRDA_DEVICE *) pNewDevice->Linkage.Flink)
|
|
{
|
|
DeviceInList = FALSE;
|
|
|
|
AddDeviceToGlobalList(pNewDevice);
|
|
|
|
for (pOldDevice = (IRDA_DEVICE *) pIrlmpCb->DeviceList.Flink;
|
|
(LIST_ENTRY *) pOldDevice != &pIrlmpCb->DeviceList;
|
|
pOldDevice = (IRDA_DEVICE *) pOldDevice->Linkage.Flink)
|
|
{
|
|
if (pNewDevice->DscvInfoLen == pOldDevice->DscvInfoLen &&
|
|
RtlCompareMemory(pNewDevice->DevAddr, pOldDevice->DevAddr,
|
|
IRDA_DEV_ADDR_LEN) == IRDA_DEV_ADDR_LEN &&
|
|
RtlCompareMemory(pNewDevice->DscvInfo, pOldDevice->DscvInfo,
|
|
pNewDevice->DscvInfoLen) ==
|
|
(ULONG) pNewDevice->DscvInfoLen)
|
|
{
|
|
DeviceInList = TRUE;
|
|
pOldDevice->NotSeenCnt = -1; // reset not seen count
|
|
// will be ++'d to 0 below
|
|
break;
|
|
}
|
|
}
|
|
if (!DeviceInList)
|
|
{
|
|
// Create an new entry in the list maintained by IRLMP
|
|
IRDA_ALLOC_MEM(pDevice, sizeof(IRDA_DEVICE), MT_IRLMP_DEVICE);
|
|
|
|
RtlCopyMemory(pDevice, pNewDevice, sizeof(IRDA_DEVICE));
|
|
pDevice->NotSeenCnt = -1; // will be ++'d to 0 below
|
|
InsertHeadList(&pIrlmpCb->DeviceList, &pDevice->Linkage);
|
|
}
|
|
}
|
|
|
|
// Run through the list and remove devices that haven't
|
|
// been seen for awhile
|
|
|
|
pOldDevice = (IRDA_DEVICE *) pIrlmpCb->DeviceList.Flink;
|
|
|
|
while ((LIST_ENTRY *) pOldDevice != &pIrlmpCb->DeviceList)
|
|
{
|
|
pDevice = (IRDA_DEVICE *) pOldDevice->Linkage.Flink;
|
|
|
|
if (++(pOldDevice->NotSeenCnt) >= IRLMP_NOT_SEEN_THRESHOLD)
|
|
{
|
|
DeleteDeviceFromGlobalList(pOldDevice);
|
|
|
|
RemoveEntryList(&pOldDevice->Linkage);
|
|
IRDA_FREE_MEM(pOldDevice);
|
|
}
|
|
pOldDevice = pDevice; // next
|
|
}
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
}*/
|
|
void
|
|
UpdateDeviceList(PIRLMP_LINK_CB pIrlmpCb, LIST_ENTRY *pNewDevList)
|
|
{
|
|
IRDA_DEVICE *pNewDevice;
|
|
IRDA_DEVICE *pOldDevice;
|
|
IRDA_DEVICE *pDevice;
|
|
BOOLEAN DeviceInList;
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
// Add new devices, set not seen count to zero if devices is
|
|
// seen again
|
|
for (pNewDevice = (IRDA_DEVICE *) pNewDevList->Flink;
|
|
(LIST_ENTRY *) pNewDevice != pNewDevList;
|
|
pNewDevice = (IRDA_DEVICE *) pNewDevice->Linkage.Flink)
|
|
{
|
|
DeviceInList = FALSE;
|
|
|
|
AddDeviceToGlobalList(pNewDevice);
|
|
|
|
for (pOldDevice = (IRDA_DEVICE *) pIrlmpCb->DeviceList.Flink;
|
|
(LIST_ENTRY *) pOldDevice != &pIrlmpCb->DeviceList;
|
|
pOldDevice = (IRDA_DEVICE *) pOldDevice->Linkage.Flink)
|
|
{
|
|
if (pNewDevice->DscvInfoLen == pOldDevice->DscvInfoLen &&
|
|
CTEMemCmp(pNewDevice->DevAddr, pOldDevice->DevAddr,
|
|
IRDA_DEV_ADDR_LEN) == 0 &&
|
|
CTEMemCmp(pNewDevice->DscvInfo, pOldDevice->DscvInfo,
|
|
(ULONG) pNewDevice->DscvInfoLen) == 0)
|
|
{
|
|
DeviceInList = TRUE;
|
|
pOldDevice->NotSeenCnt = -1; // reset not seen count
|
|
// will be ++'d to 0 below
|
|
break;
|
|
}
|
|
}
|
|
if (!DeviceInList)
|
|
{
|
|
// Create an new entry in the list maintained by IRLMP
|
|
IRDA_ALLOC_MEM(pDevice, sizeof(IRDA_DEVICE), MT_IRLMP_DEVICE);
|
|
|
|
if (pDevice)
|
|
{
|
|
RtlCopyMemory(pDevice, pNewDevice, sizeof(IRDA_DEVICE));
|
|
pDevice->NotSeenCnt = -1; // will be ++'d to 0 below
|
|
InsertHeadList(&pIrlmpCb->DeviceList, &pDevice->Linkage);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run through the list and remove devices that haven't
|
|
// been seen for awhile
|
|
|
|
pOldDevice = (IRDA_DEVICE *) pIrlmpCb->DeviceList.Flink;
|
|
|
|
while ((LIST_ENTRY *) pOldDevice != &pIrlmpCb->DeviceList)
|
|
{
|
|
pDevice = (IRDA_DEVICE *) pOldDevice->Linkage.Flink;
|
|
|
|
pOldDevice->NotSeenCnt += 1;
|
|
|
|
if (pOldDevice->NotSeenCnt == 1 || pOldDevice->NotSeenCnt == 2)
|
|
{
|
|
pIrlmpCb->DiscoveryFlags = DF_NO_SENSE_DSCV;
|
|
}
|
|
else if (pOldDevice->NotSeenCnt > 2)
|
|
{
|
|
DeleteDeviceFromGlobalList(pOldDevice);
|
|
|
|
RemoveEntryList(&pOldDevice->Linkage);
|
|
IRDA_FREE_MEM(pOldDevice);
|
|
}
|
|
pOldDevice = pDevice; // next
|
|
}
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlapConnectInd | Process the connect indication from LAP
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
VOID
|
|
IrlapConnectInd(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (pIrlmpCb->LinkState != LINK_DISCONNECTED)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
pIrlmpCb->LinkState = LINK_CONNECTING;
|
|
|
|
RtlCopyMemory(pIrlmpCb->ConnDevAddr, pMsg->IRDA_MSG_RemoteDevAddr,
|
|
IRDA_DEV_ADDR_LEN);
|
|
RtlCopyMemory(&pIrlmpCb->NegotiatedQOS, pMsg->IRDA_MSG_pQos,
|
|
sizeof(IRDA_QOS_PARMS));
|
|
pIrlmpCb->MaxPDUSize = IrlapGetQosParmVal(vDataSizeTable,
|
|
pMsg->IRDA_MSG_pQos->bfDataSize, NULL)
|
|
- sizeof(IRLMP_HEADER)
|
|
- sizeof(TTP_DATA_HEADER)
|
|
- 2 // size of irlap header
|
|
- 1; // IrComm
|
|
|
|
pIrlmpCb->WindowSize = IrlapGetQosParmVal(vWinSizeTable,
|
|
pMsg->IRDA_MSG_pQos->bfWindowSize, NULL);
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Connect indication, MaxPDU = %d\n"),
|
|
pIrlmpCb->MaxPDUSize));
|
|
|
|
// schedule the response to occur on a differnt thread
|
|
pIrlmpCb->AcceptConnection = TRUE;
|
|
IrdaEventSchedule(&EvConnectResp, pIrlmpCb->pIrdaLinkCb);
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlapConnectConf | Processes the connect confirm
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
VOID
|
|
IrlapConnectConf(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pIrlmpCb->LinkState == LINK_CONNECTING);
|
|
|
|
// Currently, the connect confirm always returns a successful status
|
|
ASSERT(pMsg->IRDA_MSG_ConnStatus == IRLAP_CONNECTION_COMPLETED);
|
|
|
|
// Update Link
|
|
pIrlmpCb->LinkState = LINK_READY;
|
|
RtlCopyMemory(&pIrlmpCb->NegotiatedQOS, pMsg->IRDA_MSG_pQos,
|
|
sizeof(IRDA_QOS_PARMS));
|
|
pIrlmpCb->MaxPDUSize = IrlapGetQosParmVal(vDataSizeTable,
|
|
pMsg->IRDA_MSG_pQos->bfDataSize, NULL)
|
|
- sizeof(IRLMP_HEADER)
|
|
- sizeof(TTP_DATA_HEADER)
|
|
- 2 // size of irlap header
|
|
- 1; // IrComm
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: IRLAP_CONNECT_CONF, TxMaxPDU = %d\n"),
|
|
pIrlmpCb->MaxPDUSize));
|
|
|
|
IrdaEventSchedule(&EvLmConnectReq, pIrlmpCb->pIrdaLinkCb);
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlapDisconnectInd | Processes the disconnect indication
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
VOID
|
|
IrlapDisconnectInd(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg)
|
|
{
|
|
IRLMP_DISC_REASON DiscReason = IRLMP_UNEXPECTED_IRLAP_DISC;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: IRLAP Disconnect Ind, status = %d\n"),
|
|
pMsg->IRDA_MSG_DiscStatus));
|
|
|
|
switch (pIrlmpCb->LinkState)
|
|
{
|
|
case LINK_CONNECTING:
|
|
if (pMsg->IRDA_MSG_DiscStatus == MAC_MEDIA_BUSY)
|
|
{
|
|
DiscReason = IRLMP_MAC_MEDIA_BUSY;
|
|
}
|
|
else
|
|
{
|
|
DiscReason = IRLMP_IRLAP_CONN_FAILED;
|
|
}
|
|
|
|
// Fall through
|
|
case LINK_READY:
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTED;
|
|
TearDownConnections(pIrlmpCb, DiscReason);
|
|
break;
|
|
|
|
case LINK_DISCONNECTING:
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTED;
|
|
// Initiate a connection if one was requested while disconnecting
|
|
ScheduleConnectReq(pIrlmpCb);
|
|
break;
|
|
|
|
default:
|
|
DEBUGMSG(1, (TEXT("Link STATE %d\n"), pIrlmpCb->LinkState));
|
|
|
|
//ASSERT(0);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func IRLMP_LSAP_CB * | GetLsapInState | returns the first occurance of
|
|
* an LSAP in the specified state
|
|
* as long as the link is in the
|
|
* specified state.
|
|
*
|
|
* @parm int | LinkState | get LSAP only as long as link is in this state
|
|
*
|
|
* @parm int | LSAP | Return the LSAP that is in this state if InThisState
|
|
* is TRUE. Else return LSAP if it is not in this state
|
|
* if InThisState = FALSE
|
|
*
|
|
* @parm BOOLEAN | InThisState | TRUE return LSAP if in this state
|
|
* FALSE return LSAP if not in this state
|
|
*
|
|
* @rdesc pointer to an LSAP control block or NULL, if an LSAP is returned
|
|
*
|
|
*/
|
|
IRLMP_LSAP_CB *
|
|
GetLsapInState(PIRLMP_LINK_CB pIrlmpCb,
|
|
int LinkState,
|
|
int LSAPState,
|
|
BOOLEAN InThisState)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Only want to find an LSAP if the link is in the specified state
|
|
if (pIrlmpCb->LinkState != LinkState)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if ((pLsapCb->State == LSAPState && InThisState == TRUE) ||
|
|
(pLsapCb->State != LSAPState && InThisState == FALSE))
|
|
{
|
|
return pLsapCb;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
*/
|
|
IRLMP_LINK_CB *
|
|
GetIrlmpCb(PUCHAR RemoteDevAddr)
|
|
{
|
|
IRDA_DEVICE *pDevice;
|
|
PIRLMP_LINK_CB pIrlmpCb;
|
|
KIRQL OldIrql;
|
|
PIRDA_LINK_CB pIrdaLinkCb;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = (PIRDA_LINK_CB) pIrdaLinkCb->Linkage.Flink)
|
|
{
|
|
pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
for (pDevice = (IRDA_DEVICE *) pIrlmpCb->DeviceList.Flink;
|
|
(LIST_ENTRY *) pDevice != &pIrlmpCb->DeviceList;
|
|
pDevice = (IRDA_DEVICE *) pDevice->Linkage.Flink)
|
|
{
|
|
if (CTEMemCmp(pDevice->DevAddr, RemoteDevAddr,
|
|
IRDA_DEV_ADDR_LEN) == 0)
|
|
{
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
return pIrlmpCb;
|
|
}
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
return NULL;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | DiscDelayTimerFunc | Timer expiration callback
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
*/
|
|
VOID
|
|
DiscDelayTimerFunc(PVOID Context)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
IRDA_MSG IMsg;
|
|
UINT rc = SUCCESS;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) Context;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Link timer expired\n")));
|
|
|
|
// The timer that expired is the disconnect delay timer. Bring
|
|
// down link if no LSAP connection exists
|
|
if (pIrlmpCb->LinkState == LINK_DISCONNECTED)
|
|
{
|
|
// already disconnected
|
|
return;
|
|
}
|
|
|
|
// Search for an LSAP that is connected or coming up
|
|
pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
while (&pIrlmpCb->LsapCbList != (LIST_ENTRY *) pLsapCb)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->State > LSAP_DISCONNECTED)
|
|
{
|
|
// Don't bring down link, an LSAP is connected or connecting
|
|
return;
|
|
}
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink;
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT(
|
|
"IRLMP: No LSAP connections, disconnecting link\n")));
|
|
// No LSAP connections, bring it down if it is up
|
|
if (pIrlmpCb->LinkState == LINK_READY)
|
|
{
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTING;
|
|
|
|
// Request IRLAP to disconnect the link
|
|
IMsg.Prim = IRLAP_DISCONNECT_REQ;
|
|
IrlapDown(pIrlmpCb->pIrdaLinkCb->IrlapContext, &IMsg);
|
|
}
|
|
|
|
return;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlapDataConf | Processes the data confirm
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*/
|
|
VOID
|
|
IrlapDataConf(IRDA_MSG *pMsg)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb = pMsg->IRDA_MSG_pOwner;
|
|
IRDA_MSG *pSegParentMsg;
|
|
BOOLEAN RequestFailed = FALSE;
|
|
UINT rc = SUCCESS;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Received IRLAP_DATA_CONF pMsg:%X LsapCb:%X\n"),
|
|
pMsg, pMsg->IRDA_MSG_pOwner));
|
|
|
|
if (pMsg->IRDA_MSG_DataStatus != IRLAP_DATA_REQUEST_COMPLETED)
|
|
{
|
|
RequestFailed = TRUE;
|
|
}
|
|
|
|
if (pMsg->IRDA_MSG_SegFlags & SEG_LOCAL)
|
|
{
|
|
// Locally generated data request
|
|
FreeIrdaBuf(IrdaMsgPool, pMsg);
|
|
|
|
if (RequestFailed)
|
|
{
|
|
; // LOG ERROR
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pMsg->IRDA_MSG_SegCount == 0)
|
|
{
|
|
if (!RequestFailed)
|
|
{
|
|
pMsg->IRDA_MSG_DataStatus = IRLMP_DATA_REQUEST_COMPLETED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// A segmented message, get its Parent
|
|
pSegParentMsg = pMsg->DataContext;
|
|
|
|
// Free the segment
|
|
FreeIrdaBuf(IrdaMsgPool, pMsg);
|
|
|
|
if (RequestFailed)
|
|
{
|
|
pSegParentMsg->IRDA_MSG_DataStatus = IRLMP_DATA_REQUEST_FAILED;
|
|
}
|
|
|
|
if (--(pSegParentMsg->IRDA_MSG_SegCount) != 0)
|
|
{
|
|
// Still outstanding segments
|
|
goto done;
|
|
}
|
|
// No more segments, send DATA_CONF to client
|
|
// First remove it from the LSAPs TxMsgList
|
|
RemoveEntryList(&pSegParentMsg->Linkage);
|
|
|
|
pMsg = pSegParentMsg;
|
|
}
|
|
|
|
// If request fails for non-segmented messages, the IRLAP error is
|
|
// returned
|
|
pMsg->Prim = IRLMP_DATA_CONF;
|
|
|
|
TdiUp(pLsapCb->TdiContext, pMsg);
|
|
done:
|
|
REFDEL(&pLsapCb->RefCnt, 'ATAD');
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | IrlapDataInd | process the data indication
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message
|
|
*
|
|
*/
|
|
VOID
|
|
IrlapDataInd(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg)
|
|
{
|
|
IRLMP_HEADER *pLMHeader;
|
|
IRLMP_CNTL_FORMAT *pCntlFormat;
|
|
UCHAR *pCntlParm1;
|
|
UCHAR *pCntlParm2;
|
|
|
|
if ((pMsg->IRDA_MSG_pWrite - pMsg->IRDA_MSG_pRead) < sizeof(IRLMP_HEADER))
|
|
{
|
|
ASSERT(0);
|
|
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Receive invalid data\n")));
|
|
|
|
return; // IRLMP_DATA_IND_BAD_FRAME;
|
|
}
|
|
|
|
pLMHeader = (IRLMP_HEADER *) pMsg->IRDA_MSG_pRead;
|
|
|
|
pMsg->IRDA_MSG_pRead += sizeof(IRLMP_HEADER);
|
|
|
|
if (pLMHeader->CntlBit != IRLMP_CNTL_PDU)
|
|
{
|
|
LmPduData(pIrlmpCb, pMsg, (int) pLMHeader->DstLsapSel,
|
|
(int) pLMHeader->SrcLsapSel);
|
|
}
|
|
else
|
|
{
|
|
pCntlFormat = (IRLMP_CNTL_FORMAT *) pMsg->IRDA_MSG_pRead;
|
|
|
|
// Ensure the control format is included. As per errate, it is
|
|
// valid to exclude the parameter (for LM-connects and only if
|
|
// no user data)
|
|
if ((UCHAR *) pCntlFormat >= pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
ASSERT(0);
|
|
// Need at least the OpCode portion
|
|
return;// IRLMP_DATA_IND_BAD_FRAME;
|
|
}
|
|
else
|
|
{
|
|
// Initialize control parameters (if exists) and point
|
|
// to beginning of user data
|
|
if (&(pCntlFormat->Parm1) >= pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
pCntlParm1 = NULL;
|
|
pCntlParm2 = NULL;
|
|
pMsg->IRDA_MSG_pRead = &(pCntlFormat->Parm1); // ie none
|
|
}
|
|
else
|
|
{
|
|
pCntlParm1 = &(pCntlFormat->Parm1);
|
|
pCntlParm2 = &(pCntlFormat->Parm2); // Access mode only
|
|
pMsg->IRDA_MSG_pRead = &(pCntlFormat->Parm2);
|
|
}
|
|
}
|
|
|
|
switch (pCntlFormat->OpCode)
|
|
{
|
|
case IRLMP_CONNECT_PDU:
|
|
if (pCntlFormat->ABit == IRLMP_ABIT_REQUEST)
|
|
{
|
|
// Connection Request LM-PDU
|
|
LmPduConnectReq(pIrlmpCb, pMsg,
|
|
(int) pLMHeader->DstLsapSel,
|
|
(int) pLMHeader->SrcLsapSel,
|
|
pCntlParm1);
|
|
}
|
|
else
|
|
{
|
|
// Connection Confirm LM-PDU
|
|
LmPduConnectConf(pIrlmpCb, pMsg,
|
|
(int) pLMHeader->DstLsapSel,
|
|
(int) pLMHeader->SrcLsapSel,
|
|
pCntlParm1);
|
|
}
|
|
break;
|
|
|
|
case IRLMP_DISCONNECT_PDU:
|
|
if (pCntlFormat->ABit != IRLMP_ABIT_REQUEST)
|
|
{
|
|
; // LOG ERROR !!!
|
|
}
|
|
else
|
|
{
|
|
LmPduDisconnectReq(pIrlmpCb, pMsg,
|
|
(int) pLMHeader->DstLsapSel,
|
|
(int) pLMHeader->SrcLsapSel,
|
|
pCntlParm1);
|
|
}
|
|
break;
|
|
|
|
case IRLMP_ACCESSMODE_PDU:
|
|
if (pCntlFormat->ABit == IRLMP_ABIT_REQUEST)
|
|
{
|
|
LmPduAccessModeReq(pIrlmpCb,
|
|
(int) pLMHeader->DstLsapSel,
|
|
(int) pLMHeader->SrcLsapSel,
|
|
pCntlParm1, pCntlParm2);
|
|
}
|
|
else
|
|
{
|
|
LmPduAccessModeConf(pIrlmpCb,
|
|
(int) pLMHeader->DstLsapSel,
|
|
(int) pLMHeader->SrcLsapSel,
|
|
pCntlParm1, pCntlParm2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | LmPduConnectReq | Process the received connect
|
|
* request LM-PDU
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | pointer to an IRDA message
|
|
* int | LocalLsapSel | The local LSAP selector,
|
|
* (destination LSAP-SEL in message)
|
|
* int | RemoteLsapSel | The remote LSAP selector,
|
|
* (source LSAP-SEL in message)
|
|
* UCHAR * | pRsvdByte | pointer to the reserved parameter
|
|
*/
|
|
VOID
|
|
LmPduConnectReq(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg,
|
|
int LocalLsapSel, int RemoteLsapSel, UCHAR *pRsvdByte)
|
|
{
|
|
IRDA_MSG IMsg;
|
|
IRLMP_LSAP_CB *pLsapCb = GetLsap(pIrlmpCb,
|
|
LocalLsapSel, RemoteLsapSel);
|
|
IRLMP_REGISTERED_LSAP *pRegLsap;
|
|
BOOLEAN LsapRegistered = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG((DBG_IRLMP | DBG_IRLMP_CONN),
|
|
(TEXT("IRLMP: Received LM_CONNECT_REQ for l=%d,r=%d\n"),
|
|
LocalLsapSel, RemoteLsapSel));
|
|
|
|
if (pRsvdByte != NULL && *pRsvdByte != 0x00)
|
|
{
|
|
// LOG ERROR (bad parm value)
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (LocalLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasConnectReq(pIrlmpCb, RemoteLsapSel);
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb == NULL) // Usually NULL, unless receiving 2nd ConnReq
|
|
{
|
|
// No reason to except connection if an LSAP hasn't been registered
|
|
for (pRegLsap = (IRLMP_REGISTERED_LSAP *)
|
|
RegisteredLsaps.Flink;
|
|
(LIST_ENTRY *) pRegLsap != &RegisteredLsaps;
|
|
pRegLsap = (IRLMP_REGISTERED_LSAP *) pRegLsap->Linkage.Flink)
|
|
{
|
|
if (pRegLsap->Lsap == LocalLsapSel)
|
|
{
|
|
LsapRegistered = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!LsapRegistered)
|
|
{
|
|
// No LSAP exists which matches the requested LSAP in the connect
|
|
// packet. IRLMP will decline this connection
|
|
UnroutableSendLMDisc(pIrlmpCb, LocalLsapSel, RemoteLsapSel);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Create a new one
|
|
if (CreateLsap(pIrlmpCb, &pLsapCb) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
pLsapCb->Flags |= pRegLsap->Flags;
|
|
pLsapCb->TdiContext = NULL;
|
|
}
|
|
|
|
// very soon this LSAP will be waiting for a connect response
|
|
// from the upper layer
|
|
pLsapCb->State = LSAP_CONN_RESP_PEND;
|
|
|
|
pLsapCb->LocalLsapSel = LocalLsapSel;
|
|
pLsapCb->RemoteLsapSel = RemoteLsapSel;
|
|
pLsapCb->UserDataLen = 0;
|
|
|
|
SetupTtpAndStoreConnData(pLsapCb, pMsg);
|
|
|
|
// Now setup the message to send to the client notifying him
|
|
// of a incoming connection indication
|
|
IMsg.Prim = IRLMP_CONNECT_IND;
|
|
|
|
RtlCopyMemory(IMsg.IRDA_MSG_RemoteDevAddr, pIrlmpCb->ConnDevAddr,
|
|
IRDA_DEV_ADDR_LEN);
|
|
IMsg.IRDA_MSG_LocalLsapSel = LocalLsapSel;
|
|
IMsg.IRDA_MSG_RemoteLsapSel = RemoteLsapSel;
|
|
IMsg.IRDA_MSG_pQos = &pIrlmpCb->NegotiatedQOS;
|
|
if (pLsapCb->UserDataLen != 0)
|
|
{
|
|
IMsg.IRDA_MSG_pConnData = pLsapCb->UserData;
|
|
IMsg.IRDA_MSG_ConnDataLen = pLsapCb->UserDataLen;
|
|
}
|
|
else
|
|
{
|
|
IMsg.IRDA_MSG_pConnData = NULL;
|
|
IMsg.IRDA_MSG_ConnDataLen = 0;
|
|
}
|
|
|
|
IMsg.IRDA_MSG_pContext = pLsapCb;
|
|
IMsg.IRDA_MSG_MaxSDUSize = pLsapCb->TxMaxSDUSize;
|
|
IMsg.IRDA_MSG_MaxPDUSize = pIrlmpCb->MaxPDUSize;
|
|
|
|
// The LSAP response timer is the time that we give the Client
|
|
// to respond to this connect indication. If it expires before
|
|
// the client responds, then IRLMP will decline the connect
|
|
IrdaTimerRestart(&pLsapCb->ResponseTimer);
|
|
|
|
TdiUp(pLsapCb->TdiContext, &IMsg);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
// Ignoring if LSAP already exists
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | LmPduConnectConf | Process the received connect
|
|
* confirm LM-PDU
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | pointer to an IRDA message
|
|
* int | LocalLsapSel | The local LSAP selector,
|
|
* (destination LSAP-SEL in message)
|
|
* int | RemoteLsapSel | The remote LSAP selector,
|
|
* (source LSAP-SEL in message)
|
|
* BYTE * | pRsvdByte | pointer to the reserved byte parameter
|
|
*/
|
|
VOID
|
|
LmPduConnectConf(PIRLMP_LINK_CB pIrlmpCb,
|
|
IRDA_MSG *pMsg, int LocalLsapSel, int RemoteLsapSel,
|
|
UCHAR *pRsvdByte)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb = GetLsap(pIrlmpCb,
|
|
LocalLsapSel, RemoteLsapSel);
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG((DBG_IRLMP | DBG_IRLMP_CONN),
|
|
(TEXT("IRLMP: Received LM_CONNECT_CONF for l=%d,r=%d\n"),
|
|
LocalLsapSel, RemoteLsapSel));
|
|
|
|
if (pRsvdByte != NULL && *pRsvdByte != 0x00)
|
|
{
|
|
// LOG ERROR, indicate bad parm
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb == NULL)
|
|
{
|
|
// This is a connect confirm to a non-existant LSAP
|
|
// LOG SOMETHING HERE !!!
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb->State != LSAP_LMCONN_CONF_PEND)
|
|
{
|
|
// received unsolicited confirm
|
|
// probably timed out
|
|
return;
|
|
}
|
|
|
|
IrdaTimerStop(&pLsapCb->ResponseTimer);
|
|
|
|
pLsapCb->State = LSAP_READY;
|
|
|
|
if (LocalLsapSel == IAS_LOCAL_LSAP_SEL && RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
SendGetValueByClassReq(pLsapCb);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
SetupTtpAndStoreConnData(pLsapCb, pMsg);
|
|
|
|
pMsg->Prim = IRLMP_CONNECT_CONF;
|
|
|
|
pMsg->IRDA_MSG_pQos = &pIrlmpCb->NegotiatedQOS;
|
|
if (pLsapCb->UserDataLen != 0)
|
|
{
|
|
pMsg->IRDA_MSG_pConnData = pLsapCb->UserData;
|
|
pMsg->IRDA_MSG_ConnDataLen = pLsapCb->UserDataLen;
|
|
}
|
|
else
|
|
{
|
|
pMsg->IRDA_MSG_pConnData = NULL;
|
|
pMsg->IRDA_MSG_ConnDataLen = 0;
|
|
}
|
|
|
|
pMsg->IRDA_MSG_pContext = pLsapCb;
|
|
pMsg->IRDA_MSG_MaxSDUSize = pLsapCb->TxMaxSDUSize;
|
|
pMsg->IRDA_MSG_MaxPDUSize = pIrlmpCb->MaxPDUSize;
|
|
|
|
TdiUp(pLsapCb->TdiContext, pMsg);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*/
|
|
VOID
|
|
SetupTtpAndStoreConnData(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
TTP_CONN_HEADER *pTTPHeader;
|
|
UCHAR PLen, *pEndParms, PI, PL;
|
|
|
|
PAGED_CODE();
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
// Upon entering this function, the pRead pointer points to the
|
|
// TTP header or the beginning of client data
|
|
|
|
if (!(pLsapCb->Flags & LCBF_USE_TTP))
|
|
{
|
|
pLsapCb->TxMaxSDUSize = pLsapCb->pIrlmpCb->MaxPDUSize;
|
|
}
|
|
else
|
|
{
|
|
if (pMsg->IRDA_MSG_pRead >= pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
// THIS IS AN ERROR, WE ARE USING TTP. There is no more
|
|
// data in the frame, but we need the TTP header
|
|
// SOME KIND OF WARNING SHOULD BE LOGGED
|
|
return;
|
|
}
|
|
pTTPHeader = (TTP_CONN_HEADER *) pMsg->IRDA_MSG_pRead;
|
|
pLsapCb->LocalTxCredit = (int) pTTPHeader->InitialCredit;
|
|
|
|
DEBUGMSG(DBG_IRLMP | DBG_IRLMP_CRED, (TEXT("IRLMP: Initial LocalTxCredit %d\n"),
|
|
pLsapCb->LocalTxCredit));
|
|
|
|
// advance the pointer to the first byte of data
|
|
pMsg->IRDA_MSG_pRead += sizeof(TTP_CONN_HEADER);
|
|
|
|
pLsapCb->TxMaxSDUSize = 0;
|
|
if (pTTPHeader->ParmFlag == TTP_PFLAG_PARMS)
|
|
{
|
|
// Parameter carrying Connect TTP-PDU
|
|
PLen = *pMsg->IRDA_MSG_pRead++;
|
|
pEndParms = pMsg->IRDA_MSG_pRead + PLen;
|
|
|
|
// NOTE: This breaks if PI other than MaxSDUSize!!!
|
|
|
|
if (PLen < 3 || pEndParms > pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
// LOG ERROR !!!
|
|
return;
|
|
}
|
|
|
|
PI = *pMsg->IRDA_MSG_pRead++;
|
|
PL = *pMsg->IRDA_MSG_pRead++;
|
|
|
|
if (PI != TTP_MAX_SDU_SIZE_PI)
|
|
{
|
|
// LOG ERROR !!!
|
|
return;
|
|
}
|
|
|
|
for ( ; PL != 0 ; PL--)
|
|
{
|
|
pLsapCb->TxMaxSDUSize <<= 8;
|
|
pLsapCb->TxMaxSDUSize += (int) (*pMsg->IRDA_MSG_pRead);
|
|
pMsg->IRDA_MSG_pRead++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there is any user data with this connection request/conf, place
|
|
// it in the control block. This is just a place to store this
|
|
// information while upper layer is looking at it. It may be over-
|
|
// written by connection data in the response from the client.
|
|
pLsapCb->UserDataLen = 0;
|
|
/*
|
|
|
|
NOTE: IF USER CONNECTION DATA IS EVER SUPPORTED, VERIFY DATA
|
|
WON'T OVERFLOW UserData BUFFER
|
|
|
|
if (pMsg->IRDA_MSG_pRead < pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
pLsapCb->UserDataLen = (UINT) (pMsg->IRDA_MSG_pWrite - pMsg->IRDA_MSG_pRead);
|
|
RtlCopyMemory(pLsapCb->UserData, pMsg->IRDA_MSG_pRead,
|
|
pLsapCb->UserDataLen);
|
|
}
|
|
*/
|
|
|
|
return;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | LmPduDisconnectReq | Process the received discconnect
|
|
* request LM-PDU
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | pointer to an IRDA message
|
|
* int | LocalLsapSel | The local LSAP selector,
|
|
* (destination LSAP-SEL in message)
|
|
* int | RemoteLsapSel | The remote LSAP selector,
|
|
* (source LSAP-SEL in message)
|
|
* BYTE * | pReason | pointer to the reason parameter
|
|
*/
|
|
VOID
|
|
LmPduDisconnectReq(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg,
|
|
int LocalLsapSel, int RemoteLsapSel, UCHAR *pReason)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
UINT rc = SUCCESS;
|
|
|
|
|
|
if (pReason == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return; // LOG ERROR !!! need reason code
|
|
}
|
|
|
|
pLsapCb = GetLsap(pIrlmpCb, LocalLsapSel, RemoteLsapSel);
|
|
|
|
DEBUGMSG((DBG_IRLMP | DBG_IRLMP_CONN),
|
|
(TEXT("IRLMP: Received LM_DISCONNECT_REQ LsapCb:%x for l=%d,r=%d\n"),
|
|
pLsapCb, LocalLsapSel, RemoteLsapSel));
|
|
|
|
if (pLsapCb == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb->State == LSAP_LMCONN_CONF_PEND)
|
|
{
|
|
IrdaTimerStop(&pLsapCb->ResponseTimer);
|
|
}
|
|
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
if (LocalLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasServerDisconnectReq(pLsapCb);
|
|
return;
|
|
}
|
|
if (LocalLsapSel == IAS_LOCAL_LSAP_SEL && RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasClientDisconnectReq(pLsapCb, *pReason);
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb->State != LSAP_DISCONNECTED)
|
|
{
|
|
pLsapCb->UserDataLen = 0;
|
|
|
|
/*
|
|
|
|
NOTE: IF USER CONNECTION DATA IS EVER SUPPORTED, VERIFY DATA
|
|
WON'T OVERFLOW UserData BUFFER
|
|
|
|
if (pMsg->IRDA_MSG_pRead < pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
// Disconnect User data
|
|
pLsapCb->UserDataLen = (UINT) (pMsg->IRDA_MSG_pWrite - pMsg->IRDA_MSG_pRead);
|
|
RtlCopyMemory(pLsapCb->UserData, pMsg->IRDA_MSG_pRead,
|
|
pLsapCb->UserDataLen);
|
|
}
|
|
*/
|
|
|
|
pLsapCb->DiscReason = *pReason;
|
|
|
|
DeleteLsap(pLsapCb);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func IRLMP_LSAP_CB *| GetLsap | For the LSAP selector pair, return the
|
|
* LSAP control block they map to. NULL
|
|
* if one does not exist
|
|
*
|
|
* @rdesc pointer to an LSAP control block or NULL
|
|
*
|
|
* @parm int | LocalLsapSel | local LSAP selector
|
|
* @parm int | RemoteLsapSel | Remote LSAP selector
|
|
*
|
|
* if an LSAP is found, its critical section is acquired
|
|
*/
|
|
IRLMP_LSAP_CB *
|
|
GetLsap(PIRLMP_LINK_CB pIrlmpCb, int LocalLsapSel, int RemoteLsapSel)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->LocalLsapSel == LocalLsapSel &&
|
|
pLsapCb->RemoteLsapSel == RemoteLsapSel)
|
|
{
|
|
return pLsapCb;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | SendCreditPdu | Send a dataless PDU to extend credit
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRLMP_LSAP_CB * | pLsapCb | pointer to an LSAP control block
|
|
*/
|
|
VOID
|
|
SendCreditPdu(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
IRDA_MSG *pMsg;
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->AvailableCredit == 0)
|
|
{
|
|
// No credit to give
|
|
return;
|
|
}
|
|
|
|
if ((pMsg = AllocIrdaBuf(IrdaMsgPool)) == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
// No Data
|
|
pMsg->IRDA_MSG_pBase =
|
|
pMsg->IRDA_MSG_pLimit =
|
|
pMsg->IRDA_MSG_pRead =
|
|
pMsg->IRDA_MSG_pWrite = pMsg->IRDA_MSG_Header + IRDA_HEADER_LEN;
|
|
|
|
pMsg->IRDA_MSG_IrCOMM_9Wire = FALSE;
|
|
|
|
pMsg->IRDA_MSG_SegFlags = SEG_FINAL;
|
|
|
|
FormatAndSendDataReq(pLsapCb, pMsg, TRUE, FALSE);
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func VOID | LmPduData | Process the received data (indication)
|
|
* LM-PDU
|
|
*
|
|
* @rdesc SUCCESS or an error code
|
|
*
|
|
* @parm IRDA_MSG * | pMsg | pointer to an IRDA message
|
|
* int | LocalLsapSel | The local LSAP selector,
|
|
* (destination LSAP-SEL in message)
|
|
* int | RemoteLsapSel | The remote LSAP selector,
|
|
* (source LSAP-SEL in message)
|
|
*/
|
|
VOID
|
|
LmPduData(PIRLMP_LINK_CB pIrlmpCb, IRDA_MSG *pMsg,
|
|
int LocalLsapSel, int RemoteLsapSel)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb = GetLsap(pIrlmpCb,
|
|
LocalLsapSel, RemoteLsapSel);
|
|
TTP_DATA_HEADER *pTTPHeader;
|
|
BOOLEAN DataPDUSent = FALSE;
|
|
BOOLEAN FinalSeg = TRUE;
|
|
|
|
if (pLsapCb == NULL)
|
|
{
|
|
// Unroutable, send disconnect
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Data sent to bad Lsap (%d,%d)\n"),
|
|
LocalLsapSel, RemoteLsapSel));
|
|
|
|
//UnroutableSendLMDisc(pIrlmpCb, LocalLsapSel, RemoteLsapSel);
|
|
return;
|
|
}
|
|
|
|
if (LocalLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasSendQueryResp(pLsapCb, pMsg);
|
|
return;
|
|
}
|
|
if (LocalLsapSel == IAS_LOCAL_LSAP_SEL && RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
IasProcessQueryResp(pIrlmpCb, pLsapCb, pMsg);
|
|
return;
|
|
}
|
|
|
|
if (pLsapCb->Flags & LCBF_USE_TTP)
|
|
{
|
|
if (pMsg->IRDA_MSG_pRead >= pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Missing TTP Header!\n")));
|
|
|
|
// NEED TTP HEADER, LOG ERROR !!!
|
|
return;
|
|
}
|
|
|
|
pTTPHeader = (TTP_DATA_HEADER *) pMsg->IRDA_MSG_pRead;
|
|
pMsg->IRDA_MSG_pRead += sizeof(TTP_DATA_HEADER);
|
|
|
|
pLsapCb->LocalTxCredit += (int) pTTPHeader->AdditionalCredit;
|
|
|
|
DEBUGMSG(DBG_IRLMP_CRED,
|
|
(TEXT("IRLMP(l%d,r%d): Rx LocalTxCredit:+%d=%d\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel,
|
|
pTTPHeader->AdditionalCredit, pLsapCb->LocalTxCredit));
|
|
|
|
if (pTTPHeader->MoreBit == TTP_MBIT_NOT_FINAL)
|
|
{
|
|
FinalSeg = FALSE;
|
|
}
|
|
}
|
|
|
|
REFADD(&pLsapCb->RefCnt, ' DNI');
|
|
|
|
if (pMsg->IRDA_MSG_pRead < pMsg->IRDA_MSG_pWrite)
|
|
{
|
|
// PDU containing data. Decrement remotes Tx Credit
|
|
pLsapCb->RemoteTxCredit--;
|
|
|
|
if (pLsapCb->State >= LSAP_READY)
|
|
{
|
|
pMsg->Prim = IRLMP_DATA_IND;
|
|
pMsg->IRDA_MSG_SegFlags = FinalSeg ? SEG_FINAL : 0;
|
|
|
|
TdiUp(pLsapCb->TdiContext, pMsg);
|
|
}
|
|
}
|
|
// else no user data, this was a dataless TTP-PDU to extend credit.
|
|
|
|
if (pLsapCb->State != LSAP_DISCONNECTED)
|
|
{
|
|
// Did we get some credit?
|
|
if ((pLsapCb->Flags & LCBF_USE_TTP) &&
|
|
pLsapCb->LocalTxCredit > 0 &&
|
|
pLsapCb->State == LSAP_NO_TX_CREDIT)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: l%d,r%d flow on\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel));
|
|
|
|
pLsapCb->State = LSAP_READY;
|
|
}
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP(l%d,r%d): Rx LocTxCredit %d,RemoteTxCredit %d\n"),
|
|
pLsapCb->LocalLsapSel, pLsapCb->RemoteLsapSel,
|
|
pLsapCb->LocalTxCredit, pLsapCb->RemoteTxCredit));
|
|
|
|
while (!IsListEmpty(&pLsapCb->SegTxMsgList) &&
|
|
pLsapCb->State == LSAP_READY)
|
|
{
|
|
pMsg = (IRDA_MSG *) RemoveHeadList(&pLsapCb->SegTxMsgList);
|
|
|
|
FormatAndSendDataReq(pLsapCb, pMsg, FALSE, FALSE);
|
|
|
|
DataPDUSent = TRUE;
|
|
}
|
|
|
|
// Do I need to extend credit to peer in a dataless PDU?
|
|
if ((pLsapCb->Flags & LCBF_USE_TTP) &&
|
|
!DataPDUSent &&
|
|
pLsapCb->RemoteTxCredit <= pIrlmpCb->WindowSize + 1)
|
|
{
|
|
SendCreditPdu(pLsapCb);
|
|
}
|
|
}
|
|
REFDEL(&pLsapCb->RefCnt, ' DNI');
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | LmPduAccessModeReq | process access mode request
|
|
* from peer
|
|
*
|
|
* @rdesc SUCCESS
|
|
*
|
|
* @parm int | LocalLsapSel | Local LSAP selector
|
|
* @parm int | LocalLsapSel | Local LSAP selector
|
|
* @parm BYTE * | pRsvdByte | Reserved byte in the Access mode PDU
|
|
* @parm BYTE * | pMode | Mode byte in Access mode PDU
|
|
*/
|
|
VOID
|
|
LmPduAccessModeReq(PIRLMP_LINK_CB pIrlmpCb,
|
|
int LocalLsapSel, int RemoteLsapSel,
|
|
UCHAR *pRsvdByte, UCHAR *pMode)
|
|
{
|
|
IRLMP_LSAP_CB *pRequestedLsapCb = GetLsap(pIrlmpCb,
|
|
LocalLsapSel,RemoteLsapSel);
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
IRDA_MSG IMsg;
|
|
|
|
if (pRequestedLsapCb==NULL || pRequestedLsapCb->State != LSAP_READY)
|
|
{
|
|
UnroutableSendLMDisc(pIrlmpCb, LocalLsapSel, RemoteLsapSel);
|
|
return;
|
|
}
|
|
|
|
if (pRsvdByte == NULL || *pRsvdByte != 0x00 || pMode == NULL)
|
|
{
|
|
// LOG ERROR, indicate bad parm
|
|
return;
|
|
}
|
|
|
|
switch (*pMode)
|
|
{
|
|
case IRLMP_EXCLUSIVE:
|
|
if (pIrlmpCb->pExclLsapCb != NULL)
|
|
{
|
|
if (pIrlmpCb->pExclLsapCb == pRequestedLsapCb)
|
|
{
|
|
// Already has exclusive mode, confirm it again I guess
|
|
// but I'm not telling my client again
|
|
SendCntlPdu(pRequestedLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_CONFIRM, IRLMP_STATUS_SUCCESS,
|
|
IRLMP_EXCLUSIVE);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// This is what spec says...
|
|
SendCntlPdu(pRequestedLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_CONFIRM, IRLMP_STATUS_FAILURE,
|
|
IRLMP_MULTIPLEXED);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Are there any other LSAPs connections? If so, NACK peer
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
if (pLsapCb->State != LSAP_DISCONNECTED &&
|
|
pLsapCb != pRequestedLsapCb)
|
|
{
|
|
SendCntlPdu(pRequestedLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_CONFIRM, IRLMP_STATUS_FAILURE,
|
|
IRLMP_MULTIPLEXED);
|
|
return;
|
|
}
|
|
}
|
|
// OK to go into exclusive mode
|
|
pIrlmpCb->pExclLsapCb = pRequestedLsapCb;
|
|
// Send confirmation to peer
|
|
SendCntlPdu(pRequestedLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_CONFIRM, IRLMP_STATUS_SUCCESS,
|
|
IRLMP_EXCLUSIVE);
|
|
// Notify client
|
|
IMsg.Prim = IRLMP_ACCESSMODE_IND;
|
|
IMsg.IRDA_MSG_AccessMode = IRLMP_EXCLUSIVE;
|
|
TdiUp(pRequestedLsapCb->TdiContext, &IMsg);
|
|
return;
|
|
|
|
case IRLMP_MULTIPLEXED:
|
|
if (pRequestedLsapCb != pIrlmpCb->pExclLsapCb)
|
|
{
|
|
// Log Error here
|
|
return;
|
|
}
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
// Send confirmation to peer
|
|
SendCntlPdu(pRequestedLsapCb, IRLMP_ACCESSMODE_PDU,
|
|
IRLMP_ABIT_CONFIRM, IRLMP_STATUS_SUCCESS,
|
|
IRLMP_MULTIPLEXED);
|
|
// Notify client
|
|
IMsg.Prim = IRLMP_ACCESSMODE_IND;
|
|
IMsg.IRDA_MSG_AccessMode = IRLMP_MULTIPLEXED;
|
|
TdiUp(pRequestedLsapCb->TdiContext, &IMsg);
|
|
return;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | LmPduAccessModeReq | process access mode request
|
|
* from peer
|
|
*
|
|
* @rdesc SUCCESS
|
|
*
|
|
* @parm int | LocalLsapSel | Local LSAP selector
|
|
* @parm int | LocalLsapSel | Local LSAP selector
|
|
* @parm BYTE * | pStatus | Status byte in the Access mode PDU
|
|
* @parm BYTE * | pMode | Mode byte in Access mode PDU
|
|
*/
|
|
VOID
|
|
LmPduAccessModeConf(PIRLMP_LINK_CB pIrlmpCb,
|
|
int LocalLsapSel, int RemoteLsapSel,
|
|
UCHAR *pStatus, UCHAR *pMode)
|
|
{
|
|
IRLMP_LSAP_CB *pRequestedLsapCb = GetLsap(pIrlmpCb,
|
|
LocalLsapSel,RemoteLsapSel);
|
|
IRDA_MSG IMsg;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: ACCESSMODE_CONF\r\n")));
|
|
|
|
if (pRequestedLsapCb==NULL)
|
|
{
|
|
UnroutableSendLMDisc(pIrlmpCb, LocalLsapSel, RemoteLsapSel);
|
|
return;
|
|
}
|
|
if (pStatus == NULL || pMode == NULL)
|
|
{
|
|
// LOG ERROR
|
|
return;
|
|
}
|
|
|
|
switch (*pMode)
|
|
{
|
|
case IRLMP_EXCLUSIVE:
|
|
if (pRequestedLsapCb != pIrlmpCb->pExclLsapCb ||
|
|
pRequestedLsapCb->State != LSAP_EXCLUSIVEMODE_PEND)
|
|
{
|
|
// LOG ERROR
|
|
return;
|
|
}
|
|
if (*pStatus != IRLMP_STATUS_SUCCESS)
|
|
{
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
return; // protocol error,
|
|
// wouldn't have Exclusive mode != SUCCESS
|
|
}
|
|
else
|
|
{
|
|
pRequestedLsapCb->State = LSAP_READY;
|
|
|
|
IMsg.Prim = IRLMP_ACCESSMODE_CONF;
|
|
IMsg.IRDA_MSG_AccessMode = IRLMP_EXCLUSIVE;
|
|
IMsg.IRDA_MSG_ModeStatus = IRLMP_ACCESSMODE_SUCCESS;
|
|
|
|
TdiUp(pRequestedLsapCb->TdiContext, &IMsg);
|
|
return;
|
|
}
|
|
|
|
case IRLMP_MULTIPLEXED:
|
|
if (pRequestedLsapCb != pIrlmpCb->pExclLsapCb ||
|
|
(pRequestedLsapCb->State != LSAP_EXCLUSIVEMODE_PEND &&
|
|
pRequestedLsapCb->State != LSAP_MULTIPLEXEDMODE_PEND))
|
|
{
|
|
return;
|
|
}
|
|
|
|
pIrlmpCb->pExclLsapCb = NULL;
|
|
pRequestedLsapCb->State = LSAP_READY;
|
|
|
|
IMsg.Prim = IRLMP_ACCESSMODE_CONF;
|
|
IMsg.IRDA_MSG_AccessMode = *pMode;
|
|
if (*pStatus == IRLMP_STATUS_SUCCESS)
|
|
{
|
|
IMsg.IRDA_MSG_ModeStatus = IRLMP_ACCESSMODE_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
IMsg.IRDA_MSG_ModeStatus = IRLMP_ACCESSMODE_FAILURE;
|
|
}
|
|
TdiUp(pRequestedLsapCb->TdiContext, &IMsg);
|
|
return;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | UnroutableSendLMDisc | Sends an LM-Disconnect to peer with
|
|
* reason = "received LM packet on
|
|
* disconnected LSAP"
|
|
* @parm int | LocalLsapSel | the local LSAP selector in LM-PDU
|
|
* @parm int | RemoteLsapSel | the remote LSAP selector in LM-PDU
|
|
*/
|
|
VOID
|
|
UnroutableSendLMDisc(PIRLMP_LINK_CB pIrlmpCb, int LocalLsapSel, int RemoteLsapSel)
|
|
{
|
|
IRLMP_LSAP_CB FakeLsapCb;
|
|
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: received unroutabled Pdu LocalLsap:%d RemoteLsap:%d\n"),
|
|
LocalLsapSel, RemoteLsapSel));
|
|
FakeLsapCb.Flags = 0;
|
|
FakeLsapCb.LocalLsapSel = LocalLsapSel;
|
|
FakeLsapCb.RemoteLsapSel = RemoteLsapSel;
|
|
FakeLsapCb.UserDataLen = 0;
|
|
FakeLsapCb.pIrlmpCb = pIrlmpCb;
|
|
#ifdef DBG
|
|
FakeLsapCb.Sig = LSAPSIG;
|
|
#endif
|
|
|
|
SendCntlPdu(&FakeLsapCb,IRLMP_DISCONNECT_PDU,
|
|
IRLMP_ABIT_REQUEST, IRLMP_DISC_LSAP, 0);
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | InitiateDiscovoryReq | A deferred processing routine that sends
|
|
* an IRLAP discovery request
|
|
*/
|
|
void
|
|
InitiateDiscoveryReq(PVOID Context)
|
|
{
|
|
IRDA_MSG IMsg;
|
|
UINT rc;
|
|
PIRLMP_LINK_CB pIrlmpCb = NULL;
|
|
PIRLMP_LINK_CB pIrlmpCb2 = NULL;
|
|
PIRDA_LINK_CB pIrdaLinkCb;
|
|
KIRQL OldIrql;
|
|
BOOLEAN ScheduleNextLink = TRUE;
|
|
BOOLEAN MediaSense = TRUE;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
DEBUGMSG(DBG_DISCOVERY, (TEXT("IRLMP: InitDscvReq event\n")));
|
|
|
|
// Find the next link to start discovery on
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = (PIRDA_LINK_CB) pIrdaLinkCb->Linkage.Flink)
|
|
{
|
|
pIrlmpCb2 = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
if (pIrlmpCb2->DiscoveryFlags)
|
|
{
|
|
if (pIrlmpCb2->DiscoveryFlags == DF_NO_SENSE_DSCV)
|
|
{
|
|
MediaSense = FALSE;
|
|
}
|
|
pIrlmpCb2->DiscoveryFlags = 0;
|
|
pIrlmpCb = pIrlmpCb2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// No more links on which to discover, send confirm up
|
|
if (pIrlmpCb == NULL)
|
|
{
|
|
if (pIrlmpCb2 == NULL)
|
|
{
|
|
IMsg.IRDA_MSG_DscvStatus = IRLMP_NO_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
IMsg.IRDA_MSG_DscvStatus = IRLAP_DISCOVERY_COMPLETED;
|
|
}
|
|
|
|
DscvReqScheduled = FALSE;
|
|
|
|
IMsg.Prim = IRLMP_DISCOVERY_CONF;
|
|
IMsg.IRDA_MSG_pDevList = &gDeviceList;
|
|
|
|
// Hold the spin lock to protect list while TDI is copying it.
|
|
|
|
TdiUp(NULL, &IMsg);
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
// Add a reference so link won't be removed from underneath us here
|
|
// (was happening coming out of hibernation)
|
|
|
|
REFADD(&pIrlmpCb->pIrdaLinkCb->RefCnt, 'VCSD');
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
LOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
if (pIrlmpCb->LinkState == LINK_DISCONNECTED &&
|
|
!pIrlmpCb->ConnReqScheduled)
|
|
{
|
|
IMsg.Prim = IRLAP_DISCOVERY_REQ;
|
|
IMsg.IRDA_MSG_SenseMedia = MediaSense;
|
|
|
|
DEBUGMSG(DBG_DISCOVERY,
|
|
(TEXT
|
|
("IRLMP: Sent IRLAP_DISCOVERY_REQ, New LinkState=LINK_IN_DISCOVERY\n")));
|
|
|
|
if ((rc = IrlapDown(pIrlmpCb->pIrdaLinkCb->IrlapContext, &IMsg)) != SUCCESS)
|
|
{
|
|
if (rc != IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR &&
|
|
rc != IRLAP_REMOTE_CONNECTION_IN_PROGRESS_ERR)
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
else
|
|
{
|
|
DEBUGMSG(DBG_DISCOVERY, (TEXT("IRLAP_DISCOVERY_REQ failed, link busy\n")));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pIrlmpCb->LinkState = LINK_IN_DISCOVERY;
|
|
// The next link will be schedule to run discovery when
|
|
// the DISCOVERY_CONF for this link is received
|
|
ScheduleNextLink = FALSE;
|
|
}
|
|
}
|
|
|
|
UNLOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
REFDEL(&pIrlmpCb->pIrdaLinkCb->RefCnt, 'VCSD');
|
|
|
|
// Discovery failed on this link or it was not in the disconnected
|
|
// state, schedule the next one
|
|
|
|
if (ScheduleNextLink)
|
|
{
|
|
IrdaEventSchedule(&EvDiscoveryReq, NULL);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ScheduleConnectReq(PIRLMP_LINK_CB pIrlmpCb)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
|
|
// Schedule the ConnectReq event if not already scheduled and if an LSAP
|
|
// has a connect pending
|
|
if (pIrlmpCb->ConnReqScheduled == FALSE)
|
|
{
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->State == LSAP_CONN_REQ_PEND)
|
|
{
|
|
IrdaEventSchedule(&EvConnectReq, pIrlmpCb->pIrdaLinkCb);
|
|
|
|
pIrlmpCb->ConnReqScheduled = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @func UINT | InitiateConnectReq | A deferred processing routine that sends
|
|
* IRLAP a connect request
|
|
* This is scheduled after an IRLMP discovery confirm or a disconnect
|
|
* indication has been received via IRLMP_Up(). This allows the possible
|
|
* IRLAP connect request to be made in a different context.
|
|
*/
|
|
void
|
|
InitiateConnectReq(PVOID Context)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
BOOLEAN ConnectIrlap = FALSE;
|
|
IRDA_MSG IMsg;
|
|
UINT rc;
|
|
PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
LOCK_LINK(pIrdaLinkCb);
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: InitiateConnectReq()!\n")));
|
|
|
|
pIrlmpCb->ConnReqScheduled = FALSE;
|
|
|
|
if (pIrlmpCb->LinkState != LINK_DISCONNECTED &&
|
|
pIrlmpCb->LinkState != LINK_CONNECTING)
|
|
{
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
// Check for LSAP in the connect request pending state.
|
|
// If one or more exists, place them in IRLAP connect pending state
|
|
// and initiate an IRLAP connection
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
if (pLsapCb->State == LSAP_CONN_REQ_PEND)
|
|
{
|
|
pLsapCb->State = LSAP_IRLAP_CONN_PEND;
|
|
ConnectIrlap = TRUE;
|
|
}
|
|
}
|
|
|
|
if (ConnectIrlap && pIrlmpCb->LinkState == LINK_DISCONNECTED)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP: IRLAP_CONNECT_REQ, State=LINK CONNECTING\r\n")));
|
|
|
|
pIrlmpCb->LinkState = LINK_CONNECTING;
|
|
|
|
pIrlmpCb->ConnDevAddrSet = FALSE; // This was previously set by
|
|
// the LSAP which set the remote
|
|
// device address. This is the
|
|
// first opportunity to clear
|
|
// the flag
|
|
|
|
// Get the connection address out of the IRLMP control block
|
|
RtlCopyMemory(IMsg.IRDA_MSG_RemoteDevAddr, pIrlmpCb->ConnDevAddr,
|
|
IRDA_DEV_ADDR_LEN);
|
|
IMsg.Prim = IRLAP_CONNECT_REQ;
|
|
if ((rc = IrlapDown(pIrlmpCb->pIrdaLinkCb->IrlapContext, &IMsg))
|
|
!= SUCCESS)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP: IRLAP_CONNECT_REQ failed, State=LINK_DISCONNECTED\r\n")));
|
|
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTED;
|
|
|
|
ASSERT(rc == IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR);
|
|
|
|
TearDownConnections(pIrlmpCb, IRLMP_IRLAP_REMOTE_DISCOVERY_IN_PROGRESS);
|
|
}
|
|
}
|
|
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
InitiateConnectResp(PVOID Context)
|
|
{
|
|
IRDA_MSG IMsg;
|
|
PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
LOCK_LINK(pIrdaLinkCb);
|
|
|
|
ASSERT(pIrlmpCb->LinkState == LINK_CONNECTING);
|
|
|
|
if (pIrlmpCb->AcceptConnection)
|
|
{
|
|
IMsg.Prim = IRLAP_CONNECT_RESP;
|
|
|
|
IrlapDown(pIrdaLinkCb->IrlapContext, &IMsg);
|
|
|
|
pIrlmpCb->LinkState = LINK_READY;
|
|
|
|
// Disconnect the link if no LSAP connection after a bit
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
IrdaEventSchedule(&EvLmConnectReq, pIrlmpCb->pIrdaLinkCb);
|
|
|
|
}
|
|
else
|
|
{
|
|
pIrlmpCb->LinkState = LINK_DISCONNECTED;
|
|
IMsg.Prim = IRLAP_DISCONNECT_REQ;
|
|
IrlapDown(pIrdaLinkCb->IrlapContext, &IMsg);
|
|
}
|
|
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
InitiateLMConnectReq(PVOID Context)
|
|
{
|
|
PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
IRLMP_LSAP_CB *pLsapCb;
|
|
|
|
LOCK_LINK(pIrdaLinkCb);
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: InitiateLMConnectReq()!\n")));
|
|
|
|
// Send the connect request PDU to peer LSAPs
|
|
while ((pLsapCb = GetLsapInState(pIrlmpCb, LINK_READY,
|
|
LSAP_IRLAP_CONN_PEND, TRUE)) != NULL)
|
|
{
|
|
pLsapCb->State = LSAP_LMCONN_CONF_PEND;
|
|
|
|
// Ask remote LSAP for a connection
|
|
SendCntlPdu(pLsapCb, IRLMP_CONNECT_PDU, IRLMP_ABIT_REQUEST,
|
|
IRLMP_RSVD_PARM, 0);
|
|
|
|
IrdaTimerRestart(&pLsapCb->ResponseTimer);
|
|
}
|
|
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
}
|
|
|
|
void
|
|
InitiateCloseLink(PVOID Context)
|
|
{
|
|
PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
IRDA_MSG IMsg;
|
|
LARGE_INTEGER SleepMs;
|
|
PIRLMP_LSAP_CB pLsapCb;
|
|
|
|
PAGED_CODE();
|
|
|
|
// //Sleep(500); // This sleep allows time for LAP to send any
|
|
// LM_DISCONNECT_REQ's that may be sitting on its TxQue.
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: InitiateCloseLink()!\n")));
|
|
|
|
LOCK_LINK(pIrdaLinkCb);
|
|
|
|
// Stop link timer
|
|
IrdaTimerStop(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
// Bring down the link...
|
|
IMsg.Prim = IRLAP_DISCONNECT_REQ;
|
|
IrlapDown(pIrdaLinkCb->IrlapContext, &IMsg);
|
|
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
|
|
// Allow LAP time to disconnect link
|
|
SleepMs.QuadPart = -(10*1000*1000);
|
|
|
|
KeDelayExecutionThread(KernelMode, FALSE, &SleepMs);
|
|
|
|
LOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
IrlapCloseLink(pIrdaLinkCb);
|
|
|
|
TearDownConnections(pIrlmpCb, IRLMP_UNSPECIFIED_DISC);
|
|
|
|
// Delete the ias entry if it exists
|
|
for (pLsapCb = (IRLMP_LSAP_CB *) pIrlmpCb->LsapCbList.Flink;
|
|
(LIST_ENTRY *) pLsapCb != &pIrlmpCb->LsapCbList;
|
|
pLsapCb = (IRLMP_LSAP_CB *) pLsapCb->Linkage.Flink)
|
|
{
|
|
if (pLsapCb->RemoteLsapSel == IAS_LSAP_SEL)
|
|
{
|
|
pLsapCb->RemoteLsapSel = 1; // DeleteLsap ignore IAS_LSAP_SEL
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP Shutdown\n")));
|
|
|
|
UNLOCK_LINK(pIrdaLinkCb);
|
|
|
|
return;
|
|
}
|
|
|
|
// IAS
|
|
|
|
// Oh my God! I'm out of time, no more function hdrs
|
|
|
|
int StringLen(char *p)
|
|
{
|
|
int i = 0;
|
|
|
|
while (*p++ != 0)
|
|
{
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
int StringCmp(char *p1, char *p2)
|
|
{
|
|
while (1)
|
|
{
|
|
if (*p1 != *p2)
|
|
break;
|
|
|
|
if (*p1 == 0)
|
|
return 0;
|
|
p1++, p2++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
UINT
|
|
IrlmpGetValueByClassReq(IRDA_MSG *pReqMsg)
|
|
{
|
|
UINT rc = SUCCESS;
|
|
PIRLMP_LINK_CB pIrlmpCb = GetIrlmpCb(pReqMsg->IRDA_MSG_pIasQuery->irdaDeviceID);
|
|
IRDA_MSG IMsg;
|
|
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: IRLMP_GETVALUEBYCLASS_REQ\n")));
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pIrlmpCb == NULL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Null IrlmpCb\n")));
|
|
return IRLMP_BAD_DEV_ADDR;;
|
|
}
|
|
|
|
LOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
if (pIrlmpCb->pIasQuery != NULL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR,
|
|
(TEXT("IRLMP: ERROR query already in progress\n")));
|
|
rc = IRLMP_IAS_QUERY_IN_PROGRESS;
|
|
|
|
UNLOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
}
|
|
else
|
|
{
|
|
// Save the pointer to the query in the control block
|
|
// and then request a connection to the remote IAS LSAP
|
|
|
|
// Save it
|
|
pIrlmpCb->pIasQuery = pReqMsg->IRDA_MSG_pIasQuery;
|
|
pIrlmpCb->AttribLen = pReqMsg->IRDA_MSG_AttribLen;
|
|
pIrlmpCb->AttribLenWritten = 0;
|
|
pIrlmpCb->FirstIasRespReceived = FALSE;
|
|
pIrlmpCb->IasRetryCnt = 0;
|
|
|
|
UNLOCK_LINK(pIrlmpCb->pIrdaLinkCb);
|
|
|
|
// request connection
|
|
IMsg.Prim = IRLMP_CONNECT_REQ;
|
|
IMsg.IRDA_MSG_RemoteLsapSel = IAS_LSAP_SEL;
|
|
IMsg.IRDA_MSG_LocalLsapSel = IAS_LOCAL_LSAP_SEL;
|
|
IMsg.IRDA_MSG_pQos = NULL;
|
|
IMsg.IRDA_MSG_pConnData = NULL;
|
|
IMsg.IRDA_MSG_ConnDataLen = 0;
|
|
IMsg.IRDA_MSG_UseTtp = FALSE;
|
|
IMsg.IRDA_MSG_pContext = NULL;
|
|
|
|
RtlCopyMemory(pIrlmpCb->IasQueryDevAddr,
|
|
pReqMsg->IRDA_MSG_pIasQuery->irdaDeviceID,
|
|
IRDA_DEV_ADDR_LEN);
|
|
|
|
RtlCopyMemory(IMsg.IRDA_MSG_RemoteDevAddr,
|
|
pReqMsg->IRDA_MSG_pIasQuery->irdaDeviceID,
|
|
IRDA_DEV_ADDR_LEN);
|
|
|
|
if ((rc = IrlmpConnectReq(&IMsg)) != SUCCESS)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Retry IasQuery at start\n")));
|
|
|
|
IrdaEventSchedule(&EvRetryIasQuery,
|
|
pIrlmpCb->pIrdaLinkCb);
|
|
rc = SUCCESS;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
VOID
|
|
SendGetValueByClassReq(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
IRDA_MSG *pMsg;
|
|
IAS_CONTROL_FIELD *pControl;
|
|
int ClassNameLen;
|
|
int AttribNameLen;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pLsapCb->pIrlmpCb;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pIrlmpCb->pIasQuery == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClassNameLen = StringLen(pIrlmpCb->pIasQuery->irdaClassName);
|
|
AttribNameLen = StringLen(pIrlmpCb->pIasQuery->irdaAttribName);
|
|
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: Send GetValueByClassReq(%hs,%hs)\n"),
|
|
pIrlmpCb->pIasQuery->irdaClassName, pIrlmpCb->pIasQuery->irdaAttribName));
|
|
|
|
// Alloc a message for data request that will contain the query
|
|
if ((pMsg = AllocIrdaBuf(IrdaMsgPool)) == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
pMsg->IRDA_MSG_pHdrRead =
|
|
pMsg->IRDA_MSG_pHdrWrite = pMsg->IRDA_MSG_Header + IRDA_HEADER_LEN;
|
|
|
|
pMsg->IRDA_MSG_pRead = \
|
|
pMsg->IRDA_MSG_pWrite = \
|
|
pMsg->IRDA_MSG_pBase = pMsg->IRDA_MSG_pHdrWrite;
|
|
pMsg->IRDA_MSG_pLimit = pMsg->IRDA_MSG_pBase +
|
|
IRDA_MSG_DATA_SIZE_INTERNAL - sizeof(IRDA_MSG) - 1;
|
|
|
|
// Build the query and then send it in a LAP data req
|
|
|
|
pControl = (IAS_CONTROL_FIELD *) pMsg->IRDA_MSG_pRead;
|
|
|
|
pControl->Last = TRUE;
|
|
pControl->Ack = FALSE;
|
|
pControl->OpCode = IAS_OPCODE_GET_VALUE_BY_CLASS;
|
|
|
|
*(pMsg->IRDA_MSG_pRead + 1) = (UCHAR) ClassNameLen;
|
|
RtlCopyMemory(pMsg->IRDA_MSG_pRead + 2,
|
|
pIrlmpCb->pIasQuery->irdaClassName,
|
|
ClassNameLen);
|
|
*(pMsg->IRDA_MSG_pRead + ClassNameLen + 2) = (UCHAR) AttribNameLen;
|
|
RtlCopyMemory(pMsg->IRDA_MSG_pRead + ClassNameLen + 3,
|
|
pIrlmpCb->pIasQuery->irdaAttribName,
|
|
AttribNameLen);
|
|
|
|
pMsg->IRDA_MSG_pWrite = pMsg->IRDA_MSG_pRead + ClassNameLen + AttribNameLen + 3;
|
|
|
|
pMsg->IRDA_MSG_IrCOMM_9Wire = FALSE;
|
|
|
|
FormatAndSendDataReq(pLsapCb, pMsg, TRUE, TRUE);
|
|
}
|
|
|
|
VOID
|
|
IasConnectReq(PIRLMP_LINK_CB pIrlmpCb, int RemoteLsapSel)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb = GetLsap(pIrlmpCb,
|
|
IAS_LSAP_SEL, RemoteLsapSel);
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: Received IAS connect request\n")));
|
|
|
|
if (pLsapCb == NULL)
|
|
{
|
|
if (CreateLsap(pIrlmpCb, &pLsapCb) != SUCCESS)
|
|
return;
|
|
|
|
pLsapCb->State = LSAP_READY;
|
|
pLsapCb->LocalLsapSel = IAS_LSAP_SEL;
|
|
pLsapCb->RemoteLsapSel = RemoteLsapSel;
|
|
}
|
|
|
|
SendCntlPdu(pLsapCb, IRLMP_CONNECT_PDU, IRLMP_ABIT_CONFIRM,
|
|
IRLMP_RSVD_PARM, 0);
|
|
}
|
|
|
|
VOID
|
|
IasServerDisconnectReq(IRLMP_LSAP_CB *pLsapCb)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: Received disconnect request IAS\n")));
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
IasClientDisconnectReq(IRLMP_LSAP_CB *pLsapCb, IRLMP_DISC_REASON DiscReason)
|
|
{
|
|
IRDA_MSG IMsg;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pLsapCb->pIrlmpCb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
if (pIrlmpCb->pIasQuery != NULL)
|
|
{
|
|
if (DiscReason != IRLMP_UNSPECIFIED_DISC)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Retry IasQuery as timeout\n")));
|
|
|
|
IrdaEventSchedule(&EvRetryIasQuery,
|
|
pIrlmpCb->pIrdaLinkCb);
|
|
}
|
|
else
|
|
{
|
|
pIrlmpCb->pIasQuery = NULL;
|
|
|
|
// Disconnect link
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
IMsg.Prim = IRLMP_GETVALUEBYCLASS_CONF;
|
|
IMsg.IRDA_MSG_IASStatus = DiscReason;
|
|
|
|
TdiUp(NULL, &IMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IasSendQueryResp(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
IAS_CONTROL_FIELD *pCntl = (IAS_CONTROL_FIELD *) pMsg->IRDA_MSG_pRead++;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pCntl->OpCode != IAS_OPCODE_GET_VALUE_BY_CLASS)
|
|
{
|
|
return;// IRLMP_UNSUPPORTED_IAS_OPERATION;
|
|
}
|
|
|
|
SendGetValueByClassResp(pLsapCb, pMsg);
|
|
}
|
|
|
|
VOID
|
|
IasProcessQueryResp(PIRLMP_LINK_CB pIrlmpCb,
|
|
IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pMsg)
|
|
{
|
|
IAS_CONTROL_FIELD *pCntl = (IAS_CONTROL_FIELD *) pMsg->IRDA_MSG_pRead++;
|
|
UCHAR ReturnCode;
|
|
int ObjID;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pIrlmpCb->pIasQuery == NULL)
|
|
{
|
|
return;
|
|
// return IRLMP_UNSOLICITED_IAS_RESPONSE;
|
|
}
|
|
|
|
if (pIrlmpCb->FirstIasRespReceived == FALSE)
|
|
{
|
|
pIrlmpCb->FirstIasRespReceived = TRUE;
|
|
|
|
ReturnCode = *pMsg->IRDA_MSG_pRead++;
|
|
|
|
if (ReturnCode != IAS_SUCCESS)
|
|
{
|
|
if (ReturnCode == IAS_NO_SUCH_OBJECT)
|
|
{
|
|
pMsg->IRDA_MSG_IASStatus = IRLMP_IAS_NO_SUCH_OBJECT;
|
|
}
|
|
else
|
|
{
|
|
pMsg->IRDA_MSG_IASStatus = IRLMP_IAS_NO_SUCH_ATTRIB;
|
|
}
|
|
|
|
// Disconnect LSAP
|
|
SendCntlPdu(pLsapCb,IRLMP_DISCONNECT_PDU,IRLMP_ABIT_REQUEST,
|
|
IRLMP_USER_REQUEST, 0);
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
// Disconnect link
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
pMsg->Prim = IRLMP_GETVALUEBYCLASS_CONF;
|
|
pMsg->IRDA_MSG_pIasQuery = pIrlmpCb->pIasQuery;
|
|
pIrlmpCb->pIasQuery = NULL;
|
|
|
|
TdiUp(NULL, pMsg);
|
|
return;
|
|
}
|
|
|
|
pIrlmpCb->QueryListLen = ((int)(*pMsg->IRDA_MSG_pRead++)) << 8;
|
|
pIrlmpCb->QueryListLen += (int) *pMsg->IRDA_MSG_pRead++;
|
|
|
|
// What I am going to do with this?
|
|
ObjID = ((int)(*pMsg->IRDA_MSG_pRead++)) << 8;
|
|
ObjID += (int) *pMsg->IRDA_MSG_pRead++;
|
|
|
|
pIrlmpCb->pIasQuery->irdaAttribType = (int) *pMsg->IRDA_MSG_pRead++;
|
|
|
|
switch (pIrlmpCb->pIasQuery->irdaAttribType)
|
|
{
|
|
case IAS_ATTRIB_VAL_MISSING:
|
|
break;
|
|
|
|
case IAS_ATTRIB_VAL_INTEGER:
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribInt = 0;
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribInt +=
|
|
((int) (*pMsg->IRDA_MSG_pRead++) << 24) & 0xFF000000;
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribInt +=
|
|
((int) (*pMsg->IRDA_MSG_pRead++) << 16) & 0xFF0000;
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribInt +=
|
|
((int) (*pMsg->IRDA_MSG_pRead++) << 8) & 0xFF00;
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribInt +=
|
|
(int) (*pMsg->IRDA_MSG_pRead++) & 0xFF;
|
|
break;
|
|
|
|
case IAS_ATTRIB_VAL_BINARY:
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribOctetSeq.Len = 0;
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribOctetSeq.Len +=
|
|
((int )(*pMsg->IRDA_MSG_pRead++) << 8) & 0xFF00;
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribOctetSeq.Len +=
|
|
((int) *pMsg->IRDA_MSG_pRead++) & 0xFF;
|
|
break;
|
|
|
|
|
|
case IAS_ATTRIB_VAL_STRING:
|
|
// char set
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribUsrStr.CharSet =
|
|
*pMsg->IRDA_MSG_pRead++;
|
|
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribUsrStr.Len =
|
|
(int) *pMsg->IRDA_MSG_pRead++;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (pIrlmpCb->pIasQuery->irdaAttribType)
|
|
{
|
|
case IAS_ATTRIB_VAL_BINARY:
|
|
while (pMsg->IRDA_MSG_pRead < pMsg->IRDA_MSG_pWrite &&
|
|
pIrlmpCb->AttribLenWritten < pIrlmpCb->AttribLen &&
|
|
pIrlmpCb->AttribLenWritten < pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribOctetSeq.Len)
|
|
{
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribOctetSeq.OctetSeq[pIrlmpCb->AttribLenWritten++] = *pMsg->IRDA_MSG_pRead++;
|
|
}
|
|
|
|
break;
|
|
|
|
case IAS_ATTRIB_VAL_STRING:
|
|
while (pMsg->IRDA_MSG_pRead < pMsg->IRDA_MSG_pWrite &&
|
|
pIrlmpCb->AttribLenWritten < pIrlmpCb->AttribLen &&
|
|
pIrlmpCb->AttribLenWritten < pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribUsrStr.Len)
|
|
{
|
|
pIrlmpCb->pIasQuery->irdaAttribute.irdaAttribUsrStr.UsrStr[pIrlmpCb->AttribLenWritten++] = *pMsg->IRDA_MSG_pRead++;
|
|
}
|
|
}
|
|
|
|
if (pCntl->Last == TRUE)
|
|
{
|
|
pMsg->IRDA_MSG_pIasQuery = pIrlmpCb->pIasQuery;
|
|
|
|
// Done with query
|
|
pIrlmpCb->pIasQuery = NULL;
|
|
|
|
// Disconnect LSAP
|
|
SendCntlPdu(pLsapCb,IRLMP_DISCONNECT_PDU,IRLMP_ABIT_REQUEST,
|
|
IRLMP_USER_REQUEST, 0);
|
|
|
|
DeleteLsap(pLsapCb);
|
|
|
|
// Disconnect link
|
|
IrdaTimerRestart(&pIrlmpCb->DiscDelayTimer);
|
|
|
|
pMsg->Prim = IRLMP_GETVALUEBYCLASS_CONF;
|
|
|
|
if (pIrlmpCb->QueryListLen > 1)
|
|
{
|
|
pMsg->IRDA_MSG_IASStatus = IRLMP_IAS_SUCCESS_LISTLEN_GREATER_THAN_ONE;
|
|
}
|
|
else
|
|
{
|
|
pMsg->IRDA_MSG_IASStatus = IRLMP_IAS_SUCCESS;
|
|
}
|
|
|
|
TdiUp(NULL, pMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
UINT
|
|
NewQueryMsg(PIRLMP_LINK_CB pIrlmpCb, LIST_ENTRY *pList, IRDA_MSG **ppMsg)
|
|
{
|
|
IRDA_MSG *pMsg;
|
|
|
|
if ((*ppMsg = AllocIrdaBuf(IrdaMsgPool)) == NULL)
|
|
{
|
|
pMsg = (IRDA_MSG *) RemoveHeadList(pList);
|
|
|
|
while (pMsg != (IRDA_MSG *) pList)
|
|
{
|
|
FreeIrdaBuf(IrdaMsgPool, pMsg);
|
|
pMsg = (IRDA_MSG *) RemoveHeadList(pList);
|
|
}
|
|
return IRLMP_ALLOC_FAILED;
|
|
}
|
|
(*ppMsg)->IRDA_MSG_pHdrRead = \
|
|
(*ppMsg)->IRDA_MSG_pHdrWrite = (*ppMsg)->IRDA_MSG_Header+IRDA_HEADER_LEN;
|
|
|
|
(*ppMsg)->IRDA_MSG_pRead = \
|
|
(*ppMsg)->IRDA_MSG_pWrite = \
|
|
(*ppMsg)->IRDA_MSG_pBase = (*ppMsg)->IRDA_MSG_pHdrWrite;
|
|
(*ppMsg)->IRDA_MSG_pLimit = (*ppMsg)->IRDA_MSG_pBase +
|
|
IRDA_MSG_DATA_SIZE_INTERNAL - sizeof(IRDA_MSG) - 1;
|
|
|
|
InsertTailList(pList, &( (*ppMsg)->Linkage) );
|
|
|
|
// reserve space for the IAS control field.
|
|
(*ppMsg)->IRDA_MSG_pWrite += sizeof(IAS_CONTROL_FIELD);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SendGetValueByClassResp(IRLMP_LSAP_CB *pLsapCb, IRDA_MSG *pReqMsg)
|
|
{
|
|
int ClassNameLen, AttribNameLen;
|
|
CHAR *pClassName, *pAttribName;
|
|
IRDA_MSG *pQMsg, *pNextMsg;
|
|
IAS_OBJECT *pObject;
|
|
IAS_ATTRIBUTE *pAttrib;
|
|
LIST_ENTRY QueryList;
|
|
IAS_CONTROL_FIELD *pControl;
|
|
UCHAR *pReturnCode;
|
|
UCHAR *pListLen;
|
|
UCHAR *pBPtr;
|
|
int ListLen = 0;
|
|
BOOLEAN ObjectFound = FALSE;
|
|
BOOLEAN AttribFound = FALSE;
|
|
int i;
|
|
#if DBG
|
|
char ClassStr[128];
|
|
char AttribStr[128];
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUGMSG(DBG_IRLMP,
|
|
(TEXT("IRLMP: Remote GetValueByClass query received\n")));
|
|
|
|
ClassNameLen = (int) *pReqMsg->IRDA_MSG_pRead;
|
|
pClassName = (CHAR *) (pReqMsg->IRDA_MSG_pRead + 1);
|
|
|
|
AttribNameLen = (int) *(pClassName + ClassNameLen);
|
|
pAttribName = pClassName + ClassNameLen + 1;
|
|
|
|
#if DBG
|
|
RtlCopyMemory(ClassStr, pClassName, ClassNameLen);
|
|
ClassStr[ClassNameLen] = 0;
|
|
RtlCopyMemory(AttribStr, pAttribName, AttribNameLen);
|
|
AttribStr[AttribNameLen] = 0;
|
|
#endif
|
|
|
|
if (pReqMsg->IRDA_MSG_pWrite != (UCHAR *) (pAttribName + AttribNameLen))
|
|
{
|
|
// The end of the message didn't point to where the end of
|
|
// the parameters.
|
|
|
|
// LOG ERROR.
|
|
//return IRLMP_BAD_IAS_QUERY_FROM_REMOTE;
|
|
return;
|
|
}
|
|
|
|
// The query may require multiple frames to transmit, build a list
|
|
InitializeListHead(&QueryList);
|
|
|
|
// Create the first message
|
|
if (NewQueryMsg(pLsapCb->pIrlmpCb, &QueryList, &pQMsg) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
pReturnCode = pQMsg->IRDA_MSG_pWrite++;
|
|
pListLen = pQMsg->IRDA_MSG_pWrite++;
|
|
pQMsg->IRDA_MSG_pWrite++; // list len get 2 bytes
|
|
|
|
for (pObject = (IAS_OBJECT *) IasObjects.Flink;
|
|
(LIST_ENTRY *) pObject != &IasObjects;
|
|
pObject = (IAS_OBJECT *) pObject->Linkage.Flink)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT(" compare object %hs with %hs\n"),
|
|
ClassStr, pObject->pClassName));
|
|
|
|
if (ClassNameLen == StringLen(pObject->pClassName) &&
|
|
CTEMemCmp(pClassName, pObject->pClassName, (ULONG) ClassNameLen) == 0)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT(" Object found\n")));
|
|
|
|
ObjectFound = TRUE;
|
|
|
|
pAttrib = pObject->pAttributes;
|
|
while (pAttrib != NULL)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT(" compare attrib %hs with %hs\n"),
|
|
pAttrib->pAttribName, AttribStr));
|
|
|
|
if (AttribNameLen == StringLen(pAttrib->pAttribName) &&
|
|
CTEMemCmp(pAttrib->pAttribName, pAttribName, (ULONG) AttribNameLen) == 0)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT(" Attrib found\n")));
|
|
|
|
AttribFound = TRUE;
|
|
|
|
ListLen++;
|
|
|
|
if (pQMsg->IRDA_MSG_pWrite + 1 > pQMsg->IRDA_MSG_pLimit)
|
|
{
|
|
// I need 2 bytes for object ID, don't want to
|
|
// split 16 bit field up
|
|
if (NewQueryMsg(pLsapCb->pIrlmpCb, &QueryList,
|
|
&pQMsg) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
*pQMsg->IRDA_MSG_pWrite++ =
|
|
(UCHAR) (((pObject->ObjectId) & 0xFF00) >> 8);
|
|
*pQMsg->IRDA_MSG_pWrite++ =
|
|
(UCHAR) ((pObject->ObjectId) & 0xFF);
|
|
|
|
if (pQMsg->IRDA_MSG_pWrite > pQMsg->IRDA_MSG_pLimit)
|
|
{
|
|
if (NewQueryMsg(pLsapCb->pIrlmpCb, &QueryList,
|
|
&pQMsg) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (pAttrib->AttribValType)
|
|
{
|
|
case IAS_ATTRIB_VAL_INTEGER:
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: integer query %d\n"),
|
|
*((int *) pAttrib->pAttribVal)));
|
|
|
|
if (pQMsg->IRDA_MSG_pWrite + 4 > pQMsg->IRDA_MSG_pLimit)
|
|
{
|
|
if (NewQueryMsg(pLsapCb->pIrlmpCb,
|
|
&QueryList, &pQMsg) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
}
|
|
*pQMsg->IRDA_MSG_pWrite++ = IAS_ATTRIB_VAL_INTEGER;
|
|
*pQMsg->IRDA_MSG_pWrite++ = (UCHAR)
|
|
((*((int *) pAttrib->pAttribVal) & 0xFF000000) >> 24);
|
|
*pQMsg->IRDA_MSG_pWrite++ = (UCHAR)
|
|
((*((int *) pAttrib->pAttribVal) & 0xFF0000) >> 16);
|
|
*pQMsg->IRDA_MSG_pWrite++ = (UCHAR)
|
|
((*((int *) pAttrib->pAttribVal) & 0xFF00) >> 8);
|
|
*pQMsg->IRDA_MSG_pWrite++ = (UCHAR)
|
|
(*((int *) pAttrib->pAttribVal) & 0xFF);
|
|
break;
|
|
|
|
case IAS_ATTRIB_VAL_BINARY:
|
|
case IAS_ATTRIB_VAL_STRING:
|
|
if (pQMsg->IRDA_MSG_pWrite + 2 > pQMsg->IRDA_MSG_pLimit)
|
|
{
|
|
if (NewQueryMsg(pLsapCb->pIrlmpCb,
|
|
&QueryList, &pQMsg) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
*pQMsg->IRDA_MSG_pWrite++ = (UCHAR) pAttrib->AttribValType;
|
|
|
|
if (pAttrib->AttribValType == IAS_ATTRIB_VAL_BINARY)
|
|
{
|
|
*pQMsg->IRDA_MSG_pWrite++ =
|
|
(UCHAR) ((pAttrib->AttribValLen & 0xFF00) >> 8);
|
|
*pQMsg->IRDA_MSG_pWrite++ =
|
|
(UCHAR) (pAttrib->AttribValLen & 0xFF);;
|
|
}
|
|
else
|
|
{
|
|
*pQMsg->IRDA_MSG_pWrite++ =
|
|
(UCHAR) pAttrib->CharSet;
|
|
*pQMsg->IRDA_MSG_pWrite++ =
|
|
(UCHAR) pAttrib->AttribValLen;
|
|
}
|
|
|
|
pBPtr = (UCHAR *) pAttrib->pAttribVal;
|
|
|
|
for (i=0; i < pAttrib->AttribValLen; i++)
|
|
{
|
|
if (pQMsg->IRDA_MSG_pWrite > pQMsg->IRDA_MSG_pLimit)
|
|
{
|
|
if (NewQueryMsg(pLsapCb->pIrlmpCb,
|
|
&QueryList, &pQMsg) != SUCCESS)
|
|
{
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
}
|
|
*pQMsg->IRDA_MSG_pWrite++ = *pBPtr++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
break; // Break out of loop, only look for single
|
|
// attrib per object (??)
|
|
}
|
|
pAttrib = pAttrib->pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send the query
|
|
if (!ObjectFound)
|
|
{
|
|
*pReturnCode = IAS_NO_SUCH_OBJECT;
|
|
}
|
|
else
|
|
{
|
|
if (!AttribFound)
|
|
{
|
|
*pReturnCode = IAS_NO_SUCH_ATTRIB;
|
|
}
|
|
else
|
|
{
|
|
*pReturnCode = IAS_SUCCESS;
|
|
*pListLen++ = (UCHAR) ((ListLen & 0xFF00) >> 8);
|
|
*pListLen = (UCHAR) (ListLen & 0xFF);
|
|
}
|
|
}
|
|
|
|
if (!IsListEmpty(&QueryList))
|
|
{
|
|
pQMsg = (IRDA_MSG *) RemoveHeadList(&QueryList);
|
|
}
|
|
else
|
|
{
|
|
pQMsg = NULL;
|
|
}
|
|
|
|
while (pQMsg)
|
|
{
|
|
if (!IsListEmpty(&QueryList))
|
|
{
|
|
pNextMsg = (IRDA_MSG *) RemoveHeadList(&QueryList);
|
|
}
|
|
else
|
|
{
|
|
pNextMsg = NULL;
|
|
}
|
|
|
|
// Build the control field
|
|
pControl = (IAS_CONTROL_FIELD *) pQMsg->IRDA_MSG_pRead;
|
|
pControl->OpCode = IAS_OPCODE_GET_VALUE_BY_CLASS;
|
|
pControl->Ack = FALSE;
|
|
if (pNextMsg == NULL)
|
|
{
|
|
pControl->Last = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pControl->Last = FALSE;
|
|
}
|
|
|
|
pQMsg->IRDA_MSG_IrCOMM_9Wire = FALSE;
|
|
|
|
FormatAndSendDataReq(pLsapCb, pQMsg, TRUE, TRUE);
|
|
|
|
pQMsg = pNextMsg;
|
|
}
|
|
}
|
|
|
|
IAS_OBJECT *
|
|
IasGetObject(CHAR *pClassName)
|
|
{
|
|
IAS_OBJECT *pObject;
|
|
|
|
for (pObject = (IAS_OBJECT *) IasObjects.Flink;
|
|
(LIST_ENTRY *) pObject != &IasObjects;
|
|
pObject = (IAS_OBJECT *) pObject->Linkage.Flink)
|
|
{
|
|
if (StringCmp(pObject->pClassName, pClassName) == 0)
|
|
{
|
|
return pObject;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
UINT
|
|
IasAddAttribute(IAS_SET *pIASSet, PVOID *pAttribHandle)
|
|
{
|
|
IAS_OBJECT *pObject = NULL;
|
|
IAS_ATTRIBUTE *pAttrib = NULL;
|
|
CHAR *pClassName = NULL;
|
|
CHAR ClassNameLen;
|
|
CHAR *pAttribName = NULL;
|
|
CHAR AttribNameLen;
|
|
int AttribValLen;
|
|
void *pAttribVal = NULL;
|
|
UINT cAttribs = 0;
|
|
KIRQL OldIrql;
|
|
BOOLEAN NewObject = FALSE;
|
|
BOOLEAN NewObjectOnList = FALSE;
|
|
UINT rc = SUCCESS;
|
|
|
|
*pAttribHandle = NULL;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
if ((pObject = IasGetObject(pIASSet->irdaClassName)) == NULL)
|
|
{
|
|
if (IRDA_ALLOC_MEM(pObject, sizeof(IAS_OBJECT), MT_IRLMP_IAS_OBJECT)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
NewObject = TRUE;
|
|
|
|
ClassNameLen = StringLen(pIASSet->irdaClassName) + 1;
|
|
|
|
if (IRDA_ALLOC_MEM(pClassName, ClassNameLen, MT_IRLMP_IAS_CLASSNAME)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
RtlCopyMemory(pClassName, pIASSet->irdaClassName, ClassNameLen);
|
|
|
|
pObject->pClassName = pClassName;
|
|
pObject->pAttributes = NULL;
|
|
|
|
NewObjectOnList = TRUE;
|
|
|
|
InsertTailList(&IasObjects, &pObject->Linkage);
|
|
|
|
pObject->ObjectId = NextObjectId++;
|
|
}
|
|
|
|
// Does the attribute already exist?
|
|
for (pAttrib = pObject->pAttributes; pAttrib != NULL;
|
|
pAttrib = pAttrib->pNext)
|
|
{
|
|
if (StringCmp(pAttrib->pAttribName, pIASSet->irdaAttribName) == 0)
|
|
{
|
|
break;
|
|
}
|
|
cAttribs++;
|
|
}
|
|
|
|
if (pAttrib != NULL)
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Attribute alreay exists\r\n")));
|
|
pAttrib = NULL;
|
|
rc = IRLMP_IAS_ATTRIB_ALREADY_EXISTS;
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
// Only allowed to add 256 attributes to an object.
|
|
if (cAttribs >= 256)
|
|
{
|
|
rc = IRLMP_IAS_MAX_ATTRIBS_REACHED;
|
|
goto done;
|
|
}
|
|
|
|
if (IRDA_ALLOC_MEM(pAttrib, sizeof(IAS_ATTRIBUTE), MT_IRLMP_IAS_ATTRIB)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
AttribNameLen = StringLen(pIASSet->irdaAttribName) + 1;
|
|
|
|
if (IRDA_ALLOC_MEM(pAttribName, AttribNameLen, MT_IRLMP_IAS_ATTRIBNAME)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
RtlCopyMemory(pAttribName, pIASSet->irdaAttribName, AttribNameLen);
|
|
|
|
}
|
|
|
|
switch (pIASSet->irdaAttribType)
|
|
{
|
|
case IAS_ATTRIB_VAL_INTEGER:
|
|
AttribValLen = sizeof(int);
|
|
if (IRDA_ALLOC_MEM(pAttribVal, AttribValLen, MT_IRLMP_IAS_ATTRIBVAL)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
*((int *) pAttribVal) = pIASSet->irdaAttribute.irdaAttribInt;
|
|
break;
|
|
|
|
case IAS_ATTRIB_VAL_BINARY:
|
|
AttribValLen = pIASSet->irdaAttribute.irdaAttribOctetSeq.Len;
|
|
if (IRDA_ALLOC_MEM(pAttribVal, AttribValLen, MT_IRLMP_IAS_ATTRIBVAL)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
RtlCopyMemory(pAttribVal, pIASSet->irdaAttribute.irdaAttribOctetSeq.OctetSeq,
|
|
AttribValLen);
|
|
break;
|
|
|
|
case IAS_ATTRIB_VAL_STRING:
|
|
AttribValLen = pIASSet->irdaAttribute.irdaAttribUsrStr.Len;
|
|
if (IRDA_ALLOC_MEM(pAttribVal, AttribValLen, MT_IRLMP_IAS_ATTRIBVAL)
|
|
== NULL)
|
|
{
|
|
rc = IRLMP_ALLOC_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
RtlCopyMemory(pAttribVal, pIASSet->irdaAttribute.irdaAttribUsrStr.UsrStr,
|
|
AttribValLen);
|
|
break;
|
|
|
|
default:
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: IasAddAttribute, invalid type\n %d\n"),
|
|
pIASSet->irdaAttribType));
|
|
rc = IRLMP_NO_SUCH_IAS_ATTRIBUTE;
|
|
goto done;
|
|
}
|
|
|
|
pAttrib->pAttribName = pAttribName;
|
|
pAttrib->pAttribVal = pAttribVal;
|
|
pAttrib->AttribValLen = AttribValLen;
|
|
pAttrib->AttribValType = (UCHAR) pIASSet->irdaAttribType;
|
|
pAttrib->CharSet = pIASSet->irdaAttribute.irdaAttribUsrStr.CharSet;
|
|
pAttrib->pNext = pObject->pAttributes;
|
|
|
|
pObject->pAttributes = pAttrib;
|
|
|
|
*pAttribHandle = pAttrib;
|
|
|
|
done:
|
|
|
|
if (rc == SUCCESS)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: Added attrib(%x) %s to class %s\n"),
|
|
pAttrib, pAttrib->pAttribName, pObject->pClassName));
|
|
;
|
|
}
|
|
else
|
|
{
|
|
DEBUGMSG(DBG_ERROR, (TEXT("IRLMP: Failed to add Ias attribute\n")));
|
|
|
|
if (pObject && NewObjectOnList) RemoveEntryList(&pObject->Linkage);
|
|
if (pObject && NewObject) IRDA_FREE_MEM(pObject);
|
|
if (pClassName) IRDA_FREE_MEM(pClassName);
|
|
if (pAttrib) IRDA_FREE_MEM(pAttrib);
|
|
if (pAttribName) IRDA_FREE_MEM(pAttribName);
|
|
if (pAttribVal) IRDA_FREE_MEM(pAttribVal);
|
|
}
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
|
|
return rc;
|
|
}
|
|
|
|
VOID
|
|
IasDelAttribute(PVOID AttribHandle)
|
|
{
|
|
KIRQL OldIrql;
|
|
IAS_OBJECT *pObject;
|
|
IAS_ATTRIBUTE *pAttrib, *pPrevAttrib;
|
|
|
|
KeAcquireSpinLock(&gSpinLock, &OldIrql);
|
|
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: Delete attribHandle %x\n"),
|
|
AttribHandle));
|
|
|
|
for (pObject = (IAS_OBJECT *) IasObjects.Flink;
|
|
(LIST_ENTRY *) pObject != &IasObjects;
|
|
pObject = (IAS_OBJECT *) pObject->Linkage.Flink)
|
|
{
|
|
pPrevAttrib = NULL;
|
|
|
|
for (pAttrib = pObject->pAttributes;
|
|
pAttrib != NULL;
|
|
pAttrib = pAttrib->pNext)
|
|
{
|
|
if (pAttrib == AttribHandle)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: attrib %hs deleted\n"),
|
|
pAttrib->pAttribName));
|
|
|
|
if (pAttrib == pObject->pAttributes)
|
|
{
|
|
pObject->pAttributes = pAttrib->pNext;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pPrevAttrib);
|
|
pPrevAttrib->pNext = pAttrib->pNext;
|
|
}
|
|
|
|
IRDA_FREE_MEM(pAttrib->pAttribName);
|
|
IRDA_FREE_MEM(pAttrib->pAttribVal);
|
|
IRDA_FREE_MEM(pAttrib);
|
|
|
|
if (pObject->pAttributes == NULL)
|
|
{
|
|
DEBUGMSG(DBG_IRLMP_IAS, (TEXT("IRLMP: No attributes associated with class %hs, deleting\n"),
|
|
pObject->pClassName));
|
|
|
|
RemoveEntryList(&pObject->Linkage);
|
|
IRDA_FREE_MEM(pObject->pClassName);
|
|
IRDA_FREE_MEM(pObject);
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
pPrevAttrib = pAttrib;
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
KeReleaseSpinLock(&gSpinLock, OldIrql);
|
|
}
|
|
|
|
void
|
|
InitiateRetryIasQuery(PVOID Context)
|
|
{
|
|
PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context;
|
|
PIRLMP_LINK_CB pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
IRDA_MSG IMsg;
|
|
LARGE_INTEGER SleepMs;
|
|
|
|
|
|
IMsg.Prim = IRLMP_CONNECT_REQ;
|
|
IMsg.IRDA_MSG_RemoteLsapSel = IAS_LSAP_SEL;
|
|
IMsg.IRDA_MSG_LocalLsapSel = IAS_LOCAL_LSAP_SEL;
|
|
IMsg.IRDA_MSG_pQos = NULL;
|
|
IMsg.IRDA_MSG_pConnData = NULL;
|
|
IMsg.IRDA_MSG_ConnDataLen = 0;
|
|
IMsg.IRDA_MSG_UseTtp = FALSE;
|
|
IMsg.IRDA_MSG_pContext = NULL;
|
|
|
|
RtlCopyMemory(IMsg.IRDA_MSG_RemoteDevAddr,
|
|
pIrlmpCb->IasQueryDevAddr,
|
|
IRDA_DEV_ADDR_LEN);
|
|
|
|
while (pIrlmpCb->IasRetryCnt < 4)
|
|
{
|
|
pIrlmpCb->IasRetryCnt++;
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Retry count is %d\n"), pIrlmpCb->IasRetryCnt));
|
|
|
|
SleepMs.QuadPart = -(5*1000*1000); // .5 second
|
|
KeDelayExecutionThread(KernelMode, FALSE, &SleepMs);
|
|
|
|
if (IrlmpConnectReq(&IMsg) == SUCCESS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
// retrying failed
|
|
|
|
DEBUGMSG(DBG_IRLMP, (TEXT("IRLMP: Retry ias failed\n")));
|
|
|
|
pIrlmpCb->pIasQuery = NULL;
|
|
|
|
IMsg.Prim = IRLMP_GETVALUEBYCLASS_CONF;
|
|
IMsg.IRDA_MSG_IASStatus = IRLMP_MAC_MEDIA_BUSY;
|
|
|
|
TdiUp(NULL, &IMsg);
|
|
}
|
|
|
|
VOID
|
|
DeleteDeviceList(LIST_ENTRY *pDeviceList)
|
|
{
|
|
IRDA_DEVICE *pDevice, *pDeviceNext;
|
|
|
|
for (pDevice = (IRDA_DEVICE *) pDeviceList->Flink;
|
|
(LIST_ENTRY *) pDevice != pDeviceList;
|
|
pDevice = pDeviceNext)
|
|
{
|
|
pDeviceNext = (IRDA_DEVICE *) pDevice->Linkage.Flink;
|
|
|
|
IRDA_FREE_MEM(pDevice);
|
|
}
|
|
|
|
InitializeListHead(pDeviceList);
|
|
}
|
|
|
|
VOID
|
|
FlushDiscoveryCache()
|
|
{
|
|
PIRDA_LINK_CB pIrdaLinkCb;
|
|
PIRLMP_LINK_CB pIrlmpCb;
|
|
|
|
// Assumes global spinlock held
|
|
|
|
// Flush the per link cache
|
|
|
|
for (pIrdaLinkCb = (PIRDA_LINK_CB) IrdaLinkCbList.Flink;
|
|
(LIST_ENTRY *) pIrdaLinkCb != &IrdaLinkCbList;
|
|
pIrdaLinkCb = (PIRDA_LINK_CB) pIrdaLinkCb->Linkage.Flink)
|
|
{
|
|
pIrlmpCb = (PIRLMP_LINK_CB) pIrdaLinkCb->IrlmpContext;
|
|
|
|
DEBUGMSG(DBG_DISCOVERY, (TEXT("IRLMP: Deleting IrlmpCb:%X discovery cache\n"),
|
|
pIrlmpCb));
|
|
|
|
DeleteDeviceList(&pIrlmpCb->DeviceList);
|
|
}
|
|
|
|
// And the global cache
|
|
|
|
DEBUGMSG(DBG_DISCOVERY, (TEXT("IRLMP: Deleting global discovery cache\n")));
|
|
|
|
DeleteDeviceList(&gDeviceList);
|
|
}
|
|
|
|
VOID
|
|
IrlmpGetPnpContext(
|
|
PVOID IrlmpContext,
|
|
PVOID *pPnpContext)
|
|
{
|
|
IRLMP_LSAP_CB *pLsapCb = (IRLMP_LSAP_CB *) IrlmpContext;
|
|
PIRDA_LINK_CB pIrdaLinkCb = NULL;
|
|
|
|
*pPnpContext = NULL;
|
|
|
|
if (pLsapCb == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
VALIDLSAP(pLsapCb);
|
|
|
|
pIrdaLinkCb = pLsapCb->pIrlmpCb->pIrdaLinkCb;
|
|
|
|
if (pIrdaLinkCb)
|
|
{
|
|
*pPnpContext = pIrdaLinkCb->PnpContext;
|
|
}
|
|
}
|