/*++ 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 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 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) VOID SpxPnPNotification( IN IPX_PNP_OPCODE OpCode, IN PVOID PnPData ); #endif _PNP_POWER #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); } 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 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; #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 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) VOID 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; NTSTATUS Status; PDEVICE Device = SpxDevice; UNICODE_STRING UnicodeDeviceName; 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; default: CTEAssert( FALSE ); } } /* NbiPnPNotification */ #endif _PNP_POWER