windows-nt/Source/XPSP1/NT/net/nwlink/spx/spxbind.c
2020-09-26 16:20:57 +08:00

687 lines
18 KiB
C

/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
spxbind.c
Abstract:
This module contains the code to bind to the IPX transport, as well as the
indication routines for the IPX transport not including the send/recv ones.
Author:
Stefan Solomon (stefans) Original Version
Nikhil Kamkolkar (nikhilk) 11-November-1993
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
// Define module number for event logging entries
#define FILENUM SPXBIND
extern IPX_INTERNAL_PNP_COMPLETE IpxPnPComplete;
VOID
SpxStatus (
IN USHORT NicId,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuffer,
IN UINT StatusBufferLength);
VOID
SpxFindRouteComplete (
IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest,
IN BOOLEAN FoundRoute);
VOID
SpxScheduleRoute (
IN PIPX_ROUTE_ENTRY RouteEntry);
VOID
SpxLineDown (
IN USHORT NicId,
IN ULONG_PTR FwdAdapterContext);
VOID
SpxLineUp (
IN USHORT NicId,
IN PIPX_LINE_INFO LineInfo,
IN NDIS_MEDIUM DeviceType,
IN PVOID ConfigurationData);
VOID
SpxFindRouteComplete (
IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest,
IN BOOLEAN FoundRoute);
#if defined(_PNP_POWER)
NTSTATUS
SpxPnPNotification(
IN IPX_PNP_OPCODE OpCode,
IN PVOID PnPData
);
#endif _PNP_POWER
VOID
SpxPnPCompletionHandler(
PNET_PNP_EVENT netevent,
NTSTATUS status
);
#if defined(_PNP_POWER)
//
// globals and externs
//
extern CTELock spxTimerLock;
extern LARGE_INTEGER spxTimerTick;
extern KTIMER spxTimer;
extern KDPC spxTimerDpc;
extern BOOLEAN spxTimerStopped;
#endif _PNP_POWER
NTSTATUS
SpxInitBindToIpx(
VOID
)
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
OBJECT_ATTRIBUTES objectAttr;
PIPX_INTERNAL_BIND_INPUT pBindInput;
PIPX_INTERNAL_BIND_OUTPUT pBindOutput;
InitializeObjectAttributes(
&objectAttr,
&IpxDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtCreateFile(
&IpxHandle,
SYNCHRONIZE | GENERIC_READ,
&objectAttr,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0L);
if (!NT_SUCCESS(status)) {
return status;
}
if ((pBindInput = CTEAllocMem(sizeof(IPX_INTERNAL_BIND_INPUT))) == NULL) {
NtClose(IpxHandle);
return(STATUS_INSUFFICIENT_RESOURCES);
}
// Fill in our bind data
#if defined(_PNP_POWER)
pBindInput->Version = ISN_VERSION;
#else
pBindInput->Version = 1;
#endif _PNP_POWER
pBindInput->Identifier = IDENTIFIER_SPX;
pBindInput->BroadcastEnable = FALSE;
pBindInput->LookaheadRequired = IPX_HDRSIZE;
pBindInput->ProtocolOptions = 0;
pBindInput->ReceiveHandler = SpxReceive;
pBindInput->ReceiveCompleteHandler = SpxReceiveComplete;
pBindInput->StatusHandler = SpxStatus;
pBindInput->SendCompleteHandler = SpxSendComplete;
pBindInput->TransferDataCompleteHandler = SpxTransferDataComplete;
pBindInput->FindRouteCompleteHandler = SpxFindRouteComplete;
pBindInput->LineUpHandler = SpxLineUp;
pBindInput->LineDownHandler = SpxLineDown;
pBindInput->ScheduleRouteHandler = SpxScheduleRoute;
#if defined(_PNP_POWER)
pBindInput->PnPHandler = SpxPnPNotification;
#endif _PNP_POWER
// First get the length for the output buffer.
status = NtDeviceIoControlFile(
IpxHandle, // HANDLE to File
NULL, // HANDLE to Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IO_STATUS_BLOCK
IOCTL_IPX_INTERNAL_BIND, // IoControlCode
pBindInput, // Input Buffer
sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length
NULL, // Output Buffer
0);
if (status == STATUS_PENDING) {
status = NtWaitForSingleObject(
IpxHandle,
(BOOLEAN)FALSE,
NULL);
}
if (status != STATUS_BUFFER_TOO_SMALL) {
CTEFreeMem(pBindInput);
NtClose(IpxHandle);
return(STATUS_INVALID_PARAMETER);
}
if ((pBindOutput = CTEAllocMem(ioStatusBlock.Information)) == NULL) {
CTEFreeMem(pBindInput);
NtClose(IpxHandle);
return(STATUS_INSUFFICIENT_RESOURCES);
}
// ioStatusBlock.Information is of type ULONG_PTR and is used as
// OutputBufferLength in NtDeviceIoControlFile.
// The length should not exceed ulong or we have to wait until
// NtDeviceIoControlFile changes to get rid of warning.
status = NtDeviceIoControlFile(
IpxHandle, // HANDLE to File
NULL, // HANDLE to Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IO_STATUS_BLOCK
IOCTL_IPX_INTERNAL_BIND, // IoControlCode
pBindInput, // Input Buffer
sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length
pBindOutput, // Output Buffer
(ULONG)(ioStatusBlock.Information));
if (status == STATUS_PENDING) {
status = NtWaitForSingleObject(
IpxHandle,
(BOOLEAN)FALSE,
NULL);
}
if (status == STATUS_SUCCESS) {
// Get all the info from the bind output buffer and save in
// appropriate places.
IpxLineInfo = pBindOutput->LineInfo;
IpxMacHdrNeeded = pBindOutput->MacHeaderNeeded;
IpxInclHdrOffset = pBindOutput->IncludedHeaderOffset;
IpxSendPacket = pBindOutput->SendHandler;
IpxFindRoute = pBindOutput->FindRouteHandler;
IpxQuery = pBindOutput->QueryHandler;
IpxTransferData = pBindOutput->TransferDataHandler;
IpxPnPComplete = pBindOutput->PnPCompleteHandler;
#if !defined(_PNP_POWER)
// Copy over the network node info.
RtlCopyMemory(
SpxDevice->dev_Network,
pBindOutput->Network,
IPX_NET_LEN);
RtlCopyMemory(
SpxDevice->dev_Node,
pBindOutput->Node,
IPX_NODE_LEN);
DBGPRINT(TDI, INFO,
("SpxInitBindToIpx: Ipx Net %lx\n",
*(UNALIGNED ULONG *)SpxDevice->dev_Network));
//
// Find out how many adapters IPX has, if this fails
// just assume one.
//
if ((*IpxQuery)(
IPX_QUERY_MAXIMUM_NIC_ID,
0,
&SpxDevice->dev_Adapters,
sizeof(USHORT),
NULL) != STATUS_SUCCESS) {
SpxDevice->dev_Adapters = 1;
}
#endif !_PNP_POWER
} else {
NtClose(IpxHandle);
status = STATUS_INVALID_PARAMETER;
}
CTEFreeMem(pBindInput);
CTEFreeMem(pBindOutput);
return status;
}
VOID
SpxUnbindFromIpx(
VOID
)
{
NtClose(IpxHandle);
return;
}
VOID
SpxStatus(
IN USHORT NicId,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuffer,
IN UINT StatusBufferLength
)
{
DBGPRINT(RECEIVE, ERR,
("SpxStatus: CALLED WITH %lx\n",
GeneralStatus));
return;
}
VOID
SpxFindRouteComplete (
IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest,
IN BOOLEAN FoundRoute
)
{
CTELockHandle lockHandle;
PSPX_FIND_ROUTE_REQUEST pSpxFrReq = (PSPX_FIND_ROUTE_REQUEST)FindRouteRequest;
PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)pSpxFrReq->fr_Ctx;
// This will be on a connection. Grab the lock, check the state and go from
// there.
if (pSpxConnFile == NULL)
{
// Should this ever happen?
KeBugCheck(0);
return;
}
// Check the state. The called routines release the lock, remove the reference.
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
if (SPX_CONN_CONNECTING(pSpxConnFile))
{
// We are doing an active connect!
SpxConnConnectFindRouteComplete(
pSpxConnFile,
pSpxFrReq,
FoundRoute,
lockHandle);
}
else // For all others call active
{
SpxConnActiveFindRouteComplete(
pSpxConnFile,
pSpxFrReq,
FoundRoute,
lockHandle);
}
// Free the find route request.
SpxFreeMemory(pSpxFrReq);
return;
}
VOID
SpxLineUp (
IN USHORT NicId,
IN PIPX_LINE_INFO LineInfo,
IN NDIS_MEDIUM DeviceType,
IN PVOID ConfigurationData
)
{
// With PnP, our local address is changed when we get PnP
// notification.
#if !defined(_PNP_POWER)
//
// If we get a line up for NicId 0, it means our local
// network number has changed, re-query from IPX.
//
if (NicId == 0) {
TDI_ADDRESS_IPX IpxAddress;
if ((*IpxQuery)(
IPX_QUERY_IPX_ADDRESS,
0,
&IpxAddress,
sizeof(TDI_ADDRESS_IPX),
NULL) == STATUS_SUCCESS) {
RtlCopyMemory(
SpxDevice->dev_Network,
&IpxAddress.NetworkAddress,
IPX_NET_LEN);
DBGPRINT(TDI, INFO,
("SpxLineUp: Ipx Net %lx\n",
*(UNALIGNED ULONG *)SpxDevice->dev_Network));
//
// The node shouldn't change!
//
if (!RtlEqualMemory(
SpxDevice->dev_Node,
IpxAddress.NodeAddress,
IPX_NODE_LEN)) {
DBGPRINT(TDI, ERR,
("SpxLineUp: Node address has changed\n"));
}
}
} else {
DBGPRINT(RECEIVE, ERR,
("SpxLineUp: CALLED WITH %lx\n",
NicId));
}
return;
#endif !_PNP_POWER
}
VOID
SpxLineDown (
IN USHORT NicId,
IN ULONG_PTR FwdAdapterContext
)
{
DBGPRINT(RECEIVE, ERR,
("SpxLineDown: CALLED WITH %lx\n",
NicId));
return;
}
VOID
SpxScheduleRoute (
IN PIPX_ROUTE_ENTRY RouteEntry
)
{
DBGPRINT(RECEIVE, ERR,
("SpxScheduleRoute: CALLED WITH %lx\n",
RouteEntry));
return;
}
#if defined(_PNP_POWER)
NTSTATUS
SpxPnPNotification(
IN IPX_PNP_OPCODE OpCode,
IN PVOID PnPData
)
/*++
Routine Description:
This function receives the notification about PnP events from IPX
Arguments:
OpCode - Type of the PnP event
PnPData - Data associated with this event.
Return Value:
None.
--*/
{
USHORT MaximumNicId = 0;
CTELockHandle LockHandle;
PDEVICE Device = SpxDevice;
UNICODE_STRING UnicodeDeviceName;
NTSTATUS Status = STATUS_SUCCESS;
#ifdef _PNP_POWER_
PNET_PNP_EVENT NetPnpEvent;
#endif // _PNP_POWER_
DBGPRINT(DEVICE, DBG,("Received a pnp notification, opcode %d\n",OpCode));
switch( OpCode ) {
case IPX_PNP_ADD_DEVICE : {
CTELockHandle TimerLockHandle;
IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData;
CTEGetLock (&Device->dev_Lock, &LockHandle);
if ( PnPInfo->FirstORLastDevice ) {
CTEAssert( PnPInfo->NewReservedAddress );
//CTEAssert( Device->dev_State != DEVICE_STATE_OPEN );
*(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress;
RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6);
//
// Start the timer. It is possible that the timer
// was still running or we are still in the timer dpc
// from the previous ADD_DEVICE - DELETE_DEVICE execution
// cycle. But it is ok simply restart this, because
// KeSetTimer implicitly cancels the previous Dpc.
//
CTEGetLock(&spxTimerLock, &TimerLockHandle);
spxTimerStopped = FALSE;
CTEFreeLock(&spxTimerLock, TimerLockHandle);
KeSetTimer(&spxTimer,
spxTimerTick,
&spxTimerDpc);
Device->dev_State = DEVICE_STATE_OPEN;
//CTEAssert( !Device->dev_Adapters );
IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize;
IpxLineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumPacketSize;
// set the provider info
SpxDevice->dev_ProviderInfo.MaximumLookaheadData = IpxLineInfo.MaximumPacketSize;
// Set the window size in statistics
SpxDevice->dev_Stat.MaximumSendWindow =
SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) *
IpxLineInfo.MaximumSendSize;
}else {
IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize;
// Set the window size in statistics
SpxDevice->dev_Stat.MaximumSendWindow =
SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) *
IpxLineInfo.MaximumSendSize;
}
Device->dev_Adapters++;
CTEFreeLock ( &Device->dev_Lock, LockHandle );
//
// Notify the TDI clients about the device creation
//
if ( PnPInfo->FirstORLastDevice ) {
UnicodeDeviceName.Buffer = Device->dev_DeviceName;
UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen;
UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR);
if ( !NT_SUCCESS( TdiRegisterDeviceObject(
&UnicodeDeviceName,
&Device->dev_TdiRegistrationHandle ) )) {
DBGPRINT(TDI,ERR, ("Failed to register Spx Device with TDI\n"));
}
}
break;
}
case IPX_PNP_DELETE_DEVICE : {
IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData;
CTEGetLock (&Device->dev_Lock, &LockHandle);
CTEAssert( Device->dev_Adapters );
Device->dev_Adapters--;
if ( PnPInfo->FirstORLastDevice ) {
Device->dev_State = DEVICE_STATE_LOADED;
Device->dev_Adapters = 0;
}
IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize;
CTEFreeLock ( &Device->dev_Lock, LockHandle );
if ( PnPInfo->FirstORLastDevice ) {
SpxTimerFlushAndStop();
//
// inform tdi clients about the device deletion
//
if ( !NT_SUCCESS( TdiDeregisterDeviceObject(
Device->dev_TdiRegistrationHandle ) )) {
DBGPRINT(TDI,ERR, ("Failed to Deregister Spx Device with TDI\n"));
}
}
//
// TBD: call ExNotifyCallback
//
break;
}
case IPX_PNP_ADDRESS_CHANGE: {
IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData;
CTEGetLock (&Device->dev_Lock, &LockHandle);
CTEAssert( PnPInfo->NewReservedAddress );
*(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress;
RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6);
CTEFreeLock ( &Device->dev_Lock, LockHandle );
break;
}
case IPX_PNP_TRANSLATE_DEVICE:
break;
case IPX_PNP_TRANSLATE_ADDRESS:
break;
#ifdef _PNP_POWER_
case IPX_PNP_QUERY_POWER:
case IPX_PNP_QUERY_REMOVE:
//
// IPX wants to know if we can power off or remove an apapter.
// We also look if there are any open connections before deciding.
// See if we support the NDIS_DEVICE_POWER_STATE
//
NetPnpEvent = (PNET_PNP_EVENT) PnPData;
UnicodeDeviceName.Buffer = Device->dev_DeviceName;
UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen;
UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR);
// First, Via TDI to our Clients.
Status = TdiPnPPowerRequest(
&UnicodeDeviceName,
NetPnpEvent,
NULL,
NULL,
IpxPnPComplete
);
#if 0
if (STATUS_SUCCESS == Status) {
// now if we do not have any open connections,
// we are all set.
Status = STATUS_DEVICE_BUSY;
}
#endif
break;
case IPX_PNP_SET_POWER:
case IPX_PNP_CANCEL_REMOVE:
NetPnpEvent = (PNET_PNP_EVENT) PnPData;
UnicodeDeviceName.Buffer = Device->dev_DeviceName;
UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen;
UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR);
//
// Just call TDI here.
//
Status = TdiPnPPowerRequest(
&UnicodeDeviceName,
NetPnpEvent,
NULL,
NULL,
IpxPnPComplete
);
break;
#endif // _PNP_POWER_
default:
CTEAssert( FALSE );
}
return Status;
} /* SpxPnPNotification */
#endif _PNP_POWER