/***************************************************************************** * * 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 #include #include #include #include #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; } }