summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/sonic/request.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/ndis/sonic/request.c890
1 files changed, 890 insertions, 0 deletions
diff --git a/private/ntos/ndis/sonic/request.c b/private/ntos/ndis/sonic/request.c
new file mode 100644
index 000000000..d17d67791
--- /dev/null
+++ b/private/ntos/ndis/sonic/request.c
@@ -0,0 +1,890 @@
+/*++
+
+Copyright (c) 1990-1992 Microsoft Corporation
+
+Module Name:
+
+ request.c
+
+Abstract:
+
+ This is the cose to handle requests for the National Semiconductor
+ SONIC Ethernet controller. This driver conforms to the NDIS 3.0
+ miniport interface.
+
+Author:
+
+ Adam Barr (adamba) 14-Nov-1990
+
+Environment:
+
+ Kernel Mode - Or whatever is the equivalent.
+
+Revision History:
+
+
+--*/
+
+#include <ndis.h>
+
+#include <sonichrd.h>
+#include <sonicsft.h>
+
+
+
+//
+// This macro determines if the directed address
+// filtering in the CAM is actually necessary given
+// the current filter.
+//
+
+#define CAM_DIRECTED_SIGNIFICANT(_Filter) \
+ ((((_Filter) & NDIS_PACKET_TYPE_DIRECTED) && \
+ (!((_Filter) & NDIS_PACKET_TYPE_PROMISCUOUS))) ? 1 : 0)
+
+
+//
+// This macro determines if the multicast filtering in
+// the CAM are actually necessary given the current filter.
+//
+
+#define CAM_MULTICAST_SIGNIFICANT(_Filter) \
+ ((((_Filter) & NDIS_PACKET_TYPE_MULTICAST) && \
+ (!((_Filter) & (NDIS_PACKET_TYPE_ALL_MULTICAST | \
+ NDIS_PACKET_TYPE_PROMISCUOUS)))) ? 1 : 0)
+
+
+STATIC
+NDIS_STATUS
+ChangeClassDispatch(
+ IN PSONIC_ADAPTER Adapter,
+ IN UINT NewFilterClasses
+ );
+
+STATIC
+NDIS_STATUS
+ChangeAddressDispatch(
+ IN PSONIC_ADAPTER Adapter,
+ IN UINT AddressCount,
+ IN CHAR Addresses[][ETH_LENGTH_OF_ADDRESS]
+ );
+
+
+
+
+extern
+NDIS_STATUS
+SonicQueryInformation(
+ IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesWritten,
+ OUT PULONG BytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ SonicQueryInformation handles a query operation for a
+ single OID.
+
+Arguments:
+
+ MiniportAdapterContext - Context registered with the wrapper, really
+ a pointer to the adapter.
+
+ Oid - The OID of the query.
+
+ InformationBuffer - Holds the result of the query.
+
+ InformationBufferLength - The length of InformationBuffer.
+
+ BytesWritten - If the call is successful, returns the number
+ of bytes written to InformationBuffer.
+
+ BytesNeeded - If there is not enough room in InformationBuffer
+ to satisfy the OID, returns the amount of storage needed.
+
+Return Value:
+
+ NDIS_STATUS_SUCCESS
+ NDIS_STATUS_PENDING
+ NDIS_STATUS_INVALID_LENGTH
+ NDIS_STATUS_INVALID_OID
+
+--*/
+
+{
+ PSONIC_ADAPTER Adapter = PSONIC_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
+ INT i;
+ INT SupportedOids;
+ NDIS_OID MaskOid;
+ PVOID SourceBuffer;
+ ULONG SourceBufferLength;
+ ULONG GenericUlong;
+ USHORT GenericUshort;
+ UCHAR VendorId[4];
+#ifdef SONIC_EISA
+ static const UCHAR EisaDescriptor[] = "SONIC EISA Bus Master Ethernet Adapter (DP83932EB-EISA)";
+#endif
+#ifdef SONIC_INTERNAL
+ static const UCHAR InternalDescriptor[] = "MIPS R4000 on-board network controller";
+#endif
+
+ static const NDIS_OID SonicSupportedOids[] = {
+ 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_DIRECTED_BYTES_XMIT,
+ OID_GEN_DIRECTED_FRAMES_XMIT,
+ OID_GEN_MULTICAST_BYTES_XMIT,
+ OID_GEN_MULTICAST_FRAMES_XMIT,
+ OID_GEN_BROADCAST_BYTES_XMIT,
+ OID_GEN_BROADCAST_FRAMES_XMIT,
+ OID_GEN_DIRECTED_BYTES_RCV,
+ OID_GEN_DIRECTED_FRAMES_RCV,
+ OID_GEN_MULTICAST_BYTES_RCV,
+ OID_GEN_MULTICAST_FRAMES_RCV,
+ OID_GEN_BROADCAST_BYTES_RCV,
+ OID_GEN_BROADCAST_FRAMES_RCV,
+ 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,
+ OID_802_3_XMIT_LATE_COLLISIONS
+ };
+
+ //
+ // Check that the OID is valid.
+ //
+
+ SupportedOids = sizeof(SonicSupportedOids)/sizeof(ULONG);
+
+ for (i=0; i<SupportedOids; i++) {
+ if (Oid == SonicSupportedOids[i]) {
+ break;
+ }
+ }
+
+ if (i == SupportedOids) {
+ *BytesWritten = 0;
+ return NDIS_STATUS_INVALID_OID;
+ }
+
+ //
+ // Initialize these once, since this is the majority
+ // of cases.
+ //
+
+ SourceBuffer = &GenericUlong;
+ SourceBufferLength = sizeof(ULONG);
+
+ switch (Oid & OID_TYPE_MASK) {
+
+ case OID_TYPE_GENERAL_OPERATIONAL:
+
+ switch (Oid) {
+
+ case OID_GEN_MAC_OPTIONS:
+
+ GenericUlong = (ULONG)(NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
+ NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
+ NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
+ NDIS_MAC_OPTION_NO_LOOPBACK
+ );
+
+ break;
+
+ case OID_GEN_SUPPORTED_LIST:
+
+ SourceBuffer = (PVOID)SonicSupportedOids;
+ SourceBufferLength = SupportedOids * sizeof(ULONG);
+ break;
+
+ case OID_GEN_HARDWARE_STATUS:
+
+ GenericUlong = NdisHardwareStatusReady;
+ break;
+
+ case OID_GEN_MEDIA_SUPPORTED:
+
+ GenericUlong = NdisMedium802_3;
+ break;
+
+ case OID_GEN_MEDIA_IN_USE:
+
+ GenericUlong = NdisMedium802_3;
+ break;
+
+ case OID_GEN_MAXIMUM_LOOKAHEAD:
+
+ GenericUlong = (SONIC_INDICATE_MAXIMUM-14 < SONIC_LOOPBACK_MAXIMUM) ?
+ SONIC_INDICATE_MAXIMUM-14 : SONIC_LOOPBACK_MAXIMUM;
+ break;
+
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+
+ GenericUlong = 1500;
+ break;
+
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+
+ GenericUlong = 1514;
+ break;
+
+ case OID_GEN_LINK_SPEED:
+
+ GenericUlong = 100000; // 10 Mbps in 100 bps units
+ break;
+
+ case OID_GEN_TRANSMIT_BUFFER_SPACE:
+
+ GenericUlong = SONIC_LARGE_BUFFER_SIZE * SONIC_NUMBER_OF_TRANSMIT_DESCRIPTORS;
+ break;
+
+ case OID_GEN_RECEIVE_BUFFER_SPACE:
+
+ GenericUlong = SONIC_LARGE_BUFFER_SIZE * SONIC_NUMBER_OF_RECEIVE_DESCRIPTORS;
+ break;
+
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+
+ GenericUlong = SONIC_LARGE_BUFFER_SIZE;
+ break;
+
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+
+ GenericUlong = SONIC_LARGE_BUFFER_SIZE;
+ break;
+
+ case OID_GEN_VENDOR_ID:
+
+ SONIC_MOVE_MEMORY(VendorId, Adapter->PermanentNetworkAddress, 3);
+ VendorId[3] = 0x0;
+ SourceBuffer = VendorId;
+ SourceBufferLength = sizeof(VendorId);
+ break;
+
+ case OID_GEN_VENDOR_DESCRIPTION:
+
+ switch (Adapter->AdapterType) {
+#ifdef SONIC_EISA
+ case SONIC_ADAPTER_TYPE_EISA:
+ SourceBuffer = (PVOID)EisaDescriptor;
+ SourceBufferLength = sizeof(EisaDescriptor);
+ break;
+#endif
+#ifdef SONIC_INTERNAL
+ case SONIC_ADAPTER_TYPE_INTERNAL:
+ SourceBuffer = (PVOID)InternalDescriptor;
+ SourceBufferLength = sizeof(InternalDescriptor);
+ break;
+#endif
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case OID_GEN_DRIVER_VERSION:
+
+ GenericUshort = (SONIC_NDIS_MAJOR_VERSION << 8) + SONIC_NDIS_MINOR_VERSION;
+ SourceBuffer = &GenericUshort;
+ SourceBufferLength = sizeof(USHORT);
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+
+ GenericUlong = Adapter->CurrentPacketFilter;
+ break;
+
+ case OID_GEN_CURRENT_LOOKAHEAD:
+
+ GenericUlong = (SONIC_INDICATE_MAXIMUM-14 < SONIC_LOOPBACK_MAXIMUM) ?
+ SONIC_INDICATE_MAXIMUM-14 : SONIC_LOOPBACK_MAXIMUM;
+ break;
+
+ default:
+
+ ASSERT(FALSE);
+ break;
+
+ }
+
+ break;
+
+ case OID_TYPE_GENERAL_STATISTICS:
+
+ MaskOid = (Oid & OID_INDEX_MASK) - 1;
+
+ switch (Oid & OID_REQUIRED_MASK) {
+
+ case OID_REQUIRED_MANDATORY:
+
+ ASSERT (MaskOid < GM_ARRAY_SIZE);
+
+ if (MaskOid == GM_RECEIVE_NO_BUFFER) {
+
+ //
+ // This one is read off the card, update unless our
+ // counter is more (which indicates an imminent
+ // overflow interrupt, so we don't update).
+ //
+
+ USHORT MissedPacket;
+ SONIC_READ_PORT(Adapter, SONIC_FRAME_ALIGNMENT_ERROR, &MissedPacket);
+
+ if ((Adapter->GeneralMandatory[GM_RECEIVE_NO_BUFFER] & 0xffff) <
+ MissedPacket) {
+
+ Adapter->GeneralMandatory[GM_RECEIVE_NO_BUFFER] =
+ (Adapter->GeneralMandatory[GM_RECEIVE_NO_BUFFER] & 0xffff0000) +
+ MissedPacket;
+
+ }
+ }
+
+ GenericUlong = Adapter->GeneralMandatory[MaskOid];
+ break;
+
+ case OID_REQUIRED_OPTIONAL:
+
+ ASSERT (MaskOid < GO_ARRAY_SIZE);
+
+ if (MaskOid == GO_RECEIVE_CRC) {
+
+ //
+ // This one is read off the card, update unless our
+ // counter is more (which indicates an imminent
+ // overflow interrupt, so we don't update).
+ //
+
+ USHORT CrcError;
+ SONIC_READ_PORT(Adapter, SONIC_FRAME_ALIGNMENT_ERROR, &CrcError);
+
+ if ((Adapter->GeneralOptional[GO_RECEIVE_CRC - GO_ARRAY_START] & 0xffff) <
+ CrcError) {
+
+ Adapter->GeneralOptional[GO_RECEIVE_CRC - GO_ARRAY_START] =
+ (Adapter->GeneralOptional[GO_RECEIVE_CRC - GO_ARRAY_START] & 0xffff0000) +
+ CrcError;
+
+ }
+ }
+
+ if ((MaskOid / 2) < GO_COUNT_ARRAY_SIZE) {
+
+ if (MaskOid & 0x01) {
+ // Frame count
+ GenericUlong = Adapter->GeneralOptionalFrameCount[MaskOid / 2];
+ } else {
+ // Byte count
+ SourceBuffer = &Adapter->GeneralOptionalByteCount[MaskOid / 2];
+ SourceBufferLength = sizeof(LARGE_INTEGER);
+ }
+
+ } else {
+
+ GenericUlong = Adapter->GeneralOptional[MaskOid - GO_ARRAY_START];
+
+ }
+
+ break;
+
+ default:
+
+ ASSERT(FALSE);
+ break;
+
+ }
+
+ break;
+
+ case OID_TYPE_802_3_OPERATIONAL:
+
+ switch (Oid) {
+
+ case OID_802_3_PERMANENT_ADDRESS:
+
+ SourceBuffer = Adapter->PermanentNetworkAddress;
+ SourceBufferLength = 6;
+ break;
+
+ case OID_802_3_CURRENT_ADDRESS:
+
+ SourceBuffer = Adapter->CurrentNetworkAddress;
+ SourceBufferLength = 6;
+ break;
+
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+
+ GenericUlong = SONIC_CAM_ENTRIES - 1;
+ break;
+
+ default:
+
+ ASSERT(FALSE);
+ break;
+
+ }
+
+ break;
+
+ case OID_TYPE_802_3_STATISTICS:
+
+ MaskOid = (Oid & OID_INDEX_MASK) - 1;
+
+ switch (Oid & OID_REQUIRED_MASK) {
+
+ case OID_REQUIRED_MANDATORY:
+
+ ASSERT (MaskOid < MM_ARRAY_SIZE);
+
+ if (MaskOid == MM_RECEIVE_ERROR_ALIGNMENT) {
+
+ //
+ // This one is read off the card, update unless our
+ // counter is more (which indicates an imminent
+ // overflow interrupt, so we don't update).
+ //
+
+ USHORT FaError;
+ SONIC_READ_PORT(Adapter, SONIC_FRAME_ALIGNMENT_ERROR, &FaError);
+
+ if ((Adapter->MediaMandatory[MM_RECEIVE_ERROR_ALIGNMENT] & 0xffff) <
+ FaError) {
+
+ Adapter->MediaMandatory[MM_RECEIVE_ERROR_ALIGNMENT] =
+ (Adapter->MediaMandatory[MM_RECEIVE_ERROR_ALIGNMENT] & 0xffff0000) +
+ FaError;
+
+ }
+ }
+
+ GenericUlong = Adapter->MediaMandatory[MaskOid];
+ break;
+
+ case OID_REQUIRED_OPTIONAL:
+
+ ASSERT (MaskOid < MO_ARRAY_SIZE);
+ GenericUlong = Adapter->MediaOptional[MaskOid];
+ break;
+
+ default:
+
+ ASSERT(FALSE);
+ break;
+
+ }
+
+ break;
+
+ }
+
+ if (SourceBufferLength > InformationBufferLength) {
+ *BytesNeeded = SourceBufferLength;
+ return NDIS_STATUS_INVALID_LENGTH;
+ }
+
+ SONIC_MOVE_MEMORY (InformationBuffer, SourceBuffer, SourceBufferLength);
+ *BytesWritten = SourceBufferLength;
+
+ return NDIS_STATUS_SUCCESS;
+
+}
+
+
+STATIC
+NDIS_STATUS
+SonicSetInformation(
+ IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesRead,
+ OUT PULONG BytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ SonicQueryInformation handles a set operation for a
+ single OID.
+
+Arguments:
+
+ MiniportAdapterContext - Context registered with the wrapper, really
+ a pointer to the adapter.
+
+ Oid - The OID of the set.
+
+ InformationBuffer - Holds the data to be set.
+
+ InformationBufferLength - The length of InformationBuffer.
+
+ BytesRead - If the call is successful, returns the number
+ of bytes read from InformationBuffer.
+
+ BytesNeeded - If there is not enough data in InformationBuffer
+ to satisfy the OID, returns the amount of storage needed.
+
+Return Value:
+
+ NDIS_STATUS_SUCCESS
+ NDIS_STATUS_PENDING
+ NDIS_STATUS_INVALID_LENGTH
+ NDIS_STATUS_INVALID_OID
+
+--*/
+
+{
+
+ PSONIC_ADAPTER Adapter = PSONIC_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
+ NDIS_STATUS Status;
+ ULONG PacketFilter;
+
+ //
+ // Now check for the most common OIDs
+ //
+
+ 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.
+ //
+
+ return NDIS_STATUS_INVALID_DATA;
+
+ }
+#if DBG
+ if (SonicDbg) {
+ DbgPrint("Processing Change Multicast List request\n");
+ }
+#endif
+
+ //
+ // Now make the change.
+ //
+
+ Status = ChangeAddressDispatch(
+ Adapter,
+ InformationBufferLength / ETH_LENGTH_OF_ADDRESS,
+ InformationBuffer
+ );
+
+ *BytesRead = InformationBufferLength;
+
+ return Status;
+
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+
+ if (InformationBufferLength != 4) {
+
+ *BytesNeeded = 4;
+ return NDIS_STATUS_INVALID_LENGTH;
+
+ }
+
+#if DBG
+ if (SonicDbg) {
+ DbgPrint("Processing Change Packet Filter request\n");
+ }
+#endif
+
+ //
+ // Now call the filter package to set the packet filter.
+ //
+
+ SONIC_MOVE_MEMORY ((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
+ )) {
+
+ *BytesRead = 4;
+ *BytesNeeded = 0;
+
+ return NDIS_STATUS_NOT_SUPPORTED;
+
+ }
+
+ Status = ChangeClassDispatch(
+ Adapter,
+ PacketFilter
+ );
+
+ *BytesRead = 4;
+ return Status;
+
+ break;
+
+ case OID_GEN_CURRENT_LOOKAHEAD:
+
+ //
+ // No need to record requested lookahead length since we
+ // always indicate the whole packet.
+ //
+
+ *BytesRead = 4;
+ return NDIS_STATUS_SUCCESS;
+ break;
+
+ default:
+
+ return NDIS_STATUS_INVALID_OID;
+ break;
+
+ }
+
+}
+
+STATIC
+NDIS_STATUS
+ChangeClassDispatch(
+ IN PSONIC_ADAPTER Adapter,
+ IN UINT NewFilterClasses
+ )
+
+/*++
+
+Routine Description:
+
+ Modifies the Receive Control Register and Cam Enable registers,
+ then re-loads the CAM if necessary.
+
+Arguments:
+
+ Adapter - The adapter.
+
+ NewFilterClasses - New set of filters.
+
+Return Value:
+
+ NDIS_STATUS_PENDING - if the CAM was reloaded.
+ NDIS_STATUS_SUCCESS - otherwise.
+
+--*/
+
+{
+ //
+ // The new value for the RCR.
+ //
+ USHORT NewReceiveControl = SONIC_RCR_DEFAULT_VALUE;
+
+ //
+ // First take care of the Receive Control Register.
+ //
+
+ if (NewFilterClasses & NDIS_PACKET_TYPE_PROMISCUOUS) {
+
+ NewReceiveControl |= SONIC_RCR_PROMISCUOUS_PHYSICAL |
+ SONIC_RCR_ACCEPT_BROADCAST |
+ SONIC_RCR_ACCEPT_ALL_MULTICAST;
+
+ } else {
+
+ if (NewFilterClasses & NDIS_PACKET_TYPE_ALL_MULTICAST) {
+
+ NewReceiveControl |= SONIC_RCR_ACCEPT_ALL_MULTICAST;
+
+ }
+
+ if (NewFilterClasses & NDIS_PACKET_TYPE_BROADCAST) {
+
+ NewReceiveControl |= SONIC_RCR_ACCEPT_BROADCAST;
+
+ }
+
+ }
+
+ Adapter->ReceiveControlRegister = NewReceiveControl;
+
+ SONIC_WRITE_PORT(Adapter, SONIC_RECEIVE_CONTROL,
+ Adapter->ReceiveControlRegister
+ );
+
+ if (CAM_DIRECTED_SIGNIFICANT(NewFilterClasses)) {
+
+ Adapter->CamDescriptorArea->CamEnable |= 1;
+
+ } else {
+
+ Adapter->CamDescriptorArea->CamEnable &= ~1;
+
+ }
+
+ if (CAM_MULTICAST_SIGNIFICANT(NewFilterClasses)) {
+
+ Adapter->CamDescriptorArea->CamEnable |=
+ Adapter->MulticastCamEnableBits;
+
+ } else {
+
+ Adapter->CamDescriptorArea->CamEnable &= 1;
+
+ }
+
+ //
+ // This will cause a LOAD_CAM interrupt when it is done.
+ //
+
+ SonicStartCamReload(Adapter);
+
+ Adapter->CurrentPacketFilter = NewFilterClasses;
+
+ return NDIS_STATUS_PENDING;
+
+}
+
+STATIC
+NDIS_STATUS
+ChangeAddressDispatch(
+ IN PSONIC_ADAPTER Adapter,
+ IN UINT AddressCount,
+ IN CHAR Addresses[][ETH_LENGTH_OF_ADDRESS]
+ )
+
+/*++
+
+Routine Description:
+
+ Modifies the Receive Control Register and Cam Enable registers,
+ then re-loads the CAM if necessary.
+
+Arguments:
+
+ Adapter - The adapter.
+
+ AddressCount - The number of addresses in Addresses
+
+ Addresses - The new multicast address list.
+
+Return Value:
+
+ NDIS_STATUS_PENDING - if the CAM was reloaded.
+ NDIS_STATUS_SUCCESS - otherwise.
+
+--*/
+
+{
+
+ ULONG EnableBit;
+ NDIS_STATUS Status;
+ UINT i;
+
+ //
+ // The first entry in the CAM is for our address.
+ //
+
+ Adapter->MulticastCamEnableBits = 1;
+ EnableBit = 1;
+
+ //
+ // Loop through, copying the addresses into the CAM.
+ //
+
+ for (i=0; i<AddressCount; i++) {
+
+ EnableBit <<= 1;
+ Adapter->MulticastCamEnableBits |= EnableBit;
+
+ SONIC_LOAD_CAM_FRAGMENT(
+ &Adapter->CamDescriptorArea->CamFragments[i+1],
+ i+1,
+ Addresses[i]
+ );
+
+ }
+
+ Adapter->CamDescriptorAreaSize = AddressCount + 1;
+
+ //
+ // Now see if we have to worry about re-loading the
+ // CAM also.
+ //
+
+ if (CAM_MULTICAST_SIGNIFICANT(Adapter->CurrentPacketFilter)) {
+
+ Adapter->CamDescriptorArea->CamEnable = Adapter->MulticastCamEnableBits;
+
+ //
+ // This will cause a LOAD_CAM interrupt when it is done.
+ //
+
+ SonicStartCamReload(Adapter);
+
+#if DBG
+ if (SonicDbg) {
+ DbgPrint("Processing Address request pended\n");
+ }
+#endif
+
+
+ Status = NDIS_STATUS_PENDING;
+
+ } else {
+
+#if DBG
+ if (SonicDbg) {
+ DbgPrint("Processing Address request succeeded\n");
+ }
+#endif
+
+ Status = NDIS_STATUS_SUCCESS;
+
+ }
+
+ return Status;
+
+}
+