diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/elnkmc/request.c | 1859 |
1 files changed, 1859 insertions, 0 deletions
diff --git a/private/ntos/ndis/elnkmc/request.c b/private/ntos/ndis/elnkmc/request.c new file mode 100644 index 000000000..6a678961c --- /dev/null +++ b/private/ntos/ndis/elnkmc/request.c @@ -0,0 +1,1859 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + This file contains code to implement MacRequest and + MacQueryGlobalStatistics. This driver conforms to the + NDIS 3.0 interface. + +Author: + + Johnson R. Apacible (JohnsonA) 10-June-1991 + +Environment: + + Kernel Mode - Or whatever is the equivalent on OS/2 and DOS. + +Revision History: + + +--*/ + +#include <ndis.h> + +// +// So we can trace things... +// +#define STATIC + +#include <efilter.h> +#include <Elnkhw.h> +#include <Elnksw.h> + +extern +BOOLEAN +ChangeClassDispatch( + IN PELNK_ADAPTER Adapter, + IN UINT OldFilterClasses, + IN UINT NewFilterClasses, + IN PELNK_OPEN Open, + IN BOOLEAN Set + ); + +extern +VOID +ChangeAddressDispatch( + IN PELNK_ADAPTER Adapter, + IN UINT AddressCount, + IN CHAR Addresses[][ETH_LENGTH_OF_ADDRESS], + IN PELNK_OPEN Open, + IN BOOLEAN Set + ); + +extern +NDIS_STATUS +ElnkQueryInformation( + IN PELNK_ADAPTER Adapter, + IN PELNK_OPEN Open, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN UINT InformationBufferLength, + IN PUINT BytesWritten, + IN PUINT BytesNeeded + ); + +extern +NDIS_STATUS +ElnkSetInformation( + IN PELNK_ADAPTER Adapter, + IN PELNK_OPEN Open, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN INT InformationBufferLength, + OUT PUINT BytesRead, + OUT PUINT BytesNeeded + ); + +extern +VOID +ElnkQueueRequest( + IN PELNK_ADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest + ); + +extern +VOID +ElnkProcessRequestQueue( + IN PELNK_ADAPTER Adapter + ); + +VOID +ElnkRemoveAdapter( + IN NDIS_HANDLE MacAdapterContext + ); + + +extern +NDIS_STATUS +ElnkChangeClass( + IN UINT OldFilterClasses, + IN UINT NewFilterClasses, + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest, + IN BOOLEAN Set + ) + +/*++ + +Routine Description: + + Action routine that will get called when a particular filter + class is first used or last cleared. + + NOTE: This routine assumes that it is called with the lock + acquired. + +Arguments: + + OldFilterClasses - The values of the class filter before it + was changed. + + NewFilterClasses - The current value of the class filter + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNK_OPEN. + + NdisRequest - the change filter request from the protocol. + + Set - If true the change resulted from a set, otherwise the + change resulted from an open closing. + +Return Value: + + None. + + +--*/ + +{ + + + PELNK_ADAPTER Adapter = PELNK_ADAPTER_FROM_BINDING_HANDLE(MacBindingHandle); + + // + // The open that made this request. + // + PELNK_OPEN Open = PELNK_OPEN_FROM_BINDING_HANDLE(MacBindingHandle); + + // + // Holds the change that should be returned to the filtering package. + // + NDIS_STATUS StatusOfChange; + + NdisRequest; + + if (Adapter->ResetInProgress) { + + StatusOfChange = NDIS_STATUS_RESET_IN_PROGRESS; + + } else { + + // + // The whole purpose of this routine is to determine whether + // the filtering changes need to result in the hardware being + // reset. + // + + ASSERT(OldFilterClasses != NewFilterClasses); + + + if (ChangeClassDispatch(Adapter, + OldFilterClasses, + NewFilterClasses, + Open, + Set + )) { + + + StatusOfChange = NDIS_STATUS_PENDING; + + } else { + + StatusOfChange = NDIS_STATUS_SUCCESS; + + } + + } + + return StatusOfChange; + +} + +BOOLEAN +ChangeClassDispatch( + IN PELNK_ADAPTER Adapter, + IN UINT OldFilterClasses, + IN UINT NewFilterClasses, + IN PELNK_OPEN Open, + IN BOOLEAN Set + ) + +/*++ + +Routine Description: + + Reconfigures the adapter. + +Arguments: + + Adapter - The adapter. + + OldFilterClasses - The values of the class filter before it + was changed. + + NewFilterClasses - The current value of the class filter + + Set - TRUE if this is due to a set. + +Return Value: + + TRUE, if we need to fill in a command block. FALSE, otherwise. + +--*/ + +{ + + // + // Status to return + // + BOOLEAN StatusToReturn = FALSE; + + // + // Default Value + // + USHORT NewParameterField = DEFAULT_PARM5; + + OldFilterClasses; + + if (NewFilterClasses & (NDIS_PACKET_TYPE_PROMISCUOUS | + NDIS_PACKET_TYPE_ALL_MULTICAST)) { + + NewParameterField |= CONFIG_PROMISCUOUS; + + } else { + + if (NewFilterClasses & NDIS_PACKET_TYPE_BROADCAST) { + + NewParameterField &= ~CONFIG_BROADCAST; + + } + + } + + if (Adapter->OldParameterField != NewParameterField) { + + IF_LOG('+'); + + Adapter->OldParameterField = NewParameterField; + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Command, + CB_CONFIG + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Status, + CB_STATUS_FREE + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Config.Parameter1, + DEFAULT_PARM1 + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Config.Parameter2, + DEFAULT_PARM2 + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Config.Parameter3, + DEFAULT_PARM3 + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Config.Parameter4, + DEFAULT_PARM4 + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Config.Parameter5, + NewParameterField + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Config.Parameter6, + DEFAULT_PARM6 + ); + + + // + // if this was not from an ndisrequest, then we need to store the + // open somewhere + // + + if (!Set) { + Adapter->TransmitInfo[ + Adapter->NumberOfTransmitBuffers].OwningOpenBinding = Open; + + Adapter->CloseResultedInChanges = TRUE; + + } else { + Adapter->TransmitInfo[ + Adapter->NumberOfTransmitBuffers].OwningOpenBinding = NULL; + + ElnkSubmitCommandBlock(Adapter, Adapter->NumberOfTransmitBuffers); + } + + + StatusToReturn = TRUE; + + } + + return(StatusToReturn); + +} + + +extern +NDIS_STATUS +ElnkChangeAddresses( + IN UINT OldAddressCount, + IN CHAR OldAddresses[][ETH_LENGTH_OF_ADDRESS], + IN UINT NewAddressCount, + IN CHAR NewAddresses[][ETH_LENGTH_OF_ADDRESS], + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest, + IN BOOLEAN Set + ) + +/*++ + +Routine Description: + + Action routine that will get called when the multicast address + list has changed. + + NOTE: This routine assumes that it is called with the lock + acquired. + +Arguments: + + OldAddressCount - The number of addresses in OldAddresses. + + OldAddresses - The old multicast address list. + + NewAddressCount - The number of addresses in NewAddresses. + + NewAddresses - The new multicast address list. + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNK_OPEN. + + RequestHandle - A value supplied by the NDIS interface that the MAC + must use when completing this request with the NdisCompleteRequest + service, if the MAC completes this request asynchronously. + + Set - If true the change resulted from a set, otherwise the + change resulted from a open closing. + +Return Value: + + None. + + +--*/ + +{ + + + PELNK_ADAPTER Adapter = PELNK_ADAPTER_FROM_BINDING_HANDLE(MacBindingHandle); + + // + // The open that made this request. + // + PELNK_OPEN Open = PELNK_OPEN_FROM_BINDING_HANDLE(MacBindingHandle); + + // + // Holds the change that should be returned to the filtering package. + // + NDIS_STATUS StatusOfChange; + + OldAddressCount; OldAddresses; NdisRequest; Set; + + if (Adapter->ResetInProgress) { + + StatusOfChange = NDIS_STATUS_RESET_IN_PROGRESS; + + } else { + + // + // The whole purpose of this routine is to determine whether + // the filtering changes need to result in the hardware being + // reset. + // + + // + // We are referencing this open + // + + ChangeAddressDispatch( + Adapter, + NewAddressCount, + NewAddresses, + Open, + Set + ); + + StatusOfChange = NDIS_STATUS_PENDING; + + } + + return StatusOfChange; + +} + +extern +VOID +ChangeAddressDispatch( + IN PELNK_ADAPTER Adapter, + IN UINT AddressCount, + IN CHAR Addresses[][ETH_LENGTH_OF_ADDRESS], + IN PELNK_OPEN Open, + IN BOOLEAN Set + ) + +/*++ + +Routine Description: + + Changes the multicast address list of the adapter. + +Arguments: + + Adapter - The adapter. + + AddressCount - The number of addresses in Addresses + + Addresses - The new multicast address list. + +Return Value: + + +--*/ + +{ + + UINT i, j; + + IF_LOG('-'); + + // + // Setup the command block. + // + + for (i = 0 ; i < AddressCount; i++ ) { + + for (j = 0; j < ETH_LENGTH_OF_ADDRESS; j++) { + + NdisWriteRegisterUchar( + &Adapter->MulticastBlock->Parm.Multicast.MulticastID[i][j], + Addresses[i][j] + ); + } + } + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Parm.Multicast.McCount, + (USHORT)(AddressCount * ETH_LENGTH_OF_ADDRESS) + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Status, + CB_STATUS_FREE + ); + + NdisWriteRegisterUshort( + &Adapter->MulticastBlock->Command, + CB_MULTICAST + ); + + // + // if this was not from an ndisrequest, then we need to store the + // open somewhere + // + + if (!Set) { + Adapter->TransmitInfo[ + Adapter->NumberOfTransmitBuffers].OwningOpenBinding = Open; + + Adapter->CloseResultedInChanges = TRUE; + + } else { + Adapter->TransmitInfo[ + Adapter->NumberOfTransmitBuffers].OwningOpenBinding = NULL; + // + // Now that we're set up, let's do it! + // + + if (Adapter->FirstReset) { + + ElnkSubmitCommandBlockAndWait(Adapter); + + } else { + + ElnkSubmitCommandBlock(Adapter, Adapter->NumberOfTransmitBuffers); + + } + } + +} + + +STATIC +VOID +ElnkCloseAction( + IN NDIS_HANDLE MacBindingHandle + ) + +/*++ + +Routine Description: + + Action routine that will get called when a particular binding + was closed while it was indicating through NdisIndicateReceive + + All this routine needs to do is to decrement the reference count + of the binding. + + NOTE: This routine assumes that it is called with the lock acquired. + +Arguments: + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNK_OPEN. + +Return Value: + + None. + + +--*/ + +{ + PELNK_OPEN_FROM_BINDING_HANDLE(MacBindingHandle)->References--; +} + + + +NDIS_STATUS +ElnkRequest( + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest + ) + +/*++ + +Routine Description: + + The ElnkRequest function handles general requests from the + protocol. Currently these include SetInformation and + QueryInformation, more may be added in the future. + +Arguments: + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNK_OPEN. + + NdisRequest - A structure describing the request. In the case + of asynchronous completion, this pointer will be used to + identify the request that is completing. + +Return Value: + + The function value is the status of the operation. + + +--*/ + +{ + // + // This holds the status we will return. + // + + NDIS_STATUS StatusOfRequest; + + // + // Points to the adapter that this request is coming through. + // + PELNK_ADAPTER Adapter; + + // + // Pts to the reserved section of the request + // + PELNK_REQUEST_RESERVED Reserved = PELNK_RESERVED_FROM_REQUEST(NdisRequest); + + Adapter = PELNK_ADAPTER_FROM_BINDING_HANDLE(MacBindingHandle); + NdisAcquireSpinLock(&Adapter->Lock); + Adapter->References++; + + if (!Adapter->ResetInProgress) { + + PELNK_OPEN Open; + + Open = PELNK_OPEN_FROM_BINDING_HANDLE(MacBindingHandle); + + if (!Open->BindingShuttingDown) { + + switch (NdisRequest->RequestType) { + + case NdisRequestSetInformation: + case NdisRequestQueryInformation: + + // + // This is a valid request, queue it. + // + + Open->References++; + + Reserved->OpenBlock = Open; + Reserved->Next = (PNDIS_REQUEST)NULL; + + ElnkQueueRequest (Adapter, NdisRequest); + + StatusOfRequest = NDIS_STATUS_PENDING; + break; + + default: + + // + // Unknown request + // + + StatusOfRequest = NDIS_STATUS_NOT_SUPPORTED; + break; + + } + + } else { + + StatusOfRequest = NDIS_STATUS_CLOSING; + + } + + } else { + + StatusOfRequest = NDIS_STATUS_RESET_IN_PROGRESS; + + } + + // + // This macro assumes it is called with the lock held, + // and releases it. + // + + ELNK_DO_DEFERRED(Adapter); + return StatusOfRequest; +} + +extern +VOID +ElnkQueueRequest( + IN PELNK_ADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest + ) + +/*++ + +Routine Description: + + ElnkQueueRequest takes an NDIS_REQUEST and ensures that it + gets processed and completed. It processes the + request immediately if nothing else is in progress, otherwise + it queues it for later processing. + + THIS ROUTINE IS CALLED WITH THE SPINLOCK HELD. + +Arguments: + + Adapter - The adapter that the request is for. + + NdisRequest - The NDIS_REQUEST structure describing the request. + The ElnkReserved section is partially filled in, except + for the queueing and current offset fields. + +Return Value: + + NDIS_STATUS_PENDING if the request was queued. + Otherwise, the return code from ElnkProcessRequestQueue. + This will be NDIS_STATUS_PENDING if the request was queued + to the adapter, otherwise the status of the request. + + +--*/ + +{ + + // + // Queue the request. + // + + if (Adapter->FirstRequest != (PNDIS_REQUEST)NULL) { + + // + // Something else on the queue, just queue it. + // + + PELNK_RESERVED_FROM_REQUEST(Adapter->LastRequest)->Next = NdisRequest; + Adapter->LastRequest = NdisRequest; + + } else { + + // + // The queue if empty, so nothing is in progress. + // + + Adapter->FirstRequest = NdisRequest; + Adapter->LastRequest = NdisRequest; + + ElnkProcessRequestQueue(Adapter); + + } +} + +extern +VOID +ElnkProcessRequestQueue( + IN PELNK_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + ElnkProcessRequestQueue takes the requests on the queue + and processes them as much as possible. It will complete + any requests that it fully processes. It will stop when + the queue is empty or it finds a request that has to pend. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD. + +Arguments: + + Adapter - The adapter that the request is for. + +Return Value: + + NDIS_STATUS_PENDING (probably should be VOID...) + + +--*/ +{ + PNDIS_REQUEST Request; + PELNK_REQUEST_RESERVED Reserved; + PELNK_OPEN Open; + NDIS_STATUS Status; + + // + // Only one request can be processed at one time + // + + if (Adapter->ProcessingRequests) { + + return; + + } else { + + Adapter->ProcessingRequests = TRUE; + + } + + Request = Adapter->FirstRequest; + + for (;;) { + + // + // Loop until we exit, which happens when a + // request pends, or we empty the queue. + // + + if ((Request == (PNDIS_REQUEST)NULL) || Adapter->ResetInProgress) { + + break; + } + + Reserved = PELNK_RESERVED_FROM_REQUEST(Request); + + switch (Request->RequestType) { + + case NdisRequestClose: + + Adapter->CloseResultedInChanges = FALSE; + + Open = Reserved->OpenBlock; + + Status = EthDeleteFilterOpenAdapter( + Adapter->FilterDB, + Open->NdisFilterHandle, + NULL + ); + + + // + // If the status is successful that merely implies that + // we were able to delete the reference to the open binding + // from the filtering code. + // + // The delete filter routine can return a "special" status + // that indicates that there is a current NdisIndicateReceive + // on this binding. See below. + // + + if (Status == NDIS_STATUS_SUCCESS) { + + // + // Account for the filter's reference to this open. + // + + Open->References--; + + } else if (Status == NDIS_STATUS_PENDING) { + + // + // When the request completes we will dereference the + // open to account for the filter package's reference. + // + + } else if (Status == NDIS_STATUS_CLOSING_INDICATING) { + + // + // When we have this status it indicates that the filtering + // code was currently doing an NdisIndicateReceive. Our + // close action routine will get called when the filter + // is done with us, we remove the reference there. + // + + Status = NDIS_STATUS_PENDING; + + } else { + + ASSERT(0); + + } + + if (Adapter->CloseResultedInChanges) { + + // + // This means that we have to submit the command that was + // formed from the close callbacks. + // + ElnkSubmitCommandBlock(Adapter, Adapter->NumberOfTransmitBuffers); + + } + + // + // This flag prevents further requests on this binding. + // + + Open->BindingShuttingDown = TRUE; + + // + // Remove the reference kept for the fact that we + // had something queued. + // + + Open->References--; + + // + // Remove the open from the open list and put it on + // the closing list. This list is checked after every + // request, and when the reference count goes to zero + // the close is completed. + // + + RemoveEntryList(&Open->OpenList); + InsertTailList(&Adapter->CloseList,&Open->OpenList); + + break; + + case NdisRequestOpen: + + Open = Reserved->OpenBlock; + + IF_LOG('O'); + + if (!EthNoteFilterOpenAdapter( + Open->OwningAdapter->FilterDB, + Open, + Open->NdisBindingContext, + &Open->NdisFilterHandle + )) { + + NdisReleaseSpinLock(&Adapter->Lock); + + NdisCompleteOpenAdapter( + Open->NdisBindingContext, + NDIS_STATUS_FAILURE, + 0); + + ELNK_FREE_PHYS(Open); + + NdisAcquireSpinLock(&Adapter->Lock); + + } else { + + // + // Everything has been filled in. Synchronize access to the + // adapter block and link the new open adapter in and increment + // the opens reference count to account for the fact that the + // filter routines have a "reference" to the open. + // + + InsertTailList(&Adapter->OpenBindings,&Open->OpenList); + Adapter->OpenCount++; + Open->References++; + + NdisReleaseSpinLock(&Adapter->Lock); + + NdisCompleteOpenAdapter( + Open->NdisBindingContext, + NDIS_STATUS_SUCCESS, + 0); + + NdisAcquireSpinLock(&Adapter->Lock); + + } + + // + // Set this, since we want to continue processing + // the queue. + // + + Status = NDIS_STATUS_SUCCESS; + + break; + + case NdisRequestQueryInformation: + + Status = ElnkQueryInformation( + Adapter, + Reserved->OpenBlock, + Request->DATA.QUERY_INFORMATION.Oid, + Request->DATA.QUERY_INFORMATION.InformationBuffer, + Request->DATA.QUERY_INFORMATION.InformationBufferLength, + &(Request->DATA.QUERY_INFORMATION.BytesWritten), + &(Request->DATA.QUERY_INFORMATION.BytesNeeded) + ); + + break; + + case NdisRequestQueryStatistics: + + IF_LOG('1'); + + Status = ElnkQueryInformation( + Adapter, + (PELNK_OPEN)NULL, + Request->DATA.QUERY_INFORMATION.Oid, + Request->DATA.QUERY_INFORMATION.InformationBuffer, + Request->DATA.QUERY_INFORMATION.InformationBufferLength, + &(Request->DATA.QUERY_INFORMATION.BytesWritten), + &(Request->DATA.QUERY_INFORMATION.BytesNeeded) + ); + + break; + + case NdisRequestSetInformation: + + IF_LOG('2'); + + Status = ElnkSetInformation( + Adapter, + Reserved->OpenBlock, + Request->DATA.SET_INFORMATION.Oid, + Request->DATA.SET_INFORMATION.InformationBuffer, + Request->DATA.SET_INFORMATION.InformationBufferLength, + &(Request->DATA.SET_INFORMATION.BytesRead), + &(Request->DATA.SET_INFORMATION.BytesNeeded)); + + break; + + } + + // + // see if operation pended + // + + if (Status == NDIS_STATUS_PENDING) { + + Adapter->ProcessingRequests = FALSE; + + return; + + } + + + // + // If we fall through here, we are done with this request. + // + + Adapter->FirstRequest = Reserved->Next; + + if (Request->RequestType == NdisRequestQueryStatistics) { + + Adapter->References++; + + NdisReleaseSpinLock(&Adapter->Lock); + + NdisCompleteQueryStatistics( + Adapter->NdisAdapterHandle, + Request, + Status + ); + + NdisAcquireSpinLock(&Adapter->Lock); + + Adapter->References--; + + } else if ((Request->RequestType == NdisRequestQueryInformation) || + (Request->RequestType == NdisRequestSetInformation)) { + + Open = Reserved->OpenBlock; + + NdisReleaseSpinLock(&Adapter->Lock); + + NdisCompleteRequest( + Open->NdisBindingContext, + Request, + Status + ); + + NdisAcquireSpinLock(&Adapter->Lock); + + Open->References--; + + } + + Request = Adapter->FirstRequest; + + // + // Now loop and continue on with the next request. + // + + } + + Adapter->ProcessingRequests = FALSE; + +} + + +extern +NDIS_STATUS +ElnkSetInformation( + IN PELNK_ADAPTER Adapter, + IN PELNK_OPEN Open, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN INT InformationBufferLength, + OUT PUINT BytesRead, + OUT PUINT BytesNeeded + ) + +/*++ + +Routine Description: + + ElnkSetInformation handles a set operation for a + single OID. + +Arguments: + + Adapter - The adapter that the set is for. + + Open - a pointer to the open instance. + + Oid - the NDIS_OID to process. + + InformationBuffer - a pointer into the + NdisRequest->InformationBuffer into which contains the value to be set + + InformationBufferLength - a pointer to the number of bytes in the + InformationBuffer. + + BytesRead - Number of bytes read. + + BytesNeeded - Number of bytes needed to satisfy this request. +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_PENDING + NDIS_STATUS_INVALID_LENGTH + NDIS_STATUS_INVALID_OID + +--*/ + +{ + + NDIS_STATUS Status; + ULONG PacketFilter; + ULONG LookAheadBufferSize; + // + // Now check for the most common OIDs + // + + *BytesNeeded = 0; + + switch (Oid) { + + case OID_802_3_MULTICAST_LIST: + + if (InformationBufferLength % ETH_LENGTH_OF_ADDRESS != 0) { + + // + // The data must be a multiple of the Ethernet + // address size. + // + + *BytesNeeded = ETH_LENGTH_OF_ADDRESS; + return NDIS_STATUS_INVALID_DATA; + + } + + // + // Now call the filter package to set up the addresses. + // + + Status = EthChangeFilterAddresses( + Adapter->FilterDB, + Open->NdisFilterHandle, + (PNDIS_REQUEST)NULL, + InformationBufferLength / ETH_LENGTH_OF_ADDRESS, + InformationBuffer, + TRUE + ); + + *BytesRead = InformationBufferLength; + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + + if (InformationBufferLength != 4) { + + *BytesNeeded = 4; + return NDIS_STATUS_INVALID_DATA; + + } + + // + // Now call the filter package to set the packet filter. + // + + NdisMoveMemory ((PVOID)&PacketFilter, InformationBuffer, sizeof(ULONG)); + + + // + // Verify bits + // + + if (PacketFilter & (NDIS_PACKET_TYPE_SOURCE_ROUTING | + NDIS_PACKET_TYPE_SMT | + NDIS_PACKET_TYPE_MAC_FRAME | + NDIS_PACKET_TYPE_FUNCTIONAL | + NDIS_PACKET_TYPE_ALL_FUNCTIONAL | + NDIS_PACKET_TYPE_GROUP + )) { + + Status = NDIS_STATUS_NOT_SUPPORTED; + + *BytesRead = 4; + *BytesNeeded = 0; + + break; + + } + + Status = EthFilterAdjust( + Adapter->FilterDB, + Open->NdisFilterHandle, + (PNDIS_REQUEST)NULL, + PacketFilter, + TRUE + ); + + *BytesRead = InformationBufferLength; + + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + + if (InformationBufferLength != 4) { + + *BytesNeeded = 4; + Status = NDIS_STATUS_INVALID_LENGTH; + break; + + } + + *BytesRead = 4; + + NdisMoveMemory(&LookAheadBufferSize, + InformationBuffer, + sizeof(ULONG)); + + + + if (LookAheadBufferSize <= (MAXIMUM_ETHERNET_PACKET_SIZE - ELNK_HEADER_SIZE)) { + + Status = NDIS_STATUS_SUCCESS; + + } else { + + Status = NDIS_STATUS_INVALID_DATA; + } + + break; + + case OID_GEN_PROTOCOL_OPTIONS: + if (InformationBufferLength != 4) { + + *BytesNeeded = 4; + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + NdisMoveMemory(&Open->ProtOptionFlags, InformationBuffer, 4); + + *BytesRead = 4; + Status = NDIS_STATUS_SUCCESS; + break; + + default: + + Status = NDIS_STATUS_INVALID_OID; + break; + + } + + return Status; +} + + + +STATIC +NDIS_STATUS +ElnkQueryInformation( + IN PELNK_ADAPTER Adapter, + IN PELNK_OPEN Open, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN UINT InformationBufferLength, + OUT PUINT BytesWritten, + OUT PUINT BytesNeeded +) + +/*++ + +Routine Description: + + The ElnkQueryProtocolInformation process a Query request for + NDIS_OIDs that are specific to a binding about the MAC. Note that + some of the OIDs that are specific to bindings are also queryable + on a global basis. Rather than recreate this code to handle the + global queries, I use a flag to indicate if this is a query for the + global data or the binding specific data. + +Arguments: + + Adapter - a pointer to the adapter. + + Open - a pointer to the open instance. If null, then return + global statistics. + + Oid - the NDIS_OID to process. + + InformationBuffer - a pointer into the + NdisRequest->InformationBuffer into which store the result of the query. + + InformationBufferLength - a pointer to the number of bytes left in the + InformationBuffer. + + BytesWritten - a pointer to the number of bytes written into the + InformationBuffer. + + BytesNeeded - If there is not enough room in the information buffer + then this will contain the number of bytes needed to complete the + request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + +static +NDIS_OID ElnkGlobalSupportedOids[] = { + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST + }; + +static +NDIS_OID ElnkProtocolSupportedOids[] = { + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE + }; + + + NDIS_MEDIUM Medium = NdisMedium802_3; + UINT GenericUlong; + USHORT GenericUShort; + UCHAR GenericArray[6]; + UINT MulticastAddresses; + + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + // + // Common variables for pointing to result of query + // + + PVOID MoveSource = (PVOID)(&GenericUlong); + ULONG MoveBytes = sizeof(GenericUlong); + USHORT TmpUshort; + + NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; + + *BytesWritten = 0; + *BytesNeeded = 0; + + // + // Switch on request type + // + + switch(Oid){ + + case OID_GEN_MAC_OPTIONS: + + GenericUlong = (ULONG)(NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_RECEIVE_SERIALIZED | + NDIS_MAC_OPTION_NO_LOOPBACK + ); + + break; + + case OID_GEN_SUPPORTED_LIST: + + if (Open == NULL) { + MoveSource = (PVOID)(ElnkGlobalSupportedOids); + MoveBytes = sizeof(ElnkGlobalSupportedOids); + } else { + MoveSource = (PVOID)(ElnkProtocolSupportedOids); + MoveBytes = sizeof(ElnkProtocolSupportedOids); + } + break; + + case OID_GEN_HARDWARE_STATUS: + + + if (Adapter->ResetInProgress){ + + HardwareStatus = NdisHardwareStatusReset; + + } else if (Adapter->FirstReset) { + + HardwareStatus = NdisHardwareStatusInitializing; + + } else { + + HardwareStatus = NdisHardwareStatusReady; + + } + + + MoveSource = (PVOID)(&HardwareStatus); + MoveBytes = sizeof(NDIS_HARDWARE_STATUS); + + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + + MoveSource = (PVOID) (&Medium); + MoveBytes = sizeof(NDIS_MEDIUM); + break; + + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_CURRENT_LOOKAHEAD: + case OID_GEN_MAXIMUM_FRAME_SIZE: + + GenericUlong = (ULONG) MAXIMUM_ETHERNET_PACKET_SIZE - ELNK_HEADER_SIZE; + + break; + + + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + case OID_GEN_MAXIMUM_TOTAL_SIZE: + + GenericUlong = (ULONG) MAXIMUM_ETHERNET_PACKET_SIZE; + + break; + + + + case OID_GEN_LINK_SPEED: + + // + // 10 Mbps + // + + GenericUlong = (ULONG)100000; + + break; + + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + + GenericUlong = (ULONG) MAXIMUM_ETHERNET_PACKET_SIZE * + Adapter->NumberOfTransmitBuffers; + + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + + GenericUlong = (ULONG) MAXIMUM_ETHERNET_PACKET_SIZE * + Adapter->NumberOfReceiveBuffers; + + break; + + +#if ELNKMC + + case OID_GEN_VENDOR_ID: + + NdisMoveMemory( + (PVOID)&GenericUlong, + Adapter->NetworkAddress, + 3 + ); + GenericUlong &= 0xFFFFFF00; + MoveSource = (PVOID)(&GenericUlong); + MoveBytes = sizeof(GenericUlong); + break; + + case OID_GEN_VENDOR_DESCRIPTION: + + MoveSource = (PVOID)"ElnkMC Adapter"; + MoveBytes = 15; + break; + +#else + + case OID_GEN_VENDOR_ID: + + NdisMoveMemory( + (PVOID)&GenericUlong, + Adapter->NetworkAddress, + 3 + ); + GenericUlong &= 0xFFFFFF00; + GenericUlong != 0x01; + MoveSource = (PVOID)(&GenericUlong); + MoveBytes = sizeof(GenericUlong); + break; + + case OID_GEN_VENDOR_DESCRIPTION: + + MoveSource = (PVOID)"Elnk16 Adapter"; + MoveBytes = 15; + break; + +#endif + + case OID_GEN_DRIVER_VERSION: + + GenericUShort = (USHORT)0x0300; + + MoveSource = (PVOID)(&GenericUShort); + MoveBytes = sizeof(GenericUShort); + break; + + + case OID_GEN_CURRENT_PACKET_FILTER: + + if (Open != NULL) { + + GenericUlong = (ULONG)(ETH_QUERY_PACKET_FILTER( + Adapter->FilterDB, + Open->NdisFilterHandle + )); + } else { + + GenericUlong = (ULONG)ETH_QUERY_FILTER_CLASSES( + Adapter->FilterDB + ); + + } + + break; + + case OID_802_3_PERMANENT_ADDRESS: + + ETH_COPY_NETWORK_ADDRESS( + (PCHAR)GenericArray, + Adapter->NetworkAddress + ); + + MoveSource = (PVOID)(GenericArray); + MoveBytes = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_CURRENT_ADDRESS: + + ETH_COPY_NETWORK_ADDRESS( + (PCHAR)GenericArray, + Adapter->CurrentAddress + ); + + MoveSource = (PVOID)(GenericArray); + MoveBytes = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_MULTICAST_LIST: + + if (Open == NULL) { + + NDIS_STATUS Status; + EthQueryGlobalFilterAddresses( + &Status, + Adapter->FilterDB, + InformationBufferLength, + &MulticastAddresses, + (PVOID)InformationBuffer); + + MoveSource = (PVOID)InformationBuffer; + MoveBytes = MulticastAddresses * ETH_LENGTH_OF_ADDRESS; + + } else { + + NDIS_STATUS Status; + EthQueryOpenFilterAddresses( + &Status, + Adapter->FilterDB, + Open->NdisFilterHandle, + InformationBufferLength, + &MulticastAddresses, + (PVOID)InformationBuffer); + + if (Status == NDIS_STATUS_SUCCESS) { + MoveSource = (PVOID)InformationBuffer; + MoveBytes = MulticastAddresses * ETH_LENGTH_OF_ADDRESS; + } else { + MoveSource = (PVOID)InformationBuffer; + MoveBytes = ETH_LENGTH_OF_ADDRESS * + EthNumberOfOpenFilterAddresses( + Adapter->FilterDB, + Open->NdisFilterHandle); + } + + } + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + + GenericUlong = (ULONG) ELNK_MAXIMUM_MULTICAST; + + break; + + default: + + if (Open != NULL) { + + StatusToReturn = NDIS_STATUS_NOT_SUPPORTED; + break; + + } + + switch(Oid){ + + case OID_GEN_XMIT_OK: + GenericUlong = (ULONG) Adapter->GoodTransmits; + break; + + case OID_GEN_RCV_OK: + GenericUlong = (ULONG) Adapter->GoodReceives; + break; + + case OID_GEN_XMIT_ERROR: + GenericUlong = (ULONG) (Adapter->RetryFailure + + Adapter->LostCarrier + + Adapter->UnderFlow + + Adapter->NoClearToSend); + break; + + case OID_GEN_RCV_ERROR: + NdisReadRegisterUshort(&Adapter->Scb->CrcErrors, &GenericUlong); + NdisReadRegisterUshort(&Adapter->Scb->AlignmentErrors, &TmpUshort); + GenericUlong += TmpUshort; + NdisReadRegisterUshort(&Adapter->Scb->ResourceErrors, &TmpUshort); + GenericUlong += TmpUshort; + NdisReadRegisterUshort(&Adapter->Scb->OverrunErrors, &TmpUshort); + GenericUlong += TmpUshort; + GenericUlong += Adapter->FrameTooShort + Adapter->NoEofDetected; + break; + + case OID_GEN_RCV_NO_BUFFER: + NdisReadRegisterUshort(&Adapter->Scb->ResourceErrors, &GenericUlong); + break; + + case OID_GEN_RCV_CRC_ERROR: + NdisReadRegisterUshort(&Adapter->Scb->CrcErrors, &GenericUlong); + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + GenericUlong = (ULONG) Adapter->TransmitsQueued; + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + NdisReadRegisterUshort(&Adapter->Scb->AlignmentErrors, &GenericUlong); + break; + + case OID_802_3_XMIT_ONE_COLLISION: + GenericUlong = (ULONG) Adapter->OneRetry; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + GenericUlong = (ULONG) Adapter->MoreThanOneRetry; + break; + + case OID_802_3_XMIT_DEFERRED: + GenericUlong = (ULONG) Adapter->Deferred; + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + GenericUlong = (ULONG) Adapter->RetryFailure; + break; + + case OID_802_3_RCV_OVERRUN: + NdisReadRegisterUshort(&Adapter->Scb->OverrunErrors, &GenericUlong); + break; + + case OID_802_3_XMIT_UNDERRUN: + GenericUlong = (ULONG) Adapter->UnderFlow; + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + GenericUlong = (ULONG) Adapter->NoClearToSend; + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + GenericUlong = (ULONG) Adapter->LostCarrier; + break; + + default: + StatusToReturn = NDIS_STATUS_NOT_SUPPORTED; + break; + + } + + } + + if (StatusToReturn == NDIS_STATUS_SUCCESS) { + + if (MoveBytes > InformationBufferLength) { + + // + // Not enough room in InformationBuffer. Punt + // + + *BytesNeeded = MoveBytes; + + StatusToReturn = NDIS_STATUS_BUFFER_TOO_SHORT; + + } else { + + // + // Copy result into InformationBuffer + // + + *BytesWritten = MoveBytes; + if (MoveBytes > 0) { + ELNK_MOVE_MEMORY( + InformationBuffer, + MoveSource, + MoveBytes + ); + } + } + } + + return(StatusToReturn); +} + + +extern +NDIS_STATUS +ElnkQueryGlobalStatistics( + IN NDIS_HANDLE MacAdapterContext, + IN PNDIS_REQUEST NdisRequest + ) + +/*++ + +Routine Description: + + ElnkQueryGlobalStatistics handles a per-adapter query + for statistics. It is similar to ElnkQueryInformation, + which is per-binding. + +Arguments: + + MacAdapterContext - The context value that the MAC passed + to NdisRegisterAdapter; actually as pointer to a + ELNK_ADAPTER. + + NdisRequest - Describes the query request. + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_PENDING + +--*/ + +{ + + // + // This holds the status we will return. + // + + NDIS_STATUS StatusOfRequest; + + // + // Points to the adapter that this request is coming through. + // + PELNK_ADAPTER Adapter = (PELNK_ADAPTER)MacAdapterContext; + + PELNK_REQUEST_RESERVED Reserved = PELNK_RESERVED_FROM_REQUEST(NdisRequest); + + NdisAcquireSpinLock(&Adapter->Lock); + Adapter->References++; + + if (!Adapter->ResetInProgress) { + + switch (NdisRequest->RequestType) { + + case NdisRequestQueryStatistics: + + // + // Valid request. + // + + Reserved->OpenBlock = (PELNK_OPEN)NULL; + Reserved->Next = (PNDIS_REQUEST)NULL; + + ElnkQueueRequest (Adapter, NdisRequest); + + StatusOfRequest = NDIS_STATUS_PENDING; + break; + + default: + + // + // Unknown request + // + + StatusOfRequest = NDIS_STATUS_NOT_SUPPORTED; + break; + + } + + } else { + + StatusOfRequest = NDIS_STATUS_RESET_IN_PROGRESS; + + } + + + // + // This macro assumes it is called with the lock held, + // and releases it. + // + + ELNK_DO_DEFERRED(Adapter); + return StatusOfRequest; +} |