windows-nt/Source/XPSP1/NT/net/irda/irlmp/irlmp.c
2020-09-26 16:20:57 +08:00

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;
}
}