windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/atkndis.c
2020-09-26 16:20:57 +08:00

3190 lines
76 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
atkndis.c
Abstract:
This module contains the support code for the stack dealing with
the NDIS 3.0 interface. The NDIS Init/Deinit and the ndis-protocol
interface code.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
19 Jun 1992 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM ATKNDIS
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, AtalkNdisInitRegisterProtocol)
#pragma alloc_text(INIT, atalkNdisInitInitializeResources)
#pragma alloc_text(PAGEINIT, AtalkNdisInitBind)
#pragma alloc_text(PAGEINIT, AtalkInitNdisQueryAddrInfo)
#pragma alloc_text(PAGEINIT, AtalkInitNdisSetLookaheadSize)
#pragma alloc_text(PAGEINIT, AtalkInitNdisStartPacketReception)
#pragma alloc_text(PAGEINIT, AtalkBindAdapter)
#pragma alloc_text(PAGEINIT, AtalkUnbindAdapter)
#endif
ATALK_ERROR
AtalkNdisInitRegisterProtocol(
VOID
)
/*++
Routine Description:
This routine is called during initialization time to register the protocol
with NDIS.
Arguments:
NameString- The name to be registered for this protocol- human-readable form
Return Value:
Status - TRUE if register went ok, FALSE otherwise.
--*/
{
NDIS_STATUS ndisStatus;
UNICODE_STRING RegName;
NDIS_PROTOCOL_CHARACTERISTICS protocolInfo;
RtlZeroMemory(&protocolInfo, sizeof(protocolInfo));
RtlInitUnicodeString(&RegName, PROTOCOL_REGISTER_NAME);
// Set up the characteristics for the protocol for registering with NDIS
protocolInfo.MajorNdisVersion = PROTOCOL_MAJORNDIS_VERSION;
protocolInfo.MinorNdisVersion = PROTOCOL_MINORNDIS_VERSION;
protocolInfo.Name.Length = RegName.Length;
protocolInfo.Name.Buffer = (PVOID)RegName.Buffer;
protocolInfo.OpenAdapterCompleteHandler = AtalkOpenAdapterComplete;
protocolInfo.CloseAdapterCompleteHandler = AtalkCloseAdapterComplete;
protocolInfo.ResetCompleteHandler = AtalkResetComplete;
protocolInfo.RequestCompleteHandler = AtalkRequestComplete;
protocolInfo.SendCompleteHandler = AtalkSendComplete;
protocolInfo.TransferDataCompleteHandler = AtalkTransferDataComplete;
protocolInfo.ReceiveHandler = AtalkReceiveIndication;
protocolInfo.ReceiveCompleteHandler = AtalkReceiveComplete;
protocolInfo.StatusHandler = AtalkStatusIndication;
protocolInfo.StatusCompleteHandler = AtalkStatusComplete;
protocolInfo.BindAdapterHandler = AtalkBindAdapter;
protocolInfo.UnbindAdapterHandler = AtalkUnbindAdapter;
protocolInfo.PnPEventHandler = AtalkPnPHandler;
ndisStatus = atalkNdisInitInitializeResources();
if (ndisStatus == NDIS_STATUS_SUCCESS)
{
NdisRegisterProtocol(&ndisStatus,
&AtalkNdisProtocolHandle,
&protocolInfo,
(UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS)+RegName.Length);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERROR(EVENT_ATALK_REGISTERPROTOCOL, ndisStatus, NULL, 0);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkNdisRegister: failed %ul\n", ndisStatus));
}
}
return AtalkNdisToAtalkError(ndisStatus);
}
VOID
AtalkNdisDeregisterProtocol(
VOID
)
/*++
Routine Description:
This routine is called to deregister the protocol
Arguments:
NONE
Return Value:
NONE
--*/
{
NDIS_STATUS ndisStatus;
if (AtalkNdisProtocolHandle != (NDIS_HANDLE)NULL)
{
NdisDeregisterProtocol(&ndisStatus, AtalkNdisProtocolHandle);
AtalkNdisProtocolHandle = (NDIS_HANDLE)NULL;
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERROR(EVENT_ATALK_DEREGISTERPROTOCOL, ndisStatus, NULL, 0);
}
}
else
{
ASSERTMSG("AtalkNdisDeregisterProtocol: NULL ProtocolHandle\n", FALSE);
}
}
LOCAL NDIS_STATUS
atalkNdisInitInitializeResources(
VOID
)
/*++
Routine Description:
Arguments:
Return Value:
Status - STATUS_SUCCESS if all resources were allocated
STATUS_INSUFFICIENT_RESOURCES otherwise.
--*/
{
NDIS_STATUS ndisStatus;
LONG numPktDescs, numBufDescs;
numPktDescs = NUM_PACKET_DESCRIPTORS;
if (AtalkRouter)
{
numPktDescs *= ROUTING_FACTOR;
}
numBufDescs = NUM_BUFFER_DESCRIPTORS;
if (AtalkRouter)
{
numBufDescs *= ROUTING_FACTOR;
}
do
{
// Setup the ndis packet descriptor pools in the Port descriptor
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("atalkNdisInitInitializeResources: Allocating %ld Packets\n",
numPktDescs));
AtalkNdisPacketPoolHandle = (PVOID)NDIS_PACKET_POOL_TAG_FOR_APPLETALK;
NdisAllocatePacketPoolEx(&ndisStatus,
&AtalkNdisPacketPoolHandle,
numPktDescs,
numPktDescs*200, // Overflow descriptors
sizeof(PROTOCOL_RESD));
if ((ndisStatus != NDIS_STATUS_SUCCESS) && (ndisStatus != NDIS_STATUS_PENDING))
{
break;
}
// Setup the ndis buffer descriptor pool
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("atalkNdisInitInitializeResources: Allocating %ld buffers\n",
numBufDescs));
NdisAllocateBufferPool(&ndisStatus,
&AtalkNdisBufferPoolHandle,
numBufDescs);
if ((ndisStatus != NDIS_STATUS_SUCCESS) && (ndisStatus != NDIS_STATUS_PENDING))
{
NdisFreePacketPool(AtalkNdisPacketPoolHandle);
AtalkNdisPacketPoolHandle = NULL;
}
} while (FALSE);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERROR(EVENT_ATALK_NDISRESOURCES, ndisStatus, NULL, 0);
}
return ndisStatus;
}
NDIS_STATUS
AtalkNdisInitBind(
IN OUT PPORT_DESCRIPTOR pPortDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NDIS_STATUS ndisStatus, openStatus, queryStatus;
ATALK_ERROR error;
UINT selectedMediumIndex;
NDIS_STRING FriendlyName;
// reference the port for bind
AtalkPortReferenceByPtr(pPortDesc, &error);
if (error != ATALK_NO_ERROR)
{
return(STATUS_UNSUCCESSFUL);
}
// Reset event before possible wait
KeClearEvent(&pPortDesc->pd_RequestEvent);
NdisOpenAdapter(&ndisStatus, // open status
&openStatus, // more info not used
&pPortDesc->pd_NdisBindingHandle,
&selectedMediumIndex,
AtalkSupportedMedia,
AtalkSupportedMediaSize,
AtalkNdisProtocolHandle,
(NDIS_HANDLE)pPortDesc,
(PNDIS_STRING)&pPortDesc->pd_AdapterName,
0, // Open options
NULL); // Addressing information
if (ndisStatus == NDIS_STATUS_PENDING)
{
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_WARN,
("AtalkNdisInitBind: OpenAdapter is pending for %Z\n",
&pPortDesc->pd_AdapterKey));
// Make sure we are not at or above dispatch level
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
// Wait on event, completion routine will set NdisRequestEvent
// Use wrappers
KeWaitForSingleObject(&pPortDesc->pd_RequestEvent,
Executive,
KernelMode,
FALSE,
NULL);
ndisStatus = pPortDesc->pd_RequestStatus;
}
if (ndisStatus == NDIS_STATUS_SUCCESS)
{
PPORT_HANDLERS pPortHandler;
pPortDesc->pd_Flags |= PD_BOUND;
pPortDesc->pd_NdisPortType = AtalkSupportedMedia[selectedMediumIndex];
pPortDesc->pd_PortType = GET_PORT_TYPE(pPortDesc->pd_NdisPortType);
if (pPortDesc->pd_PortType != ALAP_PORT)
{
pPortDesc->pd_Flags |= PD_EXT_NET;
}
else if (pPortDesc->pd_Flags & PD_SEED_ROUTER)
{
pPortDesc->pd_InitialNetworkRange.anr_LastNetwork =
pPortDesc->pd_InitialNetworkRange.anr_FirstNetwork;
}
// is this a RAS port?
if (pPortDesc->pd_NdisPortType == NdisMediumWan)
{
pPortDesc->pd_Flags |= PD_RAS_PORT;
RasPortDesc = pPortDesc;
}
// Set stuff from the PortHandler structure to the port descriptor
pPortHandler = &AtalkPortHandlers[pPortDesc->pd_PortType];
pPortDesc->pd_AddMulticastAddr = pPortHandler->ph_AddMulticastAddr;
pPortDesc->pd_RemoveMulticastAddr = pPortHandler->ph_RemoveMulticastAddr;
pPortDesc->pd_AarpProtocolType = pPortHandler->ph_AarpProtocolType;
pPortDesc->pd_AarpHardwareType = pPortHandler->ph_AarpHardwareType;
pPortDesc->pd_BroadcastAddrLen = pPortHandler->ph_BroadcastAddrLen;
RtlCopyMemory(pPortDesc->pd_BroadcastAddr,
pPortHandler->ph_BroadcastAddr,
pPortHandler->ph_BroadcastAddrLen);
FriendlyName.MaximumLength = FriendlyName.Length = 0;
FriendlyName.Buffer = NULL;
queryStatus = NdisQueryAdapterInstanceName(&FriendlyName,
pPortDesc->pd_NdisBindingHandle);
if (queryStatus == NDIS_STATUS_SUCCESS)
{
ASSERT((FriendlyName.Buffer != NULL) && (FriendlyName.Length > 0));
pPortDesc->pd_FriendlyAdapterName.Buffer =
AtalkAllocZeroedMemory(FriendlyName.Length + sizeof(WCHAR));
if (pPortDesc->pd_FriendlyAdapterName.Buffer != NULL)
{
pPortDesc->pd_FriendlyAdapterName.MaximumLength =
FriendlyName.MaximumLength;
pPortDesc->pd_FriendlyAdapterName.Length = FriendlyName.Length;
RtlCopyMemory(pPortDesc->pd_FriendlyAdapterName.Buffer,
FriendlyName.Buffer,
FriendlyName.Length);
}
NdisFreeString(FriendlyName);
}
}
else
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_OPENADAPTER,
ndisStatus,
NULL,
0);
AtalkPortDereference(pPortDesc);
}
return ndisStatus;
}
VOID
AtalkNdisUnbind(
IN PPORT_DESCRIPTOR pPortDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NDIS_STATUS ndisStatus;
KIRQL OldIrql;
// Reset event before possible wait
KeClearEvent(&pPortDesc->pd_RequestEvent);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
NdisCloseAdapter(&ndisStatus, pPortDesc->pd_NdisBindingHandle);
if (ndisStatus == NDIS_STATUS_PENDING)
{
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_WARN,
("AtalkNdisUnbind: pending for close!\n"));
// Make sure we are not at or above dispatch level
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
// Wait on event, completion routine will set NdisRequestEvent
KeWaitForSingleObject(&pPortDesc->pd_RequestEvent,
Executive,
KernelMode,
FALSE,
NULL);
ndisStatus = pPortDesc->pd_RequestStatus;
}
if (ndisStatus == NDIS_STATUS_SUCCESS)
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkNdisUnbind: CloseAdapter on %Z completed successfully\n",
((pPortDesc->pd_FriendlyAdapterName.Buffer) ?
(&pPortDesc->pd_FriendlyAdapterName) : (&pPortDesc->pd_AdapterName))
));
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
pPortDesc->pd_Flags &= ~PD_BOUND;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// Remove the reference added at bind time
AtalkPortDereference(pPortDesc);
}
else
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkNdisUnbind: CloseAdapter on %Z failed %lx\n",
((pPortDesc->pd_FriendlyAdapterName.Buffer) ?
(&pPortDesc->pd_FriendlyAdapterName) : (&pPortDesc->pd_AdapterName)),
ndisStatus
));
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_CLOSEADAPTER,
ndisStatus,
NULL,
0);
}
}
VOID
AtalkNdisReleaseResources(
VOID
)
/*++
Routine Description:
Arguments:
Return Value:
None
--*/
{
if (AtalkNdisPacketPoolHandle != NULL)
{
NdisFreePacketPool(AtalkNdisPacketPoolHandle);
AtalkNdisPacketPoolHandle = NULL;
}
if (AtalkNdisBufferPoolHandle)
{
NdisFreeBufferPool(AtalkNdisBufferPoolHandle);
AtalkNdisBufferPoolHandle = NULL;
}
}
ATALK_ERROR
AtalkInitNdisQueryAddrInfo(
IN PPORT_DESCRIPTOR pPortDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NDIS_OID ndisOid;
ULONG macOptions;
PBYTE address;
UINT addressLength;
// We assume a single thread/init time behavior
NDIS_REQUEST request;
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
do
{
// Check to see it we bound successfully to this adapter
if (!PORT_BOUND(pPortDesc))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NOTBOUNDTOMAC,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
switch (pPortDesc->pd_NdisPortType)
{
case NdisMedium802_3 :
ndisOid = OID_802_3_CURRENT_ADDRESS;
address = &pPortDesc->pd_PortAddr[0];
addressLength = MAX_HW_ADDR_LEN;
break;
case NdisMediumFddi :
ndisOid = OID_FDDI_LONG_CURRENT_ADDR;
address = &pPortDesc->pd_PortAddr[0];
addressLength = MAX_HW_ADDR_LEN;
break;
case NdisMedium802_5:
ndisOid = OID_802_5_CURRENT_ADDRESS;
address = &pPortDesc->pd_PortAddr[0];
addressLength = MAX_HW_ADDR_LEN;
break;
case NdisMediumLocalTalk :
ndisOid = OID_LTALK_CURRENT_NODE_ID;
address = (PBYTE)&pPortDesc->pd_AlapNode;
addressLength = sizeof(pPortDesc->pd_AlapNode);
break;
case NdisMediumWan:
ndisOid = OID_WAN_CURRENT_ADDRESS;
// NOTE: the following two fields not relevant for RAS
address = &pPortDesc->pd_PortAddr[0];
addressLength = MAX_HW_ADDR_LEN;
break;
default:
KeBugCheck(0);
break;
}
// Setup request
request.RequestType = NdisRequestQueryInformation;
request.DATA.QUERY_INFORMATION.Oid = ndisOid;
request.DATA.QUERY_INFORMATION.InformationBuffer = address;
request.DATA.QUERY_INFORMATION.InformationBufferLength = addressLength;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
TRUE,
NULL,
NULL);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_STATIONADDRESS,
ndisStatus,
NULL,
0);
}
// Setup request to get the mac options information
request.RequestType = NdisRequestQueryInformation;
request.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS;
request.DATA.QUERY_INFORMATION.InformationBuffer = &macOptions;
request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(ULONG);
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
TRUE,
NULL,
NULL);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
// No mac options.
ndisStatus = NDIS_STATUS_SUCCESS;
macOptions = 0;
}
pPortDesc->pd_MacOptions = macOptions;
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("AtalkNdisQueryAddrInfo: MacOptions %lx\n", macOptions));
} while (FALSE);
return AtalkNdisToAtalkError(ndisStatus);
}
ATALK_ERROR
AtalkInitNdisSetLookaheadSize(
IN PPORT_DESCRIPTOR pPortDesc,
IN INT LookaheadSize // Has to be INT
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NDIS_REQUEST request;
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
do
{
// Check to see it we bound successfully to this adapter
if (!PORT_BOUND(pPortDesc))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NOTBOUNDTOMAC,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
// Setup request
request.RequestType = NdisRequestSetInformation;
request.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD;
request.DATA.SET_INFORMATION.InformationBuffer = (PBYTE)&LookaheadSize;
request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(LookaheadSize);
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
TRUE,
NULL,
NULL);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_LOOKAHEADSIZE,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
}
} while (FALSE);
return AtalkNdisToAtalkError(ndisStatus);
}
ATALK_ERROR
AtalkInitNdisStartPacketReception(
IN PPORT_DESCRIPTOR pPortDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NDIS_REQUEST request;
ULONG packetFilter;
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
KIRQL OldIrql;
do
{
// Check to see it we bound successfully to this adapter
if (!PORT_BOUND(pPortDesc))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NOTBOUNDTOMAC,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
switch (pPortDesc->pd_NdisPortType)
{
case NdisMedium802_3 :
case NdisMediumFddi :
packetFilter = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST;
break;
case NdisMedium802_5:
packetFilter = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_FUNCTIONAL;
break;
case NdisMediumLocalTalk :
packetFilter = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST;
break;
case NdisMediumWan:
packetFilter = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST;
break;
default:
KeBugCheck(0);
break;
}
// Setup request
request.RequestType = NdisRequestSetInformation;
request.DATA.SET_INFORMATION.Oid =OID_GEN_CURRENT_PACKET_FILTER;
request.DATA.SET_INFORMATION.InformationBuffer = (PBYTE)&packetFilter;
request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(packetFilter);
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
TRUE,
NULL,
NULL);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_PACKETFILTER,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
}
} while (FALSE);
return AtalkNdisToAtalkError(ndisStatus);
}
NDIS_STATUS
AtalkNdisSubmitRequest(
PPORT_DESCRIPTOR pPortDesc,
PNDIS_REQUEST Request,
BOOLEAN ExecuteSync,
REQ_COMPLETION CompletionRoutine,
PVOID Ctx
)
/*++
Routine Description:
Arguments:
Return Value:
None
--*/
{
NDIS_STATUS ndisStatus;
PATALK_NDIS_REQ atalkNdisRequest;
// Allocate an atalk request packet
if ((atalkNdisRequest = AtalkAllocMemory(sizeof(ATALK_NDIS_REQ))) == NULL)
{
return NDIS_STATUS_RESOURCES;
}
atalkNdisRequest->nr_Request = *Request;
atalkNdisRequest->nr_Sync = ExecuteSync;
atalkNdisRequest->nr_RequestCompletion = CompletionRoutine;
atalkNdisRequest->nr_Ctx = Ctx;
if (ExecuteSync)
{
// Make sure we are not at or above dispatch level
// Also assert that the completion routine is NULL
ASSERT(KeGetCurrentIrql() == LOW_LEVEL);
ASSERT(CompletionRoutine == NULL);
// Initialize event to not signalled before possible wait.
KeInitializeEvent(&atalkNdisRequest->nr_Event,
NotificationEvent,
FALSE);
}
NdisRequest(&ndisStatus,
pPortDesc->pd_NdisBindingHandle,
&atalkNdisRequest->nr_Request);
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("atalkNdisSubmitRequest: status NdisRequest %lx\n", ndisStatus));
if (ndisStatus == NDIS_STATUS_PENDING)
{
if (ExecuteSync)
{
KeWaitForSingleObject(&atalkNdisRequest->nr_Event,
Executive,
KernelMode,
FALSE,
NULL);
ndisStatus = atalkNdisRequest->nr_RequestStatus;
AtalkFreeMemory((PVOID)atalkNdisRequest);
}
}
else if (ndisStatus == NDIS_STATUS_SUCCESS)
{
// Ndis will not call the completion routine.
if (!ExecuteSync)
{
// Call the users completion routine if specified.
if (CompletionRoutine != NULL)
{
(*CompletionRoutine)(NDIS_STATUS_SUCCESS, Ctx);
}
}
AtalkFreeMemory((PVOID)atalkNdisRequest);
}
else
{
// There was an error. Just free up the atalk ndis request.
AtalkFreeMemory((PVOID)atalkNdisRequest);
}
return ndisStatus;
}
// Protocol/NDIS interaction code
VOID
AtalkOpenAdapterComplete(
IN NDIS_HANDLE NdisBindCtx,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorStatus
)
/*++
Routine Description:
This routine is called during by NDIS to indicate that an open adapter
is complete. This happens only during initialization and single-file. Clear
the event, so the blocked init thread can go on to the next adapter. Set the
status in the ndis port descriptor for this adapter.
Arguments:
NdisBindCtx- Pointer to a port descriptor for this port
Status- completion status of open adapter
OpenErrorStatus- Extra status information
Return Value:
None
--*/
{
PPORT_DESCRIPTOR pPortDesc = (PPORT_DESCRIPTOR)NdisBindCtx;
pPortDesc->pd_RequestStatus = Status;
KeSetEvent(&pPortDesc->pd_RequestEvent, IO_NETWORK_INCREMENT, FALSE);
}
VOID
AtalkCloseAdapterComplete(
IN NDIS_HANDLE NdisBindCtx,
IN NDIS_STATUS Status
)
/*++
Routine Description:
This routine is called by NDIS to indicate that a close adapter is complete.
Arguments:
NdisBindCtx- Pointer to a port descriptor for this port
Status- completion status of close adapter
Return Value:
None
--*/
{
PPORT_DESCRIPTOR pPortDesc = (PPORT_DESCRIPTOR)NdisBindCtx;
pPortDesc->pd_RequestStatus = Status;
KeSetEvent(&pPortDesc->pd_RequestEvent, IO_NETWORK_INCREMENT, FALSE);
}
VOID
AtalkResetComplete(
IN NDIS_HANDLE NdisBindCtx,
IN NDIS_STATUS Status
)
/*++
Routine Description:
This routine is called by NDIS to indicate that a reset is complete.
Arguments:
NdisBindCtx- Pointer to a port descriptor for this port
Status- completion status of close adapter
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(NdisBindCtx);
}
VOID
AtalkRequestComplete(
IN NDIS_HANDLE NdisBindCtx,
IN PNDIS_REQUEST NdisRequest,
IN NDIS_STATUS Status
)
/*++
Routine Description:
This routine is called by NDIS to indicate that a NdisRequest is complete.
Arguments:
NdisBindCtx- Pointer to a port descriptor for this port
NdisRequest- Block identifying the request
Status- completion status of close adapter
Return Value:
None
--*/
{
PATALK_NDIS_REQ atalkRequest;
// Get the AtalkRequest block
atalkRequest = CONTAINING_RECORD(NdisRequest, ATALK_NDIS_REQ, nr_Request);
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("AtalkRequestComplete: %lx status %lx\n", atalkRequest, Status));
if (atalkRequest->nr_Sync)
{
// This was a sync request
// Set status and clear event
ASSERT(atalkRequest->nr_RequestCompletion == NULL);
atalkRequest->nr_RequestStatus = Status;
KeSetEvent(&atalkRequest->nr_Event, IO_NETWORK_INCREMENT, FALSE);
}
// Call the completion routine if specified
if (atalkRequest->nr_RequestCompletion != NULL)
{
(*atalkRequest->nr_RequestCompletion)(Status, atalkRequest->nr_Ctx);
}
if (!atalkRequest->nr_Sync)
AtalkFreeMemory(atalkRequest);
}
VOID
AtalkStatusIndication(
IN NDIS_HANDLE NdisBindCtx,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuf,
IN UINT StatusBufLen
)
/*++
Routine Description:
This routine is called by NDIS to indicate a status change.
Arguments:
NdisBindCtx- Pointer to a port descriptor for this port
GeneralStatus- A general status value
StatusBuffer - A more specific status value
Return Value:
None
--*/
{
PPORT_DESCRIPTOR pPortDesc;
pPortDesc = (PPORT_DESCRIPTOR)NdisBindCtx;
// line-up, line-down or stat request from ndiswan? deal with it!
if (pPortDesc == RasPortDesc)
{
RasStatusIndication(GeneralStatus, StatusBuf, StatusBufLen);
}
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_ERR,
("AtalkStatusIndication: Status indication called %lx\n", GeneralStatus));
}
VOID
AtalkStatusComplete (
IN NDIS_HANDLE ProtoBindCtx
)
/*++
Routine Description:
This routine is called by NDIS to allow postprocessing after a status event.
Arguments:
ProtoBindCtx- Value associated with the binding with the adapter
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(ProtoBindCtx);
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_WARN,
("AtalkStatusComplete: Status complete called\n"));
}
typedef struct
{
REQ_COMPLETION AddCompletion;
PVOID AddContext;
PBYTE Buffer;
} ADDMC, *PADDMC;
LOCAL VOID
atalkNdisAddMulticastCompletion(
IN NDIS_STATUS Status,
IN PADDMC pAmc
)
{
if (pAmc->Buffer != NULL)
AtalkFreeMemory(pAmc->Buffer);
if (pAmc->AddCompletion != NULL)
(*pAmc->AddCompletion)(Status, pAmc->AddContext);
AtalkFreeMemory(pAmc);
}
ATALK_ERROR
AtalkNdisAddMulticast(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBYTE Address,
IN BOOLEAN ExecuteSynchronously,
IN REQ_COMPLETION AddCompletion,
IN PVOID AddContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
INT sizeOfList;
PBYTE addressData, tempList;
KIRQL OldIrql;
NDIS_OID ndisOid;
NDIS_REQUEST request;
NDIS_STATUS ndisStatus;
PADDMC pAmc;
// Check to see it we bound successfully to this adapter
if (!PORT_BOUND(pPortDesc))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NOTBOUNDTOMAC,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
return ATALK_FAILURE;
}
// Grab the perport spinlock. We need to allocate within a
// critical section as the size might change. This routine
// is called very infrequently, during init and when we
// receive our default zone from zip.
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
sizeOfList = pPortDesc->pd_MulticastListSize + ELAP_ADDR_LEN;
ASSERTMSG("AtalkNdisAddMulticast: Size is not > 0\n", (sizeOfList > 0));
// Allocate/reallocate the list for the port descriptor, and also
// for a copy to be used in the NDIS request function.
tempList = (PBYTE)AtalkAllocZeroedMemory(sizeOfList);
addressData = (PBYTE)AtalkAllocZeroedMemory(sizeOfList);
pAmc = (PADDMC)AtalkAllocZeroedMemory(sizeof(ADDMC));
if ((tempList == NULL) || (addressData == NULL) || (pAmc == NULL))
{
// Release the spinlock
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
if (pAmc != NULL)
AtalkFreeMemory(pAmc);
if (tempList != NULL)
AtalkFreeMemory(tempList);
if (addressData != NULL)
AtalkFreeMemory(addressData);
return ATALK_RESR_MEM;
}
if (pPortDesc->pd_MulticastList == NULL)
{
// No old addresses to work with.
pPortDesc->pd_MulticastListSize = 0;
}
else
{
// Copy the old list over to the new space
RtlCopyMemory(tempList,
pPortDesc->pd_MulticastList,
pPortDesc->pd_MulticastListSize);
// Set the proper values back into PortDesc after freeing the old list
AtalkFreeMemory(pPortDesc->pd_MulticastList);
}
// Guaranteed space is available to copy the new address
// Ready to copy our new address here and then do the set!
RtlCopyMemory(tempList + pPortDesc->pd_MulticastListSize,
Address,
ELAP_ADDR_LEN);
pPortDesc->pd_MulticastList = tempList;
pPortDesc->pd_MulticastListSize = sizeOfList;
switch (pPortDesc->pd_NdisPortType)
{
case NdisMedium802_3 :
ndisOid = OID_802_3_MULTICAST_LIST;
break;
case NdisMediumFddi:
// FDDI supports 2byte and 6byte multicast addresses. We use the
// 6byte multicast addresses for appletalk.
ndisOid = OID_FDDI_LONG_MULTICAST_LIST;
break;
default:
KeBugCheck(0);
break;
}
// Setup request
// Move the list to our buffer
ASSERTMSG("AtalkNdisAddMulticast: Size incorrect!\n",
((ULONG)sizeOfList == pPortDesc->pd_MulticastListSize));
RtlCopyMemory(addressData,
pPortDesc->pd_MulticastList,
pPortDesc->pd_MulticastListSize);
// Release the spinlock
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
request.RequestType = NdisRequestSetInformation;
request.DATA.SET_INFORMATION.Oid = ndisOid;
request.DATA.SET_INFORMATION.InformationBuffer = addressData;
request.DATA.SET_INFORMATION.InformationBufferLength = sizeOfList;
pAmc->AddCompletion = AddCompletion;
pAmc->AddContext = AddContext;
pAmc->Buffer = addressData;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
FALSE,
atalkNdisAddMulticastCompletion,
pAmc);
// NOTE: Sumbit calls completion if success is being returned.
if ((ndisStatus != NDIS_STATUS_SUCCESS) &&
(ndisStatus != NDIS_STATUS_PENDING))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NDISREQUEST,
ndisStatus,
NULL,
0);
}
return AtalkNdisToAtalkError(ndisStatus);
}
ATALK_ERROR
AtalkNdisRemoveMulticast(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBYTE Address,
IN BOOLEAN ExecuteSynchronously,
IN REQ_COMPLETION RemoveCompletion,
IN PVOID RemoveContext
)
{
INT sizeOfList, i, numberInList;
PBYTE addressData, currentList;
KIRQL OldIrql;
NDIS_REQUEST request;
NDIS_OID ndisOid;
NDIS_STATUS ndisStatus;
PADDMC pAmc;
do
{
// Check to see it we bound successfully to this adapter
if (!PORT_BOUND(pPortDesc))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NOTBOUNDTOMAC,
STATUS_INSUFFICIENT_RESOURCES,
NULL,
0);
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
// Grab the perport spinlock. Again, a very infrequent operation.
// Probably just twice in the lifetime of the stack.
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
ASSERT(pPortDesc->pd_MulticastList != NULL);
if (pPortDesc->pd_MulticastList == NULL)
{
// Nothing to remove!
ndisStatus = NDIS_STATUS_SUCCESS;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
break;
}
numberInList = pPortDesc->pd_MulticastListSize/ELAP_ADDR_LEN;
currentList = pPortDesc->pd_MulticastList;
for (i = 0; i < numberInList; i++, currentList += ELAP_ADDR_LEN)
{
// Search for the address and remember the index if found
if (RtlCompareMemory(currentList,
Address,
ELAP_ADDR_LEN) == ELAP_ADDR_LEN)
{
// Move all address following this overwriting this address
// we ignore wasted space that will never be touched anymore.
// This could turn out to be a NOP if we are removing the last
// address in the list.
RtlMoveMemory(currentList,
currentList + ELAP_ADDR_LEN,
pPortDesc->pd_MulticastListSize-((i+1)*ELAP_ADDR_LEN));
pPortDesc->pd_MulticastListSize -= ELAP_ADDR_LEN;
// Have we removed the last address. If so, reset values.
if (pPortDesc->pd_MulticastListSize == 0)
{
AtalkFreeMemory(pPortDesc->pd_MulticastList);
pPortDesc->pd_MulticastList = NULL;
}
break;
}
}
// We assume address was found and the list is changed as expected
// Set this new list
switch (pPortDesc->pd_NdisPortType)
{
case NdisMedium802_3 :
ndisOid = OID_802_3_MULTICAST_LIST;
break;
case NdisMediumFddi:
// FDDI supports 2byte and 6byte multicast addresses. We use the
// 6byte multicast addresses for appletalk.
ndisOid = OID_FDDI_LONG_MULTICAST_LIST;
break;
default:
KeBugCheck(0);
break;
}
addressData = NULL;
sizeOfList = pPortDesc->pd_MulticastListSize;
pAmc = (PADDMC)AtalkAllocZeroedMemory(sizeof(ADDMC));
if (pAmc == NULL)
{
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
if (sizeOfList > 0)
{
// Allocate addressData and copy list to it
addressData = (PBYTE)AtalkAllocMemory(sizeOfList);
if (addressData == NULL)
{
// Release the spinlock
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
AtalkFreeMemory(pAmc);
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
// Move the list to our buffer
RtlCopyMemory(addressData,
pPortDesc->pd_MulticastList,
pPortDesc->pd_MulticastListSize);
}
// Release the spinlock
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
request.RequestType = NdisRequestSetInformation;
request.DATA.SET_INFORMATION.Oid = ndisOid;
request.DATA.SET_INFORMATION.InformationBuffer = addressData;
request.DATA.SET_INFORMATION.InformationBufferLength = sizeOfList;
pAmc->AddCompletion = RemoveCompletion;
pAmc->AddContext = RemoveContext;
pAmc->Buffer = addressData;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
FALSE,
atalkNdisAddMulticastCompletion,
pAmc);
if ((ndisStatus != NDIS_STATUS_SUCCESS) &&
(ndisStatus != NDIS_STATUS_PENDING))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NDISREQUEST,
ndisStatus,
NULL,
0);
}
} while (FALSE);
return AtalkNdisToAtalkError(ndisStatus);
}
ATALK_ERROR
AtalkNdisSendPacket(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBUFFER_DESC BufferChain,
IN SEND_COMPLETION SendCompletion OPTIONAL,
IN PSEND_COMPL_INFO pSendInfo OPTIONAL
)
/*++
Routine Description:
This routine is called by the portable code to send a packet out on
ethernet. It will build the NDIS packet descriptor for the passed in
chain and then send the packet on the specified port.
Arguments:
Return Value:
TRUE- If sent/pending, FALSE otherwise
TransmitComplete is called if this call pended by completion code
--*/
{
PNDIS_PACKET ndisPacket;
PNDIS_BUFFER ndisBuffer;
PPROTOCOL_RESD protocolResd;
ATALK_ERROR error;
PSENDBUF pSendBuf;
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
PMDL pMdl;
PMDL pFirstMdl=NULL;
if (PORT_CLOSING(pPortDesc))
{
// If we are not active, return!
return ATALK_PORT_CLOSING;
}
do
{
pSendBuf = (PSENDBUF)((PBYTE)BufferChain - sizeof(BUFFER_HDR));
ndisPacket = pSendBuf->sb_BuffHdr.bh_NdisPkt;
// Store the information needed in the packet descriptor
protocolResd = (PPROTOCOL_RESD)&ndisPacket->ProtocolReserved;
protocolResd->Send.pr_Port = pPortDesc;
protocolResd->Send.pr_BufferDesc = BufferChain;
protocolResd->Send.pr_SendCompletion = SendCompletion;
if (pSendInfo != NULL)
protocolResd->Send.pr_SendInfo = *pSendInfo;
else RtlZeroMemory(&protocolResd->Send.pr_SendInfo, sizeof(SEND_COMPL_INFO));
// For the first buffer, set up the length of the NDIS buffer to be
// the same as in indicated in the descriptor.
NdisAdjustBufferLength(pSendBuf->sb_BuffHdr.bh_NdisBuffer,
BufferChain->bd_Length);
// NOTE: There is either a PBYTE being pointed to, or a PAMDL
// being pointed to by the buffer descriptor. Also, the
// size of the data will be the size that is to be
// used. At the end, just assert that the total length
// equals length passed in.
if (BufferChain->bd_Next != NULL)
{
if (BufferChain->bd_Next->bd_Flags & BD_CHAR_BUFFER)
{
NdisAllocateBuffer(&ndisStatus,
&ndisBuffer,
AtalkNdisBufferPoolHandle,
(PVOID)BufferChain->bd_Next->bd_CharBuffer,
(UINT)BufferChain->bd_Next->bd_Length);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(DBG_COMP_NDISSEND, DBG_LEVEL_ERR,
("AtalkNdisSendPacket: NdisAllocateBuffer %lx\n", ndisStatus));
// LOG_ERROR(EVENT_ATALK_NDISRESOURCES, ndisStatus, NULL, 0);
break;
}
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced);
NdisChainBufferAtBack(ndisPacket, ndisBuffer);
}
else
{
// It is an MDL
pMdl = (PMDL)BufferChain->bd_Next->bd_OpaqueBuffer;
ASSERT(AtalkSizeMdlChain(pMdl) == BufferChain->bd_Next->bd_Length);
while (pMdl)
{
NdisCopyBuffer(&ndisStatus,
&ndisBuffer,
AtalkNdisBufferPoolHandle,
(PVOID)pMdl,
0, //Offset
(UINT)MmGetMdlByteCount(pMdl));
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
if (pFirstMdl)
{
AtalkNdisFreeBuffer(pFirstMdl);
}
break;
}
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced);
if (!pFirstMdl)
{
pFirstMdl = pMdl;
}
NdisChainBufferAtBack(ndisPacket, ndisBuffer);
pMdl = pMdl->Next;
}
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(DBG_COMP_NDISSEND, DBG_LEVEL_ERR,
("AtalkNdisSendPacket: NdisCopyBuffer %lx\n", ndisStatus));
break;
}
}
}
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_CurSendsOutstanding,
&AtalkStatsLock.SpinLock);
#endif
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumPacketsOut,
&AtalkStatsLock.SpinLock);
// Now send the built packet descriptor
NdisSend(&ndisStatus,
pPortDesc->pd_NdisBindingHandle,
ndisPacket);
// Completion will dereference the port!
if (ndisStatus != NDIS_STATUS_PENDING)
{
// Call the completion handler
AtalkSendComplete(pPortDesc->pd_NdisBindingHandle,
ndisPacket,
ndisStatus);
ndisStatus = NDIS_STATUS_PENDING;
}
} while (FALSE);
return AtalkNdisToAtalkError(ndisStatus);
}
ATALK_ERROR
AtalkNdisAddFunctional(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBYTE Address,
IN BOOLEAN ExecuteSynchronously,
IN REQ_COMPLETION AddCompletion,
IN PVOID AddContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG i;
NDIS_REQUEST request;
NDIS_STATUS ndisStatus;
KIRQL OldIrql;
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("Current %02x%02x%02x%02x, Adding %02x%02x%02x%02x\n",
pPortDesc->pd_FunctionalAddr[0], pPortDesc->pd_FunctionalAddr[1],
pPortDesc->pd_FunctionalAddr[2], pPortDesc->pd_FunctionalAddr[3],
Address[2], Address[3], Address[4], Address[5]));
// Grab the perport spinlock
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
// We only need the last four bytes of the address assuming that the
// first two bytes always remain the same (C000) and that the MAC assumes
// the same- NDIS 3.0 OID length = 4
for (i = 0;
i < sizeof(ULONG);
i++)
pPortDesc->pd_FunctionalAddr[i] |= Address[2+i];
// Release the spinlock
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("After Add %02x%02x%02x%02x\n",
pPortDesc->pd_FunctionalAddr[0], pPortDesc->pd_FunctionalAddr[1],
pPortDesc->pd_FunctionalAddr[2], pPortDesc->pd_FunctionalAddr[3]));
request.RequestType = NdisRequestSetInformation;
request.DATA.SET_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL;
request.DATA.SET_INFORMATION.InformationBuffer = pPortDesc->pd_FunctionalAddr;
request.DATA.SET_INFORMATION.InformationBufferLength = TLAP_ADDR_LEN - TLAP_MCAST_HDR_LEN;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
ExecuteSynchronously,
AddCompletion,
AddContext);
if (ndisStatus == NDIS_STATUS_PENDING)
{
ASSERT(ExecuteSynchronously != TRUE);
}
else if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NDISREQUEST,
ndisStatus,
NULL,
0);
}
return AtalkNdisToAtalkError(ndisStatus);
}
ATALK_ERROR
AtalkNdisRemoveFunctional(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBYTE Address,
IN BOOLEAN ExecuteSynchronously,
IN REQ_COMPLETION RemoveCompletion,
IN PVOID RemoveContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG i;
KIRQL OldIrql;
NDIS_REQUEST request;
NDIS_STATUS ndisStatus;
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("Current %02x%02x%02x%02x, Removing %02x%02x%02x%02x\n",
pPortDesc->pd_FunctionalAddr[0], pPortDesc->pd_FunctionalAddr[1],
pPortDesc->pd_FunctionalAddr[2], pPortDesc->pd_FunctionalAddr[3],
Address[2], Address[3], Address[4], Address[5]));
// Grab the perport spinlock
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
// We only need the last four bytes of the address assuming that the
// first two bytes always remain the same (C000) and that the MAC assumes
// the same- NDIS 3.0 OID length = 4
for (i = 0; i < sizeof(ULONG); i++)
pPortDesc->pd_FunctionalAddr[i] &= ~Address[2+i];
// Release the spinlock
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
DBGPRINT(DBG_COMP_NDISREQ, DBG_LEVEL_INFO,
("After Remove %02x%02x%02x%02x\n",
pPortDesc->pd_FunctionalAddr[0], pPortDesc->pd_FunctionalAddr[1],
pPortDesc->pd_FunctionalAddr[2], pPortDesc->pd_FunctionalAddr[3]));
request.RequestType = NdisRequestSetInformation;
request.DATA.SET_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL;
request.DATA.SET_INFORMATION.InformationBuffer = pPortDesc->pd_FunctionalAddr;
request.DATA.SET_INFORMATION.InformationBufferLength = TLAP_ADDR_LEN - TLAP_MCAST_HDR_LEN;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc,
&request,
ExecuteSynchronously,
RemoveCompletion,
RemoveContext);
if (ndisStatus == NDIS_STATUS_PENDING)
{
ASSERT(ExecuteSynchronously != TRUE);
}
else if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NDISREQUEST,
ndisStatus,
NULL,
0);
}
return AtalkNdisToAtalkError(ndisStatus);
}
USHORT
AtalkNdisBuildEthHdr(
IN PUCHAR PortAddr, // 802 address of port
IN PBYTE pLinkHdr, // Start of link header
IN PBYTE pDestHwOrMcastAddr, // Destination or multicast addr
IN LOGICAL_PROTOCOL Protocol, // Logical protocol
IN USHORT ActualDataLen // Length for ethernet packets
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USHORT len;
// Set destination address.
if (pDestHwOrMcastAddr == NULL)
pDestHwOrMcastAddr = AtalkElapBroadcastAddr;
RtlCopyMemory(pLinkHdr,
pDestHwOrMcastAddr,
ELAP_ADDR_LEN);
// Set source address.
RtlCopyMemory(pLinkHdr += ELAP_ADDR_LEN,
PortAddr,
ELAP_ADDR_LEN);
// Set length, excluding Ethernet hardware header.
len = ActualDataLen + IEEE8022_HDR_LEN;
pLinkHdr += ELAP_ADDR_LEN;
PUTSHORT2SHORT(pLinkHdr, len);
pLinkHdr += sizeof(USHORT);
ATALK_BUILD8022_HDR(pLinkHdr, Protocol);
// Return the link header length.
return (ELAP_LINKHDR_LEN + IEEE8022_HDR_LEN);
}
USHORT
AtalkNdisBuildTRHdr(
IN PUCHAR PortAddr, // 802 address of port
IN PBYTE pLinkHdr, // Start of link header
IN PBYTE pDestHwOrMcastAddr, // Destination or multicast addr
IN LOGICAL_PROTOCOL Protocol, // Logical protocol
IN PBYTE pRouteInfo, // Routing info for tokenring
IN USHORT RouteInfoLen // Length of above
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USHORT linkLen;
// Here we need to worry about the routing info.
// If we currently do not have any, set the values
if (pDestHwOrMcastAddr == NULL)
{
// Broadcast?
pRouteInfo = AtalkBroadcastRouteInfo;
RouteInfoLen = TLAP_MIN_ROUTING_BYTES;
}
else if (RouteInfoLen != 0)
{
// We are all set
}
else if (AtalkFixedCompareCaseSensitive(pDestHwOrMcastAddr,
TLAP_BROADCAST_DEST_LEN,
AtalkBroadcastDestHdr,
TLAP_BROADCAST_DEST_LEN))
{
// Multicast?
pRouteInfo = AtalkBroadcastRouteInfo;
RouteInfoLen = TLAP_MIN_ROUTING_BYTES;
}
else
{
// No routing know; use simple non-broadcast
pRouteInfo = AtalkSimpleRouteInfo;
RouteInfoLen = TLAP_MIN_ROUTING_BYTES;
}
linkLen = TLAP_MIN_LINKHDR_LEN + RouteInfoLen + IEEE8022_HDR_LEN;
// Set the first two bytes in the header
*pLinkHdr++ = TLAP_ACCESS_CTRL_VALUE;
*pLinkHdr++ = TLAP_FRAME_CTRL_VALUE ;
// Set detination address.
if (pDestHwOrMcastAddr == NULL)
pDestHwOrMcastAddr = AtalkTlapBroadcastAddr;
RtlCopyMemory(pLinkHdr,
pDestHwOrMcastAddr ,
TLAP_ADDR_LEN);
// Set source address.
RtlCopyMemory(pLinkHdr += TLAP_ADDR_LEN,
PortAddr,
TLAP_ADDR_LEN);
ASSERTMSG("AtalkNdisBuildTRHdr: Routing Info is 0!\n", (RouteInfoLen > 0));
*pLinkHdr |= TLAP_SRC_ROUTING_MASK;
// Move in routing info.
RtlCopyMemory(pLinkHdr += TLAP_ADDR_LEN,
pRouteInfo,
RouteInfoLen);
pLinkHdr += RouteInfoLen;
ATALK_BUILD8022_HDR(pLinkHdr, Protocol);
// Return the link header length.
return linkLen;
}
USHORT
AtalkNdisBuildFDDIHdr(
IN PUCHAR PortAddr, // 802 address of port
IN PBYTE pLinkHdr, // Start of link header
IN PBYTE pDestHwOrMcastAddr, // Destination or multicast addr
IN LOGICAL_PROTOCOL Protocol // Logical protocol
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
*pLinkHdr++ = FDDI_HEADER_BYTE;
// Set destination address.
if (pDestHwOrMcastAddr == NULL)
pDestHwOrMcastAddr = AtalkElapBroadcastAddr;
// Set destination address.
RtlCopyMemory(pLinkHdr,
pDestHwOrMcastAddr,
FDDI_ADDR_LEN);
// Set source address.
RtlCopyMemory(pLinkHdr += FDDI_ADDR_LEN,
PortAddr,
FDDI_ADDR_LEN);
pLinkHdr += FDDI_ADDR_LEN;
// NOTE: No Length field for FDDI, unlike Ethernet.
ATALK_BUILD8022_HDR(pLinkHdr, Protocol);
// Return the link header length.
return (FDDI_LINKHDR_LEN + IEEE8022_HDR_LEN);
}
USHORT
AtalkNdisBuildLTHdr(
IN PBYTE pLinkHdr, // Start of link header
IN PBYTE pDestHwOrMcastAddr, // Destination or multicast addr
IN BYTE AlapSrc, // Localtalk source node
IN BYTE AlapType // Localtalk ddp header type
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
// Fill in LAP header.
if (pDestHwOrMcastAddr == NULL)
pLinkHdr = AtalkAlapBroadcastAddr;
*pLinkHdr++ = *pDestHwOrMcastAddr;
*pLinkHdr++ = AlapSrc;
*pLinkHdr = AlapType;
// Return the link header length.
return ALAP_LINKHDR_LEN;
}
VOID
AtalkNdisSendTokRingTestResp(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBYTE HdrBuf,
IN UINT HdrBufSize,
IN PBYTE LkBuf,
IN UINT LkBufSize,
IN UINT PktSize
)
{
PBUFFER_DESC pBufDesc, pHdrDesc;
PBYTE pResp;
UINT routeInfoLen = 0;
// Allocate a buffer to hold the response and call NdisSend
// providing a completion routine which will free up the buffer.
ASSERT(PktSize == LkBufSize);
// make sure there are at least 14 bytes!
if (HdrBufSize < TLAP_ROUTE_INFO_OFFSET)
{
ASSERT(0);
return;
}
// First allocate a buffer to hold the link header.
AtalkNdisAllocBuf(&pHdrDesc);
if (pHdrDesc == NULL)
{
return;
}
if ((pBufDesc = AtalkAllocBuffDesc(NULL,
(USHORT)LkBufSize,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
AtalkNdisFreeBuf(pHdrDesc);
RES_LOG_ERROR();
return;
}
pResp = pHdrDesc->bd_CharBuffer;
*pResp++ = TLAP_ACCESS_CTRL_VALUE;
*pResp++ = TLAP_FRAME_CTRL_VALUE;
// Set destination address to be the incoming src addr.
ATALK_RECV_INDICATION_COPY(pPortDesc,
pResp,
HdrBuf+TLAP_SRC_OFFSET,
TLAP_ADDR_LEN);
// Make sure we do not have the routing bit set.
*pResp &= ~TLAP_SRC_ROUTING_MASK;
pResp += TLAP_ADDR_LEN;
// Set source address to be the incoming destination address.
ATALK_RECV_INDICATION_COPY(pPortDesc,
pResp,
HdrBuf+TLAP_DEST_OFFSET,
TLAP_ADDR_LEN);
// Is there routing info present?
if (HdrBuf[TLAP_SRC_OFFSET] & TLAP_SRC_ROUTING_MASK)
{
routeInfoLen = (HdrBuf[TLAP_ROUTE_INFO_OFFSET] & TLAP_ROUTE_INFO_SIZE_MASK);
ASSERT(routeInfoLen != 0);
ASSERTMSG("RouteInfo incorrect!\n",
(routeInfoLen <= TLAP_MAX_ROUTING_BYTES));
if (HdrBufSize < (TLAP_ROUTE_INFO_OFFSET+routeInfoLen))
{
ASSERT(0);
AtalkNdisFreeBuf(pHdrDesc);
AtalkFreeBuffDesc(pBufDesc);
return;
}
// Copy it in the response packet and then tune it.
ATALK_RECV_INDICATION_COPY(pPortDesc,
pResp + TLAP_ADDR_LEN,
HdrBuf+TLAP_ROUTE_INFO_OFFSET,
routeInfoLen);
// Set to "non-broadcast" and invert "direction".
*(pResp+TLAP_ADDR_LEN) &= TLAP_NON_BROADCAST_MASK;
*(pResp+TLAP_ADDR_LEN+1) ^= TLAP_DIRECTION_MASK;
// Set the routing info bit in the source address
*pResp |= TLAP_SRC_ROUTING_MASK;
}
// Set the length for this buffer descriptor.
AtalkSetSizeOfBuffDescData(pHdrDesc, TLAP_ROUTE_INFO_OFFSET + routeInfoLen);
// Copy the remaining data
ATALK_RECV_INDICATION_COPY(pPortDesc,
pBufDesc->bd_CharBuffer,
LkBuf,
LkBufSize);
// Set the source SAP to indicate FINAL (0xAB instead of 0xAA)
pBufDesc->bd_CharBuffer[IEEE8022_SSAP_OFFSET] = SNAP_SAP_FINAL;
// Chain the passed in buffer desc onto the tail of the one
// returned above.
AtalkPrependBuffDesc(pHdrDesc, pBufDesc);
// Call send at this point
if (!ATALK_SUCCESS(AtalkNdisSendPacket(pPortDesc,
pHdrDesc,
AtalkNdisSendTokRingTestRespComplete,
NULL)))
{
AtalkNdisSendTokRingTestRespComplete(NDIS_STATUS_RESOURCES,
pHdrDesc,
NULL);
}
}
VOID
AtalkNdisSendTokRingTestRespComplete(
IN NDIS_STATUS Status,
IN PBUFFER_DESC pBufDesc,
IN PSEND_COMPL_INFO pInfo
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
// Free up the buffer descriptor
ASSERT((pBufDesc != NULL) && (pBufDesc->bd_Next != NULL));
ASSERT(pBufDesc->bd_Flags & BD_CHAR_BUFFER);
AtalkFreeBuffDesc(pBufDesc->bd_Next);
AtalkNdisFreeBuf(pBufDesc);
}
NDIS_STATUS
AtalkReceiveIndication(
IN NDIS_HANDLE BindingCtx,
IN NDIS_HANDLE ReceiveCtx,
IN PVOID HdrBuf,
IN UINT HdrBufSize,
IN PVOID LkBuf,
IN UINT LkBufSize,
IN UINT PktSize
)
/*++
Routine Description:
This routine is called by NDIS to indicate a receive
Arguments:
BindingCtx- Pointer to a port descriptor for this port
ReceiveCtx- To be used in a transfer data if necessary
LkBuf- buffer with lookahead data
LkBufSize- Size of the above buffer
PktSize- Size of whole packet
Return Value:
STATUS_SUCCESS- Packet accepted
STATUS_NOT_RECOGNIZED- Not our packet
Other
--*/
{
PPORT_DESCRIPTOR pPortDesc = (PPORT_DESCRIPTOR)BindingCtx;
PNDIS_PACKET ndisPkt;
PBUFFER_HDR pBufferHdr = NULL;
PPROTOCOL_RESD protocolResd; // Protocolresd field in ndisPkt
UINT actualPktSize; // Size of data to copy
UINT bytesTransferred; // Number of bytes transferred in XferData
BOOLEAN result;
UINT xferOffset;
PBYTE lkBufOrig = (PBYTE)LkBuf;
ATALK_ERROR error = ATALK_NO_ERROR;
BOOLEAN shortDdpHdr = FALSE;
BYTE indicate = 0,
subType = 0;
NDIS_MEDIUM Media;
PBYTE packet = NULL; // Where we will copy the packet
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
LOGICAL_PROTOCOL protocol = UNKNOWN_PROTOCOL;
PARAPCONN pArapConn;
PATCPCONN pAtcpConn;
ATALK_NODEADDR ClientNode;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
TimeS = KeQueryPerformanceCounter(NULL);
#endif
do
{
if ((pPortDesc->pd_Flags & (PD_ACTIVE | PD_CLOSING)) != PD_ACTIVE)
{
// If we are not active, return!
ndisStatus = ATALK_PORT_CLOSING;
break;
}
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
Media = pPortDesc->pd_NdisPortType;
// Reduce 802.2 code, avoid making it a routine. First 802.2.
switch (Media)
{
case NdisMedium802_3:
case NdisMediumFddi:
case NdisMedium802_5:
ATALK_VERIFY8022_HDR((PBYTE)LkBuf, LkBufSize, protocol, result);
if (!result)
{
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
if (LkBufSize < IEEE8022_CONTROL_OFFSET+1)
{
ASSERT(0);
break;
}
if (Media == NdisMedium802_5)
{
// BUG #16002
// On tokenring the macs also send out a Unnumbered format
// TEST frame to which we need to respond. Check for that
// here.
if ((((PBYTE)LkBuf)[IEEE8022_DSAP_OFFSET] == SNAP_SAP) &&
(((PBYTE)LkBuf)[IEEE8022_SSAP_OFFSET] == SNAP_SAP) &&
(((PBYTE)LkBuf)[IEEE8022_CONTROL_OFFSET] == UNNUMBERED_FORMAT))
{
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_INFO,
("atalkNdisAcceptTlapPacket: LLC TEST FRAME RECD!\n"));
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// Due to the aarp lookahead size setting, we are guaranteed
// the entire frame is contained in the lookahead data.
AtalkNdisSendTokRingTestResp(pPortDesc,
(PBYTE)HdrBuf,
HdrBufSize,
(PBYTE)LkBuf,
LkBufSize,
PktSize);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
}
break;
}
if (protocol == APPLETALK_PROTOCOL)
{
// Do we at least have a 802.2 and DDP header in the indicated packet?
if ((PktSize < (IEEE8022_HDR_LEN + LDDP_HDR_LEN)) ||
(PktSize > (IEEE8022_HDR_LEN + MAX_LDDP_PKT_SIZE)))
{
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
break;
}
}
else // AARP
{
UINT routeInfoLen = 0; // length of routing info if present (802.5)
switch (Media)
{
case NdisMediumFddi:
// For fddi, there could be padding included in the packet. Shrink
// the length if so. Note header length is not included in packetlength.
//
if (PktSize >= (MIN_FDDI_PKT_LEN - FDDI_LINKHDR_LEN))
{
PktSize = (IEEE8022_HDR_LEN + AARP_MIN_DATA_SIZE);
}
break;
case NdisMedium802_5:
// Remember- routing info is in the header buffer
if (((PBYTE)HdrBuf)[TLAP_SRC_OFFSET] & TLAP_SRC_ROUTING_MASK)
{
routeInfoLen = (((PBYTE)HdrBuf)[TLAP_ROUTE_INFO_OFFSET] &
TLAP_ROUTE_INFO_SIZE_MASK);
ASSERTMSG("RouteInfo incorrect!\n",
((routeInfoLen > 0) && (routeInfoLen <= TLAP_MAX_ROUTING_BYTES)));
// Routing info must be of reasonable size, and not odd.
if ((routeInfoLen & 1) ||
(routeInfoLen > TLAP_MAX_ROUTING_BYTES))
{
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
break;
}
}
// Fall through to 802.3 case
case NdisMedium802_3:
if (PktSize >= (ELAP_MIN_PKT_LEN - ELAP_LINKHDR_LEN))
{
PktSize = (IEEE8022_HDR_LEN + AARP_MIN_DATA_SIZE);
}
}
if (((PktSize - IEEE8022_HDR_LEN) > AARP_MAX_DATA_SIZE) ||
((PktSize - IEEE8022_HDR_LEN) < AARP_MIN_DATA_SIZE))
{
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
break;
}
}
actualPktSize = (PktSize + HdrBufSize - IEEE8022_HDR_LEN);
(PBYTE)LkBuf += IEEE8022_HDR_LEN;
xferOffset = IEEE8022_HDR_LEN;
break;
case NdisMediumLocalTalk:
// No AARP/802.2 header on localtalk
protocol = APPLETALK_PROTOCOL;
// we should have enough bytes to have at least the short-header
if (LkBufSize < SDDP_PROTO_TYPE_OFFSET+1)
{
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
ASSERT(0);
break;
}
if (((PBYTE)HdrBuf)[ALAP_TYPE_OFFSET] == ALAP_SDDP_HDR_TYPE)
{
shortDdpHdr = TRUE;
}
else if (((PBYTE)HdrBuf)[ALAP_TYPE_OFFSET] != ALAP_LDDP_HDR_TYPE)
{
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
break;
}
actualPktSize = PktSize + HdrBufSize;
xferOffset = 0;
break;
case NdisMediumWan:
if (pPortDesc->pd_Flags & PD_RAS_PORT)
{
//
// 1st byte 0x01 tells us it's a PPP connection
//
if ((((PBYTE)HdrBuf)[0] == PPP_ID_BYTE1) &&
(((PBYTE)HdrBuf)[1] == PPP_ID_BYTE2))
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
DBGDUMPBYTES("Packet from PPP client:", (PBYTE)LkBuf,LkBufSize,4);
if (AtalkReferenceDefaultPort())
{
AtalkDdpPacketIn(AtalkDefaultPort, // came on which port
NULL, // Link Hdr
(PBYTE)LkBuf, // packet
(USHORT)LkBufSize, // how big is the pkt
TRUE); // did this come on WAN?
AtalkPortDereference(AtalkDefaultPort);
}
}
//
// this is ARAP connection: lot of stuff to be done before pkt
// can be given to the right destination...
//
else
{
ASSERT ((((PBYTE)HdrBuf)[0] == ARAP_ID_BYTE1) &&
(((PBYTE)HdrBuf)[1] == ARAP_ID_BYTE2));
*((ULONG UNALIGNED *)(&pArapConn)) =
*((ULONG UNALIGNED *)(&((PBYTE)HdrBuf)[2]));
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
//
// NDISWAN guarantees that all the data is in the LookAhead buffer
//
ArapRcvIndication( pArapConn,
LkBuf,
LkBufSize );
}
}
break;
default:
// Should never happen!
DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_FATAL,
("AtalkReceiveIndication: Unknown media\n"));
ASSERT(0);
ndisStatus = NDIS_STATUS_NOT_RECOGNIZED;
break;
}
// we have already taken care of the Wan case: quit here
if (Media == NdisMediumWan)
{
break;
}
//
// if pkt not interesting, quit., if this is ras adapter, quit
//
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
break;
}
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumPacketsIn,
&AtalkStatsLock.SpinLock);
INTERLOCKED_ADD_STATISTICS(&pPortDesc->pd_PortStats.prtst_DataIn,
(LONG)actualPktSize,
&AtalkStatsLock.SpinLock);
ASSERT ((protocol == APPLETALK_PROTOCOL) || (protocol == AARP_PROTOCOL));
// At this point, the IEEE802.2 header has been skipped in the lookahead
// buffer.
// Packet is to be accepted! Get an appropriate buffer and set the
// fields.
switch (protocol)
{
case APPLETALK_PROTOCOL:
// We either need to receive this packet on the default port, or
// we must be a router.
if ((pPortDesc == AtalkDefaultPort) || AtalkRouter)
{
if (shortDdpHdr)
{
// Check to see if we can indicate this to ATP/ADSP.
if ((((PBYTE)LkBuf)[SDDP_PROTO_TYPE_OFFSET] == DDPPROTO_ATP) &&
((USHORT)(LkBufSize - xferOffset) >= (SDDP_HDR_LEN + ATP_HEADER_SIZE)))
{
indicate = INDICATE_ATP;
}
}
else
{
// Check to see if we can indicate this to ATP/ADSP.
if ((((PBYTE)LkBuf)[LDDP_PROTO_TYPE_OFFSET] == DDPPROTO_ATP) &&
((USHORT)(LkBufSize - xferOffset) >= (LDDP_HDR_LEN + ATP_HEADER_SIZE)))
{
indicate = INDICATE_ATP;
}
}
}
// First check for optimizing ATP/ADSP packets.
if (indicate == INDICATE_ATP)
{
error = AtalkIndAtpPkt(pPortDesc,
(PBYTE)LkBuf,
(USHORT)(PktSize - xferOffset),
&xferOffset, // IN/OUT parameter
HdrBuf,
shortDdpHdr,
&subType,
&packet,
&ndisPkt);
if (ATALK_SUCCESS(error))
{
break;
}
else if (error == ATALK_INVALID_PKT)
{
// This indicates that the indication code has figured out that
// the packet is bad.
break;
}
else
{
// This is the case where the indication code cannot figure out
// if this packet qualifies.
indicate = 0;
error = ATALK_NO_ERROR;
}
}
if (actualPktSize > (sizeof(DDP_SMBUFFER) - sizeof(BUFFER_HDR)))
{
pBufferHdr = (PBUFFER_HDR)AtalkBPAllocBlock(BLKID_DDPLG);
}
else
{
pBufferHdr = (PBUFFER_HDR)AtalkBPAllocBlock(BLKID_DDPSM);
}
break;
case AARP_PROTOCOL:
pBufferHdr = (PBUFFER_HDR)AtalkBPAllocBlock(BLKID_AARP);
break;
default:
KeBugCheck(0);
break;
}
if (!ATALK_SUCCESS(error) || ((pBufferHdr == NULL) && (indicate == 0)))
{
#if DBG
UINT i;
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_ERR,
("AtalkReceiveIndication: Dropping packet (2) %ld\n", error));
for (i = 0; i < HdrBufSize; i++)
DBGPRINTSKIPHDR(DBG_COMP_NDISRECV, DBG_LEVEL_ERR,
("%02x ", ((PUCHAR)HdrBuf)[i]));
for (i = 0; i < LkBufSize; i++)
DBGPRINTSKIPHDR(DBG_COMP_NDISRECV, DBG_LEVEL_ERR,
("%02x ", ((PUCHAR)LkBuf)[i]));
#endif
// No logging in this critical path.
// LOG_ERRORONPORT(pPortDesc,
// EVENT_ATALK_AARPPACKET,
// actualPktSize,
// HdrBuf,
// HdrBufSize);
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (error != ATALK_DUP_PKT)
{
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumPktDropped,
&AtalkStatsLock.SpinLock);
}
ndisStatus = NDIS_STATUS_RESOURCES;
break;
}
if (indicate == 0)
{
packet = (PBYTE)pBufferHdr + sizeof(BUFFER_HDR);
// Get a pointer to the NDIS packet descriptor from the buffer header.
ndisPkt = pBufferHdr->bh_NdisPkt;
}
protocolResd = (PPROTOCOL_RESD)(ndisPkt->ProtocolReserved);
// Store the information needed in the packet descriptor
protocolResd->Receive.pr_Port = pPortDesc;
protocolResd->Receive.pr_Protocol = protocol;
protocolResd->Receive.pr_Processed = FALSE;
// Queue up the packet in the receive queue on this port
// Then, go ahead with the transfer data etc. For Atp response
// case when SubType == ATP_USER_BUFX, we do not want any
// Recv. completion processing, do not queue. In this case
// TransferData completion frees up the Ndis resources.
if ((indicate != INDICATE_ATP) ||
(protocolResd->Receive.pr_OptimizeSubType != ATP_USER_BUFX))
{
ATALK_RECV_INDICATION_COPY(pPortDesc,
protocolResd->Receive.pr_LinkHdr,
(PBYTE)HdrBuf,
HdrBufSize);
InsertTailList(&pPortDesc->pd_ReceiveQueue,
&protocolResd->Receive.pr_Linkage);
}
else
{
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_ERR,
("AtalkReceiveIndication: Skipping link hdr !!!\n"));
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_CurReceiveQueue,
&AtalkStatsLock.SpinLock);
#endif
// Adjust for the link header size. Set size in protocol reserved. We want
// to avoid changing the size described by the NDIS buffer descriptor.
if (indicate == 0)
{
actualPktSize -= HdrBufSize;
protocolResd->Receive.pr_DataLength = (USHORT)actualPktSize;
}
else
{
actualPktSize = protocolResd->Receive.pr_DataLength;
}
ASSERT(ndisStatus == NDIS_STATUS_SUCCESS);
if ((PktSize <= LkBufSize) &&
((indicate != INDICATE_ATP) || (subType != ATP_RESPONSE)))
{
// LkBuf has already been advanced to skip the ieee 802.2 header.
// We may need to skip more. Use the original lkbuf and xfer offset.
ATALK_RECV_INDICATION_COPY(pPortDesc,
packet,
(PBYTE)lkBufOrig + xferOffset,
actualPktSize);
bytesTransferred = actualPktSize;
}
else
{
// Skip 802.2 header (both AARP and Appletalk), localtalk doesnt have one!
if (actualPktSize > 0)
{
NdisTransferData(&ndisStatus,
pPortDesc->pd_NdisBindingHandle,
ReceiveCtx,
xferOffset,
actualPktSize,
ndisPkt,
&bytesTransferred);
ASSERT(bytesTransferred == actualPktSize);
}
}
if (ndisStatus == NDIS_STATUS_PENDING)
{
ndisStatus = NDIS_STATUS_SUCCESS;
}
else
{
// Transfer data completed, call the transfer data completion
// routine to do rest of the work. If an error happened, ReceiveCompletion
// will drop the packet.
protocolResd->Receive.pr_ReceiveStatus = ndisStatus;
protocolResd->Receive.pr_Processed = TRUE;
// In case of intermediate Atp response, the packet is not actually linked
// into the receive queue, just free it.
if ((protocolResd->Receive.pr_OptimizeType == INDICATE_ATP) &&
(protocolResd->Receive.pr_OptimizeSubType == ATP_USER_BUFX))
{
PNDIS_BUFFER ndisBuffer;
// Free NDIS buffers if any are present.
NdisUnchainBufferAtFront(ndisPkt, &ndisBuffer);
if (ndisBuffer != NULL)
{
AtalkNdisFreeBuffer(ndisBuffer);
}
NdisDprFreePacket(ndisPkt);
}
}
} while (FALSE);
#ifdef PROFILING
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(&pPortDesc->pd_PortStats.prtst_RcvIndProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC( &pPortDesc->pd_PortStats.prtst_RcvIndCount,
&AtalkStatsLock.SpinLock);
#endif
return ndisStatus;
}
VOID
AtalkTransferDataComplete(
IN NDIS_HANDLE BindingCtx,
IN PNDIS_PACKET NdisPkt,
IN NDIS_STATUS Status,
IN UINT BytesTransferred
)
/*++
Routine Description:
This routine is called by NDIS to indicate completion of a TransferData
Arguments:
BindingCtx- Pointer to a port descriptor for this port
NdisPkt- Ndis packet into which data was transferred
Status- Status of request
bytesTransferred- Actual number of bytes transferred
Return Value:
None
--*/
{
PPROTOCOL_RESD protocolResd;
PNDIS_BUFFER ndisBuffer;
protocolResd = (PPROTOCOL_RESD)(NdisPkt->ProtocolReserved);
protocolResd->Receive.pr_ReceiveStatus = Status;
protocolResd->Receive.pr_Processed = TRUE;
// In case of intermediate Atp response, the packet is not actually linked
// into the receive queue, just free it.
if (protocolResd->Receive.pr_OptimizeSubType == ATP_USER_BUFX)
{
// Free NDIS buffers if any are present.
NdisUnchainBufferAtFront(NdisPkt, &ndisBuffer);
if (ndisBuffer != NULL)
{
AtalkNdisFreeBuffer(ndisBuffer);
}
NdisDprFreePacket(NdisPkt);
}
}
VOID
AtalkReceiveComplete(
IN NDIS_HANDLE BindingCtx
)
/*++
Routine Description:
We experimented with queueing up a work item for receive completion. It really
KILLED performance with multiple clients as apparently the receive completion
kept getting interrupted with receive indications. AS the optimization was
put in for slow cards like the ELNKII which do not have adequate buffering,
we decided to take it out. The retry values (or timeout trimming) should be
enough for the slow cards. They will inevitably drop packets.
Arguments:
Return Value:
--*/
{
PPORT_DESCRIPTOR pPortDesc = (PPORT_DESCRIPTOR)BindingCtx;
PPROTOCOL_RESD protocolResd;
PNDIS_PACKET ndisPkt;
PNDIS_BUFFER ndisBuffer;
PBUFFER_HDR pBufHdr;
NDIS_MEDIUM Media;
PLIST_ENTRY p;
PBYTE packet;
LOGICAL_PROTOCOL protocol;
UINT packetLength;
BOOLEAN fDerefDefPort=FALSE;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
#endif
if (pPortDesc->pd_Flags & PD_RAS_PORT)
{
// give ARAP guys a chance
ArapRcvComplete();
if (!AtalkReferenceDefaultPort())
{
return;
}
fDerefDefPort = TRUE;
// give PPP guys a chance
pPortDesc = AtalkDefaultPort;
}
// Get the stuff off the receive queue for the port and send it up. Do not
// enter if the queue is initially empty.
if (IsListEmpty(&pPortDesc->pd_ReceiveQueue))
{
if (fDerefDefPort)
{
AtalkPortDereference(AtalkDefaultPort);
}
return;
}
#ifdef PROFILING
TimeS = KeQueryPerformanceCounter(NULL);
#endif
while (TRUE)
{
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
p = pPortDesc->pd_ReceiveQueue.Flink;
if (p == &pPortDesc->pd_ReceiveQueue)
{
// Queue is empty
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
break;
}
ndisPkt = CONTAINING_RECORD(p, NDIS_PACKET, ProtocolReserved[0]);
protocolResd = (PPROTOCOL_RESD)(ndisPkt->ProtocolReserved);
// Check if the queued receive is done processing. Since we are looping
// through the queue and since receive complete only checks if the first
// is done, we need this check here for subsequent queued up receives.
if (!protocolResd->Receive.pr_Processed)
{
// Queue is empty
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
break;
}
// Dequeue and indicate this packet to the ddp/atp layer
p = RemoveHeadList(&pPortDesc->pd_ReceiveQueue);
pBufHdr = protocolResd->Receive.pr_BufHdr;
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
#ifdef PROFILING
INTERLOCKED_DECREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_CurReceiveQueue,
&AtalkStatsLock.SpinLock);
#endif
Media = pPortDesc->pd_NdisPortType;
protocol = protocolResd->Receive.pr_Protocol;
if ((protocol == APPLETALK_PROTOCOL) &&
(protocolResd->Receive.pr_OptimizeType == INDICATE_ATP))
{
protocolResd->Receive.pr_OptimizeType = 0;
ASSERT(protocolResd->Receive.pr_OptimizeSubType != ATP_USER_BUFX);
// Check the receive status- accept only if ok
if (protocolResd->Receive.pr_ReceiveStatus == NDIS_STATUS_SUCCESS)
{
// Glean information. Check for route info if tokenring network.
if (Media != NdisMediumLocalTalk)
{
AtalkAarpOptGleanInfo(pPortDesc,
protocolResd->Receive.pr_LinkHdr,
&protocolResd->Receive.pr_SrcAddr,
&protocolResd->Receive.pr_DestAddr,
protocolResd->Receive.pr_OffCablePkt);
}
// Different calls for response & non-response packets.
if (protocolResd->Receive.pr_OptimizeSubType == ATP_USER_BUF)
{
AtalkAtpPacketIn(AtalkDefaultPort,
protocolResd->Receive.pr_AtpAddrObj->atpao_DdpAddr,
protocolResd->Receive.pr_AtpHdr,
(USHORT)(protocolResd->Receive.pr_DataLength + 8),
&protocolResd->Receive.pr_SrcAddr,
&protocolResd->Receive.pr_DestAddr,
ATALK_NO_ERROR,
DDPPROTO_ATP,
protocolResd->Receive.pr_AtpAddrObj,
TRUE,
protocolResd->Receive.pr_OptimizeCtx);
}
else
{
ASSERT (protocolResd->Receive.pr_OptimizeSubType == ATP_ALLOC_BUF);
packet = (PBYTE)pBufHdr + sizeof(BUFFER_HDR);
ASSERT(packet != NULL);
AtalkAtpPacketIn(AtalkDefaultPort,
protocolResd->Receive.pr_AtpAddrObj->atpao_DdpAddr,
packet,
(USHORT)protocolResd->Receive.pr_DataLength,
&protocolResd->Receive.pr_SrcAddr,
&protocolResd->Receive.pr_DestAddr,
ATALK_NO_ERROR,
DDPPROTO_ATP,
protocolResd->Receive.pr_AtpAddrObj,
TRUE,
protocolResd->Receive.pr_OptimizeCtx);
}
}
// Different calls for user buffer/allocated packets
if (protocolResd->Receive.pr_OptimizeSubType == ATP_USER_BUF)
{
// Free NDIS buffers if any are present.
NdisUnchainBufferAtFront(ndisPkt, &ndisBuffer);
if (ndisBuffer != NULL)
{
AtalkNdisFreeBuffer(ndisBuffer);
}
NdisDprFreePacket(ndisPkt);
}
else
{
AtalkBPFreeBlock(packet-sizeof(BUFFER_HDR));
}
continue;
}
// IMPORTANT:
// We know that the buffer is virtually contiguous since we allocated
// it. And we also know that only one buffer is allocated. So we use
// that knowledge to get the actual address and pass that onto the
// higher level routines.
// !!!!
// Although, the allocated buffer contains the link header tagged on at
// the end, we do not have the packet descriptor describing that. As
// far as we are concerned here, that tagged entity does not exist and
// is independently pointed to by protocolResd->pr_LinkHdr.
// !!!!
packet = (PBYTE)pBufHdr + sizeof(BUFFER_HDR);
ASSERT(packet != NULL);
packetLength = protocolResd->Receive.pr_DataLength;
// Check the receive status- accept only if ok
if (protocolResd->Receive.pr_ReceiveStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_ERR,
("AtalkReceiveComplete: ReceiveStatus FAILURE %lx!\n",
protocolResd->Receive.pr_ReceiveStatus));
AtalkBPFreeBlock(packet-sizeof(BUFFER_HDR));
continue;
}
// The packet descriptor is now associate with the buffer, and we cant
// release the buffer (and hence the descriptor) until after we indicate to
// the higher levels
switch (Media)
{
case NdisMedium802_3 :
case NdisMediumFddi :
case NdisMedium802_5 :
case NdisMediumLocalTalk :
if (protocol == APPLETALK_PROTOCOL)
{
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_INFO,
("AtalkReceiveComplete: Indicating DDP Ethernet\n"));
AtalkDdpPacketIn(pPortDesc,
protocolResd->Receive.pr_LinkHdr,
packet,
(USHORT)packetLength,
FALSE);
}
else
{
// AARP Packet
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_INFO,
("AtalkReceiveComplete: Indicating AARP Ethernet\n"));
ASSERT(Media != NdisMediumLocalTalk);
AtalkAarpPacketIn(pPortDesc,
protocolResd->Receive.pr_LinkHdr,
packet,
(USHORT)packetLength);
}
break;
default:
KeBugCheck(0);
break;
}
// !!!!
// We dont have to free the link header. This follows the packet
// buffer (and was allocated along with it) and will be freed when
// the packet is freed.
AtalkBPFreeBlock(packet-sizeof(BUFFER_HDR));
}
if (fDerefDefPort)
{
AtalkPortDereference(AtalkDefaultPort);
}
#ifdef PROFILING
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(
&pPortDesc->pd_PortStats.prtst_RcvCompProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC( &pPortDesc->pd_PortStats.prtst_RcvCompCount,
&AtalkStatsLock.SpinLock);
#endif
}
VOID
AtalkSendComplete(
IN NDIS_HANDLE ProtoBindCtx,
IN PNDIS_PACKET NdisPkt,
IN NDIS_STATUS NdisStatus
)
/*++
Routine Description:
Arguments:
ProtoBindCtx- Binding associated with mac
NdisPkt- Packet which was sent
NdisStatus- Final status of send
Return Value:
None
--*/
{
PPROTOCOL_RESD pProtocolResd;
PNDIS_BUFFER pNdisBuffer=NULL, pNdisFirstBuffer=NULL;
PPORT_DESCRIPTOR pPortDesc;
PBUFFER_DESC pBufferDesc;
SEND_COMPLETION pSendComp;
SEND_COMPL_INFO sendInfo;
// Call the completion routine, we don't care about status now
pProtocolResd = (PPROTOCOL_RESD)(NdisPkt->ProtocolReserved);
ASSERT(pProtocolResd != NULL);
pPortDesc = pProtocolResd->Send.pr_Port;
sendInfo = pProtocolResd->Send.pr_SendInfo;
pBufferDesc = pProtocolResd->Send.pr_BufferDesc;
pSendComp = pProtocolResd->Send.pr_SendCompletion;
// We free up all the ndis buffer descriptors except the first one.
// NOTE: The presence of a second buffer descriptor indicates that more
// than one NdisBuffer is present. But not necessarily just two. If
// the client had passed in a MDL chain, we would create a corresponding
// NDIS buffer descriptor chain. Therefore, remove the first, free up
// all remaining ones, then queue back the first.
NdisUnchainBufferAtFront(NdisPkt, &pNdisFirstBuffer);
if (pProtocolResd->Send.pr_BufferDesc->bd_Next != NULL)
{
while (TRUE)
{
NdisUnchainBufferAtBack(NdisPkt,
&pNdisBuffer);
if (pNdisBuffer == NULL)
{
break;
}
// Free up the ndis buffer descriptor.
AtalkNdisFreeBuffer(pNdisBuffer);
}
}
// Reintialize the packet descriptor.
NdisReinitializePacket(NdisPkt);
// Put first buffer back in.
if (pNdisFirstBuffer != NULL)
{
NdisChainBufferAtFront(NdisPkt, pNdisFirstBuffer);
}
// Call the completion routine for the transmit. This invalidates NdisPkt.
if (pSendComp)
{
(*pSendComp)(NdisStatus, pBufferDesc, &sendInfo);
}
// Dereference the port
ASSERT(pPortDesc != NULL);
#ifdef PROFILING
INTERLOCKED_DECREMENT_LONG(
&pPortDesc->pd_PortStats.prtst_CurSendsOutstanding,
&AtalkStatsLock.SpinLock);
#endif
}
VOID
AtalkBindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
)
{
// are we unloading? if so, just return
if (AtalkBindnUnloadStates & ATALK_UNLOADING)
{
DBGPRINT(DBG_COMP_NDISRECV, DBG_LEVEL_ERR,
("AtalkBindAdapter: nothing to do: driver unloading\n"));
return;
}
AtalkBindnUnloadStates |= ATALK_BINDING;
AtalkLockInitIfNecessary();
*Status = AtalkInitAdapter(DeviceName, NULL);
ASSERT(*Status != NDIS_STATUS_PENDING);
AtalkUnlockInitIfNecessary();
AtalkBindnUnloadStates &= ~ATALK_BINDING;
}
VOID
AtalkUnbindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE UnbindContext
)
{
PPORT_DESCRIPTOR pPortDesc = (PPORT_DESCRIPTOR)ProtocolBindingContext;
DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR,
("AtalkUnbindAdapter on %lx\n",ProtocolBindingContext));
ASSERT( VALID_PORT(pPortDesc) );
AtalkLockInitIfNecessary();
// First and foremost: tell guys above so they can cleanup
if ((pPortDesc->pd_Flags & PD_DEF_PORT) ||
(pPortDesc->pd_Flags & PD_RAS_PORT))
{
if (pPortDesc->pd_Flags & PD_DEF_PORT)
{
ASSERT(pPortDesc == AtalkDefaultPort);
if (TdiAddressChangeRegHandle)
{
TdiDeregisterNetAddress(TdiAddressChangeRegHandle);
TdiAddressChangeRegHandle = NULL;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkUnbindAdapter: TdiDeregisterNetAddress on %Z done\n",
&pPortDesc->pd_AdapterName));
}
// this will tell AFP
if (TdiRegistrationHandle)
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkUnbindAdapter: default adapter unbound, telling AFP, RAS\n"));
TdiDeregisterDeviceObject(TdiRegistrationHandle);
TdiRegistrationHandle = NULL;
}
}
else
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkUnbindAdapter: RAS adapter unbound! telling AFP, RAS\n"));
}
// this will take care of informing ARAP and PPP engine above
AtalkPnPInformRas(FALSE);
}
*Status = AtalkDeinitAdapter(pPortDesc);
ASSERT(*Status != NDIS_STATUS_PENDING);
AtalkUnlockInitIfNecessary();
}