/*++ Copyright (c) 1991 Microsoft Corporation Copyright (c) 1991 Nokia Data Systems Ab Module Name: llclink.c Abstract: The module implements the open, connect and close primitives for a link station object. The link stations have also been initialized within this module. Contents: LlcOpenLinkStation LlcConnectStation InitiateAsyncLinkCommand LlcDisconnectStation LlcFlowControl LinkFlowControl SearchLinkAddress SetLinkParameters CheckLinkParameters CopyLinkParameters CopyNonZeroBytes RunInterlockedStateMachineCommand Author: Antti Saarenheimo (o-anttis) 28-MAY-1991 Revision History: --*/ #include DLC_STATUS LlcOpenLinkStation( IN PLLC_SAP pSap, IN UCHAR DestinationSap, IN PUCHAR pDestinationAddress OPTIONAL, IN PUCHAR pReceivedLanHeader OPTIONAL, IN PVOID hClientStation, OUT PVOID* phLlcHandle ) /*++ Routine Description: creates a DATA_LINK structure and fills it in. Called either as a result of receiving a SABME, or via DLC.OPEN.STATION This operation is the same as ACTIVATE_LS primitive in IBM TR Arch. ref. Arguments: pSap - pointer to SAP DestinationSap - remote SAP number pDestinationAddress - remote node address. If this function is being called as a result of receiving a SABME for a new link then this parameter is NULL pReceivedLanHeader - LAN header as received off the wire, containing source and destination adapter addresses, optional source routing and source and destination SAPs hClientStation - handle (address) of LLC client's link station object phLlcHandle - pointer to returned handle (address) to LLC DATA_LINK object Return Value: DLC_STATUS Success - STATUS_SUCCESS link station has been opened successfully Failure - DLC_STATUS_INVALID_SAP_VALUE the link station already exists or the SAP is really invalid. DLC_NO_MEMORY there was no free preallocated link station --*/ { PDATA_LINK pLink; PDATA_LINK* ppLink; PADAPTER_CONTEXT pAdapterContext = pSap->Gen.pAdapterContext; DLC_STATUS LlcStatus = STATUS_SUCCESS; UINT AddressTranslation; // // We need a temporary buffer to build lan header for link, // because user may use different ndis medium from network. // UCHAR auchTempBuffer[32]; ASSUME_IRQL(DISPATCH_LEVEL); LlcZeroMem(auchTempBuffer, sizeof(auchTempBuffer)); if (pSap->Gen.ObjectType != LLC_SAP_OBJECT) { return DLC_STATUS_INVALID_SAP_VALUE; } ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); pLink = (PDATA_LINK)ALLOCATE_PACKET_LLC_LNK(pAdapterContext->hLinkPool); if (pLink == NULL) { LlcStatus = DLC_STATUS_NO_MEMORY; goto ErrorExit; } // // This reference keeps the object alive, until it is dereferenced // in the delete. // ReferenceObject(pLink); // // LLC driver have two different address formats: // // 1. External format of the binding (ethernet or token-ring, // DLC driver uses always token-ring format, the ethernet // support is compiled in conditionally. // // 2. Internal send format (always the actual lan type, // ethernet, dix or tokenring). The user provides link // address in its own mode and we must build the actual // lan link header from it. // if (pDestinationAddress != NULL) { // // link created by DLC.CONNECT.STATION // AddressTranslation = pSap->Gen.pLlcBinding->AddressTranslation; LlcBuildAddress(pSap->Gen.pLlcBinding->NdisMedium, pDestinationAddress, NULL, auchTempBuffer ); } else { // // link created by incoming SABME // pLink->Flags |= DLC_ACTIVE_REMOTE_CONNECT_REQUEST; AddressTranslation = pAdapterContext->AddressTranslationMode; LlcBuildAddressFromLanHeader(pAdapterContext->NdisMedium, pReceivedLanHeader, auchTempBuffer ); } // // We want to use always DIX lan headers in the token-ring case // if (AddressTranslation == LLC_SEND_802_5_TO_802_3) { AddressTranslation = LLC_SEND_802_5_TO_DIX; } else if (AddressTranslation == LLC_SEND_802_3_TO_802_3) { AddressTranslation = LLC_SEND_802_3_TO_DIX; } // // Now we can build the actual network header for the sending // (this same routine build lan header also for all // other packet types) // pLink->cbLanHeaderLength = CopyLanHeader(AddressTranslation, auchTempBuffer, pAdapterContext->NodeAddress, pLink->auchLanHeader, pAdapterContext->ConfigInfo.SwapAddressBits ); // // We always build a DIX header but it is only used when the Ethernet type // is actually DIX // if (pAdapterContext->NdisMedium == NdisMedium802_3 && pSap->Gen.pLlcBinding->EthernetType != LLC_ETHERNET_TYPE_DIX) { pLink->cbLanHeaderLength = 14; } // // Save the client handle, but reset and initailize everything else. // The link must be ready for any kind extrnal inputs when // we will connenct it to the hash table of the link stations. // (actually that's not true now, because we init the link to // LINK_CLOSED state, but we may change the state machine. // It would be a different thing with a 802.2 state machine) // pLink->Gen.ObjectType = LLC_LINK_OBJECT; // // RLF 07/22/92. The link state should be DISCONNECTED so that we can // accept incoming SABMEs for this SAP/link station. This is also // according to IBM LAN Tech. Ref. p. 2-33. It is safe to set the // DISCONNECTED state now because we have the send and object database // spin locks, so we can't get interrupted by NDIS driver // // // RLF 08/13/92. Ho Hum. This still isn't correct - we must put the link // into different states depending on how its being opened - DISCONNECTED // if the upper layers are creating the link, or LINK_CLOSED if we're // creating the link as a result of receiving a SABME. Use pReceivedLanHeader // as a flag: DLC calls this routine with this parameter set to NULL // ////pLink->State = LINK_CLOSED; //pLink->State = DISCONNECTED; pLink->State = pReceivedLanHeader ? LINK_CLOSED : DISCONNECTED; // // RLF 10/01/92. We need some way of knowing that the link station was // created by receiving a SABME. We need this to decide what to do with // the source routing info in a subsequent DLC.CONNECT.STATION command. // This field used to be Reserved // pLink->RemoteOpen = hClientStation == NULL; // // RLF 05/09/94 // // We set the framing type to unspecified. This field is only used if the // adapter was opened in AUTO mode. It will be set to 802.3 or DIX by the // SABME received processing (new link created by remote station) or when // the first UA is received in response to the 2 SABMEs we send out (802.3 // and DIX) // pLink->FramingType = (ULONG)LLC_SEND_UNSPECIFIED; pLink->Gen.hClientHandle = hClientStation; pLink->Gen.pAdapterContext = pAdapterContext; pLink->pSap = pSap; pLink->Gen.pLlcBinding = pSap->Gen.pLlcBinding; // // Save the node addresses used in link station // pDestinationAddress = pLink->auchLanHeader; if (pAdapterContext->NdisMedium == NdisMedium802_5) { pDestinationAddress += 2; } else if (pAdapterContext->NdisMedium == NdisMediumFddi) { ++pDestinationAddress; } memcpy(pLink->LinkAddr.Node.auchAddress, pDestinationAddress, 6); // // RLF 03/24/93 // // if we're talking Ethernet or DIX, we need to report the bit-flipped // address at the DLC interface // SwapMemCpy((BOOLEAN)((AddressTranslation == LLC_SEND_802_3_TO_DIX) || (pAdapterContext->NdisMedium == NdisMediumFddi)), pLink->DlcStatus.auchRemoteNode, pDestinationAddress, 6 ); // // Four different local saps: my code would need a cleanup // (four bytes doesn't use very much memory on the other hand) // pLink->LinkAddr.Address.DestSap = pLink->DlcStatus.uchRemoteSap = pLink->Dsap = DestinationSap; pLink->LinkAddr.Address.SrcSap = pLink->DlcStatus.uchLocalSap = pLink->Ssap = (UCHAR)pSap->SourceSap; pLink->DlcStatus.hLlcLinkStation = (PVOID)pLink; pLink->SendQueue.pObject = pLink; InitializeListHead(&pLink->SendQueue.ListHead); InitializeListHead(&pLink->SentQueue); pLink->Flags |= DLC_SEND_DISABLED; // // The next procedure returns the pointer of the slot for the // new link station pointer. The address may be in the // hash table or it may be the address of pRigth or pLeft // field within another link station structure. // ppLink = SearchLinkAddress(pAdapterContext, pLink->LinkAddr); // // this link station must not yet be in the table // of active link stations. If its slot is // empty, then save the new link station to the // list of the active link stations. // if (*ppLink != NULL) { LlcStatus = DLC_STATUS_INVALID_SAP_VALUE; DEALLOCATE_PACKET_LLC_LNK(pAdapterContext->hLinkPool, pLink); } else { pLink->Gen.pNext = (PLLC_OBJECT)pSap->pActiveLinks; pSap->pActiveLinks = pLink; // // Set the default link parameters, // Note: This creates the timer ticks. They must // be deleted with the terminate timer function, // when the link station is closed. // LlcStatus = SetLinkParameters(pLink, (PUCHAR)&pSap->DefaultParameters); if (LlcStatus != STATUS_SUCCESS) { // // We may have been started T1 and T2 timers. // TerminateTimer(pAdapterContext, &pLink->T1); TerminateTimer(pAdapterContext, &pLink->T2); DEALLOCATE_PACKET_LLC_LNK(pAdapterContext->hLinkPool, pLink); } else { // // N2 is never initilaized by IBM state machine, when // the link is created by a remote connect request. // The link can be killed by this combination of state // transmitions: // (LINK_OPENED), // (RNR-r => REMOTE_BUSY), // (RR-c => CHECKPOINTING) // (T1 timeout => DISCONNECTED) !!!!! // // This will fix the bug in IBM state machine: // pLink->P_Ct = pLink->N2; *ppLink = *phLlcHandle = (PVOID)pLink; pAdapterContext->ObjectCount++; } } ErrorExit: RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock); RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); return LlcStatus; } VOID LlcBindLinkStation( IN PDATA_LINK pStation, IN PVOID hClientHandle ) { pStation->Gen.hClientHandle = hClientHandle; } VOID LlcConnectStation( IN PDATA_LINK pStation, IN PLLC_PACKET pPacket, IN PVOID pSourceRouting OPTIONAL, IN PUSHORT pusMaxInformationField ) /*++ Routine Description: The upper level protocol may call this primitive to initiate the connection negotiation with a remote link station, to accept the connection request or to reconnect a link station that have been disconnected for some reason with a new source routing information. The command is completed asynchronously and the status is returned as an event. The primitive is the same as SET_ABME primitive in "IBM TR Architecture reference". The function implements also CONNECT_REQUEST and CONNECT_RESPONSE primitives of IEEE 802.2. Arguments: pStation - address of link station pPacket - command completion packet pSourceRouting - optional source routing information. This must be NULL if the source routing information is not used pusMaxInformationField - the maximum data size possible to use with this connection. The source routing bridges may decrease the maximum information field size. Otherwise the maximum length is used Return Value: None. --*/ { NDIS_MEDIUM NdisMedium = pStation->Gen.pAdapterContext->NdisMedium; if (pSourceRouting) { // // We first read the destination address from the // lan header and then extend the source routing // field in the LAN header of link. // if (NdisMedium == NdisMedium802_5) { // // RLF 10/01/92. If RemoteOpen is TRUE then the link was opened // due to receiving a SABME and we ignore the source routing info // (we already got it from the SABME packet) // if (!pStation->RemoteOpen) { pStation->cbLanHeaderLength = (UCHAR)LlcBuildAddress( NdisMedium, &pStation->auchLanHeader[2], pSourceRouting, pStation->auchLanHeader ); } } else { pSourceRouting = NULL; } } *pusMaxInformationField = LlcGetMaxInfoField(NdisMedium, pStation->Gen.pLlcBinding, pStation->auchLanHeader ); pStation->MaxIField = *pusMaxInformationField; pStation->Flags &= ~DLC_ACTIVE_REMOTE_CONNECT_REQUEST; // // Activate the link station at first, the remotely connected // link station is already active and in that case the state // machine return logical error from ACTIVATE_LS input. // RunInterlockedStateMachineCommand(pStation, ACTIVATE_LS); InitiateAsyncLinkCommand(pStation, pPacket, SET_ABME, LLC_CONNECT_COMPLETION); } VOID InitiateAsyncLinkCommand( IN PDATA_LINK pLink, IN PLLC_PACKET pPacket, IN UINT StateMachineCommand, IN UINT CompletionCode ) /*++ Routine Description: Initiates or removes an LLC link. We have a link station in the DISCONNECTED state. We are either sending a SABME or DISC Arguments: pLink - pointer to LLC link station structure ('object') pPacket - pointer to packet to use for transmission StateMachineCommand - command given to the state machine CompletionCode - completion command type returned asynchronously Return Value: None. --*/ { PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext; UINT Status; // // link will return an error status if it is already connected. // ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); AllocateCompletionPacket(pLink, CompletionCode, pPacket); // // After RunStateMachineCommand // the link may be deleted in any time by an NDIS command completion // indication (send or receive) => we must not use link after this // Status = RunStateMachineCommand(pLink, StateMachineCommand); // // IBM state machine does not stop the send process => we // must do it here we will get a system bug check. // if (StateMachineCommand == SET_ADM) { DisableSendProcess(pLink); } // // disconnect or connect commands may fail, because there are // not enough memory to allocate packets for them. // In that case we must complete the command here with an error code. // if (Status != STATUS_SUCCESS) { QueueCommandCompletion((PLLC_OBJECT)pLink, CompletionCode, Status); } BackgroundProcessAndUnlock(pAdapterContext); } VOID LlcDisconnectStation( IN PDATA_LINK pLink, IN PLLC_PACKET pPacket ) /*++ Routine Description: The primtive initiates the disconnection handshaking. The upper protocol must wait LLC_EVENT_DISCONNECTED event before it can close the link station. The link station must either be closed or reconnected after a disconnection event. The DLC driver disconnects the link only when it is closed. This operation is the same as SET_ADM primitive in IBM TR Arch. ref. Arguments: hStation - link station handle. hRequestHandle - opaque handle returned when the command completes Return Value: None Complete always asynchronously by calling the command completion routine. --*/ { // // We don't want send yet another DM, if the link station has // already disconnected. We don't modify the state machine, // because the state machine should be as orginal as possible. // if (pLink->State == DISCONNECTED) { pPacket->Data.Completion.CompletedCommand = LLC_DISCONNECT_COMPLETION; pPacket->Data.Completion.Status = STATUS_SUCCESS; pLink->Gen.pLlcBinding->pfCommandComplete( pLink->Gen.pLlcBinding->hClientContext, pLink->Gen.hClientHandle, pPacket ); } else { InitiateAsyncLinkCommand( pLink, pPacket, SET_ADM, LLC_DISCONNECT_COMPLETION ); } } DLC_STATUS LlcFlowControl( IN PLLC_OBJECT pStation, IN UCHAR FlowControlState ) /*++ Routine Description: The primitive sets or resets the local busy state of a single link station or all link stations of a sap. The routine also maintains the local busy user and local busy buffer states, that are returned in link station status query, because the IBM state machine support only one buffer busy state. Arguments: pStation - link station handle. FlowControlState - new flow control command bits set for the link station. The parameter is a bit field: 0 => Sets LOCAL_BUSY_USER state 0x80 => resets LOCAL_BUSY_USER state 0x40 => resets LOCAL_BUSY_BUFFER state 0xC0 => resets both local busy states Return Value: STATUS_SUCCESS --*/ { PDATA_LINK pLink; UINT Status = STATUS_SUCCESS; PADAPTER_CONTEXT pAdapterContext = pStation->Gen.pAdapterContext; ASSUME_IRQL(DISPATCH_LEVEL); // // We must prevent any LLC object to be deleted, while we // are updating the flow control states // ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); if (pStation->Gen.ObjectType != LLC_LINK_OBJECT) { if (pStation->Gen.ObjectType == LLC_SAP_OBJECT) { for (pLink = pStation->Sap.pActiveLinks; pLink != NULL; pLink = (PDATA_LINK)pLink->Gen.pNext) { Status |= LinkFlowControl(pLink, FlowControlState); } } else { Status = DLC_STATUS_INVALID_STATION_ID; } } else { Status = LinkFlowControl((PDATA_LINK)pStation, FlowControlState); } RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); BackgroundProcess(pAdapterContext); return Status; } DLC_STATUS LinkFlowControl( IN PDATA_LINK pLink, IN UCHAR FlowControlState ) /*++ Routine Description: The primitive sets or resets the local busy state for a single link. The routine also maintains the local busy user and local busy buffer states. This level do not care about the interlocking. It is done on the upper level. Arguments: hStation - link station handle. FlowControlState - new flow control command bits set for the link station. Return Value: STATUS_SUCCESS --*/ { if ((FlowControlState & 0x80) == 0) { // // Bit5 is used as an undocumented flag, that sets // the link local busy buffer state. We need this // in the DOS DLC emulation. // ACQUIRE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock); if (FlowControlState == LLC_SET_LOCAL_BUSY_BUFFER) { pLink->Flags |= DLC_LOCAL_BUSY_BUFFER; } else { pLink->Flags |= DLC_LOCAL_BUSY_USER; } RELEASE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock); return RunInterlockedStateMachineCommand(pLink, ENTER_LCL_Busy); } else { // // Optimize the buffer enabling, because RECEICE for a // SAP station should disable any non user busy states of // all link stations defined for sap (may take a long // time if a sap has very may links) // if (FlowControlState == LLC_RESET_LOCAL_BUSY_BUFFER) { FlowControlState = DLC_LOCAL_BUSY_BUFFER; } else { FlowControlState = DLC_LOCAL_BUSY_USER; } if (pLink->Flags & FlowControlState) { ACQUIRE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock); pLink->Flags &= ~FlowControlState; RELEASE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock); if ((pLink->Flags & (DLC_LOCAL_BUSY_USER | DLC_LOCAL_BUSY_BUFFER)) == 0) { return RunInterlockedStateMachineCommand(pLink, EXIT_LCL_Busy); } } else { return DLC_STATUS_LINK_PROTOCOL_ERROR; } } return STATUS_SUCCESS; } #if LLC_DBG >= 2 PDATA_LINK SearchLink( IN PADAPTER_CONTEXT pAdapterContext, IN LAN802_ADDRESS LanAddr ) /*++ Routine Description: The routine searches a link from the hash table. All links in the same hash node has been saved to a simple link list. Note: the full link address is actually 61 bits long = 7 (SSAP) + 7 (DSAP) + 47 (any non-broadcast source address). We save the address information into two ULONGs, that are used in the actual search. The hash key will be calculated by xoring all 8 bytes in the address. Arguments: pAdapterContext - MAC adapter context of data link driver LanAddr - the complete 64 bit address of link (48 bit source addr + saps) Return Value: PDATA_LINK - pointer to LLC link object or NULL if not found --*/ { USHORT usHash; PDATA_LINK pLink; // this is a very simple hash algorithm, but result is modified // by all bits => it should be good enough for us. usHash = LanAddr.aus.Raw[0] ^ LanAddr.aus.Raw[1] ^ LanAddr.aus.Raw[2] ^ LanAddr.aus.Raw[3]; pLink = pAdapterContext->aLinkHash[ ((((PUCHAR)&usHash)[0] ^ ((PUCHAR)&usHash)[1]) % LINK_HASH_SIZE)]; // // Search the first matching link in the link list. // while (pLink != NULL && (pLink->LinkAddr.ul.Low != LanAddr.ul.Low || pLink->LinkAddr.ul.High != LanAddr.ul.High)) { pLink = pLink->pNextNode; } return pLink; } #endif PDATA_LINK* SearchLinkAddress( IN PADAPTER_CONTEXT pAdapterContext, IN LAN802_ADDRESS LanAddr ) /*++ Routine Description: The routine searches the address of a link pointer in the hash table. All links in the same hash node has been saved to a simple link list. Note: the full link address is actually 61 bits long = 7 (SSAP) + 7 (DSAP) + 47 (any non-broadcast source address). We save the address information into two ULONGs, that are used in the actual search. The hash key will be calculated by xoring all 8 bytes in the address. Arguments: pAdapterContext - MAC adapter context of data link driver LanAddr - the complete 64 bits address of link (48 bit source addr + saps) Return Value: PDATA_LINK - pointer to LLC link object or NULL if not found --*/ { USHORT usHash; PDATA_LINK *ppLink; // // this is a very simple hash algorithm, but result is modified // by all bits => it should be quite good enough // usHash = LanAddr.aus.Raw[0] ^ LanAddr.aus.Raw[1] ^ LanAddr.aus.Raw[2] ^ LanAddr.aus.Raw[3]; ppLink = &pAdapterContext->aLinkHash[((((PUCHAR)&usHash)[0] ^ ((PUCHAR)&usHash)[1]) % LINK_HASH_SIZE)]; // // BUG-BUG-BUG Check, that the C- compliler produces optimal // dword compare for this. // while (*ppLink != NULL && ((*ppLink)->LinkAddr.ul.Low != LanAddr.ul.Low || (*ppLink)->LinkAddr.ul.High != LanAddr.ul.High)) { ppLink = &(*ppLink)->pNextNode; } return ppLink; } DLC_STATUS SetLinkParameters( IN OUT PDATA_LINK pLink, IN PUCHAR pNewParameters ) /*++ Routine Description: Updates new parameters for a link station and reinitializes the timers and window counters. Arguments: pLink - LLC link station object pNewParameters - new parameters set to a link station Return Value: None. --*/ { DLC_STATUS LlcStatus; USHORT MaxInfoField; CopyLinkParameters((PUCHAR)&pLink->TimerT1, pNewParameters, (PUCHAR)&pLink->pSap->DefaultParameters ); // // The application cannot set bigger information field than // supported by adapter and source routing bridges. // MaxInfoField = LlcGetMaxInfoField(pLink->Gen.pAdapterContext->NdisMedium, pLink->Gen.pLlcBinding, pLink->auchLanHeader ); if (pLink->MaxIField > MaxInfoField) { pLink->MaxIField = MaxInfoField; } // // The initial transmit and receive window size (Ww) has // a fixed initial value, because it is dynamic, but we must // set it always smaller than maxout. // The maxin value is fixed. The dynamic management of N3 // is not really worth of the effort. By default, when it is // set to maximum 127, the sender searches the optimal window // size using the pool-bit. // pLink->N3 = pLink->RW; pLink->Ww = 16; // 8 * 2; pLink->MaxOut *= 2; pLink->TW = pLink->MaxOut; if (pLink->TW < pLink->Ww) { pLink->Ww = pLink->TW; } LlcStatus = InitializeLinkTimers(pLink); return LlcStatus; } DLC_STATUS CheckLinkParameters( PDLC_LINK_PARAMETERS pParms ) /*++ Routine Description: Procedure checks the new parameters to be set for a link and returns error status if any of them is invalid. Arguments: pLink - LLC link station object pNewParameters - new parameters set to a link station Return Value: None --*/ { // // These maximum values have been defined in IBM LAN Tech-Ref // if (pParms->TimerT1 > 10 || pParms->TimerT2 > 10 || pParms->TimerTi > 10 || pParms->MaxOut > 127 || pParms->MaxIn > 127 || pParms->TokenRingAccessPriority > 3) { return DLC_STATUS_PARMETERS_EXCEEDED_MAX; } else { return STATUS_SUCCESS; } } // // Copies all non-null new link parameters, the default values are // used when the new values are nul. Used by SetLinkParameters and // and SetInfo call of sap station. // VOID CopyLinkParameters( OUT PUCHAR pOldParameters, IN PUCHAR pNewParameters, IN PUCHAR pDefaultParameters ) { // // We must use the default value, if somebody has set nul. // All parameters are UCHARs => we can do the check for a byte stream // CopyNonZeroBytes(pOldParameters, pNewParameters, pDefaultParameters, sizeof(DLC_LINK_PARAMETERS) - sizeof(USHORT) ); // // The information field is the only non-UCHAR value among the // link station parameters. // if (((PDLC_LINK_PARAMETERS)pNewParameters)->MaxInformationField != 0) { ((PDLC_LINK_PARAMETERS)pOldParameters)->MaxInformationField = ((PDLC_LINK_PARAMETERS)pNewParameters)->MaxInformationField; } else if (((PDLC_LINK_PARAMETERS)pOldParameters)->MaxInformationField == 0) { ((PDLC_LINK_PARAMETERS)pOldParameters)->MaxInformationField = ((PDLC_LINK_PARAMETERS)pDefaultParameters)->MaxInformationField; } } VOID CopyNonZeroBytes( OUT PUCHAR pOldParameters, IN PUCHAR pNewParameters, IN PUCHAR pDefaultParameters, IN UINT Length ) /*++ Routine Description: Copies and filters DLC parameter values. If the value is 0, the corresponding default is used, else the supplied value Arguments: pOldParameters - pointer to set of UCHAR values pNewParameters - pointer to array of output values pDefaultParameters - pointer to corresponding default values Length - size of values in bytes (UCHARs) Return Value: None. --*/ { UINT i; for (i = 0; i < Length; i++) { if (pNewParameters[i] != 0) { pOldParameters[i] = pNewParameters[i]; } else if (pOldParameters[i] == 0) { pOldParameters[i] = pDefaultParameters[i]; } } } UINT RunInterlockedStateMachineCommand( IN PDATA_LINK pStation, IN USHORT Command ) /*++ Routine Description: Runs the state machine for a link, within the adapter's SendSpinLock (& therefore at DPC) Arguments: pStation - pointer to DATA_LINK structure describing this link station Command - state machine command to be run Return Value: UINT Status from RunStateMachineCommand --*/ { UINT Status; ACQUIRE_SPIN_LOCK(&pStation->Gen.pAdapterContext->SendSpinLock); Status = RunStateMachineCommand(pStation, Command); RELEASE_SPIN_LOCK(&pStation->Gen.pAdapterContext->SendSpinLock); return Status; }